WinDBG详解进程初始化dll是如何加载的( 二 )

DIRECTORY_ENTRY_IMPORT 内容,它里面定义了我们需要初始化导入的 dll,我们可以用 dt r3 展开一下,然后一直点点点就好了,简化后如下:
0:000> dt -r3 0x400000+0n232 _IMAGE_NT_HEADERSConsoleApplication3!_IMAGE_NT_HEADERS+0x000 Signature: 0x4550+0x004 FileHeader: _IMAGE_FILE_HEADER+0x000 Machine: 0x14c...+0x012 Characteristics: 0x103+0x018 OptionalHeader: _IMAGE_OPTIONAL_HEADER+0x000 Magic: 0x10b+0x002 MajorLinkerVersion : 0xe ''...+0x05c NumberOfRvaAndSizes : 0x10+0x060 DataDirectory: [16] _IMAGE_DATA_DIRECTORY+0x000 VirtualAddress: 0+0x004 Size: 00:000> dx -r1 (*((ConsoleApplication3!_IMAGE_DATA_DIRECTORY (*)[16])0x400160))(*((ConsoleApplication3!_IMAGE_DATA_DIRECTORY (*)[16])0x400160))[Type: _IMAGE_DATA_DIRECTORY [16]][0][Type: _IMAGE_DATA_DIRECTORY][1][Type: _IMAGE_DATA_DIRECTORY][2][Type: _IMAGE_DATA_DIRECTORY]...[15][Type: _IMAGE_DATA_DIRECTORY]0:000> dx -r1 (*((ConsoleApplication3!_IMAGE_DATA_DIRECTORY *)0x400168))(*((ConsoleApplication3!_IMAGE_DATA_DIRECTORY *)0x400168))[Type: _IMAGE_DATA_DIRECTORY][+0x000] VirtualAddress: 0x1b1cc [Type: unsigned long][+0x004] Size: 0x50 [Type: unsigned long]从输出的 VirtualAddress=0x1b1cc 中可以看到,我们 PPEE 截图二中的 DIRECTORY_ENTRY_IMPORT 真实内容是在偏移 0x1b1cc 处,它是一个 combase!_IMAGE_IMPORT_DESCRIPTOR 结构体,输出如下:
0:005> dt 0x400000+0x1b1cc combase!_IMAGE_IMPORT_DESCRIPTOR+0x000 Characteristics: 0x1b21c+0x000 OriginalFirstThunk : 0x1b21c+0x004 TimeDateStamp: 0+0x008 ForwarderChain: 0+0x00c Name: 0x1b40c+0x010 FirstThunk: 0x1b000【WinDBG详解进程初始化dll是如何加载的】到这里就很关键了,涉及到如下几点信息:

  • 加载的 dll 名字是什么?
可以从 Name 字段提取,参考如下代码:
0:005> da 0x400000+0x1b40c0041b40c"KERNEL32.dll"
  • 加载的 方法名 是什么?
这需要提取 OriginalFirstThunk 字段,这里是一个 _IMAGE_IMPORT_BY_NAME类型的指针数组,代码如下:
0:005> dp 0x400000+0x1b21c0041b21c0001b3e8 0001b3fc 0001b954 0001b9420041b22c0001b934 0001b924 0001b912 0001b9060041b23c0001b8fa 0001b8ea 0001b8d6 0001b8ba...0:005> dt 0x400000+0x0001b3e8 combase!_IMAGE_IMPORT_BY_NAME+0x000 Hint: 0x382+0x002 Name: [1]"I"0:005> da 0x400000+0x0001b3e8+0x20041b3ea"IsDebuggerPresent"结合上面的输出,我们知道 IsDebuggerPresent() 是属于 KERNEL32.dll 下的,有了这两点信息,Windows 加载器就可以用 LoadLibraryGetProcAddress 方法将其加载到进程中了,转化为 C 代码大概是这样的 。
typedef BOOL(CALLBACK* DeubbgerFunc)();int main(intargc, char* argv[]){ HMODULE hModule = LoadLibrary(L"KERNEL32.dll"); DeubbgerFunc func = (DeubbgerFunc)GetProcAddress(hModule, "IsDebuggerPresent"); BOOL b= func();}
  • func 函数地址会保存吗?
当然会保存了,会放在 _IMAGE_IMPORT_DESCRIPTOR 结构下的 FirstThunk 字段中,这是一个函数地址的指针数组,可以用 dds 观察 。
0:005> dt 0x400000+0x1b1cc combase!_IMAGE_IMPORT_DESCRIPTOR+0x000 Characteristics: 0x1b21c+0x000 OriginalFirstThunk : 0x1b21c+0x004 TimeDateStamp: 0+0x008 ForwarderChain: 0+0x00c Name: 0x1b40c+0x010 FirstThunk: 0x1b0000:005> dds 0x400000+0x1b0000041b000753c20d0 KERNEL32!IsDebuggerPresentStub0041b004753c16c0 KERNEL32!LoadLibraryWStub0041b008753c2e80 KERNEL32!GetCurrentProcess0041b00c753bf550 KERNEL32!GetProcAddressStub0041b05c753b9910 KERNEL32!TerminateProcessStub...还有一点要注意,如果你在代码中使用 IsDebuggerPresent() 方法的话,它会从 0041b000 位置上取函数地址,参考如下汇编代码:
WinDBG详解进程初始化dll是如何加载的

经验总结扩展阅读