PE 空白区注入shellcode
实现流程:
1、在具有可执行属性的文件中进行写入相对应的shellcode,写入shellcode的时候需要先查看是否文件对齐和内存对齐一致,如果不一致那需要先转换,如果一致就可以直接在空白的地址上进行填写!
2、填写完shellcode之后,最后的跳转要跳回原程序的入口地址,所以还需要更改跳转到原入口点 !
3、因为需要一打开就先执行shellcode,所以需要将原入口点改为当前shellcode的开始地址!
注入过程:
当前要注入的EXE的信息:
MESSAGEBOXW 0x77D66534
基址 0x1000000
AddressOfEntryPoint 入口地址为 0x739D
内存对齐0x1000
文件对齐0x200
1、先计算实际位置text节 实际文件大小 0x7748 文件对齐大小 0x7800 文件偏移0x400 内存偏移0x1000 ------> 节数据 7B48 ~ 7C00
2、当前shellode的开始地址需要进行转换为内存地址,当前地址为7B60,所以转换为 7B60-400 + 1000 + 1000000 = 1008760,这个就是内存中的地址
3、然后再进行CALL指令调用,根据公式:X = 真正要跳转的地址 - E8这条指令的下一行地址
那么就是 77D66534(要跳转的地址) - (1008760 + 8 + 5) = 76D5DDC7,则填写的就是E8 C7 DD D5 76
,E8为CALL的硬编码
4、最后需要JMP到原来AddressOfEntryPoint
的地方,那么继续根据公式:X = 真正要跳转的地址(AddressOfEntryPoint) - E9这条指令的下一行地址,100739D - (1008760 + D + 5) = FFFFEC2B,则填写的就是E9 2B EC FF FF
,E9为JMP的硬编码
5、因为我们还需要当程序一加载就运行shellcode,所以我们的程序入口点需要被修改,也就是AddressOfEntryPoint要被修改,修改为当前shellcode的入口地址,也就是修改为RVA,上面已经算过了1008760
为VA,8760为RVA
!
6、运行测试,测试成功!
缺点:
1、这也只能算是单机版,因为没有进行动态加载获取MessageBoxA的地址,因为每个机器中的内存中user32.dll中MessageBoxA地址都不是唯一的
注意点:
1、这里的测试程序内存对齐和文件对齐都相同,所以好测试,但是如果使文件和内存对齐大小不同的话,那么就需要特别注意,此时我们在计算跳转地址的时候,需要先将FOA转换为RVA然后再进行计算填写对应的数据才行,比如入口点的地址这些填写的时候就需要先进行计算,先将FOA转换为RVA,然后再去改,原因就是对齐大小不同
2、这里的添加shellcode,我们只是在代码节中进行修改,大家都知道代码节的的属性是可执行的,如果想要在其他节中使用的话,那我们还需要注意该节的属性,也就是我们需要修改该节的属性为可执行!
代码实现:
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID pImageBuffer);
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer);
DWORD FOA_TO_RVA(PVOID FileAddress, DWORD FOA,PDWORD pRVA);
DWORD RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA);
void MyReadFile(PVOID* pFileBuffer,PDWORD BufferLenth);
void MyWriteFile(PVOID pMemBuffer,size_t size);
#define FILENAME "C:\\Documents and Settings\\Administrator\\桌面\\NOTEPAD.EXE"
#define NEWFILENAME "C:\\Documents and Settings\\Administrator\\桌面\\NEW_NOTEPAD.EXE"
//ReadPEFile:将文件读取到缓冲区
//参数说明:
//lpszFile 文件路径
//pFileBuffer 缓冲区指针
//返回值说明:
//读取失败返回0 否则返回实际读取的大小
void MyReadFile(PVOID* pFileBuffer,PDWORD BufferLenth){
FILE* File;
File = fopen(FILENAME,"rb");
if(File == NULL){
printf("文件句柄打开失败");
return;
}
//读取文件
fseek(File,0,SEEK_END);
*BufferLenth = ftell(File);
fseek(File,0,SEEK_SET);
//开辟新空间
*pFileBuffer = (PVOID)malloc(*BufferLenth);
//内存清零
memset(*pFileBuffer,0,*BufferLenth);
//读取到内存缓冲区
fread(*pFileBuffer,*BufferLenth,1,File);
//关闭文件句柄
fclose(File);
}
//**************************************************************************
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer
//参数说明:
//pFileBuffer FileBuffer指针
//pImageBuffer ImageBuffer指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer){
PIMAGE_DOS_HEADER pImageDosHeader = NULL;
PIMAGE_NT_HEADERS pImageNtHeader = NULL;
PIMAGE_FILE_HEADER pImageFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
DWORD ImageBufferSize = 0;
int i=0;
// DOS头
pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
// 标准PE
pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
// 可选PE
pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//节表组
pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
//获取ImageBufffer的内存大小
ImageBufferSize = pImageOptionalHeader->SizeOfImage;
//为pImageBuffer分配内存空间
*pImageBuffer = (PVOID)malloc(ImageBufferSize);
if (*pImageBuffer == NULL)
{
printf("malloc failed");
return -1;
}
//清零
memset(*pImageBuffer, 0, ImageBufferSize);
// 拷贝头+节表
memcpy(*pImageBuffer, pFileBuffer, pImageOptionalHeader->SizeOfHeaders);
//循环拷贝节表
for(i=0;i<pImageFileHeader->NumberOfSections;i++){
memcpy(
(PVOID)((DWORD)*pImageBuffer + pImageSectionHeaderGroup[i].VirtualAddress), // 要拷贝的位置 ImageBuffer中的每个节数据的偏移位置
(PVOID)((DWORD)pFileBuffer + pImageSectionHeaderGroup[i].PointerToRawData), // 被拷贝的位置是 Filebuffer中的每个节数据的偏移位置
pImageSectionHeaderGroup[i].SizeOfRawData // 被拷贝的大小为 每个节数据的文件对齐大小
);
}
return 0;
}
//**************************************************************************
//CopyImageBufferToNewBuffer:将ImageBuffer中的数据复制到新的缓冲区
//参数说明:
//pImageBuffer ImageBuffer指针
//pNewBuffer NewBuffer指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer){
PIMAGE_DOS_HEADER pImageDosHeader = NULL;
PIMAGE_NT_HEADERS pImageNtHeader = NULL;
PIMAGE_FILE_HEADER pImageFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
DWORD NewBufferSize = 0;
int i;
int j;
// DOS头
pImageDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//pImageNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew);
// 标准PE
pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
// 可选PE
pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//节表组
pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
//获取NewBufferSize的内存大小
NewBufferSize = pImageOptionalHeader->SizeOfHeaders;
//再循环加上节数据的大小
for(j=0;j<pImageFileHeader->NumberOfSections;j++){
NewBufferSize += pImageSectionHeaderGroup[j].SizeOfRawData;
}
//为NewBufferSize分配内存空间
*pNewBuffer = (PVOID)malloc(NewBufferSize);
if (*pNewBuffer == NULL)
{
printf("malloc failed");
return -1;
}
//清零
memset(*pNewBuffer, 0, NewBufferSize);
// 拷贝头+节表
memcpy(*pNewBuffer, pImageBuffer, pImageOptionalHeader->SizeOfHeaders);
//循环拷贝节表
for(i=0;i<pImageFileHeader->NumberOfSections;i++){
memcpy(
(PVOID)((DWORD)*pNewBuffer + pImageSectionHeaderGroup[j].PointerToRawData),
(PVOID)((DWORD)pImageBuffer + pImageSectionHeaderGroup[j].VirtualAddress),
pImageSectionHeaderGroup[j].SizeOfRawData
);
}
return NewBufferSize;
}
//功能:FOA 转换 RVA
DWORD FOA_TO_RVA(PVOID FileAddress, DWORD FOA,PDWORD pRVA)
{
int ret = 0;
int i;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
//RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA
if (FOA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment)
{
*pRVA = FOA;
return ret;
}
//循环判断FOA在节区中
for (i=0;i < pFileHeader->NumberOfSections; i++)
{
if (FOA >= pSectionGroup[i].PointerToRawData && FOA < pSectionGroup[i].PointerToRawData + pSectionGroup[i].SizeOfRawData)
{
*pRVA = FOA - pSectionGroup[i].PointerToRawData + pSectionGroup[i].VirtualAddress;
return *pRVA;
}
}
//没有找到地址
ret = -4;
printf("func FOA_TO_RVA() Error: %d 地址转换失败!\n", ret);
return ret;
}
//功能:RVA 转换 FOA
DWORD RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA)
{
int ret = 0;
int i=0;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
//RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA
if (RVA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment)
{
*pFOA = RVA;
return ret;
}
//循环判断RVA在节区中
for (i = 0; i < pFileHeader->NumberOfSections; i++)
{
if (RVA >= pSectionGroup[i].VirtualAddress && RVA < pSectionGroup[i].VirtualAddress + pSectionGroup[i].Misc.VirtualSize)
{
*pFOA = pSectionGroup[i].PointerToRawData + RVA - pSectionGroup[i].VirtualAddress;
return ret;
}
}
//没有找到地址
ret = -4;
printf("func RAV_TO_FOA() Error: %d 地址转换失败!\n", ret);
return ret;
}
//功能:保存文件
void MyWriteFile(PVOID pMemBuffer,size_t size){
FILE* File;
File = fopen(NEWFILENAME,"wb");
if(File == NULL){
printf("文件句柄打开失败");
return;
}
fwrite(pMemBuffer,size,1,File);
printf("文件保存成功!");
fclose(File);
}
//功能:添加shellcode
void FileBufferToAddShellcode(PVOID pFileBuffer){
PIMAGE_DOS_HEADER pImageDosHeader = NULL;
PIMAGE_FILE_HEADER pImageFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
DWORD CodeAddress = 0; //要添加shellcode的地址
DWORD FuncAddress; //MESSAGEBOX地址
HMODULE hModule; //加载User32
DWORD FOA = 0;
DWORD RVA = 0;
BYTE SHELLCODE[] = {
0X6A,0X00,0X6A,0X00,0X6A,0X00,0X6A,0X00,
0XE8,0X00,0X00,0X00,0X00,
0XE9,0X00,0X00,0X00,0X00
};
DWORD E8_Next_Address;
DWORD E9_Next_Address;
DWORD EntryOfAddress;
DWORD CodeAddress_bak;
// DOS头
pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
// 标准PE
pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
// 可选PE
pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//节表组
pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
//获取Messagebox的地址
hModule = LoadLibrary("User32.dll");
FuncAddress = (DWORD)GetProcAddress(hModule, "MessageBoxA");
// CodeAddress为SHELLCODE在文件中的起始地址
CodeAddress = (DWORD)pImageSectionHeaderGroup[0].PointerToRawData + (DWORD)pImageSectionHeaderGroup[0].Misc.VirtualSize;
CodeAddress_bak = CodeAddress;
// 计算E8这条指令的下一行地址的RVA
E8_Next_Address = CodeAddress + 13;
FOA_TO_RVA(pFileBuffer,E8_Next_Address,&RVA);
// X = 真正要跳转的地址 - E8这条指令的下一行地址
E8_Next_Address = FuncAddress - (RVA + pImageOptionalHeader->ImageBase);
//填充E8空白的空白部分
memcpy(&SHELLCODE[9], &E8_Next_Address, 4);
// 计算E9这条指令的下一行地址的RVA
E9_Next_Address = CodeAddress + 18;
FOA_TO_RVA(pFileBuffer,E9_Next_Address,&RVA);
//再获取原来入口地址的VA
EntryOfAddress = pImageOptionalHeader->AddressOfEntryPoint;
// X = 真正要跳转的地址 - E9这条指令的下一行地址
E9_Next_Address = EntryOfAddress - RVA;
memcpy(&SHELLCODE[14],&E9_Next_Address,4);
//填充完了shellcode,最后再把shellcode放进去
memcpy((PVOID)((DWORD)pFileBuffer+CodeAddress_bak),SHELLCODE,0x20);
//最后替换OEP的位置,替换为shellcode的地址
FOA_TO_RVA(pFileBuffer,CodeAddress_bak,&RVA);
pImageOptionalHeader->AddressOfEntryPoint = RVA;
}