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;
}
运行结果:
复制了一个文件,运行时加了一个弹框: