zoukankan      html  css  js  c++  java
  • 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:none
     
    include 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:_ApiMessageBox
     
    pushad
     
    ;----------------------------------------
    ;补丁功能代码,下面例子弹出对话框
    ;----------------------------------------
    ;获取user32.dll的基地址
    mov eax,offset szUser32Dll
    add eax,ebx
     
    mov edx,_loadLib
    push eax
    call edx
    mov hUser32Base,eax
     
    ;使用GetProcAddress函数的首地址
    ;传入两个参数调用GetProcAddress函数
    ;获得MessageBoxA的首地址
    mov eax,offset szMessageBox
    add eax,ebx
     
    mov edx,_getAddr
    mov ecx,hUser32Base
    push eax
    push ecx
    call edx
    mov _messageBox,eax
     
    ;调用函数MessageBox 
    mov eax,offset szHello
    add eax,ebx
    mov edx,_messageBox
     
    push MB_OK
    push NULL
    push eax
    push NULL
    call edx
     
    popad
    ret
     
    _patchFun endp
     
     
    _start proc
        local 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,ebx
     
    mov edi,hKernel32Base
    mov ecx,edi
    lea edx,_getApi
    add edx,ebx
     
    push eax
    push ecx
    call edx
    mov _getProcAddress,eax
     
    ;从基地址出发搜索LoadLibraryA函数的首地址
    mov eax,offset szLoadLib
    add eax,ebx
     
    mov edi,hKernel32Base
    mov ecx,edi
    lea edx,_getApi
    add edx,ebx
     
    push eax
    push ecx
    call edx
    mov _loadLibrary,eax
     
    ;调用补丁代码
    lea edx,_patchFun
    add edx,ebx
     
    push _loadLibrary
    push _getProcAddress
    push hKernel32Base
    call edx
     
    popad
    ret
    _start endp
     
    ;EXE文件新的入口地址
     
    _NewEntry:
    call @F ;免去重定位
    @@:
        pop ebx
    sub ebx,offset @B
     
    invoke _start
        jmpToStart 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:none
     
    include windows.inc
     
    _ProtoLoadLibrary    typedef proto :dword
    _ApiLoadLibrary      typedef ptr _ProtoLoadLibrary
     
    ;被添加到目标文件的代码从这里开始,到APPEND_CODE_END处结束
    .code
    jmp _NewEntry
     
    szLoadLib     db 'LoadLibraryA',0
    szDllName     db 'I.dll',0
     
    ;----------------------------------------
    ;获取kernel32.dll 的基地址
    ;----------------------------------------
    _getKernelBase proc
     
    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
     
     
    _start proc
        local 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,ebx
     
    mov edi,hKernel32Base
    mov ecx,edi
    lea edx,_getApi
    add edx,ebx
     
    push eax
    push ecx
    call edx
    mov _loadLibrary,eax
     
    ;调用补丁代码
     
    mov eax,offset szDllName
    push eax
    call _loadLibrary
     
    popad
    ret
    _start endp
     
    ;EXE文件新的入口地址
     
    _NewEntry:
    call @F ;免去重定位
    @@:
        pop ebx
    sub ebx,offset @B
     
    invoke _start
        ;jmpToStart db 0E9h,0F0h,0FFh,0FFh,0FFh
    ret
    end _NewEntry
     
     

    运行结果:




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

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

  • 相关阅读:
    Docker入门(windows版),利用Docker创建一个Hello World的web项目
    SpringBoot集成JWT实现token验证
    Jedis的基本操作
    Java动态代理详解
    SpringBoot利用自定义注解实现通用的JWT校验方案
    递归——汉诺塔问题(python实现)
    Datatable删除行的Delete和Remove方法的区别
    C# DEV使用心得
    总结
    安装插件时
  • 原文地址:https://www.cnblogs.com/csnd/p/12061966.html
Copyright © 2011-2022 走看看