1、方法有很多,你可能早就知道了,如果你已经了解了,就此打住,这是为还不了解这一技术而写.另外这也是病毒必用的技巧之一,如果你对病毒技术感兴趣,接着看下去. 这里假设你了解PE的基本结构,如果还不懂,找点资料来看看,到处都是呦. 在几乎每个病毒的开头都用下面的语句: call deltadelta: pop ebp sub ebp,offset delta mov dword ptr ebp+offset appBase,ebp让我们考虑一下程序的执行情况,如果下面的代码由编译器自动编译连接,那么程序执行的基址一般是400000h,如果是在Nt下执行,那么基址可能不同,比如从100000h开始,不
2、用担心,操作系统的Loader会自动为你重定位.但是这里停下来让我们看一下,如果你想要把这段代码附加到其他程序的后面并想让其正确执行的话,就不是那么简单了,因为你的代码可能要从555588h处开始执行,而在没有得到宿主程序许可的情况下期望操作系统自动为你修正偏移错误是不可能的,既然有非常的目的,就得费点力气,自己搞定重定位.而上面的代码就是首先得到eip指针(保存CPU要执行的下一个指令的地址),也是delta在程序执行时的实际偏移(用 pop ebp ,把它存入ebp中) 1)搜寻宿主的引入表获得GetModuleHandleA函数和GetProcAddress的地址,然后通过他返回系统dl
3、l的基址.因为很多程序都要使用这两个函数,因此在某些情况下是可行的,如果宿主没有使用GetProcAddress,那你就不得不搜寻Export表了. 2)直接获得kernel32.dll的基址,然后再搜寻Export表获得GetProcAddress和LoadLibraryA的地址,然后我们就能得到任何想调用的函数地址. 3)硬编码调用函数,比如在9X下GetModuleHandleA的地址一般是BFF7*. 第一种和第三种方法存在兼容性的问题,假如宿主没有调用GetModuleHandleA,那么你就不能获得基址,别的就更别想了.硬编码问题更大,操作系统不同则不能运行了,比如9X下可能在有些
4、计算机上正常,但肯定不能在Nt/2K下运行. 第二种方法兼容性比较好,因此作以介绍. 一点背景:在PE Loader装入我们的程序启动后堆栈顶的地址是程序的返回地址,肯定在Kernel中(当然是在没有动堆栈的情况下)! 因此我们可以得到这个地址,然后向低地址缩减验证一直到找到模块的起始地址,验证条件为PE头不能大于4096bytes,PE header的ImageBase(文件的优先装载地址)值应该和当前指针相等(不是我们的程序,而是那个kernel32.dll),嘿嘿,简单吧,而且兼容性还不错. 要获得Api的地址首先要获得GetModuleHandle,LoadLibraryA,GetPr
5、ocAddress的地址,这是通过搜索Export表来实现的,具体原理就是PE Export表的结构,如果了解了PE结构就很简单了.下面我加了点注释,没有优化代码,是为了便于理解. 好,这一部分结束了!这是一个例子,没有用任何预引入函数,加了一条invoke InitCommonControls是为了在2K下也能正常运行,否则不能在2K下不加载!程序得到MessageBoxA的地址然后显示一个消息框,目的在于演示,重要部分加了注释,很好明白.注意连接时加入/section:.text,RWE选项。.586.model flat, stdcalloption casemap :none ; ca
6、se sensitiveinclude c:hdhd.hhdmac.h ;- GetApiA proto :DWORD,:DWORD -.CODEappBase dd ?k32Base lpApiAddrs label near dd offset sGetModuleHandleoffset sGetProcAddressoffset sExitProcessoffset sLoadLibrary0 sGetModuleHandle db GetModuleHandleA,0sGetProcAddress db GetProcAddresssExitProcess ExitProcesssL
7、oadLibrary LoadLibraryA,0 sMessageBoxA MessageBoxAaGetModuleHandle dd 0aGetProcAddress dd 0aExitProcess aLoadLibrary aMessageBoxA dd 0 u32 User32.dllk32 Kernel32.dllsztit By Hume,2002szMsg0 Hey,Hope U enjoy it!- _Start: invokeInitCommonControls得到delta地址因为在其他程序中基址可能不是默认的所以需要重定位 dword ptr ebp+offset a
8、ppBase,ebp 呵呵仔细想想mov ecx,esp 返回地址 xor edx,edxgetK32Base: dec ecx 逐字节比较验证 dx,word ptr ecx+IMAGE_DOS_HEADER.e_lfanew 就是ecx+3ch test dx,0f000h Dos Header+stub不可能太大,超过4096byte jnz getK32Base 加速检验 cmp 看Image_Base值是否等于ecx即模块起始值, ebp+offset k32Base,ecx 如果是,就认为找到kernel32的Base值 lea edi,ebp+offset aGetModuleH
9、andle esi,ebp+offset lpApiAddrslop_get: lodsd eax,0 jz End_Get push eaxdword ptr ebp+offset k32Base callGetApiA 获取API地址 stosd jmp lop_getEnd_Get:offset u32dword ptr ebp+offset aLoadLibrary 在程序空间加载User32.dll EDX,EBP+OFFSET sMessageBoxAedx eax,dword ptr ebp+aGetProcAddress 用GetProcAddress获得MessageBoxA
10、的地址eax 调用GetProcAddress 40h+1000h styleoffset sztit titleoffset 消息内容一个消息框产生了.嘿嘿有理由为此高兴吧,因为我们没有预先引入:这些函数ebp+aExitProcess-K32_api_retrieve proc Base:DWORD ,sApi:edx 保存edx eax,eax 此时esi=sApiNext_Api:edi=AddressOfNames esi,sApi edxMatch_Api_name: bl,byte ptr esi inc esi bl,0foundit edx eax,edi+eax*4 Add
11、ressOfNames的指针,递增 add eax,Base 注意是RVA,一定要加Base值ptr eax+edx 逐字符比较 eaxMatch_Api_name 继续搜寻 eax 不匹配,下一个api loop Next_Api no_exist 若全部搜完,即未存在foundit: edx edx=AddressOfNameOrdinals shl eax,1 *2得到AddressOfNameOrdinals的指针 movzx eax,word ptr edx+eax ;eax返回指向AddressOfFunctions的指针 retno_exist: eax,eaxendp proc
12、 DWORD,sApi:DWORD local ADDRofFun: pushad edi,Base edi,IMAGE_DOS_HEADER.e_lfanew edi,edi 现在edi=off PE_HEADER edi,Base 得到IMAGE_NT_HEADERS的偏移 ebx,edi得到edi=IMAGE_EXPORT_DIRECTORY入口 eax,edi+1ch AddressOfFunctions的地址 eax,Base ADDRofFun,eaxecx=NumberOfNames ecx,edi+18h edx,edi+24h edx,Base edx=AddressOfNameOrdinals edi,edi+20h invokeK32_api_retrieve,Base,sApi ebx,ADDRofFun eax,2 要*4才得到偏移 eax,ebx eax,eax加上Base! esp+7*4,eax eax返回api地址 popad endp END_Start-End all
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1