动态定位API的shellcode
前面的<<HOOK IAT RING3>>文章中使用到了shellcode,那么这个shellcode是怎么制造出来的呢,本文将为你一步一步的解惑
这个shellcode的主要功能是调用MessageBoxA弹出一个空的对话框,注意此时的地址空间是在别的进程,由于不同的操作系统会影响动态链接库的加载地址,而且不同版本的dll中函数的偏移量RVA也不尽相同,因此写一个通用型的shellcode就显得非常的有必要
那么shellcode要怎么动态去定位API的地址呢,方法还是一样的,首先定位函数所在的模块,然后在EAT里面进行搜索即可,只要找到Kernel32.dll中的GetProcAddress和LoadLibraryA的地址就可以通过这两个获得其他模块中函数的地址
注意:网上的很多此类代码并没有通过搜索kernel32.dll模块,而是直接去寻找Ldr->InInitializationModuleList的第3个模块,这是有问题的,因为从win7开始引入了MinWin的概念导致InInitializationModuleList中的第三个模块是KernelBase.dll,第四个才是Kernel32.dll,不过Ldr->InMemoryOrderModuleList中的第三个模块并没有改变还是Kernel32.dll,为了以防万一还是搜索吧,搜索的时候用的是比较模块的第七个字符是不是'3’来匹配,因为kernel32.dll的第七个字符就是3,这样就可以不用通过计算哈希值来比较
下面贴出内嵌汇编代码,并加了一些注释(winxp,win7x86,win7x64以兼容32位的模式运行,都没问题~)
这个shellcode是我花了一个下午的时间写的,由于是新手,如果下文中有什么纰漏的话欢迎指正!
#include <windows.h> #include <stdio.h> void PopMessageBox() { __asm{ jmp start find_function: push ebp mov ebp,esp
mov eax,fs:[0x30] //fs points to teb in user mode,get pointer to peb mov eax,[eax+0x0c] //get peb->ldr mov eax,[eax+0x14] //get peb->ldr.InMemoryOrderModuleList.Flink(1st entry) module_loop: mov eax,[eax] //skip the first entry or get the next entry mov esi,[eax+0x28] //get the BaseDllName->Buffer cmp byte ptr [esi+0x0c],'3' //test the module's seventh's wchar is '3' or not,kernel32.dll jne module_loop //==================================== //find kernel32.dll module //==================================== mov eax,[eax+0x10] //LDR_DATA_TABLE_ENTRY->DllBase //==================================== //kernel32.dll PE Header //==================================== mov edi,eax add edi,[edi+0x3c] //IMAGE_DOS_HEADER->e_lfanew //==================================== //kernel32.dll export directory table //==================================== mov edi,[edi+0x78] //IMAGE_NT_HEADERS->OptinalHeader.DataDirectory[EAT].VirtualAddress add edi,eax mov ebx,edi // ebx is EAT's virtual address,we’ll use it later //==================================== //kernel32.dll Name Pointer Table //==================================== mov edi,[ebx+0x20] //IMAGE_EXPORT_DESCRIPTOR->AddressOfNames RVA add edi,eax xor ecx,ecx //NameOrdinals name_loop: mov esi,[edi+ecx*4] add esi,eax inc ecx mov edx,[esp+8] //first parameter cmp dword ptr [esi],edx jne name_loop mov edx,[esp+0xc] //second parameter cmp dword ptr [esi+4],edx jne name_loop //====================================== //kernel32.dll Ordinal Table //====================================== mov edi,[ebx+0x24] add edi,eax mov ecx,[edi+ecx*2] and ecx,0xFFFF //cause ordinal is USHORT of size,so we just use its lower 16-bits //====================================== //kernel32.dll Address Table //====================================== mov edi,[ebx+0x1c] add edi,eax dec ecx //subtract ordinal base sal ecx,2 mov edi,[edi+ecx] add eax,edi pop ebp ret 8 start: //==================================== // Get GetProcAddress's address //==================================== push 0x41636f72 //rocA push 0x50746547 //Getp call find_function push eax //store GetProcAddress in stack //==================================== //Get LoadLibraryA's address //==================================== push 0x7262694c //Libr push 0x64616f4c //Load call find_function push eax //store LoadLibraryA in stack // stack snap //-----------------------------high address // GetProcAddress's address //----------------------------- // LoadLibraryA's address <-----------------esp //-----------------------------low address //==================================== // Get User32.dll's image base //==================================== push 0x3233 //32 push 0x72657375 //user push esp //lpFileName call eax //call LoadLibraryA("user32.dll") add esp,8 //cause we push user32 string //==================================== //Get MessageBox's address //==================================== push 0x41786f //oxA push 0x42656761 //ageB push 0x7373654d //Mess push esp //lpProcName push eax //hModule call [esp+0x18] //call GetProcAddress(hModule,"MessageBoxA") add esp,0xc //cause we push MessageBoxA string push 0 push 0 push 0 push 0 call eax //call MessagBoxA(0,0,0,0) add esp,8 //cause we push GetProcAddress and LoadLibrary in stack } } int main() { PopMessageBox(); return 0; }
至于怎么转为shellcode,这里提供一个方法,就是按F10进入VS的调试模式,然后再右键选择查看汇编代码,找到内嵌汇编的开始地址,复制那个地址处的内容直到内嵌汇编的右大括号,放到十六进制的编辑器中比如NotePad++,然后替换空格为\0x,再稍作修改即可,这里说的比较的模糊,等有时间的时候再回头过来照顾一下新手吧,先把上面的汇编消化掉~
ps:喜欢贴代码,不想打字。。。怎么破。。。