加密壳(二)将shellcode写入PE
当PE文件代码段的空白区不足添加我们的shellcode时,便需要添加新的区段来保存我们的shellcode,而新区段头需要紧跟在原区段头后面
接下来我们开始详解学习如何添加一个节
添加节有以下几种方法:
1.新增节添加代码
2.扩大最后一个节添加代码
3.合并节并添加代码
接下来我们将学习这几种方法
添加节
一.判断是最后节表后到节表部分结束否有足够的空间可以添加一个节表:新增节需要新增一个节表来记录此节信息
注意:如果节表后有足够的空间但是紧接节表有数据(非0x),就要谨慎行事,因为这些数据可能是有用数据
判断方法:SizeOfHeader - (DOS + 垃圾数据 + PE标记 + 标准PE头 + 可选PE头 + 已存在节表) >= 2个节表的大小(80个字节大小)
注意:windows会根据一个结构体后面是否有至少一个同结构体大小以上的0x00来判断这个结构体是否结束,所以最后一个节表后面通常需要填补同节表宽度的0以防系统误判
二.当满足添加节表的条件时,我们可以将已有的一个节表的40字节信息复制一份紧挨着最后一个节表的末尾(节表与节表之间时连续的),然后再定义之后的40字节都是0,之后再根据新增节的信息,去修改对应节表的字段值
三.修改数据:
1.修改PE头中NumberOfSections字段(节的数量)为我们添加节以后节的总数
2.修改SizeOfImage的大小:原有值 + 新增节的大小(注意内存对齐)
3.修正新增节表的属性:
Name:按照ASCII码自定义名字,最好长度限制在8字节内
Misc.VirtualSize:内存起始偏移地址,我们通过上一个节表的VirtualAddress + VirtualSize的值再进行内存对齐后得到上一个节的结尾位置,作为新增节的内存起始偏移地址。但我们在实际操作中选择VirtualSize和SizeOfRawData中大者取值
举例:
我们有时候发现文件内存中未对齐的大小确实比文件中对齐的大小大,这是由于节中包含初始化数据的缘故。比如notepad.exe文件的第二个节.data中,内存中未对齐的大小为0x1BA8,文件中对齐后的大小为0x600。因此我们可以发现,第三个节在文件起始中应该按照上一个节在文件中对齐后的大小往后接着存放,即0x7200 + 0x600 = 0x7800,所以第三个节的PointerToRawData为0x7800。而第三个节在内存起始中应该按照上一个节在内存中对齐后的大小往后接着存放而不是再按照上一个节的文件中对齐大小挨着存放了,这是因为此时VirtualSize比SizeOfRawData大,即0x8000 + 0x1BA8 = 0x9BA8,对齐后为0xA000,因此第三个节内存偏移为0xA000
SizeOfRawData:根据文件对齐粒度来修正这个值。如果上面设定了VirtualSize的值为0x1000,说明这个节的这0x1000字节都是有用数据,那么此节在硬盘上时也应该有这0x1000字节的数据,此时需要根据文件对齐粒度来修正这个值。如果为0x200或者0x1000,就不用修改0x1000,因为已经满足是文件对齐的整数倍了
PointerToRawData:文件对齐后的文件地址,我们可以通过上一个节表的PointerToRawData + SizeOfRawData计算得到此值
Characteristics:按照我们想要的属性来修改即可(可读、可写、可执行等)
如图所示:
注意:修改SizeOfHeaders的值代价很高,所以不能随便改变。因为如果这个值变大或变小了,后面的节都要跟着往后或者往前跟着改变,其中的数据地址也会随之改变,比如这些节当中涉及到相关计算地址的数值就需要重新计算了。其他诸多类似的修改都是一个繁琐的过程,会加重我们的工作量。比如说我们上节课作业中E8和E9后面的值,是通过其他的地址计算出来的。如果SizeOfHeaders改变了,节地址也会随之改变,因此这些值也要改变。
特殊情况
在众多程序中,有一些程序在最后一个节表后有一些信息(这些信息我们无法知晓是否有用),所以我们不可轻易修改这些信息。但是我们要添加的新节表一定要与原来的节表连续紧挨着,所以在这种情况下为了不覆盖有用的信息,我们就要考虑整体上移NT头和节表
已知程序的DOS头到PE签名之间有一处区域叫做DOS Stub。该区域存储的是程序的一些说明信息相关的数据,这些数据不会影响程序的运行,并且对我们来说是垃圾数据,所以我们可以将NT头到节表末尾这一部分整体上移,把Dos Stub这块数据覆盖了,但原来节表末尾的数据不进行修改。这时我们修改DOS头中的e_lfanew字段的值为上移后PE签名的地址。此时节表末尾和原节表末尾的信息之间就会空出来一部分,我们将这部分全部初始化为0以后,该区域就是一个空白区域了,此时我们就可以往这片空白区域新增节表了。
扩大节
我们在上文了解到,我们可以通过上移NT头和节表覆盖DOS Stub以获取足够的空白区来增加新的节表以及后续新增节,但是当整体上移覆盖DOS Stub之后,多出来的空白区域还不够新增节表时,我们就可以使用扩大某个节使其留出空白区域以便添加我们的shellcode。当我们扩大其他某个节时,其他节的偏移量之类的属性都需要修改,这样很麻烦,因此为了减少麻烦,我们通常使用扩大最后一个节的方法来添加我们的shellcode
扩大节步骤:
1.修改文件最后一个区段头VirtualSize += EX,其中EX为shellcode大小
2.修改最后一个区段头SizeofRawDate += EX,此处注意文件对齐。为方便,通常取文件对齐和内存对齐较大者
3.修改最后一个区段头属性可读可写可执行
4.修改SizeOfImage += EX,此处注意内存对齐
5.将shellcode加到最后一个节中
注意事项:
1.文件加载到内存后,最后一个节的VirtualAddress + VirtualSize对齐后的位置就是一个文件在内存中的结束位置,即SizeOfImage。文件在硬盘上时,最后一个节的PointerToRawData + SizeOfRawData的位置就是整个文件再硬盘上的结尾。
2.文件在内存中,节与节之间的排列是根据VirtualAddress和VirtualSize以及其内存对齐后的结果进行判断。如果在硬盘上,节与节之间的排列,是根据PointerToRawData和SizeOfRawData来判断,这是因为SizeOfRawData已经是文件数据经过对齐后的结果了
合并节
当我们整体上移后,留出来的空白区域不足以添加我们新的节表时,我们便可以利用合并节来添加数据:合并节就是将所有的节表只保留第一个,作为所有的节表(其他节表可删可不会删,因为这些数据在将NumberOfSections改为1后就没有意义了),把第一个节开始一直到最后一个节的结尾都当成第一个节。
合并节有一个缺点:会造成合并后文件变大。在我们合并节以前,文件加载到内存时,每个节都是按照顺序通过内存对齐以后加载到内存中的。但当我们合并节以后,所有的节会默认是一个节,操作系统会以一个节的形式进行内存对齐然后加载内存。因此合并节前后,文件加载内存以后,内存分布是不统一的,当我们运行程序后,会出现差错。因此为了保证合并节以后程序在加载内存后的内存分布是不变的,我们需要将各个节按照内存加载后的内存分布的样子写入文件中,此时再加载内存后,程序的内存分布就不会出错了。
注意:合并节一定要在ImageBuffer中进行操作,不然在FileBuffer容易出现错误,比如节中未初始化数据在拉伸后导致节变大等等情况。
步骤:
1.将标准PE头中 NumberOfSections改成1
2.修改第一个节表的字段VirtualSize:
方法一:SizeOfImage - 第一个节VirtualAddress
方法二:还可以通过最后一个节的VirtualAddress + 最后一个节的VirtualSize内存对齐后的大小 - SizeOfHeader内存对齐后的大小获取整个VirtualSize
方便起见我们选择方法一
SizeOfRawData:等于VirtualSize即可
将第一个节的属性改为包含所有节的属性,即用第一个节的属性与其他的节的属性做或运算
作业
代码实现新增一个节,并添加代码
#include<stdio.h>
#include<Windows.h>
#include<string.h>
DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据d大小
{FILE* pFile = NULL;pFile = fopen(lpszFile, "rb");DWORD FileSize = 0;if (!pFile){printf("无法打开EXE文件");return 0;}fseek(pFile, 0, SEEK_END);FileSize = ftell(pFile);return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{FILE* pFile = NULL;pFile = fopen(lpszFile, "rb");DWORD FileSize = ReadPEFileSize(lpszFile);fseek(pFile, 0, SEEK_SET);char* pFileBuffer = NULL;pFileBuffer = (char*)malloc(sizeof(char) * FileSize);if (!pFileBuffer){printf("分配空间失败");fclose(pFile);return 0;}size_t i = fread(pFileBuffer, FileSize, 1, pFile);if (!i){printf("读取数据失败!");free(pFileBuffer);fclose(pFile);return 0;}PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){printf("不是有效MZ标志,结束\n");free(pFileBuffer);fclose(pFile);return 0;}if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){printf("不是有效的PE标志,打印结束\n");free(pFileBuffer);pFileBuffer = NULL;return 0;}fclose(pFile);return pFileBuffer;
}
BOOL shellcode(const char* newBuffer, const char* lpszFile, DWORD size)
{char shellcode[18] = { 0x6A, 0x00, 0x6A, 0x00, 0x6A,0x00, 0x6A, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00 };char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针if (pFileBuffer == NULL){printf("缓冲区指针无效\n");return FALSE;}PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取标准PE头指针PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针if (pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - 4 - 20 - pFileHeader->SizeOfOptionalHeader >= 2 * pFileHeader->SizeOfOptionalHeader){printf("节表后剩余空间足够添加一个新的节表\n");}else{printf("节表后剩余空间不足以添加一个新的节表,结束\n");return FALSE;}PIMAGE_SECTION_HEADER pSecHeaderEnd = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + pFileHeader->NumberOfSections * 40);memcpy(pSecHeaderEnd, pSecHeader, 40);memset(((char*)pSecHeaderEnd + 40), 0, 40); //此时已经新增了一个节表PIMAGE_SECTION_HEADER pSecHeaderLast = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + (pFileHeader->NumberOfSections - 1) * 40);//获取新增节上一个节指针pFileHeader->NumberOfSections += 1;pOptionHeader->SizeOfImage += pOptionHeader->SectionAlignment;strcpy((char*)pSecHeaderEnd->Name, (char*)".shel");pSecHeaderEnd->PointerToRawData = pSecHeaderLast->PointerToRawData + pSecHeaderLast->SizeOfRawData;char* pSecHeaderEndBuffer = (char*)pDosHeader + pSecHeaderEnd->PointerToRawData;pSecHeaderEnd->Misc.VirtualSize = sizeof(shellcode);pSecHeaderEnd->SizeOfRawData = pOptionHeader->FileAlignment;DWORD Alignment = pOptionHeader->SectionAlignment - pSecHeaderLast->Misc.VirtualSize % pOptionHeader->SectionAlignment;pSecHeaderEnd->VirtualAddress = pSecHeaderLast->VirtualAddress + pSecHeaderLast->Misc.VirtualSize + Alignment; //获取上个节对齐以后添加节的地址DWORD NewFileSize = pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData; //获取新的申请的文件内存大小char* pNewFileBuffer = (char*)malloc(NewFileSize); //重新申请一块大的内存if (pNewFileBuffer == NULL) // 判断内存是否生成成功{printf("缓冲区指针无效\n");return FALSE;}memset(pNewFileBuffer, 0, NewFileSize);memcpy(pNewFileBuffer, pDosHeader, (NewFileSize - pSecHeaderEnd->SizeOfRawData));PIMAGE_DOS_HEADER pNewDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer;PIMAGE_NT_HEADERS pNewNTHeader = (PIMAGE_NT_HEADERS)((char*)pNewFileBuffer + pNewDosHeader->e_lfanew); //获取NT头指针PIMAGE_FILE_HEADER pNewFileHeader = (PIMAGE_FILE_HEADER)((char*)pNewNTHeader + 4);//获取标准PE头指针PIMAGE_OPTIONAL_HEADER pNewOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pNewFileHeader + 20);//获取可选PE头指针PIMAGE_SECTION_HEADER pNewSecHeader = (PIMAGE_SECTION_HEADER)((char*)pNewOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针pNewSecHeader += pNewFileHeader->NumberOfSections - 1; //指向最后一个节表char* pNewSecBuffer = (char*)pNewDosHeader + pNewSecHeader->PointerToRawData;DWORD NewShellcodePoint = (DWORD)pNewOptionHeader->ImageBase + (DWORD)pNewSecHeader->VirtualAddress + 0xD;//指向虚拟内存中E8下一条指令地址DWORD MessageAddress = (DWORD)0x77D507EA - NewShellcodePoint; //获取E8指令所要的相对地址*(DWORD*)(shellcode + 9) = MessageAddress;NewShellcodePoint += 0x5; //获取虚拟内存中E9下一条指令地址DWORD EnterAdderss = (DWORD)pNewOptionHeader->ImageBase + (DWORD)pNewOptionHeader->AddressOfEntryPoint - (DWORD)NewShellcodePoint; //获取E9所需要虚拟内存相对地址*(DWORD*)(shellcode + 14) = EnterAdderss;memcpy(pNewSecBuffer, shellcode, sizeof(shellcode)); //将预备shellcode添加至空白区pNewOptionHeader->AddressOfEntryPoint = (DWORD)pNewSecHeader->VirtualAddress; //修改程序入口为添加代码处FILE* NewFile = fopen(newBuffer, "wb");DWORD Success = fwrite(pNewFileBuffer, NewFileSize, 1, NewFile);if (!Success){printf("存盘失败");}free(pFileBuffer);return TRUE;
}
int main(int argc, char* argv[])
{const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\复件 NOTEPAD.exe";const char* NewlpszFile = "C:\\Users\\扶摇\\Desktop\\NewNOTEPAD.exe";DWORD size = ReadPEFileSize(lpszFile);if (!shellcode(NewlpszFile, lpszFile, size)){printf("注入shellcode失败\n");}return 0;
}
上述代码利用xp的notepad实现,其节表最后新增节后应再初始化40个0,由于此40个0覆盖了原来存在有效数据,所以程序运行失败。但针对本次练习,该代码没有任何问题
通过上移PE头节表等数据,使上题代码生成程序正常运行
#include<stdio.h>
#include<Windows.h>
#include<string.h>
DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据d大小
{FILE* pFile = NULL;pFile = fopen(lpszFile, "rb");DWORD FileSize = 0;if (!pFile){printf("无法打开EXE文件");return 0;}fseek(pFile, 0, SEEK_END);FileSize = ftell(pFile);return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{FILE* pFile = NULL;pFile = fopen(lpszFile, "rb");DWORD FileSize = ReadPEFileSize(lpszFile);fseek(pFile, 0, SEEK_SET);char* pFileBuffer = NULL;pFileBuffer = (char*)malloc(sizeof(char) * FileSize);if (!pFileBuffer){printf("分配空间失败");fclose(pFile);return 0;}size_t i = fread(pFileBuffer, FileSize, 1, pFile);if (!i){printf("读取数据失败!");free(pFileBuffer);fclose(pFile);return 0;}IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){printf("不是有效MZ标志,结束\n");free(pFileBuffer);fclose(pFile);return 0;}if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){printf("不是有效的PE标志,打印结束\n");free(pFileBuffer);pFileBuffer = NULL;return 0;}fclose(pFile);return pFileBuffer;
}
BOOL shellcode(const char* newBuffer, const char* lpszFile, DWORD size)
{char shellcode[18] = { 0x6A, 0x00, 0x6A, 0x00, 0x6A,0x00, 0x6A, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00 };char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针if (pFileBuffer == NULL){printf("缓冲区指针无效\n");return FALSE;}PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取标准PE头指针PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针char* flag = (char*)pDosHeader + 64;memcpy(flag, (char*)pNTHeader, 4 + 20 + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * 40);DWORD memsize = (DWORD)((char*)pDosHeader + pDosHeader->e_lfanew) - (DWORD)((char*)pDosHeader + 64);memset((char*)(pSecHeader + pFileHeader->NumberOfSections - 1), 0, memsize);pDosHeader->e_lfanew = 64;pNTHeader = (PIMAGE_NT_HEADERS)((char*)pDosHeader + pDosHeader->e_lfanew); //获取上移后NT头指针pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取上移后标准PE头指针pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取上移后可选PE头指针pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取上移后文件节表指针if (pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - 4 - 20 - pFileHeader->SizeOfOptionalHeader >= 2 * pFileHeader->SizeOfOptionalHeader){printf("节表后剩余空间足够添加一个新的节表\n");}else{printf("节表后剩余空间不足以添加一个新的节表,结束\n");return FALSE;}PIMAGE_SECTION_HEADER pSecHeaderEnd = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + pFileHeader->NumberOfSections * 40);memcpy(pSecHeaderEnd, pSecHeader, 40);memset(((char*)pSecHeaderEnd + 40), 0, 40); //此时已经新增了一个节表PIMAGE_SECTION_HEADER pSecHeaderLast = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + (pFileHeader->NumberOfSections - 1) * 40);//获取新增节上一个节指针pFileHeader->NumberOfSections += 1;pOptionHeader->SizeOfImage += pOptionHeader->SectionAlignment;strcpy((char*)pSecHeaderEnd->Name, (char*)".shel");pSecHeaderEnd->PointerToRawData = pSecHeaderLast->PointerToRawData + pSecHeaderLast->SizeOfRawData;char* pSecHeaderEndBuffer = (char*)pDosHeader + pSecHeaderEnd->PointerToRawData;pSecHeaderEnd->Misc.VirtualSize = sizeof(shellcode);pSecHeaderEnd->SizeOfRawData = pOptionHeader->FileAlignment;DWORD Alignment = pOptionHeader->SectionAlignment - pSecHeaderLast->Misc.VirtualSize % pOptionHeader->SectionAlignment;pSecHeaderEnd->VirtualAddress = pSecHeaderLast->VirtualAddress + pSecHeaderLast->Misc.VirtualSize + Alignment; //获取上个节对齐以后添加节的地址DWORD NewFileSize = pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData; //获取新的申请的文件内存大小 char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针if (pFileBuffer == NULL){printf("缓冲区指针无效\n");return FALSE;}PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取标准PE头指针PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针DWORD memsize = (DWORD)((char*)pDosHeader + pDosHeader->e_lfanew) - (DWORD)((char*)pDosHeader + 64);pDosHeader->e_lfanew = 64;memset((char*)pDosHeader + 64, 0, memsize);memcpy((char*)pDosHeader + 64, (char*)pNTHeader, 4 + 20 + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * 40);pNTHeader = (PIMAGE_NT_HEADERS)((char*)pDosHeader + pDosHeader->e_lfanew); //获取上移后NT头指针pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取上移后标准PE头指针pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取上移后可选PE头指针pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取上移后文件节表指针PIMAGE_SECTION_HEADER pSecHeaderLast = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + (pFileHeader->NumberOfSections - 1) * 40);//获取新增节上一个节指针if (pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - 4 - 20 - pFileHeader->SizeOfOptionalHeader >= 2 * pFileHeader->SizeOfOptionalHeader){printf("节表后剩余空间足够添加一个新的节表\n");}else{printf("节表后剩余空间不足以添加一个新的节表,结束\n");return FALSE;}PIMAGE_SECTION_HEADER pSecHeaderEnd = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + pFileHeader->NumberOfSections * 40);memcpy(pSecHeaderEnd, pSecHeader, 40);memset((char*)pSecHeaderEnd + 40, 0, 40); //此时将节表后初始化40个0,表示节表结束pFileHeader->NumberOfSections += 1;strcpy((char*)pSecHeaderEnd->Name, (char*)".mov");pSecHeaderEnd->PointerToRawData = pSecHeaderLast->PointerToRawData + pSecHeaderLast->SizeOfRawData;pSecHeaderEnd->Misc.VirtualSize = 0x2000;pSecHeaderEnd->SizeOfRawData = Alige(0x1000, pOptionHeader->FileAlignment);pSecHeaderEnd->VirtualAddress = pSecHeaderLast->VirtualAddress + Alige(pSecHeaderLast->Misc.VirtualSize, pOptionHeader->SectionAlignment); //获取上个节对齐以后添加节的地址pOptionHeader->SizeOfImage = pOptionHeader->SizeOfImage + Alige(pSecHeaderLast->Misc.VirtualSize, pOptionHeader->SectionAlignment);DWORD NewFileSize = pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData;char* pNewFileBuffer = (char*)malloc(NewFileSize); //重新申请一块大的内存if (pNewFileBuffer == NULL) // 判断内存是否生成成功{printf("缓冲区指针无效\n");return FALSE;}memset(pNewFileBuffer, 0, NewFileSize);memcpy(pNewFileBuffer, pDosHeader, (NewFileSize - pSecHeaderEnd->SizeOfRawData));PIMAGE_DOS_HEADER pNewDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer;PIMAGE_NT_HEADERS pNewNTHeader = (PIMAGE_NT_HEADERS)((char*)pNewFileBuffer + pNewDosHeader->e_lfanew); //获取NT头指针PIMAGE_FILE_HEADER pNewFileHeader = (PIMAGE_FILE_HEADER)((char*)pNewNTHeader + 4);//获取标准PE头指针PIMAGE_OPTIONAL_HEADER pNewOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pNewFileHeader + 20);//获取可选PE头指针PIMAGE_SECTION_HEADER pNewSecHeader = (PIMAGE_SECTION_HEADER)((char*)pNewOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针pNewSecHeader += pNewFileHeader->NumberOfSections - 1; //指向最后一个节表char* pNewSecBuffer = (char*)pNewDosHeader + pNewSecHeader->PointerToRawData;DWORD NewShellcodePoint = (DWORD)pNewOptionHeader->ImageBase + (DWORD)pNewSecHeader->VirtualAddress + 0xD;//指向虚拟内存中E8下一条指令地址DWORD MessageAddress = (DWORD)0x77D507EA - NewShellcodePoint; //获取E8指令所要的相对地址*(DWORD*)(shellcode + 9) = MessageAddress;NewShellcodePoint += 0x5; //获取虚拟内存中E9下一条指令地址DWORD EnterAdderss = (DWORD)pNewOptionHeader->ImageBase + (DWORD)pNewOptionHeader->AddressOfEntryPoint - (DWORD)NewShellcodePoint; //获取E9所需要虚拟内存相对地址*(DWORD*)(shellcode + 14) = EnterAdderss;memcpy(pNewSecBuffer, shellcode, sizeof(shellcode)); //将预备shellcode添加至空白区pNewOptionHeader->AddressOfEntryPoint = (DWORD)pNewSecHeader->VirtualAddress; //修改程序入口为添加代码处FILE* NewFile = fopen(newBuffer, "wb");DWORD Success = fwrite(pNewFileBuffer, NewFileSize, 1, NewFile);if (!Success){printf("存盘失败");}free(pFileBuffer);return TRUE;
}
int main(int argc, char* argv[])
{const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\复件 NOTEPAD.exe";const char* NewlpszFile = "C:\\Users\\扶摇\\Desktop\\NewNOTEPAD.exe";DWORD size = ReadPEFileSize(lpszFile);if (!shellcode(NewlpszFile, lpszFile, size)){printf("注入shellcode失败\n");}return 0;
}
经测试,上述代码成功注入shellcode并可正常运行
代码实现扩大最后一个节
#include<stdio.h>
#include<Windows.h>
#include<string.h>
DWORD ReadPEFileSize(const char* lpszFile) //获取文件的硬盘内存数据大小
{FILE* pFile = NULL;pFile = fopen(lpszFile, "rb");DWORD FileSize = 0;if (!pFile){printf("无法打开EXE文件");return 0;}fseek(pFile, 0, SEEK_END);FileSize = ftell(pFile);return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{FILE* pFile = NULL;pFile = fopen(lpszFile, "rb");DWORD FileSize = ReadPEFileSize(lpszFile);fseek(pFile, 0, SEEK_SET);char* pFileBuffer = NULL;pFileBuffer = (char*)malloc(sizeof(char) * FileSize);if (!pFileBuffer){printf("分配空间失败");fclose(pFile);return 0;}size_t i = fread(pFileBuffer, FileSize, 1, pFile);if (!i){printf("读取数据失败!");free(pFileBuffer);fclose(pFile);return 0;}IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){printf("不是有效MZ标志,结束\n");free(pFileBuffer);fclose(pFile);return 0;}if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){printf("不是有效的PE标志,打印结束\n");free(pFileBuffer);pFileBuffer = NULL;return 0;}fclose(pFile);return pFileBuffer;
}
BOOL ExpendSection(const char* newBuffer, const char* lpszFile)
{char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针if (pFileBuffer == NULL){printf("缓冲区指针无效\n");return FALSE;}DWORD FileSize = ReadPEFileSize(lpszFile);//获取硬盘上文件大小PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取标准PE头指针PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针DWORD OptSectionNumber = pFileHeader->NumberOfSections;pSecHeader += OptSectionNumber - 1;DWORD MaxSize = pSecHeader->SizeOfRawData >= pSecHeader->Misc.VirtualSize ? pSecHeader->SizeOfRawData : pSecHeader->Misc.VirtualSize;DWORD EX; printf("请输入要扩大的字节数");scanf("%d", &EX);if (EX >= pOptionHeader->SectionAlignment){int SecAlignment = pOptionHeader->SectionAlignment - (EX - pOptionHeader->SectionAlignment);EX += SecAlignment;}//由于内存对齐通常比文件对齐大且为整数倍,所以我们将EX以内存对齐大小对齐作为最终扩大的字节数else{int SecAlignment = pOptionHeader->SectionAlignment - EX;EX += SecAlignment;}pSecHeader->SizeOfRawData = MaxSize + EX;pSecHeader->Misc.VirtualSize = MaxSize + EX;pOptionHeader->SizeOfImage += EX;DWORD NewFileSize = FileSize + EX;char* NewFileBuffer = (char*)malloc(pOptionHeader->SizeOfImage);memset(NewFileBuffer, 0, NewFileSize);memcpy(NewFileBuffer, pFileBuffer, FileSize);FILE* NewFile = fopen(newBuffer, "wb");DWORD Success = fwrite(NewFileBuffer, NewFileSize, 1, NewFile);if (!Success){printf("存盘失败");}free(pFileBuffer);return TRUE;
}
int main(int argc, char* argv[])
{const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\Notepad.exe";const char* NewlpszFile = "C:\\Users\\扶摇\\Desktop\\NewNotepad.exe";ExpendSection(NewlpszFile, lpszFile);return 0;
}