IopLoadDriver

MmLoadSystemImage

DbgLoadImageSymbols

DriverEntry

==============================================================================================

调试驱动程序的入口 (2) 强大的条件断点

调试驱动程序的入口(1) 文中的方法大多数情况下是有效的,请注意是大多数。但例外总是有的,拿最近分析的一个曾经很流行的rootkit TDSS作为例子,

下面的命令相当于系统启动时断点,会在加载第一个模块(NT内核)时断住,同时显示模块名称和调用栈。

kd> sxe -c "ds poi(@esp+4); kv" ld:nt

kd> .reboot

Shutdown occurred at (Thu Mar 22 17:02:36.023 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Machine Name:

Kernel base =

0x804d4000PsLoadedModuleList = 0x8054be30

System Uptime: not available

0005ffc4  "\WINDOWS\system32\

ntoskrnl.exe"

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46

000600c4 804d4000ffffffff nt!

DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

设置模块的加载断点,

kd> sxe ld:*tdss*

kd> g

OS启动完成了都没断下来。可见,常规的方法不起作用。不过没关系,既然知道加载驱动的函数是

MmLoadSystemImage,就可以使用威力无与伦比的条件断点。

 ,Try it again!

kd> sxe -c "ds poi(@esp+4); kv" ld:ntos

kd> .reboot

Shutdown occurred at (Thu Mar 22 17:08:52.694 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Machine Name:

Kernel base =

0x804d4000PsLoadedModuleList = 0x8054be30

System Uptime: not available

0005ffc4  "\WINDOWS\system32\

ntoskrnl.exe"

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46

000600c4 804d4000ffffffff nt!

DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

MmLoadSystemImage这个函数的原型如下,

NTSTATUS MmLoadSystemImage (

IN PUNICODE_STRING

ImageFileName,

IN PUNICODE_STRING NamePrefix OPTIONAL,

IN PUNICODE_STRING LoadedBaseName OPTIONAL,

IN ULONG LoadFlags,

OUT PVOID *ImageHandle,

OUT PVOID *

ImageBaseAddress

)

因此,可以通过观察第一个参数得知模块名称,最后一个参数来得到模块的加载地址。

设置条件断点,打印模块名称,并且加载名称为"*tdss*"的模块时断住,显示函数栈。

kd> bp MmLoadSystemImage "aS /msu ${ModuleName} poi(@esp+4); .block {.echo ${ModuleName}; r @$t1 = $spat(@\"${ModuleName}\", \"*tdss*\"); }; ad /q ${ModuleName}; .if(@$t1<=0) {gc} .else { kv }"

kd> g

......

\SystemRoot\System32\Drivers\Npfs.SYS

\systemroot\system32\drivers\

TDSSmqxt.sys

ChildEBP RetAddr  Args to Child

f9e63584 80558b1e f9e63644 00000000 00000000 nt!

MmLoadSystemImage(FPO: [Non-Fpo])

f9e63650 8068b8b4 000002ec 00000001 00000000 nt!

IopLoadDriver+0x311 (FPO: [4,40,3])

f9e636ac 8068a5f7 00034000 00000000 00000000 nt!IopInitializeSystemDrivers+0x16d (FPO: [Non-Fpo])

f9e63844 8068b3a1 00820000 00000000 817cb928 nt!IoInitSystem+0x697 (FPO: [1,97,3])

f9e63dac 8057c73a 80087000 00000000 00000000 nt!Phase1Initialization+0x83b (FPO: [1,340,3])

f9e63ddc 805124c1 8068ad55 80087000 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

nt!MmLoadSystemImage:

8055cacf 6874010000      push    174h

条件断点成功命中,

kd> gu

nt!IopLoadDriver+0x311:

80558b1e 3bc3            cmp     eax,ebx

看下返回值来判断函数是否成功。

kd> r eax

eax=c0000034

TDSS rootkit 使用不同的名字备份了自己,确保多个模块的加载总有一个能够成功,这次的加载失败了,返回值STATUS_OBJECT_NAME_NOT_FOUND,也就是没找到这个文件。(NTStatus 值参考:

http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx),

让程序继续,看看下一次的加载,

kd> g

\SystemRoot\System32\DRIVERS\rasacd.sys

\SystemRoot\System32\DRIVERS\ipsec.sys

......

\SystemRoot\System32\Drivers\ParVdm.SYS

\SystemRoot\System32\DRIVERS\srv.sys

\??\C:\WINDOWS\system32\drivers\

TDSSpaxt.sys

ChildEBP RetAddr  Args to Child

f9e9fc80 80558b1e f9e9fd40 00000000 00000000 nt!MmLoadSystemImage (FPO: [Non-Fpo])

f9e9fd4c 80550cfb 000004e4 00000001 00000000 nt!IopLoadDriver+0x311 (FPO: [4,40,3])

f9e9fd74 804ed629 000004e4 00000000 817c73c8 nt!IopLoadUnloadDriver+0x43 (FPO: [Non-Fpo])

f9e9fdac 8057c73a f9afbcf4 00000000 00000000 nt!ExpWorkerThread+0xfe (FPO: [Non-Fpo])

f9e9fddc 805124c1 804ed556 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

nt!MmLoadSystemImage:

8055cacf 6874010000      push    174h

再次命中,看看函数

MmLoadSystemImage 的参数,关键是第6个参数,

kd> dd f9e9fc80 l8

f9e9fc80  00000246 80558b1e f9e9fd40 00000000

f9e9fc90  00000000 00000000 f9e9fd24

f9e9fd30

kd> gu

nt!IopLoadDriver+0x311:

80558b1e 3bc3            cmp     eax,ebx

kd> r eax

eax=00000000

kd> dd f9e9fd30 l1

f9e9fd30

f9072000

这次的加载成功,模块的地址为f9072000,尝试使用

$iment得到驱动的入口,

kd> ? $iment(f9072000)

^ Unknown image error in '? $iment(f9072000)'

失败了。可见系统提供的不是每次都好用的,一旦发生了例外,例外就会接踵而至。

还是老老实实的尝试解析PE文件头信息。

kd> dt _image_dos_header f9072000

nt!_IMAGE_DOS_HEADER

+0x000 e_magic          : 0x5a4d

+0x002 e_cblp           : 0x90

+0x004 e_cp             : 3

+0x006 e_crlc           : 0

+0x008 e_cparhdr        : 4

+0x00a e_minalloc       : 0

+0x00c e_maxalloc       : 0xffff

+0x00e e_ss             : 0

+0x010 e_sp             : 0xb8

+0x012 e_csum           : 0

+0x014 e_ip             : 0

+0x016 e_cs             : 0

+0x018 e_lfarlc         : 0x40

+0x01a e_ovno           : 0

+0x01c e_res            : [4] 0

+0x024 e_oemid          : 0

+0x026 e_oeminfo        : 0

+0x028 e_res2           : [10] 0

+0x03c e_lfanew         :

0n224

kd> dt -r2 _image_nt_headers f9072000+0n224

ntdll!_IMAGE_NT_HEADERS

+0x000 Signature        :

0x4550

+0x004 FileHeader       : _IMAGE_FILE_HEADER

+0x000 Machine          : 0x14c

+0x002 NumberOfSections : 5

+0x004 TimeDateStamp    : 0x490f76f9

+0x008 PointerToSymbolTable : 0

+0x00c NumberOfSymbols  : 0

+0x010 SizeOfOptionalHeader : 0xe0

+0x012 Characteristics  : 0x2102

+0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER

+0x000 Magic            : 0x10b

+0x002 MajorLinkerVersion : 0x8 ''

+0x003 MinorLinkerVersion : 0 ''

+0x004 SizeOfCode       : 0x2400

+0x008 SizeOfInitializedData : 0xa800

+0x00c SizeOfUninitializedData : 0

+

0x010 AddressOfEntryPoint : 0x2c13

+0x014 BaseOfCode       : 0x1000

+0x018 BaseOfData       : 0x4000

+0x01c ImageBase        : 0x10000000

+0x020 SectionAlignment : 0x1000

+0x024 FileAlignment    : 0x200

+0x028 MajorOperatingSystemVersion : 4

+0x02a MinorOperatingSystemVersion : 0

+0x02c MajorImageVersion : 0

+0x02e MinorImageVersion : 0

+0x030 MajorSubsystemVersion : 4

+0x032 MinorSubsystemVersion : 0

+0x034 Win32VersionValue : 0

+0x038 SizeOfImage      : 0x12000

+0x03c SizeOfHeaders    : 0x400

+0x040 CheckSum         : 0xf4d3

+0x044 Subsystem        : 1

+0x046 DllCharacteristics : 0

+0x048 SizeOfStackReserve : 0x100000

+0x04c SizeOfStackCommit : 0x1000

+0x050 SizeOfHeapReserve : 0x100000

+0x054 SizeOfHeapCommit : 0x1000

+0x058 LoaderFlags      : 0

+0x05c NumberOfRvaAndSizes : 0x10

+0x060 DataDirectory    : [16] _IMAGE_DATA_DIRECTORY

+0x000 VirtualAddress   : 0

+0x004 Size             : 0

还好这个能够工作,在入口处设置断点

kd> bp f9072000+2c13

kd> bl

0 e 8055cacf     0001 (0001) nt!MmLoadSystemImage "aS /msu ${ModuleName} poi(@esp+4); .block {.echo ${ModuleName}; r @$t1 = $spat(@\"${ModuleName}\", \"*tdss*\"); }; ad /q ${ModuleName}; .if(@$t1<=0) {g} .else { kv }"

1 e

f9074c130001 (0001)

kd> g

Breakpoint 1 hit

f9074c13 eb2a            jmp     f9074c3f

断点命中,接下来就可以开始调试之旅了。但为什么TDSS驱动的模块加载断点不起作用呢?

==============================================================================================

调试驱动程序的入口 (3) 探索与发现

前面2篇文章中介绍的方法已经达到了调试驱动程序的目的,这里是一些过程中的总结与摸索。

1. 如果是有符号的驱动程序,还有一个更加直接简单的方法。

先设置初始断点,确保在要调试的驱动程序入口运行之前。

kd> sxe -c "ds poi(@esp+4); kv" ld:*oskr*

kd> .reboot

Shutdown occurred at (Fri Mar 23 15:16:11.518 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Machine Name:

Kernel base = 0x804d4000 PsLoadedModuleList = 0x8054be30

System Uptime: not available

0005ffc4  "\WINDOWS\system32\

ntoskrnl.exe"

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46

000600c4 804d4000ffffffff nt!

DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

设置目标断点,还是使用serial.sys为例,直接使用

bu命令。

kd> bu serial!driverentry

kd> bl

0 eu             0001 (0001) (serial!driverentry)

kd> g

Breakpoint 0 hit

serial!DriverEntry:

f9b26793 53              push    ebx

系统执行,断点命中。

kd> kv

ChildEBP RetAddr  Args to Child

f9e9b854 80558d13 81686480 81684000 00000000 serial!

DriverEntry(FPO: [2,0,3])

f9e9b910 80555417 80000370 81684000 81686480 nt!

IopLoadDriver+0x5e0 (FPO: [4,40,3])

f9e9b954 80571f85 e1492f20 00000001 80000370 nt!PipCallDriverAddDeviceQueryRoutine+0x239 (FPO: [Non-Fpo])

f9e9b9a0 80571fb3 f9e9ba2c e1492f0c f9e9ba00 nt!RtlpCallQueryRegistryRoutine+0x3af (FPO: [Non-Fpo])

f9e9ba04 8055a84d 00000000 00000084 00000001 nt!RtlQueryRegistryValues+0x2a4 (FPO: [Non-Fpo])

f9e9bad8 8055a657 00000000 00000001 f9e9bd54 nt!PipCallDriverAddDevice+0x237 (FPO: [3,43,3])

f9e9bd24 805a9093 817b54c8 00000001 00000000 nt!PipProcessDevNodeTree+0x147 (FPO: [Non-Fpo])

f9e9bd4c 805071b0 00000003 80549fc0 8054eddc nt!PiProcessStartSystemDevices+0x38 (FPO: [Non-Fpo])

f9e9bd74 804ed629 00000000 00000000 817c7640 nt!PipDeviceActionWorker+0x158 (FPO: [Non-Fpo])

f9e9bdac 8057c73a 00000000 00000000 00000000 nt!ExpWorkerThread+0xfe (FPO: [Non-Fpo])

f9e9bddc 805124c1 804ed556 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

bu设置的断点很强大的,这个方法方便,但前提是需要符号文件。

2. 第一个模块是相对的

这条命令会在系统加载第一个模块(即NT内核)时断住,

kd> sxe -c "!str poi(@esp+4); kv" ld:ntoskr

kd> .reboot

Shutdown occurred at (Fri Mar 23 22:07:42.387 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Kernel base = 0x804d4000 PsLoadedModuleList = 0x8054be30

System Uptime: not available

String(30,31) at

000600c4: \WINDOWS\system32\

ntoskrnl.exe

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46

000600c4 804d4000ffffffff nt!

DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

这真的是OS加载的第一个模块嘛?

启动安全模式就能知到答案,下面的图显示了断点的时刻OS已经加载的若干模块,或许有点惊讶但仔细一想,却是必然的结果。

3. 前面文章中提到了个问题,为什么有的模块的加载,调试器的模块加载断点机制失效?

由于调试引擎通过函数DbgLoadImageSymbols和调试器通信,内核中的调试引擎如果不调用这个函数,调试器是无法知道模块的加载的,这时模块加载断点会失效。

利用这点,可以观察一下有多少模块的加载没有通知调试器。要做到这点不容易,但并非绝不可能,还是要使用powerful 的条件断点。

kd> .reboot

Shutdown occurred at (Fri Mar 23 16:34:59.709 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Machine Name:

Kernel base =

0x804d4000PsLoadedModuleList = 0x8054be30

System Uptime: not available

0005ffc4  "\WINDOWS\system32\

ntoskrnl.exe"

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46

000600c4 804d4000ffffffff nt!

DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

kd> bp MmLoadSystemImage "aS /msu ${ModuleName} poi(@esp+4); r @$t3 = poi(@esp+18);.block {.echo ${ModuleName}; r @$t1 = $spat(@\"${ModuleName}\", \"*tdss*\"); }; ad /q ${ModuleName}; r @$t2 = 0; bp /1 /C @$csp /t $thread DbgLoadImageSymbols \"r @$t2 = 1; gc;\" ; dd @$t3 l1; gu; reax; dd @$t3 l1; .if(@$t2 < 1) {kv } ; gc;"

MmLoadSystemImage函数中下断点,这个断点命中的时候再给

DbgLoadImageSymbols下个一次性的断点,如果第2个断点也命中,表示工作正常,模块加载的时候调试器得到了通知,反之则是异常情况,打印函数

MmLoadSystemImage的返回值,模块的地址(函数执行的前后)及栈。

执行后的输出很长,归纳成下面几种情况。

1. eax=c000010e,STATUS_IMAGE_ALREADY_LOADED,表示该模块已经加载过。(NTSTATUS值可参考:

http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx)。

2. eax=c0000034,STATUS_OBJECT_NAME_NOT_FOUND,表示找不到该对象,也就是说驱动程序的文件找不到。在我的XP系统上,有如下的模块返回这个错误。

\SystemRoot\System32\Drivers\

lbrtfdc.SYS

f9e63634  80690e14

eax=c0000034

f9e63634  80690e14

\SystemRoot\System32\Drivers\

i2omgmt.SYS

f9e63634  80690e14

eax=c0000034

f9e63634  80690e14

\SystemRoot\System32\Drivers\

Changer.SYS

f9e63634  80690e14

eax=c0000034

f9e63634  80690e14

\systemroot\system32\drivers\

TDSSmqxt.sys

f9e63634  80690e14

eax=c0000034

f9e63634  80690e14

\SystemRoot\System32\Drivers\

PCIDump.SYS

f9e63634  80690e14

eax=c0000034

f9e63634  80690e14

从这些输出可以看到,如果模块的加载失败,模块的加载地址不会被改变,是一个栈中的无具体意义的随机值。

\??\C:\WINDOWS\system32\drivers\

TDSSpaxt.sys

f9e9bd30  00000018

eax=00000000

f9e9bd30

f8f2a000

另外,TDSS 表现的很奇怪,eax是0,表示模块加载成功,但是同样没有调用

DbgLoadImageSymbols,这又是为什么呢?

这个问题需要从操作系统的代码中得到答案。

kd> uf mmloadsystemimage

......

nt!MmLoadSystemImage+0x9ca:

8055c919 ff37            push    dword ptr [edi]

8055c91b e887050000      call    nt!

CacheImageSymbols(8055cea7)

8055c920 85c0            test    eax,eax

8055c922 0f8487000000    je      nt!MmLoadSystemImage+0xa6f (8055c9af)

nt!MmLoadSystemImage+0x9d9:

8055c928 66837da416      cmp     word ptr [ebp-5Ch],16h

8055c92d 0f867bd70700    jbe     nt!MmLoadSystemImage+0xa38 (805da0ae)

nt!MmLoadSystemImage+0x9e0:

8055c933 6a0b            push    0Bh

8055c935 68ee915980      push    offset nt!MmGetSystemRoutineAddress+0x124 (805991ee)

8055c93a ff75a8          push    dword ptr [ebp-58h]

8055c93d e8a986f8ff      call    nt!_wcsnicmp (804e4feb)

8055c942 83c40c          add     esp,0Ch

8055c945 85c0            test    eax,eax

8055c947 0f8561d70700    jne     nt!MmLoadSystemImage+0xa38 (805da0ae)

nt!MmLoadSystemImage+0x9f6:

8055c94d 8b45a4          mov     eax,dword ptr [ebp-5Ch]

8055c950 89851cffffff    mov     dword ptr [ebp-0E4h],eax

8055c956 8b45a8          mov     eax,dword ptr [ebp-58h]

8055c959 898520ffffff    mov     dword ptr [ebp-0E0h],eax

8055c95f 83c016          add     eax,16h

8055c962 898520ffffff    mov     dword ptr [ebp-0E0h],eax

8055c968 6683851cffffffea add     word ptr [ebp-0E4h],0FFEAh

8055c970 8d851cffffff    lea     eax,[ebp-0E4h]

8055c976 50              push    eax

8055c977 683400dfff      push    0FFDF0034h

8055c97c 6806925980      push    offset nt!MmGetSystemRoutineAddress+0x13c (80599206)

8055c981 ff75cc          push    dword ptr [ebp-34h]

8055c984 e8c9bef7ff      call    nt!sprintf (804d8852)

8055c989 83c410          add     esp,10h

nt!MmLoadSystemImage+0xa4c:

8055c98c ff75cc          push    dword ptr [ebp-34h]

8055c98f 8d8514ffffff    lea     eax,[ebp-0ECh]

8055c995 50              push    eax

8055c996 e8f23bfbff      call    nt!RtlInitString (8051058d)

8055c99b 6aff            push    0FFFFFFFFh

8055c99d ff37            push    dword ptr [edi]

8055c99f 8d8514ffffff    lea     eax,[ebp-0ECh]

8055c9a5 50              push    eax

8055c9a6 e806bff7ff      call    nt!

DbgLoadImageSymbols(804d88b1)

8055c9ab 804b3610        or      byte ptr [ebx+36h],10h

......

函数

MmLoadSystemImage非常复杂,这么复杂的逻辑估计就算是看源代码也会一头雾水,因此尽可能的缩小范围,只关注调用

DbgLoadImageSymbols周围的一小段代码,花点时间分析,

 可以发现

CacheImageSymbols就是根源所在。

kd> uf CacheImageSymbols

nt!CacheImageSymbols:

8055cea7 6a10            push    10h

8055cea9 6820f44f80      push    offset nt!MMDB+0x18 (804ff420)

8055ceae e86e4efbff      call    nt!_SEH_prolog (80511d21)

8055ceb3 8365fc00        and     dword ptr [ebp-4],0

8055ceb7 8d45e4          lea     eax,[ebp-1Ch]

8055ceba 50              push    eax

8055cebb 6a06            push    6

8055cebd 6a01            push    1

8055cebf ff7508          push    dword ptr [ebp+8]

8055cec2 e8c2abf9ff      call    nt!

RtlImageDirectoryEntryToData(804f7a89)

8055cec7 8945e0          mov     dword ptr [ebp-20h],eax

8055ceca 85c0            test    eax,eax

8055cecc 740f            je      nt!CacheImageSymbols+0x37 (8055cedd)

nt!CacheImageSymbols+0x27:

8055cece 834dfcff        or      dword ptr [ebp-4],0FFFFFFFFh

8055ced2 33c0            xor     eax,eax

8055ced4 40              inc     eax

nt!CacheImageSymbols+0x3d:

8055ced5 e8804efbff      call    nt!_SEH_epilog (80511d5a)

8055ceda c20400          ret     4

nt!CacheImageSymbols+0x37:

8055cedd 834dfcff        or      dword ptr [ebp-4],0FFFFFFFFh

8055cee1 33c0            xor     eax,eax

8055cee3 ebf0            jmp     nt!CacheImageSymbols+0x3d (8055ced5)

这个函数就简单的多,检测PE文件有没有包含调试目录(IMAGE_DIRECTORY_ENTRY_DEBUG:6),有就返回1,没有就返回0。

综合上面2个函数,结论就是如果PE文件中没有调试目录信息,就不会调用

DbgLoadImageSymbols,调试器也就不会得到通知。

使用PE工具观察TDSS文件,确实没有调试目录。

还可以做个简单的实验,用工具把serial.sys的调试信息去掉,测试一下。

重启系统,再设置一下上面那个高级的条件断点,输出结果里多了一条

\SystemRoot\System32\DRIVERS\serial.sys

f9e9b8f4  f9e9ba80

eax=00000000

f9e9b8f4  f9afc000

象TDSS一样,serial.sys也加载成功,但没有调用DbgLoadImageSymbols。

这时再试试模块的加载断点,

kd> sxe ld:serial

kd> .reboot

Shutdown occurred at (Sun Mar 25 11:52:08.382 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Kernel base = 0x804d4000 PsLoadedModuleList = 0x8054be30

System Uptime: not available

如预期的那样,OS已经启动完成,但模块加载断点没有起作用。下面的调试记录从另一个角度证明了这点。

kd> .reboot

Shutdown occurred at (Sun Mar 25 12:27:35.747 2012 (UTC + 8:00))...unloading all symbol tables.

Waiting to reconnect...

......

Kernel base = 0x804d4000PsLoadedModuleList = 0x8054be30

System Uptime: not available

String(30,31) at 000600c4: \WINDOWS\system32\ntoskrnl.exe

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46 000600c4 804d4000ffffffff nt!DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

设置条件断点,如果CacheImageSymbols函数返回0,打印参数和栈。

kd> bp CacheImageSymbols "r @$t1 = poi(@esp+4); gu; .if( eax > 0 ) { gc } .else { ? @$t1; kv }"

kd> g

Evaluate expression: -105988096 = f9aec000

ChildEBP RetAddr  Args to Child

f9e9b844 80558b1e f9e9b904 00000000 00000000 nt!MmLoadSystemImage+0x9d1 (FPO: [Non-Fpo])

f9e9b910 80555417 8000054c 00000000 f9e9b900 nt!IopLoadDriver+0x311 (FPO: [4,40,3])

f9e9b954 80571f85 e1445b18 00000001 8000054c nt!PipCallDriverAddDeviceQueryRoutine+0x239 (FPO: [Non-Fpo])

f9e9b9a0 80571fb3 f9e9ba2c e1445b04 f9e9ba00 nt!RtlpCallQueryRegistryRoutine+0x3af (FPO: [Non-Fpo])

f9e9ba04 8055a84d 00000000 00000084 00000001 nt!RtlQueryRegistryValues+0x2a4 (FPO: [Non-Fpo])

f9e9bad8 8055a657 00000000 00000001 f9e9bd54 nt!PipCallDriverAddDevice+0x237 (FPO: [3,43,3])

f9e9bd24 805a9093 817b54c8 00000001 00000000 nt!PipProcessDevNodeTree+0x147 (FPO: [Non-Fpo])

f9e9bd4c 805071b0 00000003 80549fc0 8054eddc nt!PiProcessStartSystemDevices+0x38 (FPO: [Non-Fpo])

f9e9bd74 804ed629 00000000 00000000 817c7640 nt!PipDeviceActionWorker+0x158 (FPO: [Non-Fpo])

f9e9bdac 8057c73a 00000000 00000000 00000000 nt!ExpWorkerThread+0xfe (FPO: [Non-Fpo])

f9e9bddc 805124c1 804ed556 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

nt!MmLoadSystemImage+0x9d1:

8055c920 85c0            test    eax,eax

kd> !ustr poi(@ebp+8); r @$t1=poi(@ebp+1c); dd @$t1 l1; gu ; reax;

String(78,80) at f9e9b904: \SystemRoot\System32\DRIVERS\serial.sys

f9e9b8f4  f9aec000

eax=00000000

kd> ? $iment(f9aec000)

^ Unknown image error in '? $iment(f9aec000)'

kd> g

Evaluate expression: -118317056 = f8f2a000

ChildEBP RetAddr  Args to Child

f9e9bc80 80558b1e f9e9bd40 00000000 00000000 nt!MmLoadSystemImage+0x9d1 (FPO: [Non-Fpo])

f9e9bd4c 80550cfb 000003e4 00000001 00000000 nt!IopLoadDriver+0x311 (FPO: [4,40,3])

f9e9bd74 804ed629 000003e4 00000000 817c7640 nt!IopLoadUnloadDriver+0x43 (FPO: [Non-Fpo])

f9e9bdac 8057c73a f9ccbcf4 00000000 00000000 nt!ExpWorkerThread+0xfe (FPO: [Non-Fpo])

f9e9bddc 805124c1 804ed556 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

nt!MmLoadSystemImage+0x9d1:

8055c920 85c0            test    eax,eax

kd> !ustr poi(@ebp+8); r @$t1=poi(@ebp+1c); dd @$t1 l1; gu ; reax;

String(88,90) at f9e9bd40: \??\C:\WINDOWS\system32\drivers\TDSSpaxt.sys

f9e9bd30  f8f2a000

eax=00000000

kd> ? $iment(f8f2a000)

^ Unknown image error in '? $iment(f8f2a000)'

kd> g

系统启动过程中,总共2个模块发现了问题,一个是刚刚去掉调试目录信息的serial.sys,另一个是TDSS。$iment 信赖于调试符号,对于没有调试目录的模块,失效了。

4. 驱动程序的入口是如何被调用的?

还是要从代码下手,重启系统,

Waiting to reconnect...

Connected to Windows XP 2600 x86 compatible target at (Sun Mar 25 13:29:15.138 2012 (UTC + 8:00)), ptr64 FALSE

Kernel Debugger connection established.

......

Kernel base = 0x804d4000PsLoadedModuleList = 0x8054be30

System Uptime: not available

String(30,31) at 000600c4: \WINDOWS\system32\ntoskrnl.exe

ChildEBP RetAddr  Args to Child

0005ff80 804d88e7 000600c4 0005ff94 00000003 nt!DebugService2+0xe (FPO: [3,0,0])

0005ffa4 80656c46 000600c4 804d4000ffffffff nt!DbgLoadImageSymbols+0x40 (FPO: [Non-Fpo])

000600cc 80691ac8 00000000 80087000 80542268 nt!KdInitSystem+0x23e (FPO: [Non-Fpo])

00060100 00420e53 80087000 00467904 00438ab7 nt!KiSystemStartup+0x264

WARNING: Frame IP not in any known module. Following frames may be wrong.

00060e3c 0041ec40 00000007 00060e5c 00000000 0x420e53

00060ecc 004014fe 004678e0 0047164f 00051d68 0x41ec40

00061ff0 10101010 00000002 00000000 04e4000d 0x4014fe

00061ff4 00000000 00000000 04e4000d 003f0001 0x10101010

nt!DebugService2+0xe:

804d88ad cc              int     3

设置断点,重复上面的实验,第一个断点会在serial.sys处,用这个作例子,

kd> bp CacheImageSymbols "r @$t1 = poi(@esp+4); gu; .if( eax > 0 ) { gc } .else { ? @$t1; kv }"

kd> g

Evaluate expression: -105988096 = f9aec000

ChildEBP RetAddr  Args to Child

f9e9f844 80558b1e f9e9f904 00000000 00000000 nt!MmLoadSystemImage+0x9d1 (FPO: [Non-Fpo])

f9e9f910 80555417 80000548 00000000 f9e9f900 nt!IopLoadDriver+0x311 (FPO: [4,40,3])

f9e9f954 80571f85 e1444fb0 00000001 80000548 nt!PipCallDriverAddDeviceQueryRoutine+0x239 (FPO: [Non-Fpo])

f9e9f9a0 80571fb3 f9e9fa2c e1444f9c f9e9fa00 nt!RtlpCallQueryRegistryRoutine+0x3af (FPO: [Non-Fpo])

f9e9fa04 8055a84d 00000000 00000084 00000001 nt!RtlQueryRegistryValues+0x2a4 (FPO: [Non-Fpo])

f9e9fad8 8055a657 00000000 00000001 f9e9fd54 nt!PipCallDriverAddDevice+0x237 (FPO: [3,43,3])

f9e9fd24 805a9093 817b54c8 00000001 00000000 nt!PipProcessDevNodeTree+0x147 (FPO: [Non-Fpo])

f9e9fd4c 805071b0 00000003 80549fc0 8054eddc nt!PiProcessStartSystemDevices+0x38 (FPO: [Non-Fpo])

f9e9fd74 804ed629 00000000 00000000 817c73c8 nt!PipDeviceActionWorker+0x158 (FPO: [Non-Fpo])

f9e9fdac 8057c73a 00000000 00000000 00000000 nt!ExpWorkerThread+0xfe (FPO: [Non-Fpo])

f9e9fddc 805124c1 804ed556 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

nt!MmLoadSystemImage+0x9d1:

8055c920 85c0            test    eax,eax

kd> !dh -f f9aec000

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES

14C machine (i386)

8 number of sections

3D6DE48B time date stamp Thu Aug 29 17:08:27 2002

0 file pointer to symbol table

0 number of symbols

E0 size of optional header

10E characteristics

Executable

Line numbers stripped

Symbols stripped

32 bit word machine

OPTIONAL HEADER VALUES

10B magic #

7.00 linker version

C200 size of code

2E80 size of initialized data

0 size of uninitialized data

A793 address of entry point

380 base of code

......

kd> bp f9aec000+A793

kd> g

Breakpoint 1 hit

f9af6793 53              push    ebx

这是断在了serial.sys!DriverEntry,也就是驱动的入口处。

kd> kv

ChildEBP RetAddr  Args to Child

WARNING: Frame IP not in any known module. Following frames may be wrong.

f9e9f854 80558d13 816bc2c8 816ae00000000000 0xf9af6793

f9e9f910 80555417 80000548 816ae000 816bc2c8 nt!IopLoadDriver+0x5e0 (FPO: [4,40,3])

f9e9f954 80571f85 e1444fb0 00000001 80000548 nt!PipCallDriverAddDeviceQueryRoutine+0x239 (FPO: [Non-Fpo])

f9e9f9a0 80571fb3 f9e9fa2c e1444f9c f9e9fa00 nt!RtlpCallQueryRegistryRoutine+0x3af (FPO: [Non-Fpo])

f9e9fa04 8055a84d 00000000 00000084 00000001 nt!RtlQueryRegistryValues+0x2a4 (FPO: [Non-Fpo])

f9e9fad8 8055a657 00000000 00000001 f9e9fd54 nt!PipCallDriverAddDevice+0x237 (FPO: [3,43,3])

f9e9fd24 805a9093 817b54c8 00000001 00000000 nt!PipProcessDevNodeTree+0x147 (FPO: [Non-Fpo])

f9e9fd4c 805071b0 00000003 80549fc0 8054eddc nt!PiProcessStartSystemDevices+0x38 (FPO: [Non-Fpo])

f9e9fd74 804ed629 00000000 00000000 817c73c8 nt!PipDeviceActionWorker+0x158 (FPO: [Non-Fpo])

f9e9fdac 8057c73a 00000000 00000000 00000000 nt!ExpWorkerThread+0xfe (FPO: [Non-Fpo])

f9e9fddc 805124c1 804ed556 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

看下代码,驱动入口是如何被调用的,

kd> ub 80558d13

nt!IopLoadDriver+0x5cd:

80558d00 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

80558d02 8bc8            mov     ecx,eax

80558d04 83e103          and     ecx,3

80558d07 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]

80558d09 8b7d78          mov     edi,dword ptr [ebp+78h]

80558d0c ff7574          push    dword ptr [ebp+74h]

80558d0f 57              push    edi

80558d10 ff572c          call    dword ptr [edi+2Ch]

kd> dd ebp+74 l1

f9e9f91c  816ae000

kd> dS 816ae000

816ae008  "\REGISTRY\MACHINE\SYSTEM\Control"

816ae048  "Set001\Services\Serial"

驱动入口的原型如下,

NTSTATUS DriverEntry(

__in  struct _DRIVER_OBJECT *DriverObject,

__in  PUNICODE_STRING RegistryPath

);

2 个参数,第一个是对象,第二个是串。

kd> dt _driver_object 816bc2c8

ntdll!_DRIVER_OBJECT

+0x000 Type             : 0n4

+0x002 Size             : 0n168

+0x004 DeviceObject     : (null)

+0x008 Flags            : 2

+0x00c DriverStart      : 0xf9aec000 Void

+0x010 DriverSize       : 0xf400

+0x014 DriverSection    : 0x816bb868 Void

+0x018 DriverExtension  : 0x816bc370 _DRIVER_EXTENSION

+0x01c DriverName       : _UNICODE_STRING "\Driver\Serial"

+0x024 HardwareDatabase : 0x806680c8 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"

+0x028 FastIoDispatch   : (null)

+0x02c DriverInit       : 0xf9af6793     long  +fffffffff9af6793

+0x030 DriverStartIo    : (null)

+0x034 DriverUnload     : (null)

+0x038 MajorFunction    : [28] 0x804f886f     long  nt!IopInvalidDeviceRequest+0

也就是说函数IopLoadDriver初始化了数据结构_DRIVER_OBJECT,然后调用了驱动的入口(+0x02c DriverInit)。

signature=d6661ffc104d47cc8a8c4281f77bbffc,调试驱动程序的入口相关推荐

  1. (33)调试驱动程序

    一.驱动和应用程序在调试上的区别 接上一节课,我们学习了如何运行一个驱动,今天,学习如何调试驱动. 我们以前编写应用程序,可以直接在IDE里调试,VS,OD这些都是三环调试器.驱动运行在内核层,需要使 ...

  2. android 调试驱动程序,Android驱动程序开发和调试环境配置

    本文用<Android深度探索(卷1):HAL与驱动开发>的随书源代码为例详细说明如何配置Android驱动开发和测试环境,并且如何使用源代码中的build.sh脚本文件在各种平台(Ubu ...

  3. linux驱动程序调试方法

    驱动程序开发的一个重大难点就是不易调试. 本文目的就是介绍驱动开发中常用的几种直接和间接的调试手段,它们是: 利用printk 查看OOP消息 利用strace 利用内核内置的hacking选项 利用 ...

  4. linux 程序退出 调试,linux驱动程序调试常用方法(printk,OOP,strace,hacking,ioctl,/proc,kgdb)...

    驱动程序开发的一个重大难点就是不易调试.本文目的就是介绍驱动开发中常用的几种直接和间接的调试手段,它们是: 利用printk 查看OOP消息 利用strace 利用内核内置的hacking选项 利用i ...

  5. linux提取驱动程序,linux驱动程序调试常用方法

    驱动程序开发的一个重大难点就是不易调试.本文目的就是介绍驱动开发中常用的几种直接和间接的调试手段,它们是: 利用printk 查看OOP消息 利用strace 利用内核内置的hacking选项 利用i ...

  6. 驱动程序调试常用方法

    转自http://blog.csdn.net/caijp1090/article/details/7471862 驱动程序开发的一个重大难点就是不易调试.本文目的就是介绍驱动开发中常用的几种直接和间接 ...

  7. 《Windows CE嵌入式开发入门——基于Xscale架构》 第9章 Windows CE BSP及驱动程序结构分析

    9.1  Windows CE驱动程序结构概述 Windows CE的驱动程序可以从多种角度进行区分. 1.从加载以及接口方式来区分 可以分为本机设备驱动(Built-In Driver).可加载驱动 ...

  8. Window XP驱动开发(二十一) 过滤驱动程序

    转载请标明是引用于 http://blog.csdn.net/chenyujing1234 欢迎大家拍砖 参考书籍<<Windows驱动开发技术详解>> 过滤驱动程序的开发十分 ...

  9. Windows驱动开发学习笔记(二)—— 驱动调试内核编程基础

    Windows驱动开发学习笔记(二)-- 驱动调试&内核编程基础 基础知识 驱动调试 PDB(Program Debug Database) WinDbg 加载 PDB 实验:调试 .sys ...

最新文章

  1. python安装psutil库及使用
  2. 计算机主板揭秘(上)图文并茂版
  3. 深入理解面向对象设计的七大原则
  4. 小米羊城通余额不足服务器维护,再不怕羊城通余额不够了!地铁站现自助补票“神器”...
  5. 面试官 | 什么是 Lambda?该如何使用?
  6. linux 文件重命名或文件移动
  7. 湖南高校教师评职称计算机等级考试,湖南高校教师职称评审出台新规,这些要点你了解了吗?...
  8. [RL] 使用 dockerfile 构建 atari 环境
  9. 罗永浩回应“调侃”俞敏洪转行做直播;苹果3月9日举行春季发布会;CentOS推出新车载Linux发行版 | 极客头条...
  10. 航空公司VIP客户查询(25 分)(Hash)
  11. Python计算器程序实现,支持括号与符号检测、小数、负数运算
  12. NXP K60使用IAR烧录教程
  13. 【JS中innerHeight/Width、clientHeight/Width和offsetHeight/Width使用及其详解】
  14. MFC通过窗口标题获得窗口句柄
  15. 手机12306买卧铺下铺技巧_12306火车票如何买下铺 手机12306买下铺技巧
  16. 计算机一级安装包怎么升级,详细教您win7如何升级为sp1
  17. 搭建超级实用的免费机器翻译api
  18. Predefined Evenly-Distributed Class Centroids(PEDCC)预定义类中心做分类解读
  19. 学CNC编程,首先要从哪里开始?
  20. BUUCTF Misc 被劫持的神秘礼物

热门文章

  1. 「THUPC2018」生生不息 / Lives(状压 + 记忆化搜索 + 打表)
  2. Mybatis分页之RowBounds
  3. Gatling 测试脚本编写
  4. 微信公众号商业化有哪些盈利模式?
  5. 【嗜血GO笔记】解决beego数据无法 更新和插入的问题
  6. Go为什么能这么“快”?
  7. 干货 | 一文详解隐含狄利克雷分布(LDA)
  8. 对recursive calls的深刻理解
  9. Springboot开发微信小游戏后台-玩家登录流程
  10. 从只有100元的建材业务员做到身家千万,因为他懂得了这个...