zoukankan      html  css  js  c++  java
  • PE结构-空白区添加代码

    空白区域添加代码

    经过一个多星期的PE学习,现在把几个能够较好体现PE结构和各种属性的练习整理出来

    目的:在notepad.exe中添加一段代码,使其在运行的时候,先弹出一个窗口,然后再执行主程序。

    所利用的知识点:

    文件对齐,内存对齐,硬编码,PE头结构

    ①首先要知道,PE文件为了更好的执行,添加了文件对齐和内存对齐的属性,在老版本编译器中,文件对齐一般为200h,内存对齐一般为1000h,在现今版本编译器中,大多数都为1000h。

    ②DOS头+DOS存根+PE标识+PE文件头+PE可选头+节表      该部分组成了宏观意义上的“PE头”。“PE头”不管是在内存对齐还是文件对齐中,都不会拉伸,中间没有空隙。

    ③而节表和节数据之间为了对齐而产生的空隙区域,为我们利用提供了方法,添加一段保护代码,就叫做壳,添加shellcode,就叫做病毒。

    步骤一 (寻找合适地址)

    首先,因为要调用MessageBox函数,所以需要先找到MessageBox函数的地址。利用od寻找

     用winhex打开notepad,找到节表后的空白区域,这里我选择在300h处添加自己的代码

    *步骤二 (RVA FOA)

    要理解怎样写入上述的数据,这里需要掌握硬编码的知识,这里给出两个公式,不花费过多时间解释,请自行领会

    E8后跟随的硬编码  =  要跳转的地址- E8所在的地址 - E8所在指令的长度

    E9后跟随的硬编码  =  要跳转的地址- E9所在的地址 - E9所在指令的长度

     而且更要注意的是,在该notepad中,文件对齐和内存对齐大小并不一样,这里又需要将FOA 转化为 RVA的知识

    FOA = n.PointerToRawData + 偏移(即在RVA中所属节+偏移,因为在内存中节空隙之间会被拉伸,但节中的数据并不会被拉伸)

    因为300h在文件中仍属于节表区域,而从DOS头到节表区域中间是没有空隙的,所以在内存中,所对应的就是ImageBase+300h,即1000300(这里已经查看了ImageBase)

    将我们所需要的数据带入公式中可得

    E8后跟的硬编码 =要跳转的地址 - E8所在的地址 - E8所在指令的长度
    77D507EA - 1000308 - 5
    76D504DD
    E9后跟的硬编码 =要跳转的地址 - E9所在的地址 - E9所在指令的长度
    1000000+739D -100030D - 5
    708B

    随后,将结果写入即可,切记(小端序存储)

    步骤三 (修改EP)

    上述步骤已经将想要添加的代码成功添加到程序中,但仍不可运行,因为,程序的入口点(EP)没有改变,当程序加载到内存中时,入口点的VA就是ImageBase+EP,于是,下一步就是要让OEP指向自己写的代码,将自己写的代码执行完毕后,再jmp到原来的OEP处,执行原来的功能。

    而则就要求熟练掌握PE头中的各个部分了,PE文件中的ImageBase属性正是在IMAGE_OPTIONAL_HEADER中,而PE头的结构想必都非常熟悉,我前面的博客中已经介绍了,请自行阅读。

    typedef struct _IMAGE_NT_HEADERS {  
        DWORD Signature;  
        IMAGE_FILE_HEADER FileHeader;  
        IMAGE_OPTIONAL_HEADER32 OptionalHeader;  
    } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;  

    PE指纹和文件头分别占4个字节和20个字节,紧接着就是可选头,可选头的大小不固定,如果不更改,在32位系统中则为E0。

    //可选PE头
    struct _IMAGE_OPTIONAL_HEADER{
        0x00 WORD Magic;                    //※幻数(魔数),0x0107:ROM image,0x010B:32位PE,0X020B:64位PE 
        //0x02 BYTE MajorLinkerVersion;     //连接器主版本号
        //0x03 BYTE MinorLinkerVersion;     //连接器副版本号
        0x04 DWORD SizeOfCode;              //所有代码段的总和大小,注意:必须是FileAlignment的整数倍,存在但没用
        0x08 DWORD SizeOfInitializedData;   //已经初始化数据的大小,注意:必须是FileAlignment的整数倍,存在但没用
        0x0c DWORD SizeOfUninitializedData; //未经初始化数据的大小,注意:必须是FileAlignment的整数倍,存在但没用
        0x10 DWORD AddressOfEntryPoint;     //※程序入口地址OEP,这是一个RVA(Relative Virtual Address),通常会落在.textsection,此字段对于DLLs/EXEs都适用。
        0x14 DWORD BaseOfCode;              //代码段起始地址(代码基址),(代码的开始和程序无必然联系)
        0x18 DWORD BaseOfData;              //数据段起始地址(数据基址)
        0x1c DWORD ImageBase;               //※内存镜像基址(默认装入起始地址),默认为4000H
        0x20 DWORD SectionAlignment;        //※内存对齐:一旦映像到内存中,每一个section保证从一个「此值之倍数」的虚拟地址开始
        0x24 DWORD FileAlignment;           //※文件对齐:最初是200H,现在是1000H
        //0x28 WORD MajorOperatingSystemVersion;    //所需操作系统主版本号
        //0x2a WORD MinorOperatingSystemVersion;    //所需操作系统副版本号
        //0x2c WORD MajorImageVersion;              //自定义主版本号,使用连接器的参数设置,eg:LINK /VERSION:2.0 myobj.obj
        //0x2e WORD MinorImageVersion;              //自定义副版本号,使用连接器的参数设置
        //0x30 WORD MajorSubsystemVersion;          //所需子系统主版本号,典型数值4.0(Windows 4.0/即Windows 95)
        //0x32 WORD MinorSubsystemVersion;          //所需子系统副版本号
        //0x34 DWORD Win32VersionValue;             //总是0
        0x38 DWORD SizeOfImage;         //※PE文件在内存中映像总大小,sizeof(ImageBuffer),SectionAlignment的倍数
        0x3c DWORD SizeOfHeaders;       //※DOS头(64B)+PE标记(4B)+标准PE头(20B)+可选PE头+节表的总大小,按照文件对齐(FileAlignment的倍数)
        0x40 DWORD CheckSum;            //PE文件CRC校验和,判断文件是否被修改
        //0x44 WORD Subsystem;          //用户界面使用的子系统类型
        //0x46 WORD DllCharacteristics;   //总是0
        0x48 DWORD SizeOfStackReserve;  //默认线程初始化栈的保留大小
        0x4c DWORD SizeOfStackCommit;   //初始化时实际提交的线程栈大小
        0x50 DWORD SizeOfHeapReserve;   //默认保留给初始化的process heap的虚拟内存大小
        0x54 DWORD SizeOfHeapCommit;    //初始化时实际提交的process heap大小
        //0x58 DWORD LoaderFlags;       //总是0
        0x5c DWORD NumberOfRvaAndSizes; //目录项数目:总为0X00000010H(16)
        0x60 _IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
    };

    在可选头中,通过偏移,即可找到AddressOfEntryPoint,它的值就是需要修改成自己所写的代码所在的偏移地址。

     这样,空白区添加代码所需的全部工作已经完成。运行发现结果正常。先弹出一个窗口,关闭窗口后,打开notepad本体。

    总结

    必须熟练掌握RVA FOA的转换,在反病毒和脱壳加壳领域中,是非常重要的基本功

  • 相关阅读:
    迷你版jQuery——zepto核心源码分析
    zepto.js 源码解析
    zepto.js swipe实现触屏tab菜单
    zepto.js 处理Touch事件
    Zepto 使用中的一些注意点(转)
    判断js对象的数据类型,有没有一个最完美的方法?
    html 5 本地数据库(Web Sql Database)核心方法openDatabase、transaction、executeSql 详解
    HTML5本地存储——Web SQL Database
    js事件监听器用法实例详解-注册与注销监听封装
    10 个非常有用的 AngularJS 框架
  • 原文地址:https://www.cnblogs.com/Virus-Faker/p/12542265.html
Copyright © 2011-2022 走看看