一、添加代码的方式
- 我们现在学过的有:直接在任意节的空白区添加代码;新增节添加代码;扩大最后一个节添加代码;今天要学习合并节并添加代码
二、合并节思路
-
必须使用拉伸后的合并!因为你要修改节表的字段的,如果使用硬盘上的修改,那么大小和地址都是对不上的
所以如果我们使用winhex或者UE打开一个文件直接做合并结果肯定是错误的,所以只能拿程序对拉伸后的做修改
-
合并节就是:把第一个节开始一直到最后一个节的结尾都当成第一个节 -
但是会造成合并后文件变大,因为如果把所有节合并,那么节与节之间就不再考虑内存对齐或者文件对齐了,由于我们是在文件装载到内存后合并的节,如果内存对齐和文件对齐不一样,那么最后将合并后的文件再还原成硬盘上的状态时,所有的节会当成一个整体,不会再文件对齐了,所以就导致了合并后文件可能会变大 -
步骤:
-
NumberOfSections改成1 -
修改第一个节表的字段
- VirtualSize:方法一,SizeOfImage - 第一个节VirtualAddress;方法二,还可以通过最后一个节的VirtualAddress + 最后一个节的VirtualSize内存对齐后的大小 - SizeOfHeader内存对齐后的大小
- SizeOfRawData:等于VirtualSize
-
将第一个节的属性改为包含所有节的属性,即用第一个节的属性与其他的节的属性做或运算
三、作业
1.扩大最后一个节,保证程序正常运行(手动)
day33.1中写过用编程实现扩大节,所以今天再试着用手动的方式体会一下扩大节,由于手动的时候是在FileBuffer中操作,所以有些值的计算包括扩大的大小都是需要计算的,没有在ImageBuffer中扩大节那么方便
-
打开notepad.exe程序,如果我们想扩大0x1000字节,则需要修改SizeOfImage,0x13000 + 0x1000内存对齐后 = 0x14000(我们知道notepad.exe的内存对齐为0x1000;文件对齐为0x200) -
接着找到最后一个节表,修改VirtualSize = 原来的0x8948内存对齐后0x9000 + 0x1000内存对齐后 = 0xA000 -
SizeOfRawData = VirtualSize即可 -
最后将最后一个节的属性0x40000040与0x60000020亦或得到0x60000060 -
最后最关键的一步就是在最后一个节末尾扩大节,我们知道如果在ImageBuffer中扩大节,就直接在末尾加上0x1000字节即可,但是由于现在是在FileBuffer中操作,不能直接扩大0x1000了,不然就跟SizeOfData对不上,我们要通过计算来得到:先得到最后一个节原来在文件中起始偏移地址为PointerToRawData = 0x7800,SizeOfRawData为0x8A00,那么最后一个节的末尾为0x10200。由于扩大后的SizeOfRawData = 0xA000,而PointerToRawData不变还是0x7800,所以扩大后最后一个节的结尾应该为0xA000 + 0x7800 = 0x11800,那么就应该新增的大小为0x11800 - 0x10200 = 0x1600,换算成十进制为5632。所以在文件末尾0x101FF处右键–Edit–Paste Zero Bytes–输入5632即可 -
保存运行,再拖入PEtool中查看是否修改成功
2.编码实现合并节
#include "stdafx.h"
#include "stdafx.h"
#include "stdlib.h"
#define IMAGE_SIZEOF_SHORT_NAME 8
//*******************************************************************************
#pragma pack(1)
typedef struct DOS_HEADER{ //DOS头字段
short e_magic;
short e_cblp;
short e_cp;
short e_crlc;
short e_cparhdr;
short e_minalloc;
short e_maxalloc;
short e_ss;
short e_sp;
short e_csum;
short e_ip;
short e_cs;
short e_lfarlc;
short e_ovno;
short e_res[4];
short e_oemid;
short e_oeminfo;
short e_res2[10];
int e_lfanew;
}Dos;
#pragma pack()
//别忘了中间还有一个PE签名
#pragma pack(1)
typedef struct FILE_HEADER{ //标准PE头字段
short Machine;
short NumberOfSections;
int TimeDateStamp;
int PointerToSymbolTable;
int NumberOfSymbols;
short SizeOfOptionalHeader;
short Characteristics;
}File;
#pragma pack()
#pragma pack(1)
typedef struct OPTIONAL_HEADER{ //可选PE头字段
short Magic;
char MajorLinkerVersion;
char MinorLinkerVersion;
int SizeOfCode;
int SizeOfInitializedData;
int SizeOfUninitializedData;
int AddressOfEntryPoint;
int BaseOfCode;
int BaseOfData;
int ImageBase;
int SectionAlignment;
int FileAlignment;
short MajorOperatingSystemVersion;
short MinorOperatingSystemVersion;
short MajorImageVersion;
short MinorImageVersion;
short MajorSubsystemVersion;
short MinorSubsystemVersion;
int Win32VersionValue;
int SizeOfImage;
int SizeOfHeaders;
int CheckSum;
short Subsystem;
short DllCharacteristics;
int SizeOfStackReserve;
int SizeOfStackCommit;
int SizeOfHeapReserve;
int SizeOfHeapCommit;
int LoaderFlags;
int NumberOfRvaAndSizes;
//后面还有几个(16)个结构体,一个结构体8字节,我们先不研究,后面再说
}Op;
#pragma pack()
#pragma pack(1)
typedef struct _IMAGE_SECTION_HEADER {
char Name[IMAGE_SIZEOF_SHORT_NAME]; //宏定义使用
union{
int PhysicalAddress;
int VirtualSize;
}Misc;
int VirtualAddress;
int SizeOfRawData;
int PointerToRawData;
int PointerToRelocations;
int PointerToLinenumbers;
short NumberOfRelocations;
short NumberOfLinenumbers;
int Characteristics;
}Sec;
#pragma pack()
//*******************************************************************************
//读入FileBuffer
char* readInFileBuffer(char* filePath){
FILE* fp = NULL;
char* p = NULL;
fp = fopen(filePath,"rb");
if(NULL == fp){
printf("文件打开失败\n");
fclose(fp);
return NULL;
}
//计算文件长度
fseek(fp,0,2);
int len = ftell(fp);
fseek(fp,0,0);
//动态申请FileBuffer内存空间
p = (char*)malloc(len);
if(NULL == p){
printf("FileBuffer内存空间分配失败\n");
fclose(fp);
return NULL;
}
//写入数据
int isSuccessed = fread(p,len,1,fp);
if(!isSuccessed){
printf("写入FileBuffer失败\n");
fclose(fp);
free(p);
return NULL;
}
fclose(fp);
return p; //将FileBuffer首地址返回
}
//FileBuffer到ImageBuffer
char* fileBufferToImageBuffer(char* fileBufferp){
Dos* dosp = (Dos*)fileBufferp; //定义DOS结构体类型指针
File* filep = (File*)(fileBufferp + dosp->e_lfanew + 4);//定义File结构体类型指针
Op* opp = (Op*)((char*)filep + 20);//定义Op结构体类型指针
Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//定义Sec结构体类型指针
//动态申请ImageBuffer的内存空间
char* ip = NULL;
ip = (char*)malloc(opp->SizeOfImage);
if(NULL == ip){
printf("动态申请ImageBuffer内存失败\n");
return NULL;
}
for(int i = 0;i < opp->SizeOfImage;i++){
*(ip + i) = 0x00;
}
//复制所有头
for(int j = 0;j < opp->SizeOfHeaders;j++){
*(ip + j) = *(fileBufferp + j);
}
//复制所有节
for(int k = 0;k < filep->NumberOfSections;k++){
for(int x = 0;x < secp->SizeOfRawData;x++){
*(ip + secp->VirtualAddress + x) = *(fileBufferp + secp->PointerToRawData + x);
}
secp++;
}
return ip; //返回ImageBuffer的起始地址
}
//ImageBuffer到newBuffer
char* imageBufferToNewBuffer(char* imageBufferp){
Dos* dosp = (Dos*)imageBufferp; //定义DOS结构体类型指针
File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//定义File结构体类型指针
Op* opp = (Op*)((char*)filep + 20);//定义Op结构体类型指针
Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//定义Sec结构体类型指针
//计算newBuffer需要的大小(day30没有解决的问题,这里解决了)
//使用最后一个节的文件偏移地址 + 最后一个节对齐后的大小
Sec* temp = secp;
secp = secp + filep->NumberOfSections - 1;
int len = secp->PointerToRawData + secp->SizeOfRawData;
secp = temp;
//动态分配NewBuffer内存
char* np = (char*)malloc(len);
if(NULL == np){
printf("NewBuffer内存分配失败\n");
return NULL;
}
for(int i = 0;i < len;i++){
*(np + i) = 0x00;
}
//复制所有头
for(int j = 0;j < opp->SizeOfHeaders;j++){
*(np + j) = *(imageBufferp + j);
}
//复制所有节
for(int k = 0;k < filep->NumberOfSections;k++){
for(int x = 0;x < secp->SizeOfRawData;x++){
*(np + secp->PointerToRawData + x) = *(imageBufferp + secp->VirtualAddress + x);
}
secp++;
}
return np; //返回NewBuffer的首地址
}
//存盘
int save(char* savePath,char* newBufferp){
Dos* dosp = (Dos*)newBufferp; //定义DOS结构体类型指针
File* filep = (File*)(newBufferp + dosp->e_lfanew + 4);//定义File结构体类型指针
Op* opp = (Op*)((char*)filep + 20);//定义Op结构体类型指针
Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//定义Sec结构体类型指针
FILE* fp = fopen(savePath,"wb");
if(NULL == fp){
printf("文件打开失败\n");
fclose(fp);
return 0;
}
secp = secp + filep->NumberOfSections - 1;
int len = secp->PointerToRawData + secp->SizeOfRawData; //得到newBuffer的大小
int isSuccessed = fwrite(newBufferp,len,1,fp);
if(!isSuccessed){
printf("存盘失败\n");
fclose(fp);
return 0;
}
fclose(fp);
printf("存盘成功\n");
return 1;
}
//合并节
char* combineSections(char* imageBufferp){
Dos* dosp = (Dos*)imageBufferp; //定义DOS结构体类型指针
File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//定义File结构体类型指针
Op* opp = (Op*)((char*)filep + 20);//定义Op结构体类型指针
Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//定义Sec结构体类型指针
//新开辟一块内存
char* combineImageBuffer = (char*)malloc(opp->SizeOfImage);
if(NULL == combineImageBuffer){
printf("动态申请失败\n");
return NULL;
}
for(int k = 0;k < opp->SizeOfImage;k++){
*(combineImageBuffer + k) = *(imageBufferp + k);
}
dosp = (Dos*)combineImageBuffer; //定义DOS结构体类型指针
filep = (File*)(combineImageBuffer + dosp->e_lfanew + 4);//定义File结构体类型指针
opp = (Op*)((char*)filep + 20);//定义Op结构体类型指针
secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//定义Sec结构体类型指针
//修改节的数量
int tempNumberOfSections = filep->NumberOfSections;
filep->NumberOfSections = 1;
//修改第一个节表属性
secp->Misc.VirtualSize = opp->SizeOfImage - secp->VirtualAddress;
secp->SizeOfRawData = secp->Misc.VirtualSize;
int characteristics = secp->Characteristics;//先等于第一个节的属性
for(int j = 1;j < tempNumberOfSections;j++){
characteristics = characteristics | (secp + j)->Characteristics;
}
secp->Characteristics = characteristics;
printf("合并节成功\n");
return combineImageBuffer;
}
int main(int argc,char* argv[]){
//读入FileBuffer
char filePath[] = "D:\\notepad.exe";
char* fileBuffer = readInFileBuffer(filePath);
if(NULL == fileBuffer){
return 0;
}
//FileBuffer到ImageBuffer
char* imageBuffer = fileBufferToImageBuffer(fileBuffer);
if(NULL == imageBuffer){
free(fileBuffer);
fileBuffer = NULL;
return 0;
}
//合并节(ImageBuffer->CombineImageBuffer)
char* combineImageBuffer = combineSections(imageBuffer);
if(NULL == combineImageBuffer){
free(fileBuffer);
free(imageBuffer);
fileBuffer = NULL;
imageBuffer = NULL;
return 0;
}
//combineImageBuffer到NewBuffer
char* newBuffer = imageBufferToNewBuffer(combineImageBuffer);
if(NULL == newBuffer){
free(fileBuffer);
free(imageBuffer);
free(combineImageBuffer);
fileBuffer = NULL;
imageBuffer = NULL;
combineImageBuffer = NULL;
return 0;
}
//存盘
char destFilePath[] = "D:\\notepad_new.exe";
int isSuccessed2 = save(destFilePath,newBuffer);
if(!isSuccessed2){
free(fileBuffer);
free(imageBuffer);
free(newBuffer);
free(combineImageBuffer);
fileBuffer = NULL;
combineImageBuffer = NULL;
imageBuffer = NULL;
newBuffer = NULL;
return 0;
}
free(fileBuffer);
free(imageBuffer);
free(newBuffer);
free(combineImageBuffer);
fileBuffer = NULL;
combineImageBuffer = NULL;
imageBuffer = NULL;
newBuffer = NULL;
return 0;
}
3.定义函数,能够返回对齐后的大小
-
说明:定义一个函数Align(int x,int y),第一个参数为我们手动输入的字节大小,第二个参数为文件对齐大小或者内存对齐大小,现在功能是如果我们传入0x222字节,文件对齐大小为0x200,那么计算0x222文件对齐后的大小为多少;如果传入0x1222字节,内存对齐大小为0x1000,那么计算0x1222内存对齐后的大小是多少 -
代码如下: #include "stdafx.h"
int Align(int x,int y){
if(x <= 0 || y <= 0){
printf("输入的数据不合法\n");
return 0;
}
return ((x - 1) / y + 1) * y;
}
int main(int argc,char* argv[]){
printf("%x",Align(0x3700,0x1000)); //0x4000
return 0;
}
为什么x - 1,因为如果y = 0x200,我们传入的x就是0x200的话,理论上最后对齐的大小为0x200,但是如果不减1,0x200 / 0x200 = 1,1 + 1 = 2,2 * 0x200 = 0x400,就变成了0x400,但是我们如果x减1后再做运算,结果就是正确的,而且其他的情况也成立了;还有C语言中/ 号会自动向下取整
|