zoukankan      html  css  js  c++  java
  • 干货分享丨如何在代码空白区添加ShellCode

    干货分享丨如何在代码空白区添加ShellCode

     

    今天分享的技术文章是作者flag0原创的文章,他为我们带来的是在记事本中实现添加空白弹窗,通过手动和编程实现在代码空白区、数据空白区添加ShellCode的干货内容,文章未经许可禁止转载!

    注:i 春秋公众号旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。

    干货分享丨如何在代码空白区添加ShellCode

     

    本文需要的知识:

    PE

    实验环境:

    Windows XP

    记事本:

    PETool

    Uedit32

    Visual c++ 6.0

     

    手动添加shellcode

    手动添加ShellCode实验有以下几个步骤:

    • 寻找代码节空白区;
    • 获取MessageBox地址,构造ShellCode代码;
    • 计算修正E8与E9后面的参数;
    • 计算并修改OEP。

    寻找代码节空白区

    代码节空白区一般指第一个节区的VirualSize(对齐前的节区大小)与第一个节区的SizeOfRawData(对齐后的节区大小)之间的空白范围。

    用PETool打开记事本,记录如下需要用到的PE参数:

    IMAGE_OPTIONAL_HEADER32(可选PE头)
    AddressEntryPint:0000739D        (程序开始执行的位置)
    ImageBase:         01000000       (内存基地址)
    SectionAlignmeng:00001000        (内存对齐单位)
    FileAlignment:     00000200        (文件对齐单位)

    IMAGE_SECTION_HEADER(节表)
    VirtualSize:     00007748        (对齐前的长度)
    VirtualAddress:     00001000         (内存中的偏移)
    SizeOfRawData:     00007800        (对齐后的长度)
    PointerToRawData:00000400        (文件中的偏移)
    Characteristics: 60000020        (节区属性)

    代码空白区的起始地址为:

    PointerToRawData + VirtualSize 即 7748+400=7B48

    用Uedit打开记事本,找到空白区。

    干货分享丨如何在代码空白区添加ShellCode

     

    获取MessageBox地址,构造ShellCode代码。

    用C++ 新建项目

    include "stdafx.h"include "windows.h"int main(int argc, char* argv[]){MessageBox(0,0,0,0);printf("Hello World!
    ");return 0;}
    按F9在MessageBox前下断点,运行后右键菜单->Go To Disassembly。
    9:        MessageBox(0,0,0,0);00401028 8B F4                mov         esi,esp0040102A 6A 00                push        00040102C 6A 00                push        00040102E 6A 00                push        000401030 6A 00                push        000401032 FF 15 8C 52 42 00    call        dword ptr [impMessageBoxA@16 (0042528c)]00401038 3B F4                cmp         esi,esp0040103A E8 C1 00 00 00       call        __chkesp (00401100)

    按F11单步步入调试进入MessageBox函数内部。

    77D5050B 8B FF mov edi,edi

    在汇编中,call内存地址对应的硬编码为E8 00 00 00 00,jmp 内存地址对应的硬编码为E9 00 00 00 00。

    编写ShellCode

    6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 006A 00 6A 00 6A 00 6A 00是push 0 push 0 push 0 push 0是调用MessageBox所传递的实参将ShellCode添加进空白区中:计算修正E8与E9后面的参数FOA -> RVAFOA:在文件中的偏移地址RVA:相对偏移地址(内存中)当程序中的文件对齐单位和内存对齐单位不一致时则需要进行FOA->RVA转换SectionAlignmeng:00001000        (内存对齐单位)

    FileAlignment: 00000200 (文件对齐单位)

    FOA->RVA 转换公式

    FOA_shellcodeAddr - PointerToRawData + VirtualAddress + ImageBase = RVA_shellcodeAddr

    即7b50 - 400 + 1000 + 01000000 = 100 8750

    如果一致的话就不需要转换,直接使用即可。

    用转换后的地址进行计算E8、E9后面的偏移。

    E8 后的地址 = MessageBox的地址 - E8的下一条指令的地址。

    即77D5050B - (100 8750 +D) = 76D4 7DAE‬

    E9 后的地址 = ImageBase + AddressEntryPoint - E9的下一条指令的地址。

    (01000000 + 739D) - (100 8750 + 12) = FFFF EC3B

    这里计算一定要用DWORD,将计算出的E8 E9后面的值添加进文件中。

    干货分享丨如何在代码空白区添加ShellCode

     

    计算并修改OEP

    计算公式

    OEP = RVA_shellcodeAddr - ImageBase

    即100 8750 - 1000000 = 8750

    修改OEP

    干货分享丨如何在代码空白区添加ShellCode

     

    使用代码添加ShellCode

    需要实现功能如下:

    • 读取exe到内存中
    • 解析PE格式
    • 判断代码节空白区大小
    • 添加shellcode代码
    • FOA->RVA
    • 修正E8
    • 修正E9
    • 修正OEP
    • 保存文件
    #include "stdafx.h"#include "windows.h"#include "stdlib.h"#define FILEPATH_IN "C:\test.exe"#define FILEPATH_OUT "C:\t.exe"#define SHELLCODELENGTH 0X12#define MESSAGEBOXADDR 0x77D5050BBYTE shellCode[] ={    0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,    0xE8,0x00,0x00,0x00,0x00,    0xE9,0x00,0x00,0x00,0x00};int FileLength(FILE* fp){    int fileSize = 0;    fseek(fp,0,SEEK_END);    fileSize = ftell(fp);    fseek(fp,0,SEEK_SET);  //指针归位    return fileSize;}DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer){    FILE* pFile = NULL;    DWORD fileSize = 0;    //打开文件    pFile = fopen(lpszFile,"rb+");    if(!pFile)    {        printf("无法打开EXE文件");        return NULL;    }    //读取文件大小    fileSize = FileLength(pFile);    //分配缓冲区大小    *pFileBuffer = malloc(fileSize);    if(!pFileBuffer)    {        printf(" 分配空间失败!");        fclose(pFile);        return NULL;    }    //将文件数据读取到缓冲区    size_t n = fread(*pFileBuffer,fileSize,1,pFile);    if(!n)    {        printf("  读取数据失败!");        free(*pFileBuffer);        fclose(pFile);        return NULL;    }    //关闭文件    fclose(pFile);    return fileSize;}VOID addShellCode(){    LPVOID pFileBuffer=NULL;    PIMAGE_DOS_HEADER pDosHeader = NULL;//声明DOS头结构体变量    PIMAGE_NT_HEADERS pNTHeader = NULL;//声明NT头结构体变量    PIMAGE_FILE_HEADER pPEHeader = NULL;//声明PE头结构体变量    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;//声明可选PE头结构体变量    PIMAGE_SECTION_HEADER pSectionHeader = NULL;//声明节表 结构体 变量    DWORD RVA_codeBegin = 0;    //将exe文件读取入内存    size_t size = ReadPEFile(FILEPATH_IN,&pFileBuffer);    //解析PE    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;//Dos头    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);//NT头    pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);//PE头    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);//可选PE头    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);//节表    //判断代码空闲区空间    if((pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize) < SHELLCODELENGTH)    {        printf("代码区空闲空间不足");        free(pFileBuffer);        exit(0);    }    //添加shellcode代码    PBYTE codeBegin = (PBYTE)((DWORD)pFileBuffer+pSectionHeader->PointerToRawData +pSectionHeader->Misc.VirtualSize +8);    printf("codeBegin:%x
    ",codeBegin);    memcpy(codeBegin,shellCode,SHELLCODELENGTH);    //FOA->RVA    if(pOptionHeader->SectionAlignment != pOptionHeader->FileAlignment)    {        RVA_codeBegin = (DWORD)codeBegin - pSectionHeader->PointerToRawData + pSectionHeader->VirtualAddress + pOptionHeader->ImageBase - (DWORD)pFileBuffer;    }else    {        RVA_codeBegin = (DWORD)codeBegin - (DWORD)pFileBuffer;    }    //修正E8    DWORD callAddr = MESSAGEBOXADDR - (RVA_codeBegin+0xD);    printf("callAddr:%x
    ",callAddr);    *(PDWORD)(codeBegin+0x9) = callAddr;    //修正E9    DWORD jmpAddr = (pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - (RVA_codeBegin+0x12);    printf("ImageBase:%x
    AddressEntryPoint:%x
    ",pOptionHeader->ImageBase,pOptionHeader->AddressOfEntryPoint);    *(PDWORD)(codeBegin+0xE) = jmpAddr;    printf("jmpAddr:%x
    ",jmpAddr);    //修正OEP    pOptionHeader->AddressOfEntryPoint = RVA_codeBegin-pOptionHeader->ImageBase;    printf("OEP:%x
    ",RVA_codeBegin-pOptionHeader->ImageBase);    //存入文件    FILE* fp = fopen(FILEPATH_OUT,"wb+");    fwrite(pFileBuffer,size,1,fp);    fclose(fp);}int main(int argc, char* argv[]){    addShellCode();    system("pause");    return 0;}

    注意:每个系统的MessageBox的地址可能都不相同,替换代码中的MessageBox地址。

    以上是今天分享的内容,大家看懂了吗?记得要实际动手操作一下,加深印象。

  • 相关阅读:
    os.environ()详解
    查看django setting 源码
    FBV or CBV django settings 源码 模板层语法 摸板的继承 摸板的导入
    jq
    centos安装docker
    idea mapper报红解决
    Method has too many Body parameters
    Gradle安装配置
    itext生成PDF报错java.io.IOException: The document has no pages
    数字千分位
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/12410109.html
Copyright © 2011-2022 走看看