扩大节:
1、拉伸到内存(只是逻辑上,实际代码操作并不需要这一步),需要注入的代码为 ShellCode
2、分配一块新的空间:SizeOfImage + sizeof ( ShellCode)
3、扩大节,修改最后一个节的 SizeOfRawData 和 VirtualSize
? ?OrgSecSize?= max ( SizeOfRawData 或 VirtualSize内存对齐后的值 )?
? ?∵?有些初始化数据未全部写入文件,VirtualSize?可能比?SizeOfRawData?大,必须保证添加的代码不影响到原程序
? ?VirtualSize =?OrgSecSize + sizeof ( ShellCode)
? ?SizeOfRawData =?( OrgSecSize + sizeof(ShellCode) ) 按文件对齐后的值
4、修改SizeOfImage大小
? ?SizeOfImage = (最后一节 VirtualAddress + VirtualSize) 按内存对齐后的值
这里依然在扩大节中植入弹窗代码,和前面一样的操作,详见前面的文章
注意,这里因为要使被扩节?执行代码,?所以需要在被扩节的Characteristics?按位或 一份可执行的设置
这里为? | 0x60000020??
上代码附详细注释:
用到的所有自定义函数均可在前面的章节找到
#include "windows.h"
#include "stdio.h"
#define MESSAGEBOXADDR 0x76AF39A0 //这个值需要将任一exe文件拖入OD打开,搜索 MessageBoxA 记录它的地址到这里(每次开机都不同)
unsigned char ShellCode320[] =
{
0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,
0xE8,0x00,0x00,0x00,0x00,
0xE9,0x00,0x00,0x00,0x00
};
void h320()
{
char FilePath[] = "CrackHead.exe"; //CRACKME.EXE CrackHead.exe
char CopyFilePath[] = "CrackHeadcopy.exe"; //CRACKMEcopy.EXE CrackHeadcopy.exe
LPVOID pFileBuffer = NULL; //会被函数改变的 函数输出之一
LPVOID* ppFileBuffer = &pFileBuffer; //传进函数的形参
LPVOID pNewFileBuffer = NULL;
int SizeOfFileBuffer;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
DWORD CallX = NULL; //即E8后跟的4字节
DWORD JmpX = NULL; //即E9后跟的4字节
SizeOfFileBuffer = ReadPEFile(FilePath, ppFileBuffer); //pFileBuffer即指向已装载到内存中的exe首部
if (!SizeOfFileBuffer)
{
printf("文件读取失败\n");
return;
}
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//首个节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++) //注意这里i从1开始 i < NumberOfSections
{}
//出循环后pSectionHeader指向最后一个节表
// OrgSecSize 为 该节的原始大小 (最后一个节的原始大小
printf("VirtualSize: %x\nSizeOfRawData: %x\n", pSectionHeader->Misc.VirtualSize, pSectionHeader->SizeOfRawData);
DWORD OrgSecSize = max(pSectionHeader->Misc.VirtualSize, pSectionHeader->SizeOfRawData);
printf("OrgSecSize: %x\n", OrgSecSize);
//ShallCode即放在节区头起Size之后,这里先计算地址,再修改PE中的值与开辟空间
//X即E8后的数 = 要跳转的地址 - (E8所在地址 + 5) (E8 所在地址+5 即 call指令的下一条指令的地址)
//那么要跳转的地址即messageboxA地址。E8所在地址即 ImageBase内存运行基址 + VirtualAddress节所在偏移+Size +8 才到E8 (∵ShallCode就在节开头)
CallX = MESSAGEBOXADDR - (pOptionalHeader->ImageBase + pSectionHeader->VirtualAddress + OrgSecSize + 8 + 5);
//jump 要跳转的地址即OEP程序入口点, X = 程序入口点 - E9所在地址 + 5
//这里程序入口点即ImageBase基址 + 修改前的OddAddressOfEntryPoint E9所在地址计算同上 下式是化简后约去了ImageBase
//ImageBase + AddressOfEntryPoint - (ImageBase + VirtualAddress +13 +5 )
JmpX = pOptionalHeader->AddressOfEntryPoint - (pSectionHeader->VirtualAddress + OrgSecSize + 13 + 5);
//将上述计算后的值放入ShellCode320
*(PDWORD)(ShellCode320 + 9) = CallX;
*(PDWORD)(ShellCode320 + 14) = JmpX;
for (int i = 0; i < sizeof(ShellCode320); i++)
{
printf("%x ", ShellCode320[i]);
}
printf("\n");
//计算完地址后 计算新节表值 VirtualSize以Size + ShellCode长度 按内存对齐(其实VirtualSize可以不用内存对齐),SizeOfRawData同理
int NewVirtualSize = OrgSecSize + sizeof(ShellCode320);
int NewSizeOfRawData = Align(OrgSecSize + sizeof(ShellCode320), pOptionalHeader->FileAlignment);
printf("NewSizeOfRawData%x, NewVirtualSize:%x\n", NewSizeOfRawData, NewVirtualSize);
// 在修改节表值之前需要用到新旧值 ∴上述节表新值暂不真正修改只作记录。
// 修改SizeOfImage值
pOptionalHeader->SizeOfImage = Align(pSectionHeader->VirtualAddress + NewVirtualSize, pOptionalHeader->SectionAlignment);
//在修改节表值之前新空间长度 原空间长度 减去 旧SizeOfRawData 加上新SizeOfRawData
int SizeOfNewFileBuffer = SizeOfFileBuffer - pSectionHeader->SizeOfRawData + NewSizeOfRawData;
//修改入口点
pOptionalHeader->AddressOfEntryPoint = pSectionHeader->VirtualAddress + OrgSecSize;
//Characteristics
printf("Characteristics:%x\n", pSectionHeader->Characteristics);
pSectionHeader->Characteristics = pSectionHeader->Characteristics | 0x60000020;
printf("Characteristics:%x\n", pSectionHeader->Characteristics);
//修改节表值
pSectionHeader->Misc.VirtualSize = NewVirtualSize;
pSectionHeader->SizeOfRawData = NewSizeOfRawData;
//printf("VirtualSize: %x\nSizeOfRawData: %x\n", pSectionHeader->Misc.VirtualSize, pSectionHeader->SizeOfRawData);
//修改值后开始重新分配空间
pNewFileBuffer = malloc(SizeOfNewFileBuffer);
memset(pNewFileBuffer, 0, SizeOfNewFileBuffer);
memcpy(pNewFileBuffer, pFileBuffer, SizeOfFileBuffer); //复制原空间
printf("SizeOfFileBuffer:%x\nSizeOfNewFileBuffer:%x\n", SizeOfFileBuffer, SizeOfNewFileBuffer);
// 复制新空间
memcpy((void*)((DWORD)pNewFileBuffer + pSectionHeader->PointerToRawData + OrgSecSize), ShellCode320, sizeof(ShellCode320));
//memcpy((void*)((DWORD)pNewFileBuffer + SizeOfFileBuffer), ShellCode320, sizeof(ShellCode320)); //复制ShellCode
//剩余部分填充0
//memset((void*)((DWORD)pNewFileBuffer + SizeOfFileBuffer + sizeof(ShellCode320)), 0, (SizeOfNewFileBuffer - SizeOfFileBuffer - sizeof(ShellCode320)));
MemeryToFile(pNewFileBuffer, SizeOfNewFileBuffer, CopyFilePath);
free(pNewFileBuffer);
free(pFileBuffer);
}
运行程序打印内容如下:
VirtualSize: 468 SizeOfRawData: 600 OrgSecSize: 600 6a 0 6a 0 6a 0 6a 0 e8 93 f3 6e 76 e9 ee c9 ff ff NewSizeOfRawData800, NewVirtualSize:612 Characteristics:c0000040 Characteristics:e0000060 SizeOfFileBuffer:1600 SizeOfNewFileBuffer:1800 success
打开程序有弹窗,确定后程序正常运行
原exe程序 PETool 查看
修改后:
?
?
|