• WIN PE文件插入MessageBox


     

     

    1.准备工作

    1.1获取MessageBox地址

    方法①:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>
    #include <windows.h>
     
    typedef void (*FuncPointer)(LPTSTR);  // 函数指针  
     
    int main()
    {   
        HINSTANCE LibHandle;
        FuncPointer GetAddr;
        // 加载成功后返回库模块的句柄 
        LibHandle = LoadLibrary("user32");  
        printf("user32 LibHandle = 0x%X\n", LibHandle);
        // 返回动态链接库(DLL)中的输出库函数地址
        GetAddr=(FuncPointer)GetProcAddress(LibHandle,"MessageBoxA");   
        printf("MessageBoxA = 0x%X\n", GetAddr);
        return 0;
    }

    方法②: 用OllyDBG加载notepad,左下角command框中输入bp MessageBoxA下断点。查看断点即可得MessageBox地址为:0x77D507EA

    1.2CALL和JMP的计算

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <stdio.h>
    #include <windows.h>
    void func()
    {
        MessageBox(0, 0, 0, 0);
    }
    int main()
    {
        func();    
        return 0;
    }

    main函数反汇编:

    1
    2
    3
    4
    11:       func();
    0040D478 E8 8D 3B FF FF       call        @ILT+5(func) (0040100a)
    12:       return 0;
    0040D47D 33 C0                xor         eax,eax

    得到CALL的硬编码是E8
    call func继续跟进去:

    1
    2
    0040100A E9 01 00 00 00       jmp         func (00401010)
    0040100F CC                   int         3

    得到JMP的硬编码是E9
    func函数反汇编:

    1
    2
    3
    4
    5
    6
    7
    6:        MessageBox(0, 0, 0, 0);
    00401028 8B F4                mov         esi,esp
    0040102A 6A 00                push        0
    0040102C 6A 00                push        0
    0040102E 6A 00                push        0
    00401030 6A 00                push        0
    00401032 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]

    观察到:

    1
    2
    3
      地址           机器指令              汇编指令                 指令所占字节数
    0040D478    E8 8D 3B FF FF   call @ILT+5(func)(0040100a)       5
    0040D47D

    E8后边的值并不是真正我们要调用的函数地址, 他们有以下关系:

    1
    2
    3
    4
    5
    E8这条指令的下一行地址    E8当前指令的地址    E8当前指令所占大小
         0x0040D47D     =     0x0040D478      +        0x5
     
    真正要跳转的地址      E8后边的值       E8当前指令的下一行地址
      0x0040100A    =    0xFFFF3B8D     +    0x0040D47D

    总结出:

    1
    2
    E8后边的值 = 真正要跳转的地址 - (E8当前指令的地址 + E8当前指令所占大小)
               = 真正要跳转的地址 - E8当前指令的下一行地址

    1.3构造shellcode

    通过之前的学习,可以构造出:

    1
    2
    3
    4
    5
    // 18 Bytes
    shellcode [] = {0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00,  /* MessageBox4个参数入栈 */  
    0xE8, 0x00, 0x00, 0x00, 0x00, /* 调用MessageBox */
    0xE9, 0x00, 0x00, 0x00, 0x00  /* 跳到原程序的入口(AddressOfEntryPoint) */
    };

    2.代码区添加shellcode

    2.1分析PE结构

     

     

                                                                                     图1

     

     

                                                                                    图2  

    从图2中可以得到以下信息:

    1
    2
    3
    4
    5
    6
    IMAGE_OPTIONAL_HEADER(可选头)中部分成员信息:
    DWORD   AddressOfEntryPoint;  // 程序执行入口RVA      (0x0000739D)
    DWORD   ImageBase; // 程序的优先装载地址(基址)       (0x01000000)
    // 程序运行时,PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为:ImageBase + AddressOfEntryPoint;
    DWORD   SectionAlignment;  // 内存中节的对齐粒度       (0x00001000)
    DWORD   FileAlignment;  // 文件中节的对齐粒度          (0x00000200
    1
    2
    3
    4
    5
    IMAGE_SECTION_HEADER(节表第一项即代码区部分成员信息):
    DWORD   VirtualSize;  // 节区在内存中没有对齐前的实际大小 (0x00007748)
    DWORD   VirtualAddress;  // 节区在内存中起始位置(RVA)  (0x00001000)
    DWORD   SizeOfRawData;  // 节区在文件中对齐后的大小      (0x00007800) 
    DWORD   PointerToRawData;  // 节区在在文件中的偏移       (0x00000400)

    2.2判断空闲区域是否能放下shellcode

    计算文件中代码区空闲空间

    SizeOfRawData(0x7800) - VirtualSize(0x007748) >  0x12

    空闲空间大于shellcode长度,可以放得下。

    2.3将构造好的ShellCode写入空闲区:

    PointerToRawData(0x0400)+SizeOfRawData(0x7800)=0x7C00,

    在0x7B48与0x7C00之间写入shellcode:

     

     

                                                     图3

    2.4计算E8后边的值

    在计算相关值由文件映射到内存时,需要考虑内存对齐和文件对齐。

    真正要跳转的地址:MessageBox地址:0x77D507EA

    1
    2
    3
    4
    5
    文件中,E8下一行地址相对PointerToRawData偏移量:0x7B5D - 0x400 = 0x775D  
     
    映射到内存中,E8下一行地址:ImageBase + VirtualAddress + 0x775D = 0x0100875D 
     
    E8后边的值:MessageBox - 0x0100875D = 0x76D4808D

    2.5计算E9后边的值

    要保证MessageBox关闭后,程序能够正常运行,需要jmp到原来的OEP

    1
    2
    3
    4
    5
    6
    7
    原来OEP(真正要跳转的地址):ImageBase + AddressOfEntryPoint = 0x0100739D
     
    文件中,E9下一行地址相对PointerToRawData偏移量:0x7B62 - 0x400 = 7762  
     
    映射到内存中,E9下一行地址:ImageBase + VirtualAddress + 0x7762 = 0x01008762 
     
    E9后边的值:`0x0100739D - 0x01008762 = 0xFFFFEC3B`

    2.6修改OEP(AddressOfEntryPoint)

    1
    2
    3
    4
    5
    文件中shellcode起始地址相对PointerToRawData偏移量:0x7B50 - 0x400 = 0x7750  
     
    映射到内存中,相对ImageBase偏移:VirtualAddress + 0x7750 = 0x8750  
     
    将原来的OEP修改为,映射到内存后的shellcode起始地址(0x8750)。

    另存文件,双击,成功弹出MessageBox,如图4:

     



  • 相关阅读:
    前台Json格式中时间的格式转换
    存储过程
    自定义注解
    递归(累加)
    java操作符
    Markdown语法参考
    for表达式的循环执行顺序
    java形参与作用域
    java引用类型数组的创建方式
    学习总结之Log4NET
  • 原文地址:https://www.cnblogs.com/ganxiang/p/13173769.html
Copyright © 2020-2023  润新知