我肯定遍历了,我贴下代码吧,就是基于原书章节改的,我都把hash去掉了。
#include <stdio.h>
char param[] = "%s\n";
int main()
{
_asm{
CLD ; clear flag DF
;store hash
push 0x1e380a6a ;hash of MessageBoxA
push 0x4fd18963 ;hash of ExitProcess
push 0x0c917432 ;hash of LoadLibraryA
mov esi,esp ; esi = addr of first function hash
lea edi,[esi-0xc] ; edi = addr to start writing function
; make some stack space
xor ebx,ebx
mov bh, 0x04
sub esp, ebx
; push a pointer to "user32" onto stack
mov bx, 0x3233 ; rest of ebx is null
push ebx
push 0x72657375
push esp
xor edx,edx
; find base addr of kernel32.dll
mov ebx, fs:[edx + 0x30] ; ebx = address of PEB
mov ecx, [ebx + 0x0c] ; ecx = pointer to loader data
mov ecx, [ecx + 0x1c] ; ecx = first entry in initialisation order list
mov ecx, [ecx] ; ecx = second entry in list (kernel32.dll)
mov ebp, [ecx + 0x08] ; ebp = base address of kernel32.dll
mov eax, [ebp + 0x3C]
mov edx, [ebp + eax + 0x78]
add edx, ebp
mov ecx, [edx + 0x18]
mov ebx, [edx + 0x20]
add ebx, ebp
search:
dec ecx
mov esi, [ebx + ecx * 4]
add esi, ebp
find_lib_functions:
lodsd ; load next hash into al and increment esi
cmp eax, 0x1e380a6a ; hash of MessageBoxA - trigger
; LoadLibrary("user32")
jne find_functions
xchg eax, ebp ; save current hash
call [edi - 0x8] ; LoadLibraryA
xchg eax, ebp ; restore current hash, and update ebp
; with base address of user32.dll
find_functions:
pushad ; preserve registers
mov eax, [ebp + 0x3c] ; eax = start of PE header
mov ecx, [ebp + eax + 0x78] ; ecx = relative offset of export table
add ecx, ebp ; ecx = absolute addr of export table
mov ebx, [ecx + 0x20] ; ebx = relative offset of names table
add ebx, ebp ; ebx = absolute addr of names table
xor edi, edi ; edi will count through the functions
dec edi ; edi = -1
next_function_loop:
inc edi ; increment function counter
mov esi, [ebx + edi * 4] ; esi = relative offset of current function name
add esi, ebp ; esi = absolute addr of current function name
cdq ; dl will hold hash (we know eax is small)
;print
push esi
lea eax, param
push eax
call dword ptr[printf]
add esp, 8
;loop
jmp next_function_loop
}
}
首先说明,打印函数名时要按导出表结构体中的NumberofNames字段限定循环次数吧,否则运行出错,除非你在动态调试中在出错前观察输出。
然后我看了一下代码估计发现问题真正所在了,请看以下模块:
; find base addr of kernel32.dll
mov ebx, fs:[edx + 0x30] ; ebx = address of PEB
mov ecx, [ebx + 0x0c] ; ecx = pointer to loader data
mov ecx, [ecx + 0x1c] ; ecx = first entry in initialisation order list
mov ecx, [ecx] ; ecx = second entry in list (kernel32.dll) mov ebp, [ecx + 0x08] ; ebp = base address of kernel32.dll
红色代码部分出纰漏了(我看下书,其实书上就出错了,书上关于模块初始化链表中的模块顺序有错吧,但论坛上未曾找到这方面的讨论)
我们先分析下,用pe工具导入表查看ntdl.dll ,kenelbase.dll, kenenl32.dll我们可以发现kenenl32.dll依赖于kenelbase.dll和ntdl.dll ,kenelbase.dll依赖于ntdl.dll ,就这方面依赖关系来看,初始化链表顺序正确的应该是1.ntdl.dll -->2.kenelbase.dll--> 3.kenenl32.dll
这样按书上的shellcode我们其实索引到的是第二个模块kenelbase.dll(pe工具看到该模块实现了LoadLibraryExA和LoadLibraryExW函数,无LoadLibraryA函数,这印证了我们的函数名输出情况)
另外,从动态调试的M窗口中我们可以看到实际的kenelbase.dll模块地址与书上shellcode得到的所谓kenel32.dll模块地址吻合。
kenel32.dll中导出的LoadLibraryExA/W其实是映射了kenelbase中的函数实现,然后我估计LoadLibraryA的实现是kenel32.dll模块中在LoadLibraryExA基础上实现的,所以要打印出LoadLibraryA我们得索引到真正的kenel32.dll。
这样我们修改代码如下,
; find base addr of kernel32.dll
mov ebx, fs:[edx + 0x30] ; ebx = address of PEB
mov ecx, [ebx + 0x0c] ; ecx = pointer to loader data
mov ecx, [ecx + 0x1c] ; ecx = first entry in initialisation order list
mov ecx, [ecx]
mov ecx, [ecx] ; ecx = second entry in list (kernel32.dll)
mov ebp, [ecx + 0x08] ; ebp = base address of kernel32.dll
这样调试成功