滴水-day24-新增节-添加ShellCode
位置计算
ShellCode
计算ShellCode插入位置(text空白区域起始位置)
通过 PointerToRawData+Misc 可以计算出
也可以通过 ImageBase+VirtualAddress+SizeOfRawData 计算出起始位置
OEP
优先这样计算,不要优化
(VirtualSize + PointerToRawData) - PointerToRawData - VirtualAddress
文件地址 - 所在节的物理偏移 + 所在节的虚拟偏移
E8和E9
DWORD EntryPoin = peheader.optionalHeader->AddressOfEntryPoint + peheader.optionalHeader->ImageBase;
DWORD E8FileOffset = ((FileOffset + MiscSize) + 0x8) + 0x5;
DWORD E9FileOffset = E8FileOffset + 0x5;
DWORD E8CALL = (DWORD_PTR)MessageBoxA_address - ((((E8FileOffset - FileOffset) + MemeryOffset)) + peheader.optionalHeader->ImageBase);
DWORD E9JMP = EntryPoin - ((((E9FileOffset - FileOffset) + MemeryOffset)) + peheader.optionalHeader->ImageBase);
新增节
手动新增节
新增0x1000H字节
2E 74 65 78 74 00 00 00 00 10 00 00 00 E0 0E 00
00 10 00 00 00 66 0D 00 00 00 00 00 00 00 00 00
00 00 00 00 20 00 00 60
前8个字节,节表名称,随意。
Misc,在内存中的大小未对齐前的,新增节多大这里就填多大
VirtualAddress 在内存中的偏移,根据上一个节表的VirtualAddress + Misc 对 SectionAlignment 对齐
SizeOfRawData 新增节多少这里的填写多少
PointerOfRawData 在文件中的偏移,根据上一个节表的 PointerToRawData + SizeOfRawData 对 FileAlignment 对齐。
Characteristics 尽量改为可执行
新增节
if (filePath == nullptr)
{
std::cerr << "Error: 参数错误" << std::endl;
return false;
}
LPVOID fileBuffer = NULL;
errno_t ReadFileResult = ReadFile((char*)filePath, &fileBuffer);
if (ReadFileResult == 0)
{
std::cerr << "Error: 读取文件失败" << std::endl;
return false;
}
//for (size_t i = 0; i < 0x20; i++) printf("%X ", *((BYTE*)fileBuffer + i));
// 获取PE信息
PEHeaders peheaders;
bool PeResult = GetPEHeaders(fileBuffer, peheaders);
if (PeResult == 0)
{
std::cerr << "Error: 获取PE头信息失败" << std::endl;
return false;
}
// 修改随机基地址
peheaders.ntHeaders->FileHeader.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
std::cerr << "固定基地址修改完成" << std::endl;
// 文件大小,文件尾插入数据
size_t fileSize = peheaders.optionalHeader->SizeOfHeaders;
size_t NumberSection = peheaders.fileHeader->NumberOfSections;
size_t FileOffset = 0, MemeryOffset = 0, MiscSize = 0;
DWORD SizeRawData = 0;
for (size_t i = 0; i < NumberSection; i++)
{
fileSize += peheaders.sectionHeader[i].SizeOfRawData;
if (i == NumberSection - 1)
{
FileOffset = peheaders.sectionHeader[i].PointerToRawData;
MemeryOffset = peheaders.sectionHeader[i].VirtualAddress;
MiscSize = peheaders.sectionHeader[i].Misc.VirtualSize;
SizeRawData = peheaders.sectionHeader[i].SizeOfRawData;
std::cout << "Name---->" << peheaders.sectionHeader[i].Name << std::endl;
}
}
printf("文件原始大小------>%X \\\\n", fileSize);
// 填充信息
unsigned char hexData[40] = {
0x2E, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x9A, 0xBA, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0xC0, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60
};
unsigned char Name[] = { 0x47,0x61, 0x47,0x61 };
// 重新分配内存以包含追加的字节
size_t newSize = fileSize + AddByte;
printf("文件最新大小------>%X \\\\n", newSize);
unsigned char* newBuffer = (unsigned char*)realloc(fileBuffer, newSize);
if (!newBuffer) {
perror("内存分配错误");
return false;
}
memset(newBuffer + fileSize, 0, AddByte);
// 更新PE信息;
PEHeaders newPeHeaders;
if (!GetPEHeaders(newBuffer, newPeHeaders)) {
std::cerr << "Error: 更新PE头信息失败" << std::endl;
free(newBuffer);
return false;
}
size_t newFileOffset = 0, newMemeryOffset = 0, newMiscSize = 0;
DWORD newSizeRawData = 0;
size_t newNumberSeciton = newPeHeaders.fileHeader->NumberOfSections;
for (size_t i = 0; i < newNumberSeciton; i++)
{
if (i == newNumberSeciton - 1)
{
newFileOffset = newPeHeaders.sectionHeader[i].PointerToRawData;
newMemeryOffset = newPeHeaders.sectionHeader[i].VirtualAddress;
newMiscSize = newPeHeaders.sectionHeader[i].Misc.VirtualSize;
newSizeRawData = newPeHeaders.sectionHeader[i].SizeOfRawData;
}
}
std::cout << "newFileOffset----> " << std::hex << newFileOffset << std::endl;
std::cout << "newMemeryOffset----> " << std::hex << newMemeryOffset << std::endl;
std::cout << "newMiscSize-------> " << std::hex << newMiscSize << std::endl;
std::cout << "newSizeRawData-------> " << std::hex << newSizeRawData << std::endl;
// 新增节表
// 确定位置
DWORD addByte = AddByte;
// 计算内存偏移和文件偏移
DWORD VirtualAddress = newMiscSize + newMemeryOffset;
if (VirtualAddress % (newPeHeaders.optionalHeader->SectionAlignment) != 0) align_1000(VirtualAddress);
std::cout << "NewSection----> VirtualAddress----> " << std::hex << VirtualAddress << std::endl;
DWORD PointerToRawData = newFileOffset + newSizeRawData;
std::cout << "NewSection----> PointerToRawData--> " << std::hex << PointerToRawData << std::endl;
// 修正hexData
memcpy_s(&hexData[1], sizeof(Name), Name, sizeof(Name));
memcpy_s(&hexData[8], sizeof(addByte), &addByte, sizeof(addByte));
memcpy_s(&hexData[12], sizeof(VirtualAddress), &VirtualAddress, sizeof(VirtualAddress));
memcpy_s(&hexData[16],sizeof(addByte), &addByte,sizeof(addByte));
memcpy_s(&hexData[20],sizeof(PointerToRawData), &PointerToRawData, sizeof(PointerToRawData));
printf("********************************节表信息*********************************\\\\n");
for (int i = 0; i < 0x28; i++)
{
printf("%02X ", hexData[i]);
}
printf("\\\\n");
// 插入节表
DWORD address = (newPeHeaders.dosHeader->e_lfanew + 0x04 + IMAGE_SIZEOF_FILE_HEADER + newPeHeaders.fileHeader->SizeOfOptionalHeader + newPeHeaders.fileHeader->NumberOfSections * 0x28);
std::cout << "新增节表偏移--------> " << address << std::endl;
BYTE* sectionAddres = (BYTE*)newBuffer + address;
std::cout << "新增节表位置--------> " << (void*)sectionAddres << std::endl;
// 数据插入
for (size_t i = 0; i < 0x28; i++)
{
sectionAddres[i] = hexData[i];
}
// 修改PE信息
newPeHeaders.fileHeader->NumberOfSections += 0x1;
newPeHeaders.optionalHeader->SizeOfImage += 0x1000;
// OEP地址
DWORD EntryPoin = newPeHeaders.optionalHeader->AddressOfEntryPoint + newPeHeaders.optionalHeader->ImageBase;
// 计算 E8 跳转地址和 E9 跳转地址
DWORD E8FileOffset = (PointerToRawData + 0x8) + 0x5;
DWORD E9FileOffset = E8FileOffset + 0x5;
DWORD E8CALL = (DWORD_PTR)MessageBoxA_address - ((((E8FileOffset - PointerToRawData) + VirtualAddress)) + newPeHeaders.optionalHeader->ImageBase);
DWORD E9JMP = EntryPoin - ((((E9FileOffset - PointerToRawData) + VirtualAddress)) + newPeHeaders.optionalHeader->ImageBase);
std::cout << "E8FileOffset----> " << std::hex << E8FileOffset << std::endl;
std::cout << "E9FileOffset----> " << std::hex << E9FileOffset << std::endl;
std::cout << "EntryPoin-------> " << std::hex << EntryPoin << std::endl;
printf("E8CALL----> %X \\\\nE9JMP-----> %X\\\\n", E8CALL, E9JMP);
// 修正ShellCode
*(PDWORD)(ShellCode + 9) = E8CALL;
*(PDWORD)(ShellCode + 0xE) = E9JMP;
// 确定 ShellCode 插入位置
BYTE* Newaddress = ((BYTE*)newBuffer + fileSize);
// 插入ShellCode。
for (int i = 0; i < ShellLength; i++)
{
Newaddress[i] = ShellCode[i];
}
// 修正OEP
std::cerr << "fileSize--------> " << fileSize << std::endl;
DWORD newOEP = (fileSize - PointerToRawData) + VirtualAddress;
std::cout << "newOEP-----> " << std::hex << newOEP << std::endl;
newPeHeaders.optionalHeader->AddressOfEntryPoint = newOEP;
// 存盘
FwritrFile(newBuffer, newSize, (char*)filePath);
free(newBuffer);
return true;
PS:如果fileSize小于ReadFIle返回的结果,那么说明这个程序的尾部不是最后一个节结束的地方,有一些数据不知道是什么,可能是垃圾数据。这种情况可以从新增节文件偏移出开始分配字节,但是会把后面的数据覆盖。问题是又不知道这些数据有没有用,还是算了把。
扩展节
与新增节逻辑差不多,不需要新增节表。只是修改原节表属性少了几个。
if (filePath == NULL)
{
perror("Para Error");
return false;
}
// 读取文件
LPVOID fileBufer = nullptr;
size_t readFileReuslt = ReadFile((char*)filePath, &fileBufer);
if (readFileReuslt == 0)
{
perror("ReadFile Error");
return false;
}
// PE信息
PEHeaders peheader;
if (!GetPEHeaders(fileBufer,peheader))
{
perror("PE Info Error");
return false;
}
// 固定基地址
peheader.fileHeader->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
if (DEBUG) std::cerr << "基地址已固定" << std::endl;
// 文件真实大小,最后一节
long FileSize = peheader.optionalHeader->SizeOfHeaders;
size_t numberSection = peheader.fileHeader->NumberOfSections;
//最后一节字段属性
size_t FileOffset = 0, MemeryOffset = 0, MiscSize = 0;
errno_t sizeData = 0;
// 使用循环获取到最后一节
for (size_t i = 0; i < numberSection; i++)
{
FileSize += peheader.sectionHeader[i].SizeOfRawData;
if (i == numberSection - 1)
{
FileOffset = peheader.sectionHeader[i].PointerToRawData;
MemeryOffset = peheader.sectionHeader[i].VirtualAddress;
MiscSize = peheader.sectionHeader[i].Misc.VirtualSize;
sizeData = peheader.sectionHeader[i].SizeOfRawData;
//std::cout << "SectionName-------> " << peheader.sectionHeader[i].Name << std::endl;
break;
}
}
if (DEBUG)
{
std::cout << "FileOffset--------> " << std::hex << FileOffset << std::endl;
std::cout << "MemeryOffset------> " << std::hex << MemeryOffset << std::endl;
std::cout << "MiscSize----------> " << std::hex << MiscSize << std::endl;
std::cout << "sizeData----------> " << std::hex << sizeData << std::endl;
std::cout << "FileSize----------> " << std::hex << FileSize << std::endl;
}
if (FileSize < readFileReuslt)
{
perror("注意:末尾节尾部有多余数据,为了您的文件安全不在扩展节");
return false;
}
// 节尾
DWORD EndSectionAddress = FileOffset + sizeData;
if(DEBUG) std::cout << "EndSectionAddress---->" << EndSectionAddress << std::endl;
// 扩展0x200字节
DWORD newBufferSize = readFileReuslt + 0x200;
unsigned char* newBuffer = (unsigned char*)realloc(fileBufer, newBufferSize);
if (newBuffer == NULL)
{
perror("malloc error");
return false;
}
memset(newBuffer + readFileReuslt, 0, 0x200);
// 更新PE信息
PEHeaders newPeheader;
if (!GetPEHeaders(newBuffer, newPeheader))
{
return false;
}
//最后一节字段属性
long newFileSize = newPeheader.optionalHeader->SizeOfHeaders;
size_t newNumbersection = newPeheader.fileHeader->NumberOfSections;
size_t newFileOffset = 0, newMemeryOffset = 0, newMiscSize = 0;
errno_t newsizeData = 0;
// 使用循环获取到最后一节
size_t j = 0;
for ( j = 0; j < newNumbersection; j++)
{
newFileSize += newPeheader.sectionHeader[j].SizeOfRawData;
if (j == numberSection - 1)
{
newFileOffset = newPeheader.sectionHeader[j].PointerToRawData;
newMemeryOffset = newPeheader.sectionHeader[j].VirtualAddress;
newMiscSize = newPeheader.sectionHeader[j].Misc.VirtualSize;
newsizeData = newPeheader.sectionHeader[j].SizeOfRawData;
//std::cout << "SectionName-------> " << peheader.sectionHeader[j].Name << std::endl;
break;
}
}
// 修正节表属性
newPeheader.sectionHeader[j].Characteristics |= newPeheader.sectionHeader->Characteristics;
if (DEBUG) std::cout << "节表已改为可执行" << std::endl;
newPeheader.sectionHeader[j].SizeOfRawData += 0x200;
newPeheader.sectionHeader[j].Misc.VirtualSize += 0x200;
newPeheader.optionalHeader->SizeOfImage += 0x200;
// ShellCode位置
BYTE* shellCodeAddress = (BYTE*)newBuffer + newFileSize;
// 修正E8 E9
DWORD OepAddress = newPeheader.optionalHeader->AddressOfEntryPoint + newPeheader.optionalHeader->ImageBase;
DWORD E8OFFSET = ((DWORD)EndSectionAddress + 0x8) + 0x5;
DWORD E9OFFSET = E8OFFSET + 0x5;
DWORD E8_VA = (((E8OFFSET - newFileOffset) + newMemeryOffset) + newPeheader.optionalHeader->ImageBase);
DWORD E9_VA = (((E9OFFSET - newFileOffset) + newMemeryOffset) + newPeheader.optionalHeader->ImageBase);
DWORD E8CALL = (DWORD)MessageBoxA_address - (((E8OFFSET - newFileOffset) + newMemeryOffset) + newPeheader.optionalHeader->ImageBase);
DWORD E9JMP = OepAddress - (((E9OFFSET - newFileOffset) + newMemeryOffset) + newPeheader.optionalHeader->ImageBase);
if (DEBUG)
{
std::cout << "E8_VA-------> " << std::hex << E8_VA << std::endl;
std::cout << "E9_VA-------> " << std::hex << E9_VA << std::endl;
std::cout << "E8OFFSET----> " << std::hex << E8OFFSET << std::endl;
std::cout << "E9OFFSET----> " << std::hex << E9OFFSET << std::endl;
std::cout << "OepAddress-------> " << std::hex << OepAddress << std::endl;
printf("E8CALL----> %X \\\\nE9JMP-----> %X\\\\n", E8CALL, E9JMP);
}
// 更新ShellCode
*(LPDWORD)(ShellCode + 9) = E8CALL;
*(LPDWORD)(ShellCode + 0xE) = E9JMP;
// 添加ShellCode
for (size_t i = 0; i < ShellLength; i++)
{
shellCodeAddress[i] = ShellCode[i];
}
// 修正OEP
DWORD newOEP = (newFileSize - newFileOffset) + newMemeryOffset;
newPeheader.optionalHeader->AddressOfEntryPoint = newOEP;
if (DEBUG == 1)
{
std::cerr << "fileSize--------> " << newFileSize << std::endl;
std::cout << "newOEP-----> " << std::hex << newOEP << std::endl;
}
// 存盘
DWORD writeResult = FwritrFile(newBuffer, newBufferSize, (char*)filePath);
return true;
感觉写的有点麻烦了,等PE课程完结,重构代码。
合并节
合并节是有点复杂的,但是如果函数封装好的话也不是很复杂
首先需要把文件读取到fileBuffer中,再有 fileBuffer 拉伸到 ImageBuffer,在 ImageBuffer 中修改第一个节表的属性:
Misc
SizeOfRawData
修改NT头中 NumberOfSections 的数量
修改节表属性为全部属性,因为合并了节,最好把所有节的属性都选上,但每个节的属性都不固定,干脆直接把属性全部点开.
if (filePath == NULL) return false;
LPVOID fileBuffer = nullptr;
if (!ReadFile((char*)filePath, &fileBuffer)) return false;
LPVOID ImageBuffer = nullptr;
if (!CopyFileBufferToImageBuffer(fileBuffer, &ImageBuffer)) return false;
PEHeaders peheader;
if (!GetPEHeaders(ImageBuffer, peheader)) return false;
DWORD VS = peheader.optionalHeader->SizeOfImage - peheader.sectionHeader->VirtualAddress;
//DWORD S = peheader.optionalHeader->SizeOfImage - peheader.optionalHeader->SizeOfHeaders;
//std::cout << "VS " << VS << std::endl;
DWORD SR = VS;
peheader.sectionHeader->Misc.VirtualSize = VS;
peheader.sectionHeader->SizeOfRawData = SR;
peheader.sectionHeader->Characteristics |= 0xFFFFFFFF;
peheader.fileHeader->NumberOfSections = 0x1;
LPVOID newBuffer = NULL;
if (!CopyImageBufferToNewBuffer(ImageBuffer, &newBuffer)) return false;
FwritrFile(newBuffer, VS + peheader.optionalHeader->SizeOfHeaders, (char*)filePath);
return true;
拉伸函数和缩放函数都是前几节课写好的。