title: viruslab5
date: 2016-01-13 15:47:40
categories: virus
tags: virus
PE文件注入
- part1
- 要求:附带程序将一个完整的PE文件(hellow.exe)读到内存中
并将其分解为PE头、节表、以及各个节。
试编写void OutputPEInMem()函数,用其打印下列信息:- PE头中的ImageBase字段、AddressOfEntryPoint字段、
NumberOfSections字段与SizeOfImage字段; - 节表(Section Table)中每一项的Name字段、Virtual Size字段、Virtual Address字段、
RawData Size字段、RawData Offset字段、Characteristics字段。
- PE头中的ImageBase字段、AddressOfEntryPoint字段、
- 要求:附带程序将一个完整的PE文件(hellow.exe)读到内存中
#include <windows.h>
#include <stdio.h>
#define MAX_SECTION_NUM 16
#define MAX_IMPDESC_NUM 64
HANDLE hHeap;
DWORD dwBaseAddress;
PIMAGE_DOS_HEADER pDosHeader;
PCHAR pDosStub;
DWORD dwDosStubSize;
DWORD dwDosStubOffset;
PIMAGE_NT_HEADERS pNtHeaders;
PIMAGE_FILE_HEADER pFileHeader;
PIMAGE_OPTIONAL_HEADER32 pOptHeader;
PIMAGE_SECTION_HEADER pSecHeaders;
PIMAGE_SECTION_HEADER pSecHeader[MAX_SECTION_NUM];
WORD wSecNum;
PBYTE pSecData[MAX_SECTION_NUM];
DWORD dwSecSize[MAX_SECTION_NUM];
DWORD dwFileSize;
static DWORD PEAlign(DWORD dwTarNum,DWORD dwAlignTo)
{
//PEAlign参数1:源节表 文件中的块大小
//PEAlign参数2:文件对齐值
//返回对齐后的文件大小
return (((dwTarNum+dwAlignTo - 1) / dwAlignTo) * dwAlignTo);
}
static DWORD RVA2Ptr(DWORD dwBaseAddress, DWORD dwRva)
{
if ((dwBaseAddress != 0) && dwRva)
return (dwBaseAddress + dwRva);
else
return dwRva;
}
//----------------------------------------------------------------
static PIMAGE_SECTION_HEADER RVA2Section(DWORD dwRVA)
{
int i;
for(i = 0; i < wSecNum; i++) {
if ( (dwRVA >= pSecHeader[i]->VirtualAddress)
&& (dwRVA <= (pSecHeader[i]->VirtualAddress
+ pSecHeader[i]->SizeOfRawData)) ) {
return ((PIMAGE_SECTION_HEADER)pSecHeader[i]);
}
}
return NULL;
}
//----------------------------------------------------------------
static PIMAGE_SECTION_HEADER Offset2Section(DWORD dwOffset)
{
int i;
for(i = 0; i < wSecNum; i++) {
if( (dwOffset>=pSecHeader[i]->PointerToRawData)
&& (dwOffset<(pSecHeader[i]->PointerToRawData +
pSecHeader[i]->SizeOfRawData)))
{
return ((PIMAGE_SECTION_HEADER)pSecHeader[i]);
}
}
return NULL;
}
//================================================================
static DWORD RVA2Offset(DWORD dwRVA)
{
PIMAGE_SECTION_HEADER pSec;
pSec = RVA2Section(dwRVA);//ImageRvaToSection(pimage_nt_headers,Base,dwRVA);
if(pSec == NULL) {
return 0;
}
return (dwRVA + (pSec->PointerToRawData) - (pSec->VirtualAddress));
}
//----------------------------------------------------------------
static DWORD Offset2RVA(DWORD dwOffset)
{
PIMAGE_SECTION_HEADER pSec;
pSec = Offset2Section(dwOffset);
if(pSec == NULL) {
return (0);
}
return(dwOffset + (pSec->VirtualAddress) - (pSec->PointerToRawData));
}
//将PE结构复制到内存的堆中
BOOL CopyPEFileToMem(LPCSTR lpszFilename)
{
HANDLE hFile;
PBYTE pMem;
DWORD dwBytesRead;
int i;
DWORD dwSecOff;
PIMAGE_NT_HEADERS pMemNtHeaders;
PIMAGE_SECTION_HEADER pMemSecHeaders;
hFile = CreateFile(//返回文件句柄
lpszFilename,//文件名 这里是hello.exe可执行文件
GENERIC_READ,//访问模式 读
FILE_SHARE_READ,//共享模式
NULL,//指向安全属性的指针
OPEN_EXISTING,//如何创建
FILE_ATTRIBUTE_NORMAL,//文件属性
0);//用于复制文件句柄
if (hFile == INVALID_HANDLE_VALUE) {//INVALID_HANDLE_VALUE 表示出错
printf("[E]: Open file (%s) failed.
", lpszFilename);
return FALSE;
}
dwFileSize = GetFileSize(hFile, 0);//判断文件长度 返回文件大小
printf("[I]: Open file (%s) ok,
with size of 0x%08x.
", lpszFilename, dwFileSize);
//pMem是hello.exe在堆中的首地址 也就是dos头地址
pMem = (PBYTE)HeapAlloc(//在堆上分配内存 返回指向所分配内存块的首地址的指针
hHeap,//main函数中有 是全局变量 要分配堆的句柄
HEAP_ZERO_MEMORY,//将分配的内存全部清零
dwFileSize);//要分配堆的字节数
if(pMem == NULL) {//在堆上分配内存失败
printf("[E]: HeapAlloc failed (with the size of 0x%08x).
", dwFileSize);
CloseHandle(hFile);
return FALSE;
}
ReadFile(//从文件指针指向的位置开始将数据读到一个文件中
hFile,//文件句柄
pMem,//用于保存读入数据的一个缓冲区 这里是堆中分配的内存首地址
dwFileSize,//要读入的字节数
&dwBytesRead,//指向实际读取字节数的指针
NULL);
CloseHandle(hFile);//关闭内核对象
//复制dos header
//堆中的dos头pDosHeader
//在堆上分配内存 返回指向所分配内存块的首地址的指针
pDosHeader = (PIMAGE_DOS_HEADER)HeapAlloc(
hHeap,//main函数中有 是全局变量 要分配堆的句柄
HEAP_ZERO_MEMORY,//将分配的内存全部清零
sizeof(IMAGE_DOS_HEADER));//要分配堆的字节数
if(pDosHeader == NULL) {//在堆上分配内存失败
printf("[E]: HeapAlloc failed for DOS_HEADER
");
CloseHandle(hFile);
return FALSE;
}
CopyMemory(//将内存中的数据从一个位置复制到另一个位置
pDosHeader,//要复制内存块的目的地址
pMem,//要复制内存块的源地址
sizeof(IMAGE_DOS_HEADER));//要复制内存块的大小
//复制DOS Stub
//先计算dos stub的大小 dos头大小
//然后分配dos stub大小的内存空间
dwDosStubSize = pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);
dwDosStubOffset = sizeof(IMAGE_DOS_HEADER);
pDosStub = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwDosStubSize);
if ((dwDosStubSize & 0x80000000) == 0x00000000)//dwDosStubSize 最高位是0
{
CopyMemory(pDosStub,
(const void *)(pMem + dwDosStubOffset ), dwDosStubSize);
}
//复制NT header
//先找到源堆中的NT头地址 pMemNtHeaders
//分配NT头内存空间 返回目的NT头地址 pNtHeaders
//复制
pMemNtHeaders = (PIMAGE_NT_HEADERS)(pMem + pDosHeader->e_lfanew);
//返回目的NT头地址
pNtHeaders = (PIMAGE_NT_HEADERS)
HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(IMAGE_NT_HEADERS));
if(pNtHeaders == NULL) {
printf("[E]: HeapAlloc failed for NT_HEADERS
");
CloseHandle(hFile);
return FALSE;
}
//pMemNtHeaders 源地址
CopyMemory(pNtHeaders, pMemNtHeaders, sizeof(IMAGE_NT_HEADERS));
//NT头里面有一个OPT头 目的 pOptHeader
pOptHeader = &(pNtHeaders->OptionalHeader);
//NT头里面有一个FILE头 目的 pFileHeader
pFileHeader = &(pNtHeaders->FileHeader);
//复制 节表
//寻找源节表地址 pMemSecHeaders源
//通过NT头找到FIFE头 FIFE头中有节的数目 wSecNum
//分配节表的内存空间 返回节表地址 pSecHeaders 目的
//复制
pMemSecHeaders = (PIMAGE_SECTION_HEADER) ((DWORD)
pMemNtHeaders + sizeof(IMAGE_NT_HEADERS));
wSecNum = pFileHeader->NumberOfSections;//文件节的数目
pSecHeaders = (PIMAGE_SECTION_HEADER)
HeapAlloc(hHeap,
HEAP_ZERO_MEMORY, wSecNum * sizeof(IMAGE_SECTION_HEADER));
if(pSecHeaders == NULL) {
printf("[E]: HeapAlloc failed for SEC_HEADERS
");
CloseHandle(hFile);
return FALSE;
}//pMemSecHeaders 源
CopyMemory(pSecHeaders,
pMemSecHeaders, wSecNum * sizeof(IMAGE_SECTION_HEADER));
for(i = 0; i < wSecNum; i++) {//pSecHeaders 目的
pSecHeader[i] = (PIMAGE_SECTION_HEADER) //pSecHeader[i] 各个节表 目的
((DWORD)pSecHeaders + i * sizeof(IMAGE_SECTION_HEADER));
}
//复制节
//(目的节表 文件中的块偏移 + dos头地址 ) 复制到 dwSecOff
//PEAlign()返回对齐后的文件大小 dwSecSize
//分配堆中内存 返回内存首地址 pSecData
//复制
for(i = 0; i < wSecNum; i++) {//PointerToRawData 源节表 文件中节偏移
dwSecOff = (DWORD)(pMem + pSecHeader[i]->PointerToRawData);
//PEAlign参数1:源节表 文件中的块大小
//PEAlign参数2:文件对齐值
dwSecSize[i] = PEAlign(pSecHeader[i]->SizeOfRawData,
pOptHeader->FileAlignment);
pSecData[i] = (PBYTE)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSecSize[i]);
if (pSecData[i] == NULL) {
printf("[E]: HeapAlloc failed for the section of %d
", i);
CloseHandle(hFile);
return FALSE;
}
CopyMemory(pSecData[i], (PVOID)dwSecOff, dwSecSize[i]);
}
HeapFree(//释放堆内存
hHeap,//堆句柄
0,
pMem);//被释放的内存块首地址 pMem 源
printf("[I]: Load PE from file (%s) ok
", lpszFilename);
return TRUE;
}
void OutputPEInMem()
{
int i;
printf("**********************
");
printf("Base Address: 0x%08x
", pDosHeader);
printf("e_lfanew: 0x%08x
", pDosHeader->e_lfanew);
printf("PE NT Headers Address: 0x%08x
", pNtHeaders);
printf("NumberOfSections: 0x%08x
",wSecNum);
//AddressOfEntryPoint 程序入口RVA地址
printf("AddressOfEntryPoint: 0x%08x
", pOptHeader->AddressOfEntryPoint);
//ImageBase 基址
printf("ImageBase: 0x%08x
", pOptHeader->ImageBase);
//SizeOfImage 映像大小
printf("SizeOfImage: 0x%08x
", pOptHeader->SizeOfImage);
printf("-------------------
");
for(i = 0;i < wSecNum;i++)
{//pSecHeader 目的节表
printf("pSecHeaders:%s
",pSecHeader[i]->Name);
printf("VirtualSize:0x%08x
",
pSecHeader[i]->Misc.VirtualSize);//内存中节大小
printf("VirtualAddress:0x%08x
",
pSecHeader[i]->VirtualAddress);//内存中节RVA值
printf("SizeOfRawData:0x%08x
",
pSecHeader[i]->SizeOfRawData);//文件中的节大小
printf("PointerToRawData:0x%08x
",
pSecHeader[i]->PointerToRawData);//文件中的节偏移
printf("Characteristics:0x%08x
",
pSecHeader[i]->Characteristics);//节属性
printf("-------------------
");
}
return;
}
int main()
{
LPCSTR lpszFileName = "hello.exe";
//L:long指针 P:指针 C:常量 STR:字符串
LPCSTR lpszInjFileName = "hello_inj0.exe";
hHeap = GetProcessHeap();
//GetProcessHeap返回调用进程的默认内存堆句柄
if (! CopyPEFileToMem(lpszFileName)) {//复制PE结构进入堆
return 1;
}
OutputPEInMem();//打印堆中的PE结构
return 0;
}
- part2
- 为”hello.exe”PE结构增加一个新的节,填入4个0xCC的值。具体步骤为:
- 为PE结构添加一个新的节。
请仔细阅读所附的AddNewSection()函数的代码,并理解如何为PE结构添加一个可执行的新节。
请填充函数中的所缺失的两处代码。请理解并使用PEAlign函数来完成对齐操作。 - 请编写函数SaveMemToPEFile()来将修改过的PE结构重新打包保存为可执行文件”hello_inj0.exe”。
该函数的原型声明如下:
BOOL SaveMemToPEFile(LPCSTR lpszFileName)
请确保新文件”hello_inj0.exe”的正确性,并可以正常运行。
然后请用Ollydbg来确认新节已正确插入到PE结构中。
- 为PE结构添加一个新的节。