我试图了解 Windows 如何在用户空间中递归加载 DLL。
以跟踪kernel32.ReadProcessMemory
为例:
的第一步ReadProcessMemory
是 IAT kernel32
:
00007FF901F6AFA0 | 48:FF25 21D20500 | jmp qword ptr ds:[<&ReadProcessMemory>] |
哪个jmp
是kernelbase.ReadProcessMemory
:
00007FF9002D22F0 | 48:83EC 48 | sub rsp,48 |
00007FF9002D22F4 | 48:8D4424 30 | lea rax,qword ptr ss:[rsp+30] |
00007FF9002D22F9 | 48:894424 20 | mov qword ptr ss:[rsp+20],rax |
00007FF9002D22FE | 48:FF15 C3521400 | call qword ptr ds:[<&ZwReadVirtualMemory>] |
<snip>
这call
是ntdll.ZwReadVirtualMemory
:
00007FF902F5C840 | 4C:8BD1 | mov r10,rcx |
00007FF902F5C843 | B8 3F000000 | mov eax,3F | 3F:'?'
00007FF902F5C848 | F60425 0803FE7F 01 | test byte ptr ds:[7FFE0308],1 |
00007FF902F5C850 | 75 03 | jne ntdll.7FF902F5C855 |
00007FF902F5C852 | 0F05 | syscall |
00007FF902F5C854 | C3 | ret |
00007FF902F5C855 | CD 2E | int 2E |
00007FF902F5C857 | C3 | ret |
所以这个例子中用户模式的流程是:
kernel32.ReadProcessMemory
kernelbase.ReadProcessMemory
ntdll.ZwReadVirtualMemory
期望是上述每个 DLL 在加载时都可以根据它们的 IAT/从其他 DLL 导入的函数“找到”适当的函数。
使用dumpbin
和跟踪IMPORTS
this 是正确的kernel32.ReadProcessMemory
(哪里api-ms-win-core-memory-l1-1-0.dll
是ApiSet
for kernelbase.dll
):
api-ms-win-core-memory-l1-1-0.dll
180078178 Import Address Table
18009E120 Import Name Table
0 time date stamp
0 Index of first forwarder reference
35 VirtualQueryEx
<snip>
1C ReadProcessMemory
然而,这不是真的kernelbase.dll
-NtReadVirtualMemory
是进口的,但ZwReadVirtualMemory
不是进口的:
ntdll.dll
1801A67C8 Import Address Table
180262A48 Import Name Table
0 time date stamp
0 Index of first forwarder reference
893 __C_specific_handler
<snip>
205 NtReadVirtualMemory
所以,我的问题是:在 DLL 加载过程中,如果没有导入,如何kernelbase.dll
识别“位置” ?ZwReadVirtualMemory
该ZwReadVirtualMemory
函数由 调用kernelbase.dll
,所以它一定已经在某个时候被解析/存储在 IAT 中,但是这在技术上是如何发生的呢?
当这些函数解析到相同的地址时NtReadVirtualMemory
,加载器映射到的地方是否有一些间接?ZwReadVirtualMemory