1.工具頭文件
#ifndef PETOOL_H
#define PETOOL_H
#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
//函數聲明
//**************************************************************************
//ReadPEFile:將文件讀取到緩沖區
//參數說明:
//lpszFile 文件路徑
//pFileBuffer 緩沖區指針
//返回值說明:
//讀取失敗返回0 否則返回實際讀取的大小
//**************************************************************************
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer);
//**************************************************************************
//CopyFileBufferToImageBuffer:將文件從FileBuffer復制到ImageBuffer
//參數說明:
//pFileBuffer FileBuffer指針
//pImageBuffer ImageBuffer指針
//返回值說明:
//讀取失敗返回0 否則返回復制的大小
//**************************************************************************
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer);
//**************************************************************************
//CopyImageBufferToNewBuffer:將ImageBuffer中的數據復制到新的緩沖區
//參數說明:
//pImageBuffer ImageBuffer指針
//pNewBuffer NewBuffer指針
//返回值說明:
//讀取失敗返回0 否則返回復制的大小
//**************************************************************************
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer);
//**************************************************************************
//MemeryTOFile:將內存中的數據復制到文件
//參數說明:
//pMemBuffer 內存中數據的指針
//size 要復制的大小
//lpszFile 要存儲的文件路徑
//返回值說明:
//讀取失敗返回0 否則返回復制的大小
//**************************************************************************
BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile);
//**************************************************************************
//RvaToFileOffset:將內存偏移轉換為文件偏移
//參數說明:
//pFileBuffer FileBuffer指針
//dwRva RVA的值
//返回值說明:
//返回轉換后的FOA的值 如果失敗返回0
//**************************************************************************
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva);
#endif
2.工具實現
#include "stdafx.h"
#include "petool.h"
//ReadPEFile:將文件讀取到緩沖區
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer){
//1.打開文件
FILE* file = fopen(lpszFile, "rb");
if(!file){
printf("打開文件失敗\n");
return 0;
}
//2.計算文件大小
fseek(file, 0, SEEK_END);
DWORD len = ftell(file);
fseek(file, 0, SEEK_SET);
//3.申請內存
LPVOID buf = malloc(len);
if(!buf){
fclose(file);
printf("申請內存失敗\n");
return 0;
}
//4.讀取文件到內存
size_t n = fread(buf, len, 1, file);
if(!n){
printf("讀取文件失敗\n");
free(buf);
fclose(file);
return 0;
}
//5.返回
printf("讀取文件到緩沖區成功\n");
*pFileBuffer = buf;
buf = NULL;
fclose(file);
return len;
}
//將文件從FileBuffer復制到ImageBuffer
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer){
//定義pe頭結構指針
PIMAGE_DOS_HEADER dosHeader = NULL; //dos頭指針
PIMAGE_NT_HEADERS ntHeader = NULL; //nt頭指針
PIMAGE_FILE_HEADER peHeader = NULL; //pe頭指針
PIMAGE_OPTIONAL_HEADER32 optionHeader = NULL; //可選pe頭指針
PIMAGE_SECTION_HEADER sectionHeader = NULL; //節表指針
if(!pFileBuffer){
printf("文件加載失敗\n");
return 0;
}
//判斷是否有mz標記
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE){
printf("不是有效mz標記\n");
return 0;
}
//找到dos頭
dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
//根據dos頭的e_flanew找到nt頭並判斷是否有pe標記
if(*((PDWORD)((DWORD)pFileBuffer + dosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
printf("不是有效pe標記\n");
return 0;
}
//找到pe頭
peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew +4);
//找到可選pe頭
optionHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
//**********開始復制***********
//1.申請內存空間,拉伸后的大小在可選pe頭的SizeOfImage中
DWORD imageSize = optionHeader ->SizeOfImage;
LPVOID image = malloc(imageSize);
if(!image){
printf("申請imagebuf內存空間失敗\n");
return 0;
}
//初始化內存空間
memset(image, 0, imageSize);
//2.拷貝頭部文件,內存鏡像和文件鏡像的頭部是一樣的
DWORD headSize = optionHeader->SizeOfHeaders;
memcpy(image, pFileBuffer, headSize);
//3.拷貝各個節
WORD sectionNum = peHeader->NumberOfSections; //節的數量在pe頭中
WORD opHeaderSize = peHeader->SizeOfOptionalHeader; //可選pe頭的字節數,用來計算節表文件鏡像的位置;
//找到節表開頭
sectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)optionHeader + opHeaderSize);
for(int i=0; i<sectionNum; i++,sectionHeader++){
memcpy((LPVOID)((DWORD)image + sectionHeader->VirtualAddress),
(LPVOID)((DWORD)pFileBuffer + sectionHeader->PointerToRawData),
sectionHeader->SizeOfRawData);
}
//4.返回
*pImageBuffer = image;
printf("拉伸文件鏡像成功\n");
return imageSize;
}
//將ImageBuffer中的數據復制到新的緩沖區
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer){
//定義pe頭結構指針
PIMAGE_DOS_HEADER dosHeader = NULL; //dos頭指針
PIMAGE_NT_HEADERS ntHeader = NULL; //nt頭指針
PIMAGE_FILE_HEADER peHeader = NULL; //pe頭指針
PIMAGE_OPTIONAL_HEADER32 opHeader = NULL; //可選pe頭指針
PIMAGE_SECTION_HEADER sectionHeader = NULL; //節表指針
//找到dos頭
dosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//找到nt頭
ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)pImageBuffer + dosHeader->e_lfanew);
//找到pe頭
peHeader = (PIMAGE_FILE_HEADER) ((DWORD)ntHeader + 4);
//找到可選pe頭
opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
//找到節表
sectionHeader = (PIMAGE_SECTION_HEADER) ((DWORD)opHeader + peHeader->SizeOfOptionalHeader);
//1.申請內存
//內存鏡像壓縮后的空間大小:最后一個節的偏移PointerToRawData + 最后一個節的大小SizeOfRawData
//獲取最后一個節的指針
PIMAGE_SECTION_HEADER pLastSection = sectionHeader + (peHeader->NumberOfSections - 1);
DWORD size = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;
//申請內存空間
LPVOID buf = malloc(size);
if(!buf){
printf("壓縮內存鏡像時:分配空間失敗\n");
return NULL;
}
//初始化內存空間
memset(buf, 0, size);
//2.拷貝頭部文件
memcpy(buf, pImageBuffer, opHeader->SizeOfHeaders);
//3.拷貝節
for(int i=0;i<peHeader->NumberOfSections;i++,sectionHeader++){
memcpy((LPVOID)((DWORD)buf + sectionHeader->PointerToRawData),
(LPVOID)((DWORD)pImageBuffer + sectionHeader->VirtualAddress),
sectionHeader->SizeOfRawData);
}
//4.返回
*pNewBuffer = buf;
printf("壓縮內存鏡像成功\n");
return size;
}
//MemeryTOFile:將內存中的數據復制到文件
BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile){
//打開文件
FILE* file = fopen(lpszFile, "a+b");
if(!file){
printf("打開文件失敗\n");
return 0;
}
//寫出
size_t n = fwrite(pMemBuffer, size, 1, file);
if(!n){
printf("寫出文件失敗\n");
fclose(file);
return 0;
}
fclose(file);
return 1;
}
//將內存偏移轉換為文件偏移
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva){
//定義文件頭
PIMAGE_DOS_HEADER dosHeader = NULL; //dos頭指針
PIMAGE_NT_HEADERS ntHeader = NULL; //nt頭指針
PIMAGE_FILE_HEADER peHeader = NULL; //pe頭指針
PIMAGE_OPTIONAL_HEADER32 opHeader = NULL; //可選pe頭指針
PIMAGE_SECTION_HEADER sectionHeader = NULL; //節表指針
//找到文件頭
dosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)pFileBuffer + dosHeader->e_lfanew);
peHeader = (PIMAGE_FILE_HEADER) ((DWORD)ntHeader + 4);
opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
sectionHeader = (PIMAGE_SECTION_HEADER) ((DWORD)opHeader + peHeader->SizeOfOptionalHeader);
//1.判斷是哪個節
int sec = -1;
for(int i=0;i<peHeader->NumberOfSections;i++){
DWORD va = (sectionHeader+i)->VirtualAddress;
DWORD size = (sectionHeader+i)->SizeOfRawData;
if(dwRva > va && dwRva<(va+size) ){
sec = i;
printf("在第%d個節\n",sec);
break;
}
}
if(sec<0){
printf("內存偏移不在任何一個節\n");
return 0;
}
//2.轉換
DWORD secOffset = dwRva - (sectionHeader + sec)->VirtualAddress;
DWORD foa = (sectionHeader + sec)->PointerToRawData + secOffset;
return foa;
}
3.在空白區添加代碼
#include "stdafx.h"
#include "petool.h"
#define SHELLCODELEN 18
#define SRC "C:\\Users\\Administrator\\Desktop\\TraceMe.exe"
#define DEST "C:\\Users\\Administrator\\Desktop\\copy1.exe"
#define MESSAGEBOXADDR 0x75780026
//需要添加的代碼,跳轉地址為0
BYTE shellCode[SHELLCODELEN]={
0x6a,00,0x6a,00,0x6a,00,0x6a,00,
0xe8,00,00,00,00,
0xe9,00,00,00,00
};
//在代碼節空白區添加代碼
void addCodeInCodeSec(){
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
PIMAGE_FILE_HEADER peHeader = NULL; //pe頭指針
PIMAGE_OPTIONAL_HEADER32 opHeader = NULL; //可選pe頭指針
PIMAGE_SECTION_HEADER seHeader = NULL; //節表指針
PBYTE codeBegin = NULL; //添加代碼的起始位置
DWORD size = 0; //內存鏡像壓縮后的大小
BOOL isOK = FALSE;
//1.從文件中讀到緩沖區
ReadPEFile(SRC, &pFileBuffer);
if(!pFileBuffer){
printf("讀取文件失敗\n");
return;
}
//2.拉伸文件
CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
if(!pImageBuffer){
printf("拉伸文件失敗\n");
free(pFileBuffer);
return;
}
//3.判斷空間是否足夠
peHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageBuffer + ((PIMAGE_DOS_HEADER)pImageBuffer)->e_lfanew + 4);
opHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
seHeader = (PIMAGE_SECTION_HEADER)((DWORD)opHeader + peHeader->SizeOfOptionalHeader);
//條件是:節的文件對齊大小 - 節的實際大小 > 代碼大小
if(seHeader->SizeOfRawData - seHeader->Misc.VirtualSize < SHELLCODELEN){
printf("代碼節空白區空間不足\n");
//注意釋放申請的內存空間,誰拿到指針誰釋放
free(pFileBuffer);
free(pImageBuffer);
return;
}
//4.往內存鏡像中添加代碼
codeBegin = (PBYTE)((DWORD)(pImageBuffer) + seHeader->VirtualAddress + seHeader->Misc.VirtualSize);
memcpy(codeBegin, shellCode,SHELLCODELEN);
//修正e8
DWORD callAddr = MESSAGEBOXADDR - (opHeader->ImageBase + ((DWORD)codeBegin+0x0d - (DWORD)pImageBuffer));
*(PDWORD)(codeBegin+9) = callAddr;
//修正e9
DWORD jmpAddr = opHeader->AddressOfEntryPoint - ((DWORD)(codeBegin+0x12) - (DWORD)pImageBuffer);
*(PDWORD)(codeBegin+0x0e) = jmpAddr;
//修正入口點oep
opHeader->AddressOfEntryPoint = (DWORD)codeBegin-(DWORD)pImageBuffer;
//5.壓縮內存鏡像
size = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);
if(size == 0 || !pNewBuffer){
printf("壓縮內存鏡像失敗\n");
free(pFileBuffer);
free(pImageBuffer);
return;
}
//6.復制壓縮后的內存鏡像到文件
isOK = MemeryTOFile(pNewBuffer,size,DEST);
if(isOK){
printf("存盤成功\n");
}else{
printf("存盤失敗\n");
}
//7.釋放內存
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
}
int main(int argc, char* argv[])
{
addCodeInCodeSec();
getchar();
return 0;
}
運行結果:

復制了一個文件,運行時加了一個彈框:
