ʕ •ᴥ•ʔ ɔ:
0 驱动相关对象
驱动通信:
《Windows内核安全与驱动开发》 2.3、2.5、5、10.3.5、13.3、16.5、16.6
看雪的那两片驱动开发的文章
《内核原理与实现》6
《内核情景分析》 9
《驱动开发详解》 1 4 7
01 驱动对象
一个驱动对象代表了一个驱动程序,或者说一个内核模块。
内核对象结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; ULONG Flags; PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; UNICODE_STRING DriverName; PUNICODE_STRING HardwareDatabase; PFAST_IO_DISPATCH FastIoDispatch; PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; } DRIVER_OBJECT; typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
|
Windbg查看结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| kd> dt _DRIVER_OBJECT -v nt!_DRIVER_OBJECT struct _DRIVER_OBJECT, 15 elements, 0xa8 bytes +0x000 Type : Int2B +0x002 Size : Int2B +0x004 DeviceObject : Ptr32 to struct _DEVICE_OBJECT, 25 elements, 0xb8 bytes +0x008 Flags : Uint4B +0x00c DriverStart : Ptr32 to Void +0x010 DriverSize : Uint4B +0x014 DriverSection : Ptr32 to Void +0x018 DriverExtension : Ptr32 to struct _DRIVER_EXTENSION, 6 elements, 0x1c bytes +0x01c DriverName : struct _UNICODE_STRING, 3 elements, 0x8 bytes +0x024 HardwareDatabase : Ptr32 to struct _UNICODE_STRING, 3 elements, 0x8 bytes +0x028 FastIoDispatch : Ptr32 to struct _FAST_IO_DISPATCH, 28 elements, 0x70 bytes +0x02c DriverInit : Ptr32 to long +0x030 DriverStartIo : Ptr32 to void +0x034 DriverUnload : Ptr32 to void +0x038 MajorFunction : [28] Ptr32 to long
|
02 设备对象
一个驱动程序可以支持多个设备,所以,驱动程序对象中有一个链表记录了它所负责的所有设备对象。每个设备对象都必定有一个为它负责的驱动程序对设备对象的各种操作实际上是由为它负贵的驱动程序中的例程来完成的。
1 内核空间
每个进程的低2G都是独立的,而高2G是共享的。
1.1 练习:验证高2G内存共享
先在驱动程序中定义一个全局变量,获取地址(高2G)。地址为:0xF79F9014。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT driver) { DbgPrint("驱动程序停止运行了\n"); }
ULONG ulNum = 12345678;
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { DbgPrint("变量ulNum的地址为:[%p]:%08d\n",&ulNum,ulNum);
driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
|
在Windbg使用!process 0 0
找任意一个进程的基地址,如DbgView:0x85d9b8d8 ,并使用.process 0x85d9b8d8
切换到该进程空间。
如下图,得到地址0xF79F9014中值为:0x00bc614e(12345678)。
1.2 内核模块
高2G里有许多模块,操作系统内核(如101012分页的ntoskrnl.exe)也在其中。接下来的课后试验我们会编程遍历高2G模块。
内核模块一般是.sys
,也可以是其他格式,他们都遵循PE格式。我们经常说“驱动”,这个名字的来源其实是内核程序入口函数的参数。
1
| NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);
|
PDRIVER_OBJECT
驱动对象,就是驱动这个名字的由来。
driver
:指向当前模块的DRIVER_OBJECT。
reg_path
:将当前模块注册到注册表哪个位置。
硬件种类繁多,不可能做一个兼容所有硬件的内核,所以,微软提供规定的接口格式,让硬件驱动人员安装规定的格式编写“驱动程序” 。
这些驱动程序每一个都是一个模块,称为“内核模块”,都可以加载到内核中,都遵守PE结构。但本质上讲,任意一个.sys
文件与内核文件没有区别。
1.3 DRIVER_OBJECT 结构体
DRIVER_OBJECT为一个结构体,每个内核模块都有一个这样对应的结构体,来描述这个模块在内核中的:位置、大小、名称等等。
结构体DRIVER_OBJECT主要目的就是描述当前的这个模块,或者叫做驱动对象。
模块:驱动对象。
该结构在VS2010的wdm.h
及MSDN中的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; ULONG Flags; PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; UNICODE_STRING DriverName; PUNICODE_STRING HardwareDatabase; PFAST_IO_DISPATCH FastIoDispatch; PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; } DRIVER_OBJECT; typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
|
几个重要的属性:
DriverStart:驱动在内存中的基址。
DriverSize:驱动在内存中的大小。
DriverSection:内核模块链表基址。_LDR_DATA_TABLE_ENTRY
DriverName:驱动(模块)名称。
在Windbg中查看该结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| kd> dt _DRIVER_OBJECT ntdll!_DRIVER_OBJECT +0x000 Type : Int2B +0x002 Size : Int2B +0x004 DeviceObject : Ptr32 _DEVICE_OBJECT +0x008 Flags : Uint4B +0x00c DriverStart : Ptr32 Void +0x010 DriverSize : Uint4B +0x014 DriverSection : Ptr32 Void +0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING +0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH +0x02c DriverInit : Ptr32 long +0x030 DriverStartIo : Ptr32 void +0x034 DriverUnload : Ptr32 void +0x038 MajorFunction : [28] Ptr32 long
|
获取当前驱动入口函数第一个参数PDRIVER_OBJECT的值(是一个结构体指针):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT driver) { DbgPrint("驱动程序停止运行了.\r\n"); }
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { DbgPrint("PDRIVER_OBJECT: %08X,%wZ\n",driver,reg_path); driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
|
DbgPrint输出格式可参见:DbgPrint输出格式
- %Z :ANSI_STRING字符串,注意输出时需要用
&
取字符串的地址。
- %wZ:UNICODE_STRING字符串,注意输出时需要用
&
取字符串的地址。
如图,结构体_DRIVER_OBJECT的起始地址为:0x85EA8BC0。在windbg中查看这个驱动进程的 _DRIVER_OBJECT 结构体:dt _DRIVER_OBJECT 85EA8BC0
指令: dt _DRIVER_OBJECT 地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| kd> dt _DRIVER_OBJECT 85EA8BC0 ntdll!_DRIVER_OBJECT +0x000 Type : 0n4 +0x002 Size : 0n168 +0x004 DeviceObject : (null) +0x008 Flags : 0x12 +0x00c DriverStart : 0xf789e000 Void +0x010 DriverSize : 0x6000 +0x014 DriverSection : 0x863064b0 Void +0x018 DriverExtension : 0x85ea8c68 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING "\Driver\0115_2_输出参数PDRIVER_OBJECT" +0x024 HardwareDatabase : 0x8067e260 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM" +0x028 FastIoDispatch : (null) +0x02c DriverInit : 0xf789f020 long 0115_2_!DriverEntry+0 +0x030 DriverStartIo : (null) +0x034 DriverUnload : 0xf789f000 void 0115_2_!DriverUnload+0 +0x038 MajorFunction : [28] 0x804f5552 long nt!IopInvalidDeviceRequest+0
|
1.4 遍历内核模块
我们可以根据_DRIVER_OBJECT结构的DriverSection(内核模块链表)属性来获取0环所有的内核模块。它实际上是 _LDR_DATA_TABLE_ENTRY
结构类型。
双向循环链表
0环所有模块的_LDR_DATA_TABLE_ENTRY
是一个双向循环链表。结构_LDR_DATA_TABLE_ENTRY
存储当前模块的信息。
DriverSection 属性类型为PVOID
的链表指针,该指针指向的结构为当前模块的_LDR_DATA_TABLE_ENTRY
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| kd> dt _LDR_DATA_TABLE_ENTRY ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY +0x008 InMemoryOrderLinks : _LIST_ENTRY +0x010 InInitializationOrderLinks : _LIST_ENTRY +0x018 DllBase : Ptr32 Void +0x01c EntryPoint : Ptr32 Void +0x020 SizeOfImage : Uint4B +0x024 FullDllName : _UNICODE_STRING +0x02c BaseDllName : _UNICODE_STRING +0x034 Flags : Uint4B +0x038 LoadCount : Uint2B +0x03a TlsIndex : Uint2B +0x03c HashLinks : _LIST_ENTRY +0x03c SectionPointer : Ptr32 Void +0x040 CheckSum : Uint4B +0x044 TimeDateStamp : Uint4B +0x044 LoadedImports : Ptr32 Void +0x048 EntryPointActivationContext : Ptr32 Void +0x04c PatchInformation : Ptr32 Void
|
当前模块的DriverSection = 0x863064b0,解析当前模块的_LDR_DATA_TABLE_ENTRY
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; WORD LoadCount; WORD TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; _ACTIVATION_CONTEXT * EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| kd> dt _LDR_DATA_TABLE_ENTRY 0x863064b0 ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x8055e720 - 0x85d1fa88 ] +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0xffffffff - 0xffffffff ] +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x12 - 0x0 ] +0x018 DllBase : 0xf78a6000 Void +0x01c EntryPoint : 0xf78a7020 Void +0x020 SizeOfImage : 0x6000 +0x024 FullDllName : _UNICODE_STRING "\??\C:\Documents and Settings\A1v1n\桌面\0115_2_输出参数PDRIVER_OBJECT.sys" +0x02c BaseDllName : _UNICODE_STRING "0115_2_输出参数PDRIVER_OBJECT.sys" +0x034 Flags : 0x1104000 +0x038 LoadCount : 1 +0x03a TlsIndex : 0x63 +0x03c HashLinks : _LIST_ENTRY [ 0xffffffff - 0x2e30 ] +0x03c SectionPointer : 0xffffffff Void +0x040 CheckSum : 0x2e30 +0x044 TimeDateStamp : 0xfffffffe +0x044 LoadedImports : 0xfffffffe Void +0x048 EntryPointActivationContext : (null) +0x04c PatchInformation : 0x00310030 Void
|
其中较重要的几个属性:
- InLoadOrderLinks:是个结构体,是个链表。有两个成员:Flink 和 Blink。。
- DllBase:当前模块的基址
- SizeOfImage:当前模块大小
- FullDllName:完整的模块名,以
\??\
格式开始
3环有点区别,在0环中InMemoryOrderLinks 和 InInitializationOrderLinks 是没用的,只需要关注第一个链表 InLoadOrderLinks。_LIST_ENTRY 这个结构体存了两个地址,指向前一个节点和下一个节点:
1 2 3 4 5 6 7 8 9
| typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
kd> dt _LIST_ENTRY ntdll!_LIST_ENTRY +0x000 Flink : Ptr32 _LIST_ENTRY +0x004 Blink : Ptr32 _LIST_ENTRY
|
通过这个 InLoadOrderLinks,我们可以遍历整个高2G的模块了。InLoadOrderLinks.Flink 指向的就是下一个 _LDR_DATA_TABLE_ENTRY。下面给出遍历内核模块链表的代码:
网上说的比较常见的4种方法:
1、通过DriverEntry传入的DriverObject参数的DriverSection成员指向LDR_DATA_TABLE_ENTRY结构,通过遍历这张表得到ntoskrnl的基址和大小
2、ZwQuerySystemInformation大法
3、搜索内存
4、利用KPCR结构
则InLoadOrderLinks = 0x863064b0,执行如下:
1 2 3 4 5
| kd> dt _LIST_ENTRY 0x863064b0 ntdll!_LIST_ENTRY [ 0x8055e720 - 0x85d1fa88 ] +0x000 Flink : 0x8055e720 _LIST_ENTRY [ 0x863fc3b0 - 0x863064b0 ] +0x004 Blink : 0x85d1fa88 _LIST_ENTRY [ 0x863064b0 - 0x85fa9e80 ]
|
可知:
- 0x8055e720:Flink,指向后一个_LDR_DATA_TABLE_ENTRY
- 0x85d1fa88:Blink,指向前一个_LDR_DATA_TABLE_ENTRY
1.5 练习:遍历内核中所有模块
宏CONTAINING_RECORD:返回一个结构体实例的基地址,该结构的类型和结构的一个成员地址已知。
1 2 3 4 5
| PCHAR CONTAINING_RECORD( IN PCHAR Address, IN TYPE Type, IN PCHAR Field );
|
- Address:指向当前结构体实例某成员的指针。
- Type:结构体名称。
- Field:结构体某个成员的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include <ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT driver) { DbgPrint("驱动程序停止运行了\n"); }
typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT driver, IN PUNICODE_STRING reg_path) { PLDR_DATA_TABLE_ENTRY pLDR_Head = NULL; PLDR_DATA_TABLE_ENTRY pLDR_Tail = NULL;
pLDR_Head = (PLDR_DATA_TABLE_ENTRY)driver->DriverSection; pLDR_Tail = pLDR_Head;
do { DbgPrint("DllBase:%08X,SizeOfImage:%08X,FullDllName:%wZ\r\n",pLDR_Tail->DllBase,pLDR_Tail->SizeOfImage,\ &pLDR_Tail->FullDllName); pLDR_Tail = (PLDR_DATA_TABLE_ENTRY)pLDR_Tail->InLoadOrderLinks.Flink; } while(pLDR_Head != pLDR_Tail);
driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
|
注意,Windbg 无法打印带中文的 UNICODE_STRING,建议把项目名取成全英文。
2 未导出函数PspTerminateProcess
PspTerminateProcess是0环的一个未导出函数,用来结束一个进程。函数PspTerminateProcess在ntoskerlpa.exe(2-9-9-12)中,是未导出函数,许多流氓软件无法关闭,就是因为HOOK了这个函数。
从3环到0环的大概调用过程:TerminateProcess --> ntdll.NtTerminateProcess(ntdll.ZwTerminateProcess) --> PsTerminteProcess --> PspTerminateProcess --> PspTerminateThreadByPointer --> KeInitializeApc/KeInsertQueueApc(插入了一个核心态的APC调用,若是用户线程,会再插入用户态的APC调用,最终线程在自己的执行环境中) -> PspExitThread。
在结束一个进程的过程中,可能有很多API会被HOOK。海哥说过,如果他HOOK 3环的API,你就HOOK 0环的API,比他更底层他就防不住你。但是越是底层,对用户的体验越不好,稍微操作不当就会蓝屏。
函数PspTerminateProcess
定义如下:
1 2 3 4
| NTSTATUS NTAPI PspTerminateProcess( PEPROCESS Process, NTSTATUS ExitStatus );
|
其中第一个参数是PEPROCESS的指针,可以根据PID通过PsLookupProcessByProcessId 获得(头文件ntifs.h
),而第二个参数指定退出状态码。可以用ZwQuerySystemInformation通过进程名找PID。在驱动开发引入的<wdm.h>
里提供了MmGetSystemRoutineAddress
函数,根据函数名可以直接获取到已导出的函数地址。
1 2 3 4 5 6 7 8 9 10 11
| NTSTATUS PsLookupProcessByProcessId( IN HANDLE ProcessId, OUT PEPROCESS *Process );
NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );
|
PspTerminateProcess
是未导出函数,找未导出函数的几种思路:
- Windbg + .pdb(得到函数特征码)
- 特征码搜索,先得到函数所在模块的加载基址,然后遍历内存
- A –> B,A是导出函数,调用了B(未导出函数),通过A调用的地址来定位B
- 利用函数想对当前模块的偏移来定位函数:ImageBase+Offset
2.1 Windbg + .pdb
在有内核PDB的情况下,用Windbg可以直接找到该函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| kd> u PspTerminateProcess L20 nt!PspTerminateProcess: 805d3b18 8bff mov edi,edi 805d3b1a 55 push ebp 805d3b1b 8bec mov ebp,esp 805d3b1d 56 push esi 805d3b1e 64a124010000 mov eax,dword ptr fs:[00000124h] 805d3b24 8b7508 mov esi,dword ptr [ebp+8] 805d3b27 3b7044 cmp esi,dword ptr [eax+44h] 805d3b2a 7507 jne nt!PspTerminateProcess+0x1b (805d3b33) 805d3b2c b80d0000c0 mov eax,0C000000Dh 805d3b31 eb5a jmp nt!PspTerminateProcess+0x75 (805d3b8d) 805d3b33 57 push edi 805d3b34 8dbe48020000 lea edi,[esi+248h] 805d3b3a f6470120 test byte ptr [edi+1],20h 805d3b3e 7412 je nt!PspTerminateProcess+0x3a (805d3b52) 805d3b40 8d8674010000 lea eax,[esi+174h] 805d3b46 50 push eax 805d3b47 56 push esi 805d3b48 68ea3a5d80 push offset nt!NtTerminateProcess+0x14c (805d3aea) 805d3b4d e8f2eeffff call nt!PspCatchCriticalBreak (805d2a44) 805d3b52 6a08 push 8 805d3b54 58 pop eax 805d3b55 f00907 lock or dword ptr [edi],eax 805d3b58 6a00 push 0 805d3b5a 56 push esi 805d3b5b e8784f0000 call nt!PsGetNextProcessThread (805d8ad8) 805d3b60 8bf8 mov edi,eax 805d3b62 85ff test edi,edi 805d3b64 741e je nt!PspTerminateProcess+0x6c (805d3b84) 805d3b66 ff750c push dword ptr [ebp+0Ch] 805d3b69 57 push edi 805d3b6a e807fdffff call nt!PspTerminateThreadByPointer (805d3876) 805d3b6f 57 push edi
|
0x805d3b18 就是函数头,然而这个值由于重定位,可能会变的,所以我们就要用其他办法,确保每次都能找到这个函数。
2.2 通过已导出函数
第二种办法是根据已导出函数找未导出函数,在驱动开发引入的<wdm.h>
里提供了MmGetSystemRoutineAddress
函数,根据函数名可以直接获取到已导出的函数地址。
在IDA中任意找一个导出函数的地址:ZwQueryInformationProcess:0x00429934。
在Windbg中获取该函数的地址:0x80501934。
1 2 3 4 5 6 7 8
| kd> u ZwQueryInformationProcess nt!ZwQueryInformationProcess: 80501934 b89a000000 mov eax,9Ah 80501939 8d542404 lea edx,[esp+4] 8050193d 9c pushfd 8050193e 6a08 push 8 80501940 e80c0b0400 call nt!KiSystemService (80542451) 80501945 c21400 ret 14h
|
函数PspTerminateProcess在Windbg的地址:0x805d3b18。则0x805d3b18 - (0x80501934 - 0x00429934) = 0x004FBB18。
在IDA中按G
输入0x004FBB18定位到代码段:(该方法如果ntoskerlpa.exe
重定位了就无法使用了)
在IDA,鼠标定位到该函数,View - Open subviews - Cross reference 查看该函数的交叉引用情况。
可以看到A函数没有一个是导出函数,故该方法不适用。
2.3 模块基址+偏移
虽然模块基址会变,但是函数相对基址的偏移是不变的,通过这个规律也可以找到想要的函数。
PspTerminateProcess 相对内核基址的偏移 = 0x805d3b18 - (0x80501934 - 0x00429934) = 0x004FBB18
只要找到内核基址,加上 0x004FBB18 就是 PspTerminateProcess 的地址。
这种方法我就不贴代码了,因为原理比较简单。
2.4 特征码匹配(最常用)
提取特征码(硬编码)注意几个问题:
- 绕开所有函数都有的指令。如PUSH EBP;MOV EBP,ESP等对应的硬编码
- 然开全局变量等和重定位有关的指令,该模块下次加载地址会变
- 要提取代码中特征明显,功能很小众的代码
- 最好跳着提特征码:8b 75 08 3b 70 44 ?? ?? ?? ?? 68 20 d5 62 80 ?? ?? ?? ?? 68 20
2.5 练习:使用PspTerminateProcess关闭记事本
大致流程如下:
- 先获取函数特征码。
- 确定函数所在的内核模块,通过内核模块遍历获取
ntoskerlpa.exe
模块的基址和大小(使用到PDRIVER_OBJECT
)。
- 到指定模块中,根据特征码按字节比对,确定函数
PspTerminateProcess
的地址。
- 通过函数指针,关闭指定进程。
选取函数一段特征码如下:
1 2 3 4 5 6 7 8 9 10 11
| 805d3b1e 64a124010000 mov eax,dword ptr fs:[00000124h] 805d3b24 8b7508 mov esi,dword ptr [ebp+8] 805d3b27 3b7044 cmp esi,dword ptr [eax+44h] 805d3b2a 7507 jne nt!PspTerminateProcess+0x1b (805d3b33) 805d3b2c b80d0000c0 mov eax,0C000000Dh 805d3b31 eb5a jmp nt!PspTerminateProcess+0x75 (805d3b8d) 805d3b33 57 push edi 805d3b34 8dbe48020000 lea edi,[esi+248h] 805d3b3a f6470120 test byte ptr [edi+1],20h 805d3b3e 7412 je nt!PspTerminateProcess+0x3a (805d3b52) 805d3b40 8d8674010000 lea eax,[esi+174h]
|
特征码搜索,先得到函数所在模块(ntoskerlpa.exe)的加载基址,然后遍历内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| #include <ntddk.h>
ULONG GetKernelMoudleInfo(IN PDRIVER_OBJECT pDriver,OUT PVOID* DllBase,OUT PULONG SizeOfImage); PVOID Find_FeatureCode(PVOID pShellCode,ULONG ulShellCode_Len,PVOID DllBase,ULONG ulSizeOfImage);
VOID DriverUnload(PDRIVER_OBJECT driver) { DbgPrint("驱动程序停止运行了\r\n"); }
typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT driver, IN PUNICODE_STRING reg_path) { ULONG ulShellCode[] = { 0x0124a164, 0x758b0000, 0x44703b08, 0x0db80775, 0xebc00000, 0xbe8d575a, 0x00000248, 0x200147f6, 0x868d1274, 0x00000174 }; ULONG ulRet = 0; PVOID DllBase = NULL; ULONG SizeOfImage = 0; typedef NTSTATUS (*_PspTerminateProcess)(PEPROCESS pEprocess, NTSTATUS ExitCode); _PspTerminateProcess PspTerminateProcess; PEPROCESS pEprocess = NULL;
ulRet = GetKernelMoudleInfo(driver,&DllBase,&SizeOfImage); if(!ulRet) { DbgPrint("驱动程序获取ntoskrnl.exe模块失败.\r\n"); return 0; } PspTerminateProcess = (_PspTerminateProcess)Find_FeatureCode((PVOID)ulShellCode,sizeof(ulShellCode),DllBase,SizeOfImage); if(!PspTerminateProcess) { DbgPrint("PspTerminateProcess特征码搜索失败.\r\n"); return 0; }
PsLookupProcessByProcessId((HANDLE)2796,&pEprocess); PspTerminateProcess(pEprocess, 0); DbgPrint("记事本进程被 PspTerminateProcess 函数关闭了.\n");
driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; } ULONG GetKernelMoudleInfo(IN PDRIVER_OBJECT pDriver,OUT PVOID* DllBase,OUT PULONG SizeOfImage) { PLDR_DATA_TABLE_ENTRY pLDR_Head = NULL; PLDR_DATA_TABLE_ENTRY pLDR_Tail = NULL; UNICODE_STRING UnicodeMoudleName = {0}; RtlInitUnicodeString(&UnicodeMoudleName,L"ntoskrnl.exe");
pLDR_Head = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection; pLDR_Tail = pLDR_Head;
do { if(RtlCompareUnicodeString(&pLDR_Tail->BaseDllName, &UnicodeMoudleName, TRUE) == 0) { *DllBase = pLDR_Tail->DllBase; *SizeOfImage = pLDR_Tail->SizeOfImage; return 1; } pLDR_Tail = (PLDR_DATA_TABLE_ENTRY)pLDR_Tail->InLoadOrderLinks.Flink; } while(pLDR_Head != pLDR_Tail); return 0; } PVOID Find_FeatureCode(PVOID pShellCode,ULONG ulShellCode_Len,PVOID DllBase,ULONG ulSizeOfImage) { ULONG ulDllLastByte = 0; PVOID pNextByte = NULL;
pNextByte = DllBase; ulDllLastByte = (ULONG)DllBase + ulSizeOfImage; while((ULONG)pNextByte + ulShellCode_Len <= ulDllLastByte) { if(RtlCompareMemory(pNextByte,pShellCode,ulShellCode_Len) == ulShellCode_Len) { return (PVOID)((ULONG)pNextByte - 6); } pNextByte = (PVOID)((ULONG)pNextByte + 1); }
return NULL; }
|
3 3环PEB断链
TEB:线程环境块,每个线程都有这么一块内存存储信息来描述当前线程的信息,fs:[0]指向TEB。
PEB:进程环境块,TEB偏移0x30就是PEB,即fs:[0x30]。
PEB、TEB是3环的,都是3环的内存,在3环就可读取。FS段寄存器在3环和0环的含义不一样。
fs:[0]:TEB --> fs:[0x30]:PEB --> PEB+0xC:PEB_LDR_DATA --> PEB_LDR_DATA+0xC:LDR_DATA_TABLE_ENTRY
示意图如下:具体可以参考:《PEB及LDR链PEB及LDR链》
⚠️注意:这里的LDR_DATA_TABLE_ENTRY
是3环的,在3环通过该结构只能够列举该进程在3环加载的所有模块。
3.1 TEB、PEB
一、TEB
段寄存器FS(fs:[0])存的值指向TEB结构,在OD中可以看到3环下FS的值(0x7FFDF000
)和对应的段选择子(0x3B
)。
查看当前进程:
1 2 3 4 5 6 7 8 9
| kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS 861969f8 SessionId: 0 Cid: 0924 Peb: 7ffd9000 ParentCid: 0688 DirBase: 06e401c0 ObjectTable: e1e919e8 HandleCount: 74. Image: ollydbg.exe
PROCESS 861e1020 SessionId: 0 Cid: 0970 Peb: 7ffda000 ParentCid: 0b54 DirBase: 06e40320 ObjectTable: e1473788 HandleCount: 12. Image: 0119_PEB.exe
|
切换到当前进程空间:
1 2 3
| kd> .process 861e1020 Implicit process is now 861e1020 WARNING: .cache forcedecodeuser is not enabled
|
TEB结构为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| kd> dt _TEB 0x7FFDF000 nt!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : (null) +0x020 ClientId : _CLIENT_ID +0x028 ActiveRpcHandle : (null) +0x02c ThreadLocalStoragePointer : (null) +0x030 ProcessEnvironmentBlock : 0x7ffdd000 _PEB +0x034 LastErrorValue : 0 +0x038 CountOfOwnedCriticalSections : 0 +0x03c CsrClientThread : (null) +0x040 Win32ThreadInfo : (null) +0x044 User32Reserved : [26] 0 +0x0ac UserReserved : [5] 0 +0x0c0 WOW32Reserved : (null) +0x0c4 CurrentLocale : 0x804 +0x0c8 FpSoftwareStatusRegister : 0 +0x0cc SystemReserved1 : [54] (null) +0x1a4 ExceptionCode : 0n0 +0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK +0x1bc SpareBytes1 : [24] "" +0x1d4 GdiTebBatch : _GDI_TEB_BATCH +0x6b4 RealClientId : _CLIENT_ID +0x6bc GdiCachedProcessHandle : (null) +0x6c0 GdiClientPID : 0 +0x6c4 GdiClientTID : 0 +0x6c8 GdiThreadLocalInfo : (null) +0x6cc Win32ClientInfo : [62] 0 +0x7c4 glDispatchTable : [233] (null) +0xb68 glReserved1 : [29] 0 +0xbdc glReserved2 : (null) +0xbe0 glSectionInfo : (null) +0xbe4 glSection : (null) +0xbe8 glTable : (null) +0xbec glCurrentRC : (null) +0xbf0 glContext : (null) +0xbf4 LastStatusValue : 0 +0xbf8 StaticUnicodeString : _UNICODE_STRING "kernel32.dll" +0xc00 StaticUnicodeBuffer : [261] 0x6b +0xe0c DeallocationStack : 0x00030000 Void +0xe10 TlsSlots : [64] (null) +0xf10 TlsLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0xf18 Vdm : (null) +0xf1c ReservedForNtRpc : (null) +0xf20 DbgSsReserved : [2] (null) +0xf28 HardErrorsAreDisabled : 0 +0xf2c Instrumentation : [16] (null) +0xf6c WinSockData : (null) +0xf70 GdiBatchCount : 0 +0xf74 InDbgPrint : 0 '' +0xf75 FreeStackOnTermination : 0 '' +0xf76 HasFiberData : 0 '' +0xf77 IdealProcessor : 0 '' +0xf78 Spare3 : 0 +0xf7c ReservedForPerf : (null) +0xf80 ReservedForOle : (null) +0xf84 WaitingOnLoaderLock : 0 +0xf88 Wx86Thread : _Wx86ThreadState +0xf94 TlsExpansionSlots : (null) +0xf98 ImpersonationLocale : 0 +0xf9c IsImpersonating : 0 +0xfa0 NlsCache : (null) +0xfa4 pShimData : (null) +0xfa8 HeapVirtualAffinity : 0 +0xfac CurrentTransactionHandle : (null) +0xfb0 ActiveFrame : (null) +0xfb4 SafeThunkCall : 0 '' +0xfb5 BooleanSpare : [3] ""
|
二、PEB结构
由TEB结构可以看到偏移0x30
处即指向PEB(fs:[0x30]),即为0x7ffdd000
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| kd> dt _PEB 0x7ffdd000 nt!_PEB +0x000 InheritedAddressSpace : 0 '' +0x001 ReadImageFileExecOptions : 0 '' +0x002 BeingDebugged : 0x1 '' +0x003 SpareBool : 0 '' +0x004 Mutant : 0xffffffff Void +0x008 ImageBaseAddress : 0x00400000 Void +0x00c Ldr : 0x00241ea0 _PEB_LDR_DATA +0x010 ProcessParameters : 0x00020000 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : (null) +0x018 ProcessHeap : 0x00140000 Void +0x01c FastPebLock : 0x7c99d600 _RTL_CRITICAL_SECTION +0x020 FastPebLockRoutine : 0x7c921000 Void +0x024 FastPebUnlockRoutine : 0x7c9210e0 Void +0x028 EnvironmentUpdateCount : 1 +0x02c KernelCallbackTable : (null) +0x030 SystemReserved : [1] 0 +0x034 AtlThunkSListPtr32 : 0 +0x038 FreeList : (null) +0x03c TlsExpansionCounter : 0 +0x040 TlsBitmap : 0x7c99d5c0 Void +0x044 TlsBitmapBits : [2] 1 +0x04c ReadOnlySharedMemoryBase : 0x7f6f0000 Void +0x050 ReadOnlySharedMemoryHeap : 0x7f6f0000 Void +0x054 ReadOnlyStaticServerData : 0x7f6f0688 -> (null) +0x058 AnsiCodePageData : 0x7ffa0000 Void +0x05c OemCodePageData : 0x7ffa0000 Void +0x060 UnicodeCaseTableData : 0x7ffd1000 Void +0x064 NumberOfProcessors : 1 +0x068 NtGlobalFlag : 0x70 +0x070 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000 +0x078 HeapSegmentReserve : 0x100000 +0x07c HeapSegmentCommit : 0x2000 +0x080 HeapDeCommitTotalFreeThreshold : 0x10000 +0x084 HeapDeCommitFreeBlockThreshold : 0x1000 +0x088 NumberOfHeaps : 4 +0x08c MaximumNumberOfHeaps : 0x10 +0x090 ProcessHeaps : 0x7c99cfc0 -> 0x00140000 Void +0x094 GdiSharedHandleTable : (null) +0x098 ProcessStarterHelper : (null) +0x09c GdiDCAttributeList : 0 +0x0a0 LoaderLock : 0x7c99b178 Void +0x0a4 OSMajorVersion : 5 +0x0a8 OSMinorVersion : 1 +0x0ac OSBuildNumber : 0xa28 +0x0ae OSCSDVersion : 0x300 +0x0b0 OSPlatformId : 2 +0x0b4 ImageSubsystem : 3 +0x0b8 ImageSubsystemMajorVersion : 4 +0x0bc ImageSubsystemMinorVersion : 0 +0x0c0 ImageProcessAffinityMask : 0 +0x0c4 GdiHandleBuffer : [34] 0 +0x14c PostProcessInitRoutine : (null) +0x150 TlsExpansionBitmap : 0x7c99d5b8 Void +0x154 TlsExpansionBitmapBits : [32] 0 +0x1d4 SessionId : 0 +0x1d8 AppCompatFlags : _ULARGE_INTEGER 0x0 +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0 +0x1e8 pShimData : (null) +0x1ec AppCompatInfo : (null) +0x1f0 CSDVersion : _UNICODE_STRING "Service Pack 3" +0x1f8 ActivationContextData : (null) +0x1fc ProcessAssemblyStorageMap : (null) +0x200 SystemDefaultActivationContextData : 0x00130000 Void +0x204 SystemAssemblyStorageMap : (null) +0x208 MinimumStackCommit : 0
|
三、PEB_LDR_DATA结构,该结构的LIST_ENTRY
即为模块。
- PEB_LDR_DATA结构:该结构的
LIST_ENTRY
即为模块。
- _LDR_DATA_TABLE_ENTRY结构:该结构为模块的详细信息。
在PEB偏移0xC
得位置指向LDR链,LDR结构为:
1 2 3 4 5 6 7 8 9 10
| typedef struct _PEB_LDR_DATA { +0x00 ULONG Length; +0x04 BOOLEAN Initialized; +0x08 PVOID SsHandle; +0x0c LIST_ENTRY InLoadOrderModuleList; +0x14 LIST_ENTRY InMemoryOrderModuleList; +0x1c LIST_ENTRY InInitializationOrderModuleList; +0x24 PVOID EntryInProgress; } PEB_LDR_DATA,*PPEB_LDR_DATA;
|
其中InLoadOrderModuleList
、InMemoryOrderModuleList
、InInitializationOrderModuleList
为三个双向链表结构:
InLoadOrderModuleList
: 模块加载顺序
InMemoryOrderModuleList
:加载后在内存中的顺序
InInitializationOrderModuleList
:模块初始化的顺序
指向的结构为:**_LDR_DATA_TABLE_ENTRY**,该结构为模块的详细信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; WORD LoadCount; WORD TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; _ACTIVATION_CONTEXT * EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
|
- 在3环:使用了
InLoadOrderLinks
,使用InMemoryOrderModuleList
、InInitializationOrderModuleList
这三个链表。
- 在0环:
_LDR_DATA_TABLE_ENTRY
中只使用了InLoadOrderLinks
,并没有使用InMemoryOrderModuleList
、InInitializationOrderModuleList
这两个链表。
使用如下方法查看:
1 2 3 4 5 6 7 8 9
| kd> dt _PEB_LDR_DATA 0x00241ea0 nt!_PEB_LDR_DATA +0x000 Length : 0x28 +0x004 Initialized : 0x1 '' +0x008 SsHandle : (null) +0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x241ee0 - 0x242010 ] +0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x241ee8 - 0x242018 ] +0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x241f58 - 0x242020 ] +0x024 EntryInProgress : (null)
|
[ 0x241ee0 - 0x242010 ]
分别为:Flink(当前模块)与Blink(上一个模块)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| kd> dt _LDR_DATA_TABLE_ENTRY 0x241ee0 nt!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x241f48 - 0x241eac ] +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x241f50 - 0x241eb4 ] +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x018 DllBase : 0x00400000 Void +0x01c EntryPoint : 0x00401190 Void +0x020 SizeOfImage : 0x2c000 +0x024 FullDllName : _UNICODE_STRING "C:\Documents and Settings\A1v1n\桌面\VC6\Microsoft Visual Studio\MyProjects\0119_PEB\Debug\0119_PEB.exe" +0x02c BaseDllName : _UNICODE_STRING "0119_PEB.exe" +0x034 Flags : 0x5000 +0x038 LoadCount : 0xffff +0x03a TlsIndex : 0 +0x03c HashLinks : _LIST_ENTRY [ 0x7c99b2d8 - 0x7c99b2d8 ] +0x03c SectionPointer : 0x7c99b2d8 Void +0x040 CheckSum : 0x7c99b2d8 +0x044 TimeDateStamp : 0x61e7f524 +0x044 LoadedImports : 0x61e7f524 Void +0x048 EntryPointActivationContext : (null) +0x04c PatchInformation : (null)
|
有大佬总结在Win XP和Win7下,InLoadOrderModuleList
指向的模块按照顺序:第一个是EXE模块本身,第二个是NTDLL.DLL,第三个是KERNEL32.DLL。这样,KERNEL32.DLL的顺序是不是又固定了呢?实际上这样也并非能在任何场景下通用,还是老老实实拿着模块名称来遍历查询的ShellCode才通用。(可参考:PEB及LDR链最后一段。)
图片来源:Window中的shellcode编写框架(入门篇)
3.2 断链原理
模块断链只是隐藏起来不让API查到,但是正常的功能不受影响。
3环下PEB断链是一种常见的模块隐藏技术,原理是修改_PEB_LDR_DATA
中的三个双向链表,删除链表中的项,让 CreateToolhelp32Snapshot
之类的API无法通过模块列表枚举DLL。
结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| typedef struct _PEB_LDR_DATA { +0x00 ULONG Length; +0x04 BOOLEAN Initialized; +0x08 PVOID SsHandle; +0x0c LIST_ENTRY InLoadOrderModuleList; +0x14 LIST_ENTRY InMemoryOrderModuleList; +0x1c LIST_ENTRY InInitializationOrderModuleList; +0x24 PVOID EntryInProgress; } PEB_LDR_DATA,*PPEB_LDR_DATA;
typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
|
需要注意的三个结构:
- InLoadOrderModuleList :描述当前进程中模块的加载顺序
- InMemoryOrderModuleList:所有模块在内存中的顺序
- InInitializationOrderModuleList :所有模块初始化的顺序
3.2 代码:隐藏所有模块
所谓断链,就是把三个链表断掉,让3环的API读不出模块信息。最简单的做法就是让当前进程的Flink和Blink指向它自己。
⚠️注意:要隐藏模块,则这个模块在这三个结构中都要断开。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include "stdafx.h" #include <Windows.h> #include <stdlib.h> #include <tchar.h>
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; } PEB_LDR_DATA,*PPEB_LDR_DATA;
VOID HideModule(); int _tmain(int argc, _TCHAR* argv[]) { printf("未断链\n"); system("Pause"); HideModule(); printf("断链后\n"); system("Pause"); return 0; } VOID HideModule() { PPEB_LDR_DATA Ldr = NULL; PLIST_ENTRY Head = NULL; __asm { pushad; mov eax, fs:[30h]; mov eax, [eax+0xc] mov Ldr, eax; popad; } Head = Ldr->InLoadOrderModuleList.Flink; Head->Flink = Head; Head->Blink = Head;
Head = Ldr->InMemoryOrderModuleList.Flink; Head->Flink = Head; Head->Blink = Head;
Head = Ldr->InInitializationOrderModuleList.Flink; Head->Flink = Head; Head->Blink = Head;
return; }
|
运行环境:Windows7 SP1 x64,VC6++
断链前:
断链后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| __declspec(naked) DWORD getKernel32() { __asm { mov eax,fs:[30h] mov eax,[eax+0ch] mov eax,[eax+14h] mov eax,[eax] mov eax,[eax] mov eax,[eax+10h] ret } } int main() { HMODULE hLoadLibrary = LoadLibraryA("kernel32.dll"); HMODULE _hLoadLibrary = (HMODULE)getKernel32(); printf("LoadLibraryA动态获取的地址: 0x%x\n", hLoadLibrary); printf("内嵌汇编获取的地址: 0x%x\n", _hLoadLibrary); return 0; }
|
3.3 代码:隐藏指定模块
隐藏本进程的kernel32.dll
模块。
方案1:在PEB_LDR_DATA断开:
InLoadOrderModuleList
InMemoryOrderModuleList
InInitializationOrderModuleList
这种和方案3实质是一样的
方案2:在三个List的LDR_DATA_TABLE_ENTRY
仅断开InLoadOrderLinks
,不可行。
方案3:在LDR_DATA_TABLE_ENTRY里断开对应的:
InLoadOrderLinks
InMemoryOrderLinks
InInitializationOrderLinks
经过实际代码运行分析,有这样的对应关系:
InLoadOrderModuleList->Flink == InLoadOrderLinks
InMemoryOrderModuleList->Flink == InMemoryOrderLinks
InInitializationOrderModuleList->Flink == InInitializationOrderLinks
示意图如下:
由上图可知,InLoadOrderModuleList->Flink == InLoadOrderLinks
与其他两个不一定在同一个结构体中。且都是对应指过去。
故如下代码注释可以改为:
1 2 3
| Ldte = (PLDR_DATA_TABLE_ENTRY)pTemp; Ldte = (PLDR_DATA_TABLE_ENTRY)(pTemp - 0x8); Ldte = (PLDR_DATA_TABLE_ENTRY)(pTemp - 0x10);
|
因为:InLoadOrderModuleList
既是LIST_ENTRY
结构,也是LDR_DATA_TABLE_ENTRY
结构,而InLoadOrderLinks
仅为LIST_ENTRY
结构。由于对应结构都是一一指向过去,拿InMemoryOrderList
举例,其InMemoryOrderList.Flink == InMemoryOrderLinks
,相对于LDR_DATA_TABLE_ENTRY
结构偏移量为0x8。
⚠️:InLoadOrderModuleList不同InMemoryOrderLinks。InLoadOrderModuleList.Flink为当前模块的LDR_DATA_TABLE_ENTRY
,InMemoryOrderLinks.Flink为下一个模块的LIST_ENTRY
。
隐藏模块代码如下(如果题目改为隐藏除了kernel32.dll
外的所有模块,只需要将UnicodeStringCMP
函数比较结果取反即可):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| #include "stdafx.h" #include <Windows.h> #include <stdlib.h> #include <tchar.h>
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; } PEB_LDR_DATA,*PPEB_LDR_DATA; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; WORD LoadCount; WORD TlsIndex; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
VOID HideOneModule(PWSTR pMoudleName); BOOL UnicodeStringCMP(PWSTR Sting_1, PWSTR Sting_2);
int _tmain(int argc, _TCHAR* argv[]) { printf("隐藏模块kernel32.dll前\n"); system("Pause");
HideOneModule(L"kernel32.dll"); printf("隐藏模块kernel32.dll后\n");
system("Pause"); return 0; }
VOID HideOneModule(PWSTR pMoudleName) { PPEB_LDR_DATA Ldr = NULL; PLIST_ENTRY Head = NULL; PLIST_ENTRY pTemp = NULL; PLDR_DATA_TABLE_ENTRY Ldte = NULL; __asm { pushad; mov eax, fs:[30h]; mov eax, [eax+0xc] mov Ldr, eax; popad; }
Head = &(Ldr->InLoadOrderModuleList); pTemp = Head->Flink; do { Ldte = CONTAINING_RECORD(pTemp, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if(UnicodeStringCMP(Ldte->BaseDllName.Buffer, pMoudleName)) { Ldte->InLoadOrderLinks.Blink->Flink = Ldte->InLoadOrderLinks.Flink; Ldte->InLoadOrderLinks.Flink->Blink = Ldte->InLoadOrderLinks.Blink; } pTemp = pTemp->Flink; } while(Head != pTemp);
Head = &(Ldr->InMemoryOrderModuleList); pTemp = Head->Flink; do { Ldte = CONTAINING_RECORD(pTemp, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); if(UnicodeStringCMP(Ldte->BaseDllName.Buffer, pMoudleName)) { Ldte->InMemoryOrderLinks.Blink->Flink = Ldte->InMemoryOrderLinks.Flink; Ldte->InMemoryOrderLinks.Flink->Blink = Ldte->InMemoryOrderLinks.Blink; } pTemp = pTemp->Flink; } while(Head != pTemp);
Head = &(Ldr->InInitializationOrderModuleList); pTemp = Head->Flink; do { Ldte = CONTAINING_RECORD(pTemp, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks); if(UnicodeStringCMP(Ldte->BaseDllName.Buffer, pMoudleName)) { Ldte->InInitializationOrderLinks.Blink->Flink = Ldte->InInitializationOrderLinks.Flink; Ldte->InInitializationOrderLinks.Flink->Blink = Ldte->InInitializationOrderLinks.Blink; } pTemp = pTemp->Flink; } while(Head != pTemp);
return; }
BOOL UnicodeStringCMP(PWSTR Sting_1, PWSTR Sting_2) { while (*Sting_1) { if (*Sting_1 != *Sting_2) { return FALSE; break; } Sting_1++; Sting_2++; }
return TRUE; }
|
运行环境:Windows7 SP1 x64,VC6++
隐藏模块kernel32.dll前:
隐藏模块kernel32.dll后: