//此代码出自《0day安全:软件漏洞分析技术》
//由于一开始读不懂,就慢慢一点点的加入了注释
//欢迎各位拍砖
//注释中实际内存地址,就是虚拟地址。
#include <stdlib.h>
#include <stdio.h>
int main()
{
_asm
{
CLD
push 0x1e380a6a ;MessageBoxA
push 0x4fd18963 ;ExitProcess
push 0x0c917432 ;LoadLibraryA
mov esi, esp ;esi指向的是LoadLibraryA
lea edi, [esi - 0xc] ;edi指向的是未使用的栈地址起始位置,用来保存函数地址
xor ebx, ebx ;ebx = 0
mov bh, 0x04 ;ebx = 0x4
sub esp, ebx ;栈地址提升4个偏移
mov bx, 0x3233 ;32
push ebx
push 0x72657375 ;user
push esp ;将栈中放入user32
xor edx, edx ;清edx
mov ebx, fs:[edx + 0x30] ;ebx进程环境块PEB的地址
mov ecx, [ebx + 0xc] ;ecx存放PEB_LDR_DATA结构体的指针
mov ecx, [ecx + 0x1c] ;ecx中存放指向模块初始化链表的头指针InInitializationOrderModuleList
mov ecx, [ecx] ;InInitializationOrderModuleList中按顺序存放着PE装入运行时初始化模块的信息,
;第一个是ntdll.dll,ntdll.dll所指向的就是kernel32.dll
mov ebp, [ecx + 0x8] ;ecx + 0x8就是kernel32.dll在内存中的加载基地址
find_lib_functions:
lodsd ;lodsd是把DS:ESI所指向的地址处的数据放入eax中,也就是0x0c917432
;然后ESI+4,此时ESI指向的是ExitProcess
cmp eax, 0x1e380a6a ;此处用来判断是否把所有需要的函数地址都获取完成,MessageBoxA
jne find_functions ;由于MessageBoxA在user32.dll中,因此,需要先加载user32.dll
xchg eax, ebp ;使用ebp保存MessageBoxA的hash
call [edi - 0x8] ;LoadLibraryA 参数在上面,push esp那里
xchg eax, ebp ;此时ebp为user32.dll的基地址,eax为MessageBoxA的hash
find_functions:
pushad ;Push(EAX);0x0c917432 LoadLibraryA
;Push(ECX);kernel32.dll链表节点
;Push(EDX);为0
;Push(EBX);进程环境块PEB的地址
;Push(ESP);该ESP为执行pushad之前的ESP
;Push(EBP);kernel32.dll在内存中的加载基地址
;Push(ESI);esi指向的是ExitProcess
;Push(EDI);edi指向的是未使用的栈地址起始位置,用来保存函数地址
;此处入栈顺序必须记得,后面会使用到
mov eax, [ebp + 0x3c] ;Kernel32.dll基地址 + 0x3c是PE头
mov ecx, [ebp + eax + 0x78] ;此时ecx存放的是函数导出表的指针
add ecx, ebp ;导出表的此次加载实际内存地址
mov ebx, [ecx + 0x20] ;偏移0x20处指向导出函数函数名的列表
add ebx, ebp ;函数名的列表此次加载的实际内存地址
xor edi, edi ;edi清0,用来计数
next_function_loop:
inc edi
mov esi, [ebx + edi * 4] ;取出每个函数名的地址
add esi, ebp ;函数名的实际内存地址
cdq ;把EDX的所有位都设成EAX最高位的值,可以暂时简单理解为把EDX清0
hash_loop: ;此处循环计算hash
movsx eax, byte ptr[esi]
cmp al, ah ;判断是否把函数名计算完成
jz compare_hash
ror edx, 7
add edx, eax
inc esi
jmp hash_loop
compare_hash:
cmp edx, [esp + 0x1c] ;由于刚刚的pushad,把寄存器都压入栈中,此时esp指向的是栈中的EDI,
;esp + 1c指向的则是EAX,LoadLibraryA
jnz next_function_loop
mov ebx, [ecx + 0x24] ;在导出表结构中,偏移0x24是导出函数序号的相对虚拟地址
add ebx, ebp ;导出函数序号的虚拟地址
mov di, [ebx + 2 * edi] ;获取第edi个函数的函数序号,由于函数序号是一个WORD型的数据,因此edi * 2
mov ebx, [ecx + 0x1c] ;此处为导出函数的相对虚拟地址
add ebx, ebp ;导出函数的虚拟地址
add ebp, [ebx + 4 * edi];获取函数序号所对应的函数地址,这就是我们最终所需要的函数地址。
xchg eax, ebp ;eax为所需要的函数地址
pop edi ;EDI出栈,用来保存LoadLibraryA的函数地址
stosd ;mov [edi],eax 函数地址保存
;add edi,4
push edi
popad ;此时,相对于pushad的时候,只有edi变化了,增加了4,其它寄存器均未变化
cmp eax, 0x1e380a6a ;当所有地址都获取完成后
jne find_lib_functions
function_call:
xor ebx, ebx
push ebx
push 0x2121216E ;
push 0x3167304C ;
mov eax, esp
push ebx ;压入参数
push eax
push eax
push ebx
call [edi - 0x04] ;调用MessageBoxA
push ebx
call [edi - 0x08] ;调用ExitProcess
nop
nop
nop
nop
}
system("pause");
return 0;
}