新增一个节

可以向pe程序的空白区添加代码;
但是,如果想要添加一段复杂功能的代码,空白区可能空间不够;
一种解决办法是新增一个节,把自己的代码加到这个节中;
 
1.添加节需要做的事情
添加节需要做两件事:
    1】判断是否有足够的空间
    2】修改的数据
 
pe文件的结构:
可以看到:
    dos头紧接着一堆垃圾数据;
    后面是pe标记、pe头、可选pe头;
    然后一堆节表用来描述各个节的信息;
    后面是一堆节;
 
其中SizeOfHeader最好不要随便变动,因为代价太大;
例如:如果该值增大10个字节,后面的节必须向后偏移10个字节,然后一大堆数据要变;
 
关于节表:
    一个节表有40个字节;
    一个完整的结构,在所有节表后面必须跟一个全0的数;
    也就是必须要有一个40个字节的0;
    如果想在后面新增一个节表,需要保证在新增节表后面有40个字节的空间用来放0;
    也就是说要计算剩下的空间是否够80个(新节表40+补0的40个);
 
1)判断空间是否足够
SizeOfHeader - (DOS头 + 垃圾数据 + PE标记 + 标准PE头 + 可选PE头 + 已存在节表)>= 2个节表的大小
 
2)需要修改的数据
    1】 添加一个新的节(可以copy一份)
    2】 在新增节后面 填充一个节大小的000
    3】 修改PE头中节的数量    
        ->pe头里面的NumberOfSections
    4】修改sizeOfImage的大小 
            ->在可选pe头里面;表示内存镜像中pe文件的大小,因为新增了一个节所以要相应的增加,注意对齐  
    5】 在原有数据的最后,新增一个节的数据(内存对齐的整数倍).
    6】修正新增节表的属性
        ->内存对齐前的大小misc
        ->内存镜像中的偏移VirtualAddress
        ->文件对齐后的尺寸SizeOfRawData
        ->节从哪里开始PointerToRawData
 
2.手动添加一个节
用二进制工具如winhex打开pe文件:
可以看到:从278-1000之间有足够的空间来放一个新节表和全0的40个字节;
 
为了方便,可以将第一个节的数据复制一份,放到最后一个节的后面;
因为第一个节一般是代码节,可以直接运行;
后面再修改数据;
 
修改节的数量以及内存镜像的总大小:
节的数量+1;在pe头中;这里有4个节改为5;
修改SizeOfImage;
如下图,原来的值为7000;考虑到内存对齐,将新节设为1000;要改为8000;
修改:
 
然后修改新加的节表,可以参照最后一个节表的数据来改
节名随便改,只要不重名即可;
VirtualSize    ->对齐前的长度,设为1000够用了;
VirtualAddress    ->内存中的偏移,相当于在最后加了一个节;因此偏移量为内存镜像的大小,也就是SizeOfImage,即7000;
SizeOfRawData    ->文件中对齐后的大小,需要考虑文件对齐,文件对齐为1000,这里要为1000的倍数,就给1000;
PointToRawData    ->文件中的偏移,这个节紧跟最后一个节,这个值为上一个节的偏移6000+上一个节的文件镜像大小1000=7000;
其它的值不重要就先不管,最后一个标志块和是复制的代码节,和那个一样即可,不用管;
 
然后在文件最后面添加一段空间作为新节的数据;
从7000~8000;先不插代码,全补0即可;
添加新节完成;接下来只要在新节中添加代码即可;
 
 
3.极端情况
1)可能最后一个节表后面不是0,而是编译器插入的代码;
因为无法确定这些代码是否有用,不好直接修改;
但没地方添加新节表了;
在dos头和nt头之间有一段无用区域,用来写描述信息,该地方修改不影响程序;
可以把nt头和节表整体向上提,足够80个字节的空间即可;
然后修改dos头存有nt头地址的字段e_flanew即可;
 
2)nt头和dos头之间的空间也不够用时
可以考虑扩充最后一个节;
因为扩充中间的节会造成后面的节整体偏移而定位不准确;
但扩充最后一个节不会有这个问题;
 
4.代码实现新增节
#include "stdafx.h"
#include "petool.h"
#include "string.h"
 
#define SHELLCODELEN 18
#define SRC "C:\\Users\\Administrator\\Desktop\\TraceMe.exe"
#define DEST "C:\\Users\\Administrator\\Desktop\\copy1.exe"
 
//新增一个节并插入代码
void newSec(){
    //定义头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_SECTION_HEADER seHeader = NULL;    //节表指针
    PIMAGE_SECTION_HEADER newSeHeader = NULL;    //新节表指针
 
    LPVOID pFileBuffer = NULL;
 
    //1.加载文件到内存
    DWORD size = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("读取文件失败\n");
        return;
    }
    //2.初始化头指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    seHeader = (PIMAGE_SECTION_HEADER)((DWORD)opHeader + peHeader->SizeOfOptionalHeader);
    //3.判断空间是否够插入节表
    newSeHeader = seHeader + peHeader->NumberOfSections;
 
 
    if((opHeader->SizeOfHeaders - ((DWORD)newSeHeader - (DWORD)pFileBuffer)) < 80){
        printf("空间不够插入节表\n");
        free(pFileBuffer);
        return;
    }
    //4.设置节表
    strcpy((char*)(newSeHeader->Name), ".NewSec");    //节名
    newSeHeader->Misc.VirtualSize = 0x1000;    //对齐前大小设为1000
    newSeHeader->VirtualAddress = 0x7000;        //节的内存偏移为sizeofimage
    newSeHeader->SizeOfRawData = 0x1000;        //节的文件对齐大小设为1000
    newSeHeader->PointerToRawData = 0x7000;    //节的偏移紧接着上一个节的尾部即可;
    newSeHeader->Characteristics = seHeader->Characteristics;    //属性和代码节一样
    //5.插入一个全0的节表
    LPVOID seEnd = (LPVOID) (newSeHeader + 1);
    memset(seEnd, 0, 40);
    //6.修头信息
    peHeader->NumberOfSections = peHeader->NumberOfSections + 1;
    opHeader->SizeOfImage = opHeader->SizeOfImage + 0x1000;
    //7.写出到新文件
    FILE* copy = fopen(DEST, "a+b");
    if(!copy){
        printf("打开写出文件失败\n");
        free(pFileBuffer);
        return;
    }
    size_t m = fwrite(pFileBuffer, size, 1, copy);
    if(!m){
        printf("写出文件第一部分失败\n");
        fclose(copy);
        free(pFileBuffer);
        return;
    }
    //8.追加写出新节表
    LPVOID newBuf = malloc(0x1000);
    if(!newBuf){
        printf("给新节申请内存失败\n");
        free(pFileBuffer);
        return;
    }
    memset(newBuf, 0, 0x1000);
    size_t n = fwrite(newBuf, 0x1000, 1, copy);
    if(!n){
        printf("写出第二部分失败\n");
        free(pFileBuffer);
        free(newBuf);
        newBuf = NULL;
        fclose(copy);
        return;
    }
    //9.释放内存返回
    fclose(copy);
    free(pFileBuffer);
    free(newBuf);
    printf("存盘成功\n");
    return;
 
 
}
 
int main(int argc, char* argv[])
{
    newSec();
    getchar();
    return 0;
}
 
 
 
 
posted @ 2019-10-24 11:34  L丶银甲闪闪  阅读(839)  评论(0编辑  收藏  举报