获取dll列表,并定位kernel32.dll: mov eax,fs:[30h] test eax,eax js os_9x os_nt: mov eax,[eax+0ch] mov esi,[eax+1ch] lodsd mov eax,[eax+8] jmp k_finished os_9x: mov eax,[eax+34h] mov eax,[eax+7ch] mov eax,[eax+3ch] k_finished: sub esp,200 mov edi,esp mov [edi+8],eax ;获取kernel32地址 寻找方式如下: 获得kernel32基地址后,
先了解一下PE格式: 每个windows的PE格式如下:
IMAGE_NT_HEADERS STRUCT
其中,opt结构里含有
IMAGE_OPTIONAL_HEADER32 STRUCT
image_data_directory数组里的第13个元素既是导入表相关结构,而第一个元素为导出表相关结构。
|
data directory结构如下:
IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress dd ?
isize dd ?
IMAGE_DATA_DIRECTORY ENDS
其中只包含地址和大小。
定位到Export表,其中
共有11 个成员,常用的列于下表。
Field Name | Meaning |
---|---|
nName | 模块的真实名称。本域是必须的,因为文件名可能会改变。这种情况下,PE装载器将使用这个内部名字。 |
nBase | 基数,加上序数就是函数地址数组的索引值了。 |
NumberOfFunctions | 模块引出的函数/符号总数。 |
NumberOfNames | 通过名字引出的函数/符号数目。该值不是模块引出的函数/符号总数,这是由上面的NumberOfFunctions给出。本域可以为0,表示模块可能仅仅通过序数引出。如果模块根本不引出任何函数/符号,那么数据目录中引出表的RVA为0。 |
AddressOfFunctions | 模块中有一个指向所有函数/符号的RVAs数组,本域就是指向该RVAs数组的RVA。简言之,模块中所有函数的RVAs都保存在一个数组里,本域就指向这个数组的首地址。 |
AddressOfNames | 类似上个域,模块中有一个指向所有函数名的RVAs数组,本域就是指向该RVAs数组的RVA。 |
AddressOfNameOrdinals | RVA,指向包含上述 AddressOfNames数组中相关函数之序数的16位数组。 |
FindApi: ;获取API函数地址子过程 push ebp push edi mov ebp,edi mov ebx,esp add ebx,8 xor edx,edx mov eax,[ebp+8] add eax,3ch ;指向PE头部偏移值e_lfanew mov eax,[eax] ;取得e_lfanew值 add eax,[ebp+8] ;指向PE header cmp dword ptr[eax],4550h ;判断是否为'PE' jne NotFound ;kernel32基址错误 mov [ebp+0ch],eax ;保存PE文件头 mov eax,[eax+78h] add eax,[ebp+8] mov [ebp+0ch],eax ;指向IMAGE_EXPORT_DIRECTORY mov eax,[eax+20h] add eax,[ebp+8] mov [ebp+4],eax ;保存函数名指针数组的指针值 mov ecx,[ebp+0ch] mov ecx,[ecx+14h] FindLoop: push ecx mov eax,[eax] add eax,[ebp+8] mov esi,ebx add esi,8 mov edi,eax mov ecx,[ebx+4] cld repe cmpsb jne FindNext add esp,4 mov eax,[ebp+0ch] mov eax,[eax+1ch] add eax,[ebp+8] shl edx,2 add eax,edx mov eax,[eax] add eax,[ebp+8] jmp Found FindNext: inc edx add dword ptr[ebp+4],4 mov eax,[ebp+4] pop ecx loop FindLoop NotFound: xor eax,eax Found: pop edi pop ebp ret