欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 运维知识 > windows >内容正文

windows

Windows PE变形练手2-开发一套自己的PE嵌入模板

发布时间:2025/6/17 windows 13 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Windows PE变形练手2-开发一套自己的PE嵌入模板 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

PE嵌入模板

    编写一段代码,生成一个已经处理过重定位信息,同时所有的内容都在代码段里,并且没有导入表的PE程序,把这个程序嵌入到其他PE的相关位置,能够独立的运行,接下来是整理了2个模板,一个是HelloWorld的,另一个是通用模板LoadLibrary的模板。

    开发环境masm32vs2012,相关环境搭建我之前整理过http://blog.csdn.net/u013761036/article/details/52186683 ];

直接C++的话貌似不行,我用vs2015本地创建了一个空的项目,默认优化全开:


还是会有kernel32.dll

 

接下来是模板1,功能是所有的东西都在一个独立的模块,弹出对话框,同时处理了全局变量的重定位问题。模板如下[代码是仿照Windows PE 权威指南,但是有些地方已经改过了,书上代码在我本地跑有问题,再获取Kernel32.dll地址的时候并不能成功得到地址。那个函数我已经重写了]

;---------------------------------------- ;补丁代码 ;本段代码使用了API函数地址动态获取以及重定位技术 ;程序功能:弹出对话框 ;作者:Thinker ;开发日期:2017.4.30 ;开发测试环境:vs2012+masm32、Win7 X64 ;----------------------------------------.386 .model flat,stdcall option casemap:noneinclude windows.inc;注意,此处不静态包含引入任何其他动态链接库,包括Kernell32.dll_ProtoGetProcAddress typedef proto :dword,:dword _ProtoLoadLibrary typedef proto :dword _ApiGetProcAddress typedef ptr _ProtoGetProcAddress _ApiLoadLibrary typedef ptr _ProtoLoadLibrary;---------------------------------------- ;补丁代码中引入的其他动态链接库函数的声明 ;----------------------------------------_ProtoMessageBox typedef proto :dword,:dword,:dword,:dword _ApiMessageBox typedef ptr _ProtoMessageBox;被添加到目标文件的代码从这里开始,到APPEND_CODE_END处结束 .code jmp _NewEntry;以下内容为两个重要函数名 ;几乎所有补丁都必须使用 szGetProcAddr db 'GetProcAddress',0 szLoadLib db 'LoadLibraryA',0;---------------------------------------- ;补丁代码中其他全局变量的定义 ;----------------------------------------szUser32Dll db 'user32.dll',0 szMessageBox db 'MessageBoxA',0 szHello db 'HelloWorldPE',0 ;---------------------------------------- ;获取kernel32.dll 的基地址 ;----------------------------------------_getKernelBase proc;下面是Windows PE权威指南上的代码,获取Kernel32.dll地址失败,无奈我用自己之前的C++代码改了下,测试几次发现可以;local @dwRet ;pushad ;assume fs:nothing ;mov eax,fs:[30h] ;PEB ;mov eax,[eax+0ch] ;PEB_LDE_DATA ;mov esi,[eax+1ch] ;InInitializationOrderModuleList ;lodsd ;mov eax,[eax+8] ;Kernel32.dll的基地址 ;mov @dwRet ,eax ;popad ;mov eax,@dwRet ;ret;之前写的C++代码改的[http://blog.csdn.net/u013761036/article/details/71006302]local @dwRet pushad assume fs:nothing mov ebx, fs:[30h] ;得到peb结构体的地址 mov ebx, [ebx + 0ch] ;得到Ldr结构体的地址 mov ebx, [ebx + 0ch] ;得到ldr.InLoadOrderModuleList.Flink 第一个模块,当前进程 mov ebx, [ebx] ;得到第二个模块地址 ntdll.dll mov ebx, [ebx] ;得到第三个模块地址 kernel32.dll mov ebx, [ebx + 18h] ;得到第三个模块地址(kernel32模块的dllbase) mov @dwRet, ebx popad mov eax,@dwRet ret _getKernelBase endp;---------------------------------------- ;获取制定字符串的API函数的调用地址 ;入口参数:_hModule 为动态链接库的基地址 ; _lpApi为API函数名称首指 ;出口参数:eax 位函数在虚拟地址空间中的真实地址 ;---------------------------------------- _getApi proc _hModule,_lpApi local @ret local @dwLen pushad mov @ret,0 ;计算API字符串的长度,含最后的0 mov edi,_lpApi mov ecx,-1 xor al,al cld repnz scasb mov ecx,edi sub ecx,_lpApi mov @dwLen,ecx;从PE文件头的数据目录获取导出表地址 mov esi,_hModule add esi,[esi+3ch] assume esi:ptr IMAGE_NT_HEADERS mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress add esi,_hModule assume esi:ptr IMAGE_EXPORT_DIRECTORY;查找符合名称的导出函数名 mov ebx,[esi].AddressOfNames add ebx,_hModule xor edx,edx .repeat push esi mov edi,[ebx] add edi,_hModule mov esi,_lpApi mov ecx,@dwLen repz cmpsb .if ZERO? pop esi jmp @F .endif pop esi add ebx,4 inc edx .until edx >= [esi].NumberOfNames jmp _ret @@:;通过API名称索引获取序号索引,再获取地址索引 sub ebx,[esi].AddressOfNames sub ebx,_hModule shr ebx,1 add ebx,[esi].AddressOfNameOrdinals add ebx,_hModule movzx eax,word ptr [ebx] shl eax,2 add eax,[esi].AddressOfFunctions add eax,_hModule;从地址表得到导出函数地址 mov eax,[eax] add eax,_hModule mov @ret,eax_ret:assume esi:nothing popad mov eax,@ret ret _getApi endp;---------------------------------------- ;补丁功能部分 ;传入3个参数 ; _kernel :kernel32.dll的基地址 ; _getAddr:函数GetProcAddress地址 ; _LoadLib:函数LoadLibraryA地址 ;---------------------------------------- _patchFun proc _kernel,_getAddr,_loadLib;---------------------------------------- ;补丁代码局部变量定义 ;----------------------------------------local hUser32Base:dword local _messageBox:_ApiMessageBoxpushad;---------------------------------------- ;补丁功能代码,下面例子弹出对话框 ;---------------------------------------- ;获取user32.dll的基地址 mov eax,offset szUser32Dll add eax,ebxmov edx,_loadLib push eax call edx mov hUser32Base,eax;使用GetProcAddress函数的首地址 ;传入两个参数调用GetProcAddress函数 ;获得MessageBoxA的首地址 mov eax,offset szMessageBox add eax,ebxmov edx,_getAddr mov ecx,hUser32Base push eax push ecx call edx mov _messageBox,eax;调用函数MessageBox mov eax,offset szHello add eax,ebx mov edx,_messageBoxpush MB_OK push NULL push eax push NULL call edxpopad ret_patchFun endp_start proclocal hKernel32Base:dword ;存放kernel32.dll基地址local _getProcAddress:_ApiGetProcAddress local _loadLibrary :_ApiLoadLibrary pushad ;获取kernel32.dll的基地址 lea edx,_getKernelBase add eax,ebx call edx mov hKernel32Base,eax;从基地址出发搜索GetProcAddress函数的首地址 mov eax,offset szGetProcAddr add eax,ebxmov edi,hKernel32Base mov ecx,edi lea edx,_getApi add edx,ebxpush eax push ecx call edx mov _getProcAddress,eax;从基地址出发搜索LoadLibraryA函数的首地址 mov eax,offset szLoadLib add eax,ebxmov edi,hKernel32Base mov ecx,edi lea edx,_getApi add edx,ebxpush eax push ecx call edx mov _loadLibrary,eax;调用补丁代码 lea edx,_patchFun add edx,ebxpush _loadLibrary push _getProcAddress push hKernel32Base call edxpopad ret _start endp;EXE文件新的入口地址_NewEntry: call @F ;免去重定位 @@:pop ebx sub ebx,offset @Binvoke _startjmpToStart db 0E9h,0F0h,0FFh,0FFh,0FFh ret end _NewEntry

    最后的黑体是跳转用的。如果单独跑这个地方可以去掉,不然一条转都崩溃了,通常留着是为了嵌入到别的程序里之后,执行完之后跳转到原来程序入口地址等。


模板1的执行结果如下:


并没有导入表,同时相关字符串也没有存在数据段里:


代码段部分就是全部了,可以拷贝来独立的用。


模板2:和1类似,就是做了一个loadlibrary的模板,这样就和其他注入姿势一样了,只是加载一个dll,然后所有的东西在dll里面写就行了。Dllmain函数被调用的时候记得触发下自己就OK了。模板代码如下[一样书上的获取kernel32.dll代码无效,此处已经修正]

;---------------------------------------- ;补丁代码 ;本段代码使用了API函数地址动态获取以及重定位技术 ;程序功能:loadlibrary实现通用补丁代码 ;作者:Thinker ;开发日期:2017.4.30 ;开发测试环境:vs2012+masm32、Win7 X64 ;----------------------------------------.386 .model flat,stdcall option casemap:noneinclude windows.inc_ProtoLoadLibrary typedef proto :dword _ApiLoadLibrary typedef ptr _ProtoLoadLibrary;被添加到目标文件的代码从这里开始,到APPEND_CODE_END处结束 .code jmp _NewEntryszLoadLib db 'LoadLibraryA',0 szDllName db 'I.dll',0;---------------------------------------- ;获取kernel32.dll 的基地址 ;---------------------------------------- _getKernelBase proclocal @dwRet pushad assume fs:nothing mov ebx, fs:[30h] ;得到peb结构体的地址 mov ebx, [ebx + 0ch] ;得到Ldr结构体的地址 mov ebx, [ebx + 0ch] ;得到ldr.InLoadOrderModuleList.Flink 第一个模块,当前进程 mov ebx, [ebx] ;得到第二个模块地址 ntdll.dll mov ebx, [ebx] ;得到第三个模块地址 kernel32.dll mov ebx, [ebx + 18h] ;得到第三个模块地址(kernel32模块的dllbase) mov @dwRet, ebx popad mov eax,@dwRet ret _getKernelBase endp;---------------------------------------- ;获取制定字符串的API函数的调用地址 ;入口参数:_hModule 为动态链接库的基地址 ; _lpApi为API函数名称首指 ;出口参数:eax 位函数在虚拟地址空间中的真实地址 ;---------------------------------------- _getApi proc _hModule,_lpApi local @ret local @dwLen pushad mov @ret,0 ;计算API字符串的长度,含最后的0 mov edi,_lpApi mov ecx,-1 xor al,al cld repnz scasb mov ecx,edi sub ecx,_lpApi mov @dwLen,ecx;从PE文件头的数据目录获取导出表地址 mov esi,_hModule add esi,[esi+3ch] assume esi:ptr IMAGE_NT_HEADERS mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress add esi,_hModule assume esi:ptr IMAGE_EXPORT_DIRECTORY;查找符合名称的导出函数名 mov ebx,[esi].AddressOfNames add ebx,_hModule xor edx,edx .repeat push esi mov edi,[ebx] add edi,_hModule mov esi,_lpApi mov ecx,@dwLen repz cmpsb .if ZERO? pop esi jmp @F .endif pop esi add ebx,4 inc edx .until edx >= [esi].NumberOfNames jmp _ret @@:;通过API名称索引获取序号索引,再获取地址索引 sub ebx,[esi].AddressOfNames sub ebx,_hModule shr ebx,1 add ebx,[esi].AddressOfNameOrdinals add ebx,_hModule movzx eax,word ptr [ebx] shl eax,2 add eax,[esi].AddressOfFunctions add eax,_hModule;从地址表得到导出函数地址 mov eax,[eax] add eax,_hModule mov @ret,eax_ret:assume esi:nothing popad mov eax,@ret ret _getApi endp_start proclocal hKernel32Base:dword ;存放kernel32.dll基地址 local _loadLibrary :_ApiLoadLibrary pushad ;获取kernel32.dll的基地址 lea edx,_getKernelBase add eax,ebx call edx mov hKernel32Base,eax;从基地址出发搜索LoadLibraryA函数的首地址 mov eax,offset szLoadLib add eax,ebxmov edi,hKernel32Base mov ecx,edi lea edx,_getApi add edx,ebxpush eax push ecx call edx mov _loadLibrary,eax;调用补丁代码mov eax,offset szDllName push eax call _loadLibrarypopad ret _start endp;EXE文件新的入口地址_NewEntry: call @F ;免去重定位 @@:pop ebx sub ebx,offset @Binvoke _start;jmpToStart db 0E9h,0F0h,0FFh,0FFh,0FFh ret end _NewEntry

运行结果:




    代码段大小0x109,学习资料上的是优化到0x80,然而我照着写完MD各种运行不了,而且他里面定义了一堆变量最后根本没用上。获取Kernel32.dll部分的代码也是错的。FK浪费我太多时间调那个汇编代码,后来直接自己改了一个。上面的那个没有采取优化的姿势,需要用的,如果感觉大的话可以进行下汇编代码调整,优化下体积。

    以后的PE嵌入通常用的也是这个通用的模板,因为只要通常LoadlibraryOK了。当然如果想做反弹或者别的,对应的用这个姿势改就行了。目标就是不要有导入表,记得处理重定位,函数用到的数据记得是在代码段,不是放在数据段里面。

总结

以上是生活随笔为你收集整理的Windows PE变形练手2-开发一套自己的PE嵌入模板的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。