比較好了解的圖檔解釋
滴水逆向-RVA和FOA之間互相轉換 核心代碼部分
// gbpeall.cpp: implementation of the gbpeall class.
//
//
#include "stdafx.h"
#include "gbpeall.h"
//
// Construction/Destruction
//
//定義一個全局變量
BYTE ShellCode[] =
{
0x6A,00,0x6A,00,0x6A,00,0x6A,00, //MessageBox push 0的寫死
0xE8,00,00,00,00, // call彙編指令E8和後面待填充的寫死
0xE9,00,00,00,00 // jmp彙編指令E9和後面待填充的寫死
};
//ExeFile->FileBuffer 傳回值為計算所得檔案大小
//讀取一個exe檔案,然後輸出為FileBuffer
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* pFileBuffer)
{
//下面有個IN和OUT,大緻意思就是參數的類型傳入進來之後不進行宏擴充;
//啥也不幹,即使了解成幹,也是擴充成空白,這個是C++文法中允許的;
//LPSTR ----> typedef CHAR *LPSTR, *PSTR; 意思就是char* 指針;在WINNT.H頭檔案裡面
FILE* pFile = NULL;
//定義一個FILE結構體指針,在标準的Stdio.h檔案頭裡面
//可參考:https://blog.csdn.net/qq_15821725/article/details/78929344
DWORD fileSize = 0;
// typedef unsigned long DWORD; DWORD是無符号4個位元組的整型
LPVOID pTempFileBuffer = NULL;
//LPVOID ----> typedef void far *LPVOID;在WINDEF.H頭檔案裡面;别名的void指針類型
//打開檔案
pFile = fopen(lpszFile,"rb"); //lpszFile是當作參數傳遞進來
if (!pFile)
{
printf("打開檔案失敗!\r\n");
return 0;
}
/*
關于在指針類型中進行判斷的操作,下面代碼出現的情況和此一樣,這裡解釋下:
1.因為指針判斷都要跟NULL比較,相當于0,假值,其餘都是真值
2.if(!pFile)和if(pFile == NULL), ----> 為空,就執行語句;這裡是兩個等于号不是一個等于号
3.if(pFile)就是if(pFile != NULL), 不為空,就執行語句;
*/
//讀取檔案内容後,擷取檔案的大小
fseek(pFile,0,SEEK_END);
fileSize = ftell(pFile);
fseek(pFile,0,SEEK_SET);
/*
fseek 通過使用二進制的方式打開檔案,移動檔案讀寫指針的位置,在stdio.h頭檔案裡
int fseek(FILE * stream, long offset, int fromwhere);
上面是fseek的函數原型
第一個參數stream 為檔案指針
第二個參數offset 為偏移量,整數表示正向偏移,負數表示負向偏移
第三個參數fromwhere 為指針的起始位置,設定從檔案的哪裡開始偏移,可能取值為:SEEK_CUR,SEEK_END,SEEK_SET
SEEK_SET 0 檔案開頭
SEEK_CUR 1 目前讀寫的位置
SEEK_END 2 檔案尾部
下面是相關用法和例子:
fseek(fp,100L,0);把fp指針移動到離檔案開頭100位元組處;
fseek(fp,100L,1);把fp指針移動到離檔案目前位置100位元組處;
fseek(fp,100L,2);把fp指針退回到離檔案結尾100位元組處。
fseek(fp,0,SEEK_SET);将讀寫位置移動到檔案開頭;
fseek(fp,0,SEEK_END);将讀寫位置移動到檔案尾時;
fseek(fp,100L,SEEK_SET);将讀寫位置移動到離檔案開頭100位元組處;
fseek(fp,100L,SEEK_CUR);将讀寫位置移動到離檔案目前位置100位元組處;
fseek(fp,-100L,SEEK_END);将讀寫指針退回到離檔案結尾100位元組處;
fseek(fp,1234L,SEEK_CUR);把讀寫位置從目前位置向後移動1234位元組;
fseek(fp,0L,2);把讀寫位置移動到檔案尾;
其中 ---> L字尾表示長整數
ftell()用于傳回檔案目前指針指向的位置,與fseek配合可以算出檔案元素資料總數。
參考:http://c.biancheng.net/cpp/html/2519.html
ftell()函數用來擷取檔案讀寫指針的目前位置,其原型為:long ftell(FILE * stream); 同樣在stdio.h頭檔案裡
參數:stream 為已打開的檔案指針。
*/
//動态申請記憶體空間
pTempFileBuffer = malloc(fileSize);
/*
參考:http://c.biancheng.net/cpp/html/137.html
原型:void* malloc (size_t size);
size_t ---> typedef unsigned int size_t; 無符号整型别名是size_t
void* ---> 函數的傳回值類型是 void* ;void并不是說沒有傳回值或者傳回空指針,而是傳回的指針類型未知;
是以在使用 malloc() 時通常需要進行強制類型轉換,将 void 指針轉換成我們希望的類型;
例如:char *ptr = (char *)malloc(10); //配置設定10個位元組的記憶體空間,用來存放字元
參數說明 ---> size 為需要配置設定的記憶體空間的大小,以位元組(Byte)計。
函數說明 ---> malloc()在堆區配置設定一塊指定大小的記憶體空間,用來存放資料。這塊記憶體空間在函數執行完成後不會被初始化;
它們的值是未知的,是以配置設定完成記憶體之後需要初始化;
傳回值:配置設定成功傳回指向該記憶體的位址,失敗則傳回 NULL。
*/
if (!pTempFileBuffer)
{
printf("記憶體配置設定失敗!\r\n");
fclose(pFile);
return 0;
}
//根據申請到的記憶體空間,讀取資料
size_t n = fread(pTempFileBuffer,fileSize,1,pFile);
if (!n)
{
printf("讀取資料失敗!\r\n");
free(pTempFileBuffer); // 釋放記憶體空間
fclose(pFile); // 關閉檔案流
return 0;
}
//資料讀取成功,關閉檔案
*pFileBuffer = pTempFileBuffer; // 将讀取成功的資料所在的記憶體空間的首位址放入指針類型pFileBuffer
pTempFileBuffer = NULL; // 初始化清空臨時申請的記憶體空間
fclose(pFile); // 關閉檔案
return fileSize; // 傳回擷取檔案的大小
}
//CopyFileBuffer --> ImageBuffer
//将讀取的FileBuffer拉伸加載到ImageBuffer,用作測試驗證檔案拉伸;
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer)
{
//LPVOID ----> typedef void far *LPVOID;在WINDEF.H頭檔案裡面;别名的void指針類型
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTempImageBuffer = NULL;
/*
上面都是PE裡面的相關結構體類型,使用其類型進行自定義變量,并初始化值為NULL
PIMAGE_DOS_HEADER ---> 指向結構體,别名為這兩個 IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER
PIMAGE_NT_HEADERS ---> 指向結構體,typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
PIMAGE_FILE_HEADER ---> 指向結構體,别名為這兩個 IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
PIMAGE_OPTIONAL_HEADER32 ---> 指向結構體,别名為這兩個 IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
PIMAGE_SECTION_HEADER ---> 指向結構體,别名為這兩個 IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
*/
if (pFileBuffer == NULL)
{
printf("FileBuffer 擷取失敗!\r\n");
return 0;
}
//判斷是否是有效的MZ标志
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("無效的MZ辨別\r\n");
return 0;
}
/*
IMAGE_DOS_SIGNATURE 這個在頭檔案WINNT.H裡面,對應是個無參數宏;
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
在宏擴充的時候就會替換為0x5A4D ,然後根據架構的不同進行排序存儲,分大端和小端模式;
使用上面方式進行比對是否是有效的MZ頭是非常有效;
而且IMAGE_DOS_SIGNATURE存儲的值是兩個位元組,剛好就是PWORD ---> typedef WORD near *PWORD;
是以在進行比較的時候需要強制類型轉換為相同的類型進行比較
*/
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//這裡的定義,就相當于已經确定了,其頭肯定是MZ了,然後強制轉換類型為PIMAGE_DOS_HEADER,就是Dos頭
//判斷是否是有效的PE标志
if (*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("無效的PE标記\r\n");
return 0;
}
/*
IMAGE_NT_SIGNATURE ---> #define IMAGE_NT_SIGNATURE 0x00004550 // PE00
上述同樣是個宏擴充,在頭檔案WINNT.H裡面;
在進行比對的時候因為在Dos頭裡面有個值是 e_lfanew 對應的時候DWORD類型,是以在進行指針相加的時候
需要先進行強制類型轉換,然後相加,即移動指針位置;然後最終需要比對的結果是0x4550站兩個位元組
是以又要強制轉換類型為PWORD;
*/
//定位NT頭
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
//上面偏移完成之後pFileBuffer的指針偏移到了NT頭---> pNTHeader
//****************************************************************************************
//定位PE檔案頭
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);
//根據PE頭的結構體内容,PE檔案頭位置在NT頭首位址偏移4個位元組即可得到pPEHeader
//****************************************************************************************
//定位可選PE頭
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
/*
要得到可選PE的首位址位置,就根據上面得到的PE檔案頭位置裡面的IMAGE_SIZEOF_FILE_HEADER來定位;
IMAGE_SIZEOF_FILE_HEADER也是個宏擴充,裡面位元組描述了PE檔案頭的大小是20個位元組;
#define IMAGE_SIZEOF_FILE_HEADER 20,是以隻要在PE檔案頭的首位址偏移20個位元組即可移動到可選PE頭;
指針相加的時候,此處的類型依然是DWORD
*/
//****************************************************************************************
//第一個節表指針
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
/*
這裡要移動到第一個節表指針的首位址,就需要根據上面标準PE檔案頭中的SizeOfOptionalHeader擷取具體可選PE
頭的大小,然後根據這個大小進行偏移即可;
*/
//****************************************************************************************
/*
到了節表的首位址位置之後,因為需要将FileBuffer複制到ImageBuffer,這個過程中,節表之前的Dos頭,NT頭
PE檔案頭,可選PE頭,她們的大小都是不變的,是以定位出來之後,到後面的操作中直接複制即可,而節表不一樣
她在FileBuffer狀态和ImageBuffer狀态是不相同的,她們節表之間複制轉換到ImageBuffer是需要拉長節表,是以
在操作的時候是需要确定FileBuffer到ImageBuffer之後ImageBuffer的大小是多少,而這個大小,已經在可選PE頭
裡面的某一個值中已經給出來了 ---> SizeOfImage ;
注意:FileBuffer和ImageBuffer都是在記憶體中的展示,隻不過FileBuffer是使用winhex等類似的形式打開檢視其
二進制的形式,而ImageBuffer則是輕按兩下打開應用程式,将其加載至記憶體中顯示的二進制的形式;
*/
//****************************************************************************************
//根據SizeOfImage申請新的記憶體空間
pTempImageBuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImageBuffer)
{
printf("再次在堆中申請一塊記憶體空間失敗\r\n");
return 0;
}
//因為下面要開始對記憶體空間進行複制操作,是以需要初始化操作,将其置為0,避免垃圾資料,或者其他異常
//初始化新的緩沖區
memset(pTempImageBuffer,0,pOptionHeader->SizeOfImage);
/*
參考:http://c.biancheng.net/cpp/html/157.html
在頭檔案string.h裡面
void* memset( void* ptr,int value,size_t num );
memset()函數用來将指定記憶體的前n個位元組設定為特定的值;
參數說明:
ptr 為要操作的記憶體的指針;
value 為要設定的值;既可以向value傳遞int類型的值,也可以傳遞char類型的值,int和char可以根據ASCII碼互相轉換;
num 為ptr的前num個位元組,size_t就是unsigned int。
函數說明:memset()會将ptr所指的記憶體區域的前num個位元組的值都設定為value,然後傳回指向ptr的指針;
*/
//****************************************************************************************
//根據SizeOfHeaders大小的确定,先複制Dos頭
memcpy(pTempImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
/*
參考:http://c.biancheng.net/cpp/html/155.html
在頭檔案string.h裡面
void* memcpy (void* dest,const void* src,size_t num);
memcpy()函數功能用來複制記憶體的;她會複制src所指向内容的首位址,作為起始位置,然後偏移num個位元組到dest所指的記憶體位址
的位置;此函數有個特征就是,她并不關心被複制的資料類型,隻是逐位元組地進行複制,這給函數的使用帶來了很大的靈活性,
可以面向任何資料類型進行複制;
需要注意的是:
dest 指針要配置設定足夠的空間,也就是要大于等于num位元組的空間,如果沒有配置設定足夠的空間會出現錯誤;
dest和src所指的記憶體空間不能重疊(如果發生了重疊,使用 memmove() 會更加安全)。
是以上面的代碼的含義如下:
(1)pDosHeader ---> 是指向pFileBuffer的首位址,也就是記憶體複制的時候從這裡開始;
(2)pTempImageBuffer ---> 這裡是表示上面要複制的目的,要把内容複制到這塊記憶體來;
(3)pOptionHeader->SizeOfHeaders ---> 這裡表示複制多大的内容到pTempImageBuffer裡面去;
(4)從上面看來我們就知道複制到目标pOptionHeader->SizeOfHeaders所在的記憶體空間一定要比pTempImageBuffer大;
*/
//****************************************************************************************
//上面把已經确定的頭都複制好了,那麼下面就可以開始複制節的裡面的内容,因為節不僅僅是一個,是以需要用到for循環進行操作
//根據節表循環copy節的内容
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
//定義一個臨時節表的指針
for (int i=0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress),
(void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);
}
/*
上面的大概操作就是根據标準PE檔案頭裡面的值 NumberOfSections确定有幾個節,然後不斷的計算并增加指針偏移位置,不停的複制
PointerToRawData ---> 節在檔案中的偏移位址;
VirtualAddress ---> 節在記憶體中的偏移位址;
SizeOfRawData ---> 節在檔案中對齊後的尺寸;
(void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress) ---> Dest(目的地)
上面我們已經知道了函數memcpy是怎麼複制操作的,是以這裡我們依依解釋下:
首先我們知道,上面展示的是目的地,而且我們的目的是要從FileBuffer節内容複制到ImageBuffer節的内容,
那麼要使用到的是檔案被輕按兩下打開之後在記憶體中的偏移位址,這個位址就是VirtualAddress;這裡舉個例子:
正常打開notepad.exe,然後使用winhex加載這個notepad.exe的記憶體資料,同時使用PE解析工具得到兩個值的資訊如下:
可選PE頭 ---> ImageBase ---> 0x01000000
第一個節表顯示的VirtualAddress ---> 00001000
上面兩個值相加就得到了檔案被打開在記憶體中第一個節的真實資料的起始位置 ---> 0x01001000
檢視winhex對應的位址,确認是對的;
(void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData) ---> Src(源複制的起始記憶體位址)
同樣是上面的例子:
PointerToRawData是節在檔案中的偏移位址,而我們知道,在檔案中和在記憶體中是不一樣的,因為在記憶體中有ImageBase的說法,
但在檔案中沒有,是以她的起始位置就是檔案存儲在硬碟的時候使用winhex打開的開頭位置,為這裡同樣使用winhex以二進制的形式
打開notepad.exe(非輕按兩下打開),發現檔案的起始位置是0x00000000,同時使用PE解析工具确認出了PointerToRawData的值
PointerToRawData ---> 0x00000400 ; 起始位置為0x00000000 ,她們相加就得到第一個節表的起始位置為0x00000400
檢視winhex對應的位址,确認是對的;
是以這裡總結下來的Src,就是記憶體複制的時候,從這個偏移位址開始拿資料開始複制;
pTempSectionHeader->SizeOfRawData
這裡就是告訴我們上面複制要複制多大的内容到 (void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress)
SizeOfRawData ---> 節在檔案中對齊後的尺寸;
例子還是以上面的為例:
通過PE解析工具确認SizeOfRawData的大小為:0x00007800
總結:
memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress),
(void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData),
pTempSectionHeader->SizeOfRawData);
上面代碼就是在檔案中的形式找到要複制的位置0x00000400的起始位置開始複制,要複制0x00007800個位元組大小,也就是從
0x00000400這個位址開始向後偏移7800個位元組,将這些資料複制到檔案輕按兩下被打開時候的記憶體位址0x01001000為起點向後覆寫複制
完成即可,為這裡測試算了下;0x00000400+0x00007800=0x00007C00 ; 0x00007C00這個位址剛好是第二個節的PointerToRawData
這樣就可以很好的了解for循環對第二個節的複制;
*/
//****************************************************************************************
//傳回資料
*pImageBuffer = pTempImageBuffer;
//将複制好後節的首位址儲存到指針pImageBuffer中
pTempImageBuffer = NULL;
//初始化清空臨時使用的pTempImageBuffer
return pOptionHeader->SizeOfImage;
}
//FileBuffer ---> NewImageBuffer(新增節操作)?
//通過複制FileBuffer并增加1000H到新的NewImageBuffer,用作新增節;
DWORD CopyFileBufferToNewImageBuffer(IN LPVOID pFileBuffer,IN size_t fileSize,OUT LPVOID* pNewImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeder = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pLastSectionHeader = NULL;
LPVOID pTempNewImageBuffer = 0;
DWORD sizeOfFile = 0;
DWORD numberOfSection = 0;
DWORD okAddSections = 0;
//判斷讀取pFileBuffer讀取是否成功
if (!pFileBuffer)
{
printf("緩沖區指針無效\r\n");
return 0;
}
//判斷是否為MZ标志
if ((*(PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)// IMAGE_DOS_SIGNATURE --> MZ
{
printf("不是一個有效的MZ标志\r\n");
return 0;
}
//判斷是否為PE标志
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if (*((PWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // IMAGE_NT_SIGNATURE --> PE
{
printf("不是有效的PE标志\r\n");
return 0;
}
//*********************申請開辟記憶體空間*****************************************************************
sizeOfFile = fileSize+0x1000;
pTempNewImageBuffer = malloc(sizeOfFile);
//判斷記憶體空間開辟是否成功
if (!pTempNewImageBuffer)
{
printf("pTempNewImageBuffer開辟記憶體空間失敗\r\n");
return 0;
}
//初始化記憶體内容
memset(pTempNewImageBuffer,0,sizeOfFile);
//初始化完成之後,先把為修改的記憶體空間全部拷貝到新的記憶體空間
memcpy(pTempNewImageBuffer,pFileBuffer,fileSize);
//定位Dos頭位址
pDosHeader = (PIMAGE_DOS_HEADER)(pTempNewImageBuffer);
//定位NT頭的位址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pTempNewImageBuffer+pDosHeader->e_lfanew);
//定位标志PE頭位址
pPEHeder = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+0x04);//PE SIGNATURE 站4個位元組
//定位可選PE頭位址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)(((DWORD)pPEHeder)+IMAGE_SIZEOF_FILE_HEADER);//IMAGE_SIZEOF_FILE_HEADER -> 20個位元組
//定位第一個節表位址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeder->SizeOfOptionalHeader);
//定位最後一個節表的位址
pLastSectionHeader = &pSectionHeader[pPEHeder->NumberOfSections-1];
//判斷是否有足夠的空間添加一個節表
//判斷條件:
/*
SizeOfHeader - (DOS + 垃圾資料 + PE标記 + 标準PE頭 + 可選PE頭 + 已存在節表) >= 2個節表的大小
SizeOfHeader在可選PE頭裡面
*/
okAddSections = (DWORD)(pOptionHeader->SizeOfHeaders - (pDosHeader->e_lfanew + 0x04 + \
sizeof(PIMAGE_FILE_HEADER) + pPEHeder->SizeOfOptionalHeader + sizeof(PIMAGE_SECTION_HEADER) \
* pPEHeder->NumberOfSections));
if (okAddSections < 2*sizeof(PIMAGE_SECTION_HEADER))
{
printf("這個exe檔案頭不剩餘空間不夠\r\n");
free(pTempNewImageBuffer);
return 0;
}
//上面沒問題,就開始修改内容了
//*************************修改内容*******************************************************************
//初始化新節表資訊
PWORD pNumberOfSection = &pPEHeder->NumberOfSections;
PDWORD pSizeOfImage = &pOptionHeader->SizeOfImage;
numberOfSection = pPEHeder->NumberOfSections;
PVOID pSecName = &pSectionHeader[numberOfSection].Name;
PDWORD pSecMisc = &pSectionHeader[numberOfSection].Misc.VirtualSize;
PDWORD pSecVirtualAddress = &pSectionHeader[numberOfSection].VirtualAddress;
PDWORD pSecSizeOfRawData = &pSectionHeader[numberOfSection].SizeOfRawData;
PDWORD pSecPointToRawData = &pSectionHeader[numberOfSection].PointerToRawData;
PDWORD pSecCharacteristics = &pSectionHeader[numberOfSection].Characteristics;
//修改PE檔案頭裡面的節數量資訊
printf("*pNumberOfSection:%#X \r\n",pPEHeder->NumberOfSections);
*pNumberOfSection = pPEHeder->NumberOfSections + 1;
printf("*pNumberOfSection:%#X \r\n",pPEHeder->NumberOfSections);
//修改PE可選頭裡面SizeOfImage資訊
printf("*pSizeOfImage:%#X \r\n",pOptionHeader->SizeOfImage);
*pSizeOfImage = pOptionHeader->SizeOfImage + 0x1000;
printf("*pSizeOfImage:%#X \r\n",pOptionHeader->SizeOfImage);
//向節表中添加資料
memcpy(pSecName,".newSec",8);
*pSecMisc = 0x1000;
//這裡VirtualAddress的位址需要根據最後一個節表中對齊前記憶體中的實際大小?
//和檔案中對齊後的大小,分别使用VirtualAddress加上她們的值,哪個大,就是
//哪個;
//VirtualAddress+max(VirtualSize,SizeOfRawData)
//就是上面的公式
//判斷出要添加的值
DWORD add_size = pLastSectionHeader->Misc.VirtualSize > pLastSectionHeader->SizeOfRawData?\
pLastSectionHeader->Misc.VirtualSize:pLastSectionHeader->SizeOfRawData;
//上面是個三目運算符
printf("pLastSectionHeader: %#X \r\n",pLastSectionHeader);
printf("add_size: %#X \r\n",add_size);
printf("numberOfSection: %#X \r\n",pPEHeder->NumberOfSections);
printf("pLastSectionHeader->Misc.VirtualSize: %#X \r\n",pLastSectionHeader->Misc.VirtualSize);
printf("pLastSectionHeader->SizeOfRawData: %#X \r\n",pLastSectionHeader->SizeOfRawData);
printf("add_size: %#X \r\n",add_size);
*pSecVirtualAddress = pLastSectionHeader->VirtualAddress + add_size;
//SectionAlignment對齊
if (*pSecVirtualAddress % pOptionHeader->SectionAlignment)
{
*pSecVirtualAddress = *pSecVirtualAddress / pOptionHeader->SectionAlignment * \
pOptionHeader->SectionAlignment + pOptionHeader->SectionAlignment;
}
*pSecSizeOfRawData = 0x1000;
*pSecPointToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
//FileAlignment對齊
if (*pSecPointToRawData % pOptionHeader->FileAlignment)
{
*pSecPointToRawData = *pSecPointToRawData / pOptionHeader->FileAlignment * \
pOptionHeader->FileAlignment + pOptionHeader->FileAlignment;
}
*pSecCharacteristics = 0xFFFFFFFF;
*pNewImageBuffer = pTempNewImageBuffer;
pTempNewImageBuffer = NULL;
return sizeOfFile;
}
//求對齊後的大小
//Actuall_size ---> 記憶體中對齊前實際的大小 ---> VirtualSize
//Align_size ---> 檔案中對齊後的大小 ---> SizeOfRawData
DWORD AlignLength(DWORD Actuall_size,DWORD Align_size)
{
if (Actuall_size % Align_size == 0)
{
return Actuall_size;
}
else
{
DWORD n = Actuall_size / Align_size;
return Align_size*(n+1);
}
}
//ImageBuffer ---> NewImageBuffer
//将拉伸後加載到記憶體的ImageBuffer存入到NewImageBuffer,修改資料完成之後,準備存盤操作
DWORD FileBufferToModifyImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pNewImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pLastSectionHeader = NULL;
LPVOID pTempNewImageBuffer = NULL;
DWORD ImageBuffer_Size = 0;
DWORD numberOfSection = 0;
//判斷讀取pImageBuffer是否成功
if (!pFileBuffer)
{
printf("緩沖區指針無效\r\n");
}
//判斷是否是有效的MZ頭
if ((*(PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ頭\r\n");
return 0;
}
//判斷是否是有效的PE頭
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE頭\r\n");
return 0;
}
//定位NT頭
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
//定位标準的PE檔案頭
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04);
//定位可選PE頭
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
//定位第一個節表位址
numberOfSection = pPEHeader->NumberOfSections;
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
//定位最後一個節表位址
pLastSectionHeader = &pSectionHeader[numberOfSection-1];
// printf("numberOfSection --> %#X \r\n",numberOfSection);
// printf("*pSectionHeader --> %#X \r\n",pSectionHeader->Misc.VirtualSize);
// printf("*pLastSectionHeader --> %#X \r\n",&pLastSectionHeader);
//開始操作需要修改的部分
//最後一個節中記憶體中對齊前的大小;
PDWORD pVirtualSize = &pLastSectionHeader->Misc.VirtualSize;
//最後一個節在檔案中對齊後的大小;
PDWORD pSizeOfRawData = &pLastSectionHeader->SizeOfRawData;
//檔案中SizeOfImage的大小;
PDWORD pSizeOfImage = &pOptionHeader->SizeOfImage;
//擴充修改之前的資料
// printf("&pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",pVirtualSize);
// printf("*pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",*pVirtualSize);
//
// printf("&pLastSectionHeader->SizeOfRawData --> %#X \r\n",pSizeOfRawData);
// printf("*pLastSectionHeader->SizeOfRawData --> %#X \r\n",*pSizeOfRawData);
//
// printf("&pOptionHeader->SizeOfImage --> %#X \r\n",pSizeOfImage);
// printf("*pOptionHeader->SizeOfImage --> %#X \r\n",*pSizeOfImage);
//擴充修改pVirtualSize
*pVirtualSize = AlignLength(*pVirtualSize,pOptionHeader->SectionAlignment)+0x1000;
// printf("&pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",pVirtualSize);
// printf("*pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",*pVirtualSize);
// printf("&pLastSectionHeader->SizeOfRawData --> %#X \r\n",pSizeOfRawData);
// printf("*pLastSectionHeader->SizeOfRawData --> %#X \r\n",*pSizeOfRawData);
//
//擴充修改pSizeOfRawData
*pSizeOfRawData = AlignLength(*pSizeOfRawData,pOptionHeader->SectionAlignment)+0x1000;
// printf("&pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",pVirtualSize);
// printf("*pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",*pVirtualSize);
// printf("&pLastSectionHeader->SizeOfRawData --> %#X \r\n",pSizeOfRawData);
// printf("*pLastSectionHeader->SizeOfRawData --> %#X \r\n",*pSizeOfRawData);
// printf("&pOptionHeader->SizeOfImage --> %#X \r\n",pSizeOfImage);
// printf("*pOptionHeader->SizeOfImage --> %#X \r\n",*pSizeOfImage);
//修改SizeOfImage
*pSizeOfImage += 0x1000;
// printf("&pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",pVirtualSize);
// printf("*pLastSectionHeader->Misc.VirtualSize --> %#X \r\n",*pVirtualSize);
// printf("&pLastSectionHeader->SizeOfRawData --> %#X \r\n",pSizeOfRawData);
// printf("*pLastSectionHeader->SizeOfRawData --> %#X \r\n",*pSizeOfRawData);
// printf("&pOptionHeader->SizeOfImage --> %#X \r\n",pSizeOfImage);
// printf("*pOptionHeader->SizeOfImage --> %#X \r\n",*pSizeOfImage);
//得到修改之後的大小準備申請記憶體空間
ImageBuffer_Size = pOptionHeader->SizeOfImage;
pTempNewImageBuffer = malloc(ImageBuffer_Size);
if (!pTempNewImageBuffer)
{
printf("配置設定記憶體空間失敗\r\n");
return 0;
}
//初始化記憶體空間
memset(pTempNewImageBuffer,0,ImageBuffer_Size);
//複制SizeOfHeaders
memcpy(pTempNewImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
//建立臨時節的結構體指針,周遊資料
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{
memcpy((PVOID)((DWORD)pTempNewImageBuffer+pTempSectionHeader->VirtualAddress),\
(void*)((DWORD)pFileBuffer+pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);
}
*pNewImageBuffer = pTempNewImageBuffer;
pTempNewImageBuffer = NULL;
return *pSizeOfImage;
}
//将ImageBuffer更改為一個節表和節然後壓縮為NewBuffer,供存盤準備
DWORD FileBufferToModifyOneImageBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pLastSectionHeader = NULL;
LPVOID pTempNewBuffer = NULL;
DWORD finalSections_Size = 0;
DWORD numberOfSection = 0;
DWORD lastSectionsMax_Size = 0;
DWORD sizeOfFile = 0;
//判斷讀取pImageBuffer是否成功
if (pImageBuffer == NULL)
{
printf("緩沖區指針無效\r\n");
}
//判斷是否是有效的MZ标志
if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ頭\r\n");
return 0;
}
//判斷是否是有效的PE标志
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if (*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\r\n");
return 0;
}
//定位NT頭
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
//定位标準的PE檔案頭
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04);
//定位可選PE頭
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
//定位第一個節表位址
numberOfSection = pPEHeader->NumberOfSections;
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
//定位最後一個節表位址
pLastSectionHeader = &pSectionHeader[numberOfSection-1];
//最後一個節中記憶體對齊前的大小;
PDWORD pVirtualSize = &pLastSectionHeader->Misc.VirtualSize;
//最後一個節在檔案中對齊後的大小;
PDWORD pSizeOfRawData = &pLastSectionHeader->SizeOfRawData;
//計算最後一個節中最大值是SizeOfRawData還是VirtualSize
lastSectionsMax_Size = *pSizeOfRawData > *pVirtualSize ? *pSizeOfRawData : *pVirtualSize;
//計算最終合并後節對應SizeOfRawData或VirtualSize的大小
finalSections_Size = pLastSectionHeader->VirtualAddress + lastSectionsMax_Size - pOptionHeader->SizeOfHeaders;
//修改第一個節的SizeOfRawData和VirtualSize的值,将其更改為上面計算出來的最終值
pSectionHeader->Misc.VirtualSize = finalSections_Size;
pSectionHeader->SizeOfRawData = finalSections_Size;
//修改pPEHeader->NumberOfSections的屬性為0x01
numberOfSection = 0x01;
sizeOfFile = pOptionHeader->SizeOfHeaders + finalSections_Size;
//使用winhex打開notepad.exe 是0x00000400,這是第一個節之前的所有大小
// for(DWORD i = 0;i<numberOfSection;i++)
// {
// sizeOfFile += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一種加法
// }
//根據SizeOfImage申請新的空間
pTempNewBuffer = malloc(sizeOfFile);
if (!pTempNewBuffer)
{
printf("申請記憶體空間失敗\r\n");
return 0;
}
//初始化新的緩沖區
memset(pTempNewBuffer,0,sizeOfFile);
//根據SizeOfHeaders 先copy頭
memcpy(pTempNewBuffer,pDosHeader,sizeOfFile);
//根據節表循環複制節
//PIMAGE_SECTION_HEADER pTempSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader);
// PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
// for (int j=0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++)
// {
// memcpy((PDWORD)((DWORD)pTempNewBuffer+pTempSectionHeader->PointerToRawData),
// (PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),
// pTempSectionHeader->SizeOfRawData);
// }
//傳回資料
*pNewBuffer = pTempNewBuffer;
pTempNewBuffer = NULL;
return sizeOfFile;
}
//ImageBuffer ---> NewBuffer
//将拉伸後加載到記憶體的ImageBuffer存入到NewBuffer裡面,然後準備存盤;
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer)
{
//下面大部分操作都是跟上面一樣的,這裡就不再贅述了
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTempNewBuffer = NULL;
DWORD sizeOfFile = 0;
DWORD numberOfSection = 0;
if (pImageBuffer == NULL)
{
printf("緩沖區指針無效\r\n");
}
//判斷是否是有效的MZ标志
if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ頭\r\n");
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//判斷是否是有效的PE标志
if (*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\r\n");
return 0;
}
//NT頭位址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//标準PE檔案頭
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
//可選PE頭
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
//第一個節表位址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//計算檔案需要的空間--最後一個節的檔案偏移+節對齊後的長度
/*
numberOfSection = pPEHeader->NumberOfSections;
pSectionHeader = pSectionHeader[numberOfSection-1];
sizeOfFile = (pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize + pOptionHeader->FileAlignment);
printf("sizeOfFile %X \r\n",sizeOfFile);
for (DWORD i=0;i<=numberOfSection;i++)
{
sizeOfFile += sizeOfFile[i];
}
*/
sizeOfFile = pOptionHeader->SizeOfHeaders;
//使用winhex打開notepad.exe 是0x00000400,這是第一個節之前的所有大小
for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++)
{
sizeOfFile += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一種加法
}
/*
上面的for循環大概意思就是基于幾個節的數量依次循環疊加sizeOfFile的值;因為SizeOfRawData是檔案中對齊後的大小;
是以循環計算如下:
sizeOfFile = 0x00000400 + 0x00007800 = 0x00007C00
sizeOfFile = 0x00007C00 + 0x00000800 = 0x00008400
sizeOfFile = 0x00008400 + 0x00008000 = 0x00010400
*/
//根據SizeOfImage申請新的空間
pTempNewBuffer = malloc(sizeOfFile);
if (!pTempNewBuffer)
{
printf("申請記憶體空間失敗\r\n");
return 0;
}
//初始化新的緩沖區
memset(pTempNewBuffer,0,sizeOfFile);
//根據SizeOfHeaders 先copy頭
memcpy(pTempNewBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
//根據節表循環複制節
//PIMAGE_SECTION_HEADER pTempSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader);
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (int j=0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++)
{
/*memcpy((LPVOID)((DWORD)pTempNewBuffer + pTempSectionHeader->PointerToRawData),
(LPVOID)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress),
pTempSectionHeader->SizeOfRawData);*/
//PointerToRawData節區在檔案中的偏移,VirtualAddress節區在記憶體中的偏移位址,SizeOfRawData節在檔案中對齊後的尺寸
memcpy((PDWORD)((DWORD)pTempNewBuffer+pTempSectionHeader->PointerToRawData),
(PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),
pTempSectionHeader->SizeOfRawData);
//printf("%X --> PoniterToRadata\r\n",pTempSectionHeader->PointerToRawData);
//printf("%X --> VirtualAddress\r\n",pTempSectionHeader->VirtualAddress);
//printf("%X --> VirtualSize\r\n",pTempSectionHeader->Misc.VirtualSize);
}
//傳回資料
*pNewBuffer = pTempNewBuffer;
pTempNewBuffer = NULL;
return sizeOfFile;
}
//将上面得到的MemBuffer存盤到本地;
BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile)
{
FILE* fp = NULL;
fp = fopen(lpszFile, "wb+");
if (!fp) // 這裡我剛開始寫漏了一個等于号,變成複制NULL了,導緻錯誤
// if(fp == NULL) 可以這麼寫,沒問題
{
fclose(fp);
return FALSE;
}
fwrite(pMemBuffer,size,1,fp);
fclose(fp);
fp = NULL;
return TRUE;
}
//RVA格式轉換FOA --- RvaToFileOffset
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
DWORD numberOfSection = 0;
DWORD dwFOAValue = 0;
//判斷指針是否有效
if (!pFileBuffer)
{
printf("pFileBuffer 指針無效\r\n");
return 0;
}
//判斷是否是有效的MZ标志
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("pFileBuffer不是有效的MZ标志\r\n");
return 0;
}
//判斷是否是一個有效的PE标志
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("pFileBuffer不是一個有效的PE标志\r\n");
return 0;
}
printf("目前的Rva位址: %#X \r\n",dwRva);
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//定義個臨時節表指針進行下面的計算操作
numberOfSection = pPEHeader->NumberOfSections;
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
//判斷dwRva所處的節
if (dwRva <= pOptionHeader->SizeOfHeaders)
{
return (DWORD)dwRva;
}
//上面是判斷如果rva位址所處的節在第一個節之前那麼直接傳回rva的位址;
//否則下面就是開始周遊查找節;
else
{
for (DWORD n = 0; n < numberOfSection; n++, pTempSectionHeader++)
{//下面是判斷在哪個節的範圍,然後根據rva所在的位址減去所在節的VirtualAddress得到的偏移值加上檔案中對應節的偏移值PointerToRawData
if ((dwRva >= pTempSectionHeader->VirtualAddress) && (dwRva < pTempSectionHeader->VirtualAddress + pTempSectionHeader->Misc.VirtualSize))
{
dwFOAValue = dwRva - pTempSectionHeader->VirtualAddress + pTempSectionHeader->PointerToRawData;
}
else
{
printf("RvaToFoa 轉換失敗!\r\n");
return 0;
}
}
}
return dwFOAValue;
}
//FOA格式轉換RVA --- ImageOffset
DWORD FoaToImageOffset(IN LPVOID pFileBuffer,IN DWORD dwFoa)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
DWORD numberOfSection = 0;
DWORD dwRVAValue = 0;
//判斷指針是否有效
if (!pFileBuffer)
{
printf("pFileBuffer 指針無效\r\n");
return 0;
}
//判斷是否是有效的MZ标志
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("pFileBuffer不是有效的MZ标志\r\n");
return 0;
}
//判斷是否是一個有效的PE标志
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("pFileBuffer不是一個有效的PE标志\r\n");
return 0;
}
printf("目前的Foa位址: %#X \r\n",dwFoa);
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//定義個臨時節表指針進行下面的計算操作
numberOfSection = pPEHeader->NumberOfSections;
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
if (dwFoa <= pOptionHeader->SizeOfHeaders)
{
return (DWORD)dwFoa;
}
else
{
for (DWORD n = 0; n < numberOfSection; n++, pTempSectionHeader++)
{
if ((dwFoa >= pTempSectionHeader->PointerToRawData) && (dwFoa < pTempSectionHeader->SizeOfRawData))
{
dwRVAValue = dwFoa - pTempSectionHeader->PointerToRawData + pTempSectionHeader->VirtualAddress;
}
else
{
printf("FoaToRva 轉換失敗!\r\n");
return 0;
}
}
}
return dwRVAValue;
}
//在原有的exe檔案中開始操作添加ShellCode代碼;
VOID AddCodeInCodeSec()
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PBYTE codeBegin = NULL;
BOOL isOK = FALSE;
DWORD size = 0;
//File-->FileBuffer
ReadPEFile(FilePath_In,&pFileBuffer);
if (!pFileBuffer)
{
printf("檔案-->緩沖區失敗\r\n");
return ;
}
//FileBuffer-->ImageBuffer
CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
if (!pImageBuffer)
{
printf("FileBuffer-->ImageBuffer失敗\r\n");
free(pFileBuffer);
return ;
}
//判斷代碼段空閑區域是否能夠足夠存儲ShellCode代碼
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)(((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)(((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_NT_OPTIONAL32_HEADER);
if (((pSectionHeader->SizeOfRawData) - (pSectionHeader->Misc.VirtualSize)) < SHELLCODELENGTH)
{
printf("代碼區域空閑空間不夠\r\n");
free(pFileBuffer);
free(pImageBuffer);
}
//将代碼複制到空閑區域
codeBegin = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);
printf("pSectionHeader->VirtualAddress: %#010X\r\n", pSectionHeader->VirtualAddress);
printf("pSectionHeader->Misc.VirtualSize: %#010X\r\n", pSectionHeader->Misc.VirtualSize);
printf("codeBegin: %#010X\r\n", codeBegin);
memcpy(codeBegin,ShellCode,SHELLCODELENGTH);
//修正E8-->call後面的代碼區域
DWORD callAddr = (MESSAGEBOXADDR - (pOptionHeader->ImageBase + ((DWORD)(codeBegin + 0xD) - (DWORD)pImageBuffer)));
printf("callAddr ---> %#010X \r\n",callAddr);
*(PDWORD)(codeBegin + 0x09) = callAddr;
printf("*(PWORD)(codeBegin + 0x09) ---> %#010X \r\n",*(PDWORD)(codeBegin + 0x09));
/*
關于修正E8的了解,公式:X = 要跳轉的位址 - (E8目前的位址 + 5);
要跳轉的位址,這裡是毋庸置疑的,就是我們要加入代碼MessageBox的位址;
然後要減去E8目前的位址+5的位置,這裡不是太好了解;
我們的目的是要将E8後面的4個位元組計算出來,然後寫入到E8後面,也就是公式中X;
上面公式E8目前位址+5 ,而在此情況要定位到這個位置就要從代碼的Dos開始通過指針相加;
進行位置偏移到E8目前位址+5的位置;
是以定位codeBegin的位置是:pImageBuffer指針最開始的位置(Dos頭位置)通過記憶體中偏移的寬度移動到第一個節表的位置;
也就是上面的pSectionHeader->VirtualAddress 操作形式;
然後再偏移第一個節表在記憶體中對齊前實際的寬度(尺寸)pSectionHeader->Misc.VirtualSize;
上述一番操作之後就到了第一個節表沒有對齊前的位置,這個位置就是我們可以添加ShellCode代碼的起始位置;
到了添加ShellCode代碼的起始位置之後,就要想辦法添加E8位置後面的4個位元組,此時根據ShellCode代碼的寬度;
進行計算,确認0x6A 00 0x6A 00 0x6A 00 0x6A 00 E8 00 00 00 00 剛好向後面數13個位置,按照十六進制看;
就是0xD,是以在codeBegin偏移0xD個位置即可到達E9的位置,這也就是我們說的(E8目前的位址 + 5);
到了上面的位置之後,由于我們最終是需要在程式運作之後在記憶體中添加ShellCode代碼;是以這裡一定要計算出;
其準确的偏移位址,這樣不管怎麼拉伸到哪個位置,都能準确找到位置;
注意:這裡需要注意一點了解,上面說的pImageBuffer這個是我們加載程式到我們申請的記憶體中,絕不是程式在;
運作中的那個記憶體,這裡一定要了解清楚,她們是不一樣的,了解了這個就能了解上面代碼為什麼要減去Dos頭的;
首位址,(DWORD)(codeBegin + 0xD) - (DWORD)pImageBuffer)
*/
//修正E9-->jmp後面的代碼區域
DWORD jmpAddr = ((pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - (pOptionHeader->ImageBase + ((DWORD)(codeBegin + SHELLCODELENGTH) - (DWORD)pImageBuffer)));
printf("jmpAddr ---> %#010X \r\n",jmpAddr);
*(PDWORD)(codeBegin + 0x0E) = jmpAddr;
printf("*(PWORD)(codeBegin + 0x0E) ---> %#010X \r\n",*(PDWORD)(codeBegin + 0x0E));
/*
公式:X = 要跳轉的位址 - (E9目前的位址 + 5)
這裡同樣是要計算出E9後面4個位元組的位址,我們的目的是在這裡添加OEP的位址,讓其執行完成MessageBox之後跳轉;
OEP的位址,那麼這裡就要先計算出OEP位址,就是pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint;
再減去(E9目前的位址 + 5) 0x6A 00 0x6A 00 0x6A 00 0x6A 00 E8 00 00 00 00 E9 00 00 00 00;
(DWORD)codeBegin + SHELLCODELENGTH 就是加上ShellCode總長度,偏移完成之後減去ImageBuffer首位址再加上ImageBase;
*/
//修正OEP
printf("pOptionHeader->AddressOfEntryPoint ---> %#010X \r\n",pOptionHeader->AddressOfEntryPoint);
printf("(DWORD)codeBegin ---> %#010X \r\n",((DWORD)codeBegin - (DWORD)pImageBuffer));
pOptionHeader->AddressOfEntryPoint = (DWORD)codeBegin - (DWORD)pImageBuffer;
printf("pOptionHeader->AddressOfEntryPoint ---> %#010X \r\n",pOptionHeader->AddressOfEntryPoint);
//修正OEP好了解,就是定位到OEP位址,然後直接通過codeBegin位址減去pImageBuffer的首位址即可;
//ImageBuffer-->NewBuffer
size = CopyImageBufferToNewBuffer(pImageBuffer,&pNewBuffer);
if (size == 0 || !pNewBuffer)
{
printf("ImageBuffer-->NewBuffer失敗\r\n");
free(pFileBuffer);
free(pImageBuffer);
return ;
}
//NewBuffer-->檔案
isOK = MemeryTOFile(pNewBuffer,size,FilePath_Out);
if (isOK)
{
printf("修改代碼添加SHELLCODE 存盤成功\r\n");
return ;
}
//釋放記憶體
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
}
//調用函數新增節表和節操作,成功之後并存盤到本地;
VOID NewSectionsInCodeSec()
{
LPVOID pFileBuffer = NULL;
LPVOID pNewImageBuffer = NULL;
BOOL isOK = FALSE;
DWORD size1 = 0;
DWORD size2 = 0;
//File-->FileBuffer
size1 = ReadPEFile(FilePath_In,&pFileBuffer);
if (size1 == 0 || !pFileBuffer)
{
printf("檔案-->緩沖區失敗\r\n");
return ;
}
printf("fileSize - Final: %#X \r\n",size1);
//FileBuffer-->NewImageBuffer
size2 = CopyFileBufferToNewImageBuffer(pFileBuffer,size1,&pNewImageBuffer);
if (size2 == 0 || !pFileBuffer)
{
printf("FileBuffer-->NewImageBuffer失敗\r\n");
free(pFileBuffer);
return ;
}
printf("sizeOfFile - Final: %#X \r\n",size2);
//NewImageBuffer-->檔案
isOK = MemeryTOFile(pNewImageBuffer,size2,FilePath_Out);
if (isOK)
{
printf("新增節表和節存盤成功\r\n");
return ;
}
//釋放記憶體
free(pFileBuffer);
free(pNewImageBuffer);
}
//調用函數擴大一個節
VOID ExtendLastSectionsInCodeSec()
{
//ReadPEFile CopyFileBufferToImageBuffer CopyImageBufferToNewImageBuffer
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewImageBuffer = NULL;
BOOL isOK = FALSE;
DWORD FileBufferSize = 0;
DWORD ImageBufferSize = 0;
DWORD size = 0;
//File-->FileBuffer
FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer);
if (FileBufferSize == 0 || !pFileBuffer)
{
printf("檔案-->緩沖區失敗\r\n");
return ;
}
printf("FileBufferSize - Final: %#X \r\n",FileBufferSize);
//FileBuffer-->ImageBuffer
ImageBufferSize = FileBufferToModifyImageBuffer(pFileBuffer,&pImageBuffer);
if (ImageBufferSize == 0 || !pFileBuffer)
{
printf("FileBuffer-->ImageBuffer失敗\r\n");
free(pFileBuffer);
return ;
}
printf("ImageBufferSize - Final: %#X \r\n",ImageBufferSize);
size = CopyImageBufferToNewBuffer(pImageBuffer,&pNewImageBuffer);
if (size == 0 || !pImageBuffer)
{
printf("pImageBuffer-->pNewImageBuffer失敗\r\n");
free(pFileBuffer);
return ;
}
//pNewImageBuffer-->檔案
isOK = MemeryTOFile(pNewImageBuffer,size,FilePath_Out);
if (isOK)
{
printf("擴大一個節成功,并存盤\r\n");
return ;
}
//釋放記憶體
free(pFileBuffer);
free(pImageBuffer);
free(pNewImageBuffer);
}
//調用函數擴大一個節
VOID ModifySectionsOneInCodeSec()
{
//ReadPEFile CopyFileBufferToImageBuffer FileBufferToModifyOneImageBuffer MemeryTOFile
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewImageBuffer = NULL;
BOOL isOK = FALSE;
DWORD FileBufferSize = 0;
DWORD ImageBufferSize = 0;
DWORD size = 0;
//File-->FileBuffer
FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer);
if (FileBufferSize == 0 || !pFileBuffer)
{
printf("檔案-->緩沖區失敗\r\n");
return ;
}
printf("FileBufferSize - Final: %#X \r\n",FileBufferSize);
//FileBuffer-->ImageBuffer
ImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
if (ImageBufferSize == 0 || !pFileBuffer)
{
printf("FileBuffer-->ImageBuffer失敗\r\n");
free(pFileBuffer);
return ;
}
printf("ImageBufferSize - Final: %#X \r\n",ImageBufferSize);
size = FileBufferToModifyOneImageBuffer(pImageBuffer,&pNewImageBuffer);
if (size == 0 || !pImageBuffer)
{
printf("pImageBuffer-->pNewImageBuffer失敗\r\n");
free(pFileBuffer);
return ;
}
//pNewImageBuffer-->檔案
isOK = MemeryTOFile(pNewImageBuffer,size,FilePath_Out);
if (isOK)
{
printf("合并節完成,存盤成功\r\n");
return ;
}
//釋放記憶體
free(pFileBuffer);
free(pImageBuffer);
free(pNewImageBuffer);
}
VOID RvaAndFoaConversion()
{
LPVOID pFileBuffer = NULL;
DWORD FileBufferSize = 0;
DWORD FoaFinalVaule = 0;
DWORD RvaFinalVaule = 0;
size_t pRVA = 1180;
size_t pFOA = 2279;
//File-->FileBuffer
FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer);
if (FileBufferSize == 0 || !pFileBuffer)
{
printf("檔案-->緩沖區失敗\r\n");
return ;
}
printf("FileBufferSize: %#X \r\n",FileBufferSize);
FoaFinalVaule = RvaToFileOffset(pFileBuffer,pRVA);
if (FoaFinalVaule == 0 || !pFileBuffer)
{
printf("pFileBuffer-->讀取失敗\r\n");
free(pFileBuffer);
return ;
}
printf("轉換成功 --> FoaFinalVaule 傳進來的pRVA值: : %#X %#X\r\n",FoaFinalVaule,pRVA);
RvaFinalVaule = FoaToImageOffset(pFileBuffer,pFOA);
if (RvaFinalVaule == 0 || !pFileBuffer)
{
printf("pFileBuffer-->讀取失敗\r\n");
free(pFileBuffer);
return ;
}
printf("轉換成功 --> RvaFinalVaule 傳進來的pFOA值 : %#X %#X\r\n",RvaFinalVaule,pFOA);
free(pFileBuffer);
}
頭檔案代碼
// gbpeall.h: interface for the gbpeall class.
//
//
#if !defined(AFX_GBPEALL_H__C24C6881_E003_41F7_BE14_24DDA1702CCD__INCLUDED_)
#define AFX_GBPEALL_H__C24C6881_E003_41F7_BE14_24DDA1702CCD__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <string.h>
#include <windows.h>
#include <stdlib.h>
//#define FILEPATH_IN "C:\\WINDOWS\\system32\\kernel32.dll"
// #define FilePath_In "C:\\cntflx\\notepad.exe"
#define FilePath_In "C:\\cntflx\\ipmsg.exe"
//#define FilePath_Out "C:\\cntflx\\notepadnewpes.exe"
//#define FilePath_Out "C:\\cntflx\\ipmsgnewpeaddcodes.exe"
//#define FilePath_Out "C:\\cntflx\\ipmsgnewaddsections.exe"
//#define FilePath_Out "C:\\cntflx\\ipmsgextendsections.exe"
#define FilePath_Out "C:\\cntflx\\ipmsgemerge.exe"
#define MESSAGEBOXADDR 0x77D5050B
#define SHELLCODELENGTH 0x12 //16進制的,轉換為十進制就是18
extern BYTE ShellCode[];
//讀檔案 --->FileBuffer
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer);
//寫到ImageBuffer,FileBuffer ---> ImageBuffer
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer);
//寫到NewImageBuffer, FileBuffer ---> NewImageBuffer
DWORD CopyFileBufferToNewImageBuffer(IN LPVOID pFileBuffer,IN size_t fileSize,OUT LPVOID* pNewImageBuffer);
//寫到NewImageBuffer, 這裡供擴大節使用;
DWORD FileBufferToModifyImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pNewImageBuffer);
//寫入到NewBuffer,目的是将拉伸後的ImageBuffer再縮回來,為存盤準備
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer);
//寫入到NewImageBuffer,這裡供合并節使用?
DWORD FileBufferToModifyOneImageBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer);
//寫到pNewBuffer裡面,從pNewImageBuffer寫入 ---> pNewBuffer
//DWORD ModifyImageBufferToNewBuffer(IN LPVOID pNewImageBuffer,OUT LPVOID* pNewBuffer);
//Rva轉Foa
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva);
//Foa轉RVA
DWORD FoaToImageOffset(IN LPVOID pFileBuffer,IN DWORD dwFoa);
//對齊大小
DWORD AlignLength(DWORD Actuall_size,DWORD Align_size);
//将MemBuffer寫入到硬碟,這裡就是将各種修改好的記憶體檔案,存入到本地硬碟中;
BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile);
//DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva);
//調用函數,添加ShellCode代碼
VOID AddCodeInCodeSec(); //這個調用函數用到下面的4個函數
//ReadPEFile CopyFileBufferToImageBuffer CopyImageBufferToNewBuffer MemeryTOFile
//調用函數,新增節表和節操作;
VOID NewSectionsInCodeSec(); //這個調用函數用到下面的3個函數
//ReadPEFile CopyFileBufferToNewImageBuffer MemeryTOFile
//調用函數,擴大最後一個節
VOID ExtendLastSectionsInCodeSec(); //這個調用函數用到下面的4個函數
//ReadPEFile FileBufferToModifyImageBuffer CopyImageBufferToNewImageBuffer MemeryTOFile
//調用函數,合并節
VOID ModifySectionsOneInCodeSec(); //這個調用函數用到下面的4個函數
//ReadPEFile CopyFileBufferToImageBuffer FileBufferToModifyOneImageBuffer MemeryTOFile
//調用函數,Rva和Foa之間的互相轉換;
VOID RvaAndFoaConversion(); //這個調用函數用到下面的3個函數
//ReadPEFile RvaToFileOffset FoaToImageOffset
#endif // !defined(AFX_GBPEALL_H__C24C6881_E003_41F7_BE14_24DDA1702CCD__INCLUDED_)
main函數入口代碼
// allpelx.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "gbpeall.h"
int main(int argc, char* argv[])
{
//Fun();
//AddCodeInCodeSec();
//NewSectionsInCodeSec();
//ExtendLastSectionsInCodeSec();
//ModifySectionsOneInCodeSec();
RvaAndFoaConversion();
printf("Hello World! Cntf\r\n");
system("pause");
return 0;
}