| 查看: 512 | 回复: 2 | |||
| 当前主题已经存档。 | |||
sdlj8051金虫 (著名写手)
|
[交流]
【转贴】病毒基础系列【已搜无重复】
|
||
|
前言: 病毒没有什么可怕的,也并不象想像中的复杂,玩汇编的人如果没有看过病毒?简直是白活一遭...病毒就象是双刃剑,恶意使用就会带来恶果,我本人对于此类行为深恶痛绝!我们研究不是为了破坏而是为了知己知彼,另外病毒中确实也有很多高超的技巧值得我们学习,这才是我们的目的所在,我绝没有教唆人犯罪的意图而且就我的水平来讲也远达不到. 在研究病毒之前有几项基础知识要了解: 1)ring0的获取,可参见我翻译的一篇ring0的文章,另外这些资料internet上也很多. 2)Seh的知识,可参见我写的< 3)PE结构的知识,ZouDan大侠的论文,IczeLion的PE教学和载 LUEVELSMEYER的《PE文件格式》,最为重要! 4)文件读写的基本知识,主要是CreateFileA,ReadFile,WriteFile,CreateFileMapping...等文件读写知识,如果你还不了解,最好先学学win32ASM的基础知识. 5)PolyEngine...MetaMorphism等基本概念 6)anti-debug的一些概念 7)有关MBR,FAT等知识,对了解一些老Virus有用. 下面我有的一些tips,可能会对理解病毒有所帮助.另外这些技术在加壳类软件中也很常见. -------------------------------------------------------------------------------- tip1: api函数地址的获取 这是一个老题目了,如果我们不用任何引入库,能否在程序中调用api函数?当然可以!方法有很多,你可能早就知道了,如果你已经了解了,就此打住,这是为还不了解这一技术而写.另外这也是病毒必用的技巧之一,如果你对病毒技术感兴趣,接着看下去. 这里假设你了解PE的基本结构,如果还不懂,找点资料来看看,到处都是呦. 在几乎每个病毒的开头都用下面的语句: call delta delta: pop ebp sub ebp,offset delta mov dword ptr [ebp+offset appBase],ebp 让我们考虑一下程序的执行情况,如果下面的代码由编译器自动编译连接,那么程序执行的基址一般是400000h,如果是在Nt下执行,那么基址可能不同,比如从100000h开始,不用担心,操作系统的Loader会自动为你重定位.但是这里停下来让我们看一下,如果你想要把这段代码附加到其他程序的后面并想让其正确执行的话,就不是那么简单了,因为你的代码可能要从555588h处开始执行,而在没有得到宿主程序许可的情况下期望操作系统自动为你修正偏移错误是不可能的,既然有非常的目的,就得费点力气,自己搞定重定位.而上面的代码就是首先得到eip指针,也是delta在程序执行时的实际偏移,然后减掉代码头到delta的偏移从而得到你的代码的真正基址,后面对于偏移的操作都应以这个真正的偏移为准.这就是你上面看到的.如果不明白,就仔细想一下,nothing difficult!下面的例子演示了这一点,并没有全部重定位,因为这只是技术演示. 现在回到本文的正式内容,要想获得api的地址,得首先获得诸如kernel32.dll,user32.dll的基址,然后再找到真正的函数地址.如何获得基址和函数地址呢呢?有几种方法 1)搜寻宿主的引入表获得GetModuleHandleA函数和GetProcAddress的地址,然后通过他返回系统dll的基址.因为很多程序都要使用这两个函数,因此在某些情况下是可行的,如果宿主没有使用GetProcAddress,那你就不得不搜寻Export表了. 2)直接获得kernel32.dll的基址,然后再搜寻Export表获得GetProcAddress和LoadLibraryA的地址,然后我们就能得到任何想调用的函数地址. 3)硬编码调用函数,比如在9X下GetModuleHandleA的地址一般是BFF7****. 第一种和第三种方法存在兼容性的问题,假如宿主没有调用GetModuleHandleA,那么你就不能获得基址,别的就更别想了...硬编码问题更大,操作系统不同则不能运行了,比如9X下可能在有些计算机上正常,但肯定不能在Nt/2K下运行... 第二种方法兼容性比较好,因此作以介绍. 一点背景:在PE Loader装入我们的程序启动后堆栈顶的地址是是程序的返回地址,肯定在Kernel中! 因此我们可以得到这个地址,然后向低地址缩减验证一直到找到模块的起始地址,验证条件为PE头不能大于4096bytes,PE header的ImageBase值应该和当前指针相等,嘿嘿,简单吧,而且兼容性还不错. 要获得Api的地址首先要获得GetModuleHandle,LoadLibraryA,GetProcAddress的地址,这是通过搜索Export表来实现的,具体原理就是PE Export表的结构,如果了解了PE结构就很简单了.下面我加了点注释,没有优化代码,是为了便于理解. 好,这一部分结束了! 这是一个例子,没有用任何预引入函数,加了一条invoke InitCommonControls是为了在2K下也能正常运行,否则不能在2K下不加载! 程序得到MessageBoxA的地址然后显示一个消息框,目的在于演示,重要部分加了注释,很好明白. 注意连接时加入/section:.text,RWE选项。 .586 .model flat, stdcall option casemap :none ; case sensitive include c:\hd\hd.h include c:\hd\mac.h ;;-------------- GetApiA proto WORD, WORD;;-------------- .CODE appBase dd ? k32Base dd ? lpApiAddrs label near dd offset sGetModuleHandle dd offset sGetProcAddress dd offset sExitProcess dd offset sLoadLibrary dd 0 sGetModuleHandle db "GetModuleHandleA",0 sGetProcAddress db "GetProcAddress",0 sExitProcess db "ExitProcess",0 sLoadLibrary db "LoadLibraryA",0 sMessageBoxA db "MessageBoxA",0 aGetModuleHandle dd 0 aGetProcAddress dd 0 aExitProcess dd 0 aLoadLibrary dd 0 aMessageBoxA dd 0 u32 db "User32.dll",0 k32 db "Kernel32.dll",0 sztit db "By Hume,2002",0 szMsg0 db "Hey,Hope U enjoy it!",0 ;;----------------------------------------- __Start: invokeInitCommonControls call delta delta: pop ebp ;得到delta地址 sub ebp,offset delta ;因为在其他程序中基址可能不是默认的所以需要重定位 mov dword ptr [ebp+offset appBase],ebp ;呵呵仔细想想 mov ecx,[esp] ;返回地址 xor edx,edx getK32Base: dec ecx ;逐字节比较验证 mov dx,word ptr [ecx+IMAGE_DOS_HEADER.e_lfanew] ;就是ecx+3ch test dx,0f000h ;Dos Header+stub不可能太大,超过4096byte jnz getK32Base ;加速检验 cmp ecx,dword ptr [ecx+edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase] jnz getK32Base ;看Image_Base值是否等于ecx即模块起始值, mov [ebp+offset k32Base],ecx ;如果是,就认为找到kernel32的Base值 lea edi,[ebp+offset aGetModuleHandle] lea esi,[ebp+offset lpApiAddrs] lop_get: lodsd cmp eax,0 jz End_Get push eax push dword ptr [ebp+offset k32Base] callGetApiA ;获取API地址 stosd jmp lop_get End_Get: push offset u32 call dword ptr [ebp+offset aLoadLibrary] ;在程序空间加载User32.dll lea EDX,[EBP+OFFSET sMessageBoxA] push edx push eax mov eax,dword ptr [ebp+aGetProcAddress] ;用GetProcAddress获得MessageBoxA的地址 call eax ;调用GetProcAddress push 40h+1000h ;style push offset sztit ;title push offset szMsg0 ;消息内容 push 0 call eax ;一个消息框产生了...嘿嘿 ;有理由为此高兴吧,因为我们没有预先引入 @@: ;这些函数 push 0 call [ebp+aExitProcess] ;----------------------------------------- K32_api_retrieve proc Base WORD ,sApi WORD push edx ;保存edx xor eax,eax ;此时esi=sApi Next_Api: ;edi=AddressOfNames mov esi,sApi xor edx,edx dec edx Match_Api_name: mov bl,byte ptr [esi] inc esi cmp bl,0 jz foundit inc edx push eax mov eax,[edi+eax*4] ;AddressOfNames的指针,递增 add eax,Base ;注意是RVA,一定要加Base值 cmp bl,byte ptr [eax+edx] ;逐字符比较 pop eax jz Match_Api_name ;继续搜寻 inc eax ;不匹配,下一个api loop Next_Api jmp no_exist ;若全部搜完,即未存在 foundit: pop edx ;edx=AddressOfNameOrdinals shl eax,1 ;*2得到AddressOfNameOrdinals的指针 movzx eax,word ptr [edx+eax] ;eax返回指向AddressOfFunctions的指针 ret no_exist: pop edx xor eax,eax ret K32_api_retrieve endp ;----------------------------------------- GetApiA proc Base WORD,sApi WORDlocal ADDRofFun WORDpushad mov edi,Base add edi,IMAGE_DOS_HEADER.e_lfanew mov edi,[edi] ;现在edi=off PE_HEADER add edi,Base ;得到IMAGE_NT_HEADERS的偏移 mov ebx,edi mov edi,[edi+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress] add edi,Base ;得到edi=IMAGE_EXPORT_DIRECTORY入口 mov eax,[edi+1ch] ;AddressOfFunctions的地址 add eax,Base mov ADDRofFun,eax ;ecx=NumberOfNames mov ecx,[edi+18h] mov edx,[edi+24h] add edx,Base ;edx=AddressOfNameOrdinals mov edi,[edi+20h] add edi,Base ;edi=AddressOfNames invokeK32_api_retrieve,Base,sApi mov ebx,ADDRofFun shl eax,2 ;要*4才得到偏移 add eax,ebx mov eax,[eax] add eax,Base ;加上Base! mov [esp+7*4],eax ;eax返回api地址 popad ret GetApiA endp ;----------------------------------------- END__Start ;------------------------------------------End all -------------------------------------------------------------------------------- 标 题:看看我的代码 发信人:wowocock 时 间:2002/08/22 08:55pm 详细信息: 看看我的代码 ;适用系统Win9x/me/2k/xp/nt .586p .model flat, stdcall option casemap :none ; case sensitive include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib GetApiAddress PROTO WORD,:BYTE.data Kernel32Addrdd ? ExportKernel dd ? GetProcAddr dd ? GetModuleHandleAddr dd ? LoadLibraryAddr dd ? ExitProcessAddr dd ? aGetProcAddr db "GetProcAddress",0 GetProcAddLen equ $-aGetProcAddr-1 aGetModuleHandle db "GetModuleHandle",0 GetModuleHandleLen equ $-aGetModuleHandle-1 aLoadLibrary db "LoadLibrary",0 LoadLibraryLen equ $-aLoadLibrary-1 aExitProcess db "ExitProcess",0 ExitProcessLen equ $-aExitProcess-1 szTitle db "Test",0 temp1 db " Kernel32.dll Address is:%8x:",0dh,0ah db " GetProcAddress Address is:%8x:",0dh,0ah db "GetModuleHandle Address is:%8x",0dh,0ah db " LoadLibrary Address is:%8x",0dh,0ah db " ExitProcess Address is:%8x",0 temp2 db 256 dup(?) .code Start: mov eax,[esp] ;//取Kernel32返回地址 and ax,0f000h mov esi,eax ;//得到Kernel.PELoader代码位置(不精确) LoopFindKernel32: sub esi,1000h cmp word ptr[esi],'ZM' ;//搜索EXE文件头 jnz short LoopFindKernel32 GetPeHeader: movzx edi,word ptr[esi+3ch] add edi,esi cmp word ptr[edi],'EP' ;//确认是否PE文件头 jnz short LoopFindKernel32 ;esi->kernel32,edi->kernel32 PE HEADER ;//////////////////////////////////////////////////任务:查找GetProcAddress函数地址 mov Kernel32Addr,esi GetPeExportTable: mov ebp,[edi+78h];4+14h+60h add ebp,Kernel32Addr ;//得到输出函数表 mov ExportKernel,ebp push GetProcAddLen push offset aGetProcAddr call GetApiAddress mov GetProcAddr,eax push GetModuleHandleLen push offset aGetModuleHandle call GetApiAddress mov GetModuleHandleAddr,eax push LoadLibraryLen push offset aLoadLibrary call GetApiAddress mov LoadLibraryAddr,eax push ExitProcessLen push offset aExitProcess call GetApiAddress mov ExitProcessAddr,eax invoke wsprintf,addr temp2,addr temp1,Kernel32Addr,GetProcAddr,GetModuleHandleAddr,LoadLibraryAddr,ExitProcessAddr invoke MessageBoxA,0,addr temp2,addr szTitle,0 push 0 call dword ptr[ExitProcessAddr] GetApiAddress proc AddressOfName:dword,ApiLength:byte push ebx push esi push edi mov edi,ExportKernel assume edi:ptr IMAGE_EXPORT_DIRECTORY GetExportNameList: mov ebx,[edi].AddressOfNames ;//得到输出函数名表 add ebx,Kernel32Addr ;ebx->AddressOfNames(函数名字的指针地址). xor eax,eax ;//函数序号计数 mov edx,Kernel32Addr ;//暂存Kernel32模块句柄;edx->kernel32 push edi ;保存EDI LoopFindApiStr: add ebx,04 inc eax ;//增加函数计数 mov edi,[ebx] add edi,edx ;//得到一个Api函数名字符串.edi->函数名 StrGetProcAddress: mov esi,AddressOfName ;//得到Api名字字符串 cmpsd ;比较前4个字符是否相等 jnz short LoopFindApiStr ;eax=函数名的INDEX xor ecx,ecx mov cl, ApiLength sub cl,4 ;//比较剩余的GetProcAddress串 cld Goon: cmpsb jnz short LoopFindApiStr ;eax=函数名的INDEX loop Goon pop edi ;恢复EDI mov esi,edx movebx,[edi].AddressOfNameOrdinals addebx,esi ;//取函数序号地址列表,ebx->AddresssOfNameOrdinals movzx ecx,word ptr [ebx+eax*2] mov ebx,[edi].AddressOfFunctions add ebx,esi ;//得到Kernel32函数地址列表 mov ebx,[ebx+ecx*4] add ebx,esi ;//计算GetProcAddress函数地址 mov eax,ebx ;eax=API函数地址,esi=Kernel32.dll hModule pop edi pop esi pop ebx ret GetApiAddress endp end Start [ Last edited by 幻影无痕 on 2007-7-30 at 07:57 ] |
» 猜你喜欢
读博
已经有5人回复
到新单位后,换了新的研究方向,没有团队,持续积累2区以上论文,能申请到面上吗
已经有13人回复
博士申请都是内定的吗?
已经有6人回复
之前让一硕士生水了7个发明专利,现在这7个获批发明专利的维护费可从哪儿支出哈?
已经有5人回复
博士读完未来一定会好吗
已经有29人回复
投稿精细化工
已经有4人回复
高职单位投计算机相关的北核或SCI四区期刊推荐,求支招!
已经有4人回复
导师想让我从独立一作变成了共一第一
已经有9人回复
心脉受损
已经有5人回复
Springer期刊投稿求助
已经有4人回复
2楼2006-12-29 01:18:46
3楼2007-07-28 16:40:34













WORD,
回复此楼