新增一個節


可以向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;
}
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM