为什么要移动表

程序启动时,系统会根据表做初始化工作。比如将DLL中的函数地址储存到IAT表中。为了保护程序,可以对二进制代码进行加密操作,但各种表的信息与客户字节的代码和数据都混在一起了,如果进行加密,那么系统无法完成初始化工作,程序也无法运行。

移动导出表步骤

  1. 计算导出表的大小(包含导出表、三个子表和AddressOfNames指向的所有字符串的大小)
    注意:AddressOfNames储存的是名称表地址的RVA,首先需要转换FOA后加DOS头得到名称表文件地址,而名称表里储存的依然是RVA,需要继续转换为FOA后加上DOS头才能得到函数字符串的文件地址
  2. 在DLL中新增一个节
  3. 移动AddressOfFunctions
    长度:AddressOfFunctions * 4
  4. 移动AddressOfNameOrdinals
    长度:AddressOfNameOrdinals * 2
  5. 移动AddressOfNames
    长度:AddressOfNames * 4,可以先直接复制,在移动函数名后将新的地址写入到AddressOfNames修复
  6. 移动函数名,完成后修复AddressOfNames
  7. 移动IMAGE_EXPORT_DIRECTORY
  8. 修复导出表中的三个子表
  9. 修复目录项,指向新的IMAGE_EXPORT_DIRECROTY

移动重定位表步骤

重定位表直接复制,遍历每个块写入到新的节区,然后修复目录项第六个成员

实现移动表

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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#define OUTFILEPATH TEXT("D:\\absolution\\temporary\\output.dll")
#define DLLPATH TEXT("D:\\absolution\\temporary\\myDll.dll")
#include<PETool.h>
#pragma comment(lib,"PETool.lib")


/*计算导出表大小(包含三个子表和函数名称),以文件大小对齐*/
DWORD computeExportSize(LPVOID lpFileBuffer) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);

/*导出表IDD结构*/
IMAGE_DATA_DIRECTORY exportIDD = pOption->DataDirectory[0];
/*导出表地址是RVA,需要转换成FOA*/
PIMAGE_EXPORT_DIRECTORY pExportDir =
(PIMAGE_EXPORT_DIRECTORY)(RVA2FOA(lpFileBuffer, exportIDD.VirtualAddress) + (DWORD)lpFileBuffer);

/*获取函数地址表、函数名称表、函数序号表的地址*/
PDWORD lpAddOfFns = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfFunctions) + (DWORD)lpFileBuffer);
PDWORD lpAddOfNames = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNames) + (DWORD)lpFileBuffer);
/*序号表的数据为2个字节*/
PWORD lpAddOfOrdinals = (PWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNameOrdinals) + (DWORD)lpFileBuffer);


/*导出表和三个子表的大小*/
DWORD computeResult = sizeof(IMAGE_EXPORT_DIRECTORY) +
pExportDir->NumberOfFunctions * 4 +
pExportDir->NumberOfNames * 2 +
pExportDir->NumberOfNames * 4;

/*计算所有函数名的大小*/
for (int i = 0; i < pExportDir->NumberOfNames; i++) {
LPCTSTR fnName = (LPCTSTR)(RVA2FOA(lpFileBuffer, lpAddOfNames[i]) + (DWORD)lpFileBuffer);
/*大小+1储存\0结尾*/
DWORD strLen = strlen(fnName) + 1;
computeResult += strLen;
}


/*返回对齐后的大小*/
return PEAlign(computeResult, pOption->FileAlignment);
}


DWORD moveExportDir(LPVOID lpFileBuffer, DWORD sectionIndex, DWORD writeOffSet) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
PIMAGE_SECTION_HEADER((DWORD)pOption + pFile->SizeOfOptionalHeader);
/*获取最后一个节表的地址*/
PIMAGE_SECTION_HEADER pWriteSection =
PIMAGE_SECTION_HEADER((DWORD)pFirstSection + (sectionIndex * IMAGE_SIZEOF_SECTION_HEADER));
/*导出表IDD结构*/
IMAGE_DATA_DIRECTORY exportIDD = pOption->DataDirectory[0];
/*导出表地址是RVA,需要转换成FOA*/
PIMAGE_EXPORT_DIRECTORY pExportDir =
(PIMAGE_EXPORT_DIRECTORY)(RVA2FOA(lpFileBuffer, exportIDD.VirtualAddress) + (DWORD)lpFileBuffer);

/*获取函数地址表、函数名称表、函数序号表的地址*/
PDWORD lpAddOfFns = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfFunctions) + (DWORD)lpFileBuffer);
PDWORD lpAddOfNames = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNames) + (DWORD)lpFileBuffer);
/*序号表的数据为2个字节*/
PWORD lpAddOfOrdinals = (PWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNameOrdinals) + (DWORD)lpFileBuffer);

/*复制函数地址表*/
PDWORD lpNewAddOfFns = (PDWORD)(pWriteSection->PointerToRawData + (DWORD)lpFileBuffer + writeOffSet);
memcpy(lpNewAddOfFns, lpAddOfFns, pExportDir->NumberOfFunctions * 4);

/*复制序号表*/
PDWORD lpNewAddOfOrdinals = (PDWORD)((DWORD)lpNewAddOfFns + pExportDir->NumberOfFunctions * 4);
memcpy(lpNewAddOfOrdinals, lpAddOfOrdinals, pExportDir->NumberOfNames * 2);

/*复制名称表,需要修复*/
PDWORD lpNewAddOfNames = (PDWORD)((DWORD)lpNewAddOfOrdinals + pExportDir->NumberOfNames * 2);
/*数据块,储存函数名字符串*/
PBYTE lpNameStrBlock = (PBYTE)((DWORD)lpNewAddOfNames + pExportDir->NumberOfNames * 4);

/*复制函数名字符串,并修复名称表*/
for (int i = 0; i < pExportDir->NumberOfNames; i++) {
LPCTSTR tempStr = (LPCTSTR)(RVA2FOA(lpFileBuffer, lpAddOfNames[i]) + (DWORD)lpFileBuffer);
memcpy(lpNameStrBlock, tempStr, strlen(tempStr) + 1);
/*修复名称表,储存字符串地址的RVA*/
lpNewAddOfNames[i] = FOA2RVA(lpFileBuffer, (DWORD)lpNameStrBlock - (DWORD)lpFileBuffer);
lpNameStrBlock += strlen(tempStr) + 1;
}

/*复制导出表*/
PIMAGE_EXPORT_DIRECTORY pNewExportDir = (PIMAGE_EXPORT_DIRECTORY)lpNameStrBlock;
memcpy(pNewExportDir, pExportDir, sizeof(PIMAGE_EXPORT_DIRECTORY));

/*修复导出表的三个子表*/
pNewExportDir->AddressOfFunctions = FOA2RVA(lpFileBuffer, (DWORD)lpNewAddOfFns - (DWORD)lpFileBuffer);
pNewExportDir->AddressOfNameOrdinals = FOA2RVA(lpFileBuffer, (DWORD)lpNewAddOfOrdinals - (DWORD)lpFileBuffer);
pNewExportDir->AddressOfNames = FOA2RVA(lpFileBuffer, (DWORD)lpNewAddOfNames - (DWORD)lpFileBuffer);

/*修复目录项指向导出表的地址*/
exportIDD.VirtualAddress = FOA2RVA(lpFileBuffer, (DWORD)pNewExportDir - (DWORD)lpFileBuffer);
return 0;
}


DWORD computeRelocSize(LPVOID lpFileBuffer) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
(PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader);

/*重定位表的信息结构*/
IMAGE_DATA_DIRECTORY pReloc = (IMAGE_DATA_DIRECTORY)pOption->DataDirectory[5];

/*获取重定位表的文件偏移*/
PIMAGE_BASE_RELOCATION pRelocFileOffset =
(PIMAGE_BASE_RELOCATION)(RVA2FOA(lpFileBuffer, (DWORD)pReloc.VirtualAddress) + (DWORD)lpFileBuffer);

DWORD dwComputeResult = 0;
for (int i = 0; pRelocFileOffset->VirtualAddress; i++) {
dwComputeResult += pRelocFileOffset->SizeOfBlock;
pRelocFileOffset = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocFileOffset + pRelocFileOffset->SizeOfBlock);
}

return dwComputeResult;

}

DWORD moveRelocDir(LPVOID lpFileBuffer, DWORD sectionIndex, DWORD writeOffSet) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
(PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader);

PIMAGE_SECTION_HEADER pWriteSection =
PIMAGE_SECTION_HEADER((DWORD)pFirstSection + (sectionIndex * IMAGE_SIZEOF_SECTION_HEADER));

/*重定位表的信息结构*/
IMAGE_DATA_DIRECTORY pReloc = (IMAGE_DATA_DIRECTORY)pOption->DataDirectory[5];

/*获取重定位表的文件偏移*/
PIMAGE_BASE_RELOCATION pRelocFileOffset =
(PIMAGE_BASE_RELOCATION)(RVA2FOA(lpFileBuffer, (DWORD)pReloc.VirtualAddress) + (DWORD)lpFileBuffer);

/*新的重定位表地址*/
DWORD pNewRelocFileOffset = pWriteSection->PointerToRawData + (DWORD)lpFileBuffer + writeOffSet;


for (int i = 0; pRelocFileOffset->VirtualAddress; i++) {
DWORD blockSize = pRelocFileOffset->SizeOfBlock;
memcpy((LPVOID)pNewRelocFileOffset, pRelocFileOffset, blockSize);
pRelocFileOffset = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocFileOffset + blockSize);
pNewRelocFileOffset += blockSize;
}

/*将新的地址转为RVA用以修复目录项*/
pOption->DataDirectory[5].VirtualAddress =
FOA2RVA(lpFileBuffer, pWriteSection->PointerToRawData + writeOffSet);

return 0;
}

int main() {


LPVOID lpFileBuffer = NULL;
lpFileBuffer = createFileBuffer(DLLPATH);
DWORD exportSize = computeExportSize(lpFileBuffer);
DWORD relocSize = computeRelocSize(lpFileBuffer);

LPVOID lpModifyBuffer = (PIMAGE_SECTION_HEADER)addNewSection(lpFileBuffer, exportSize+relocSize);


PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)lpModifyBuffer;
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)pDOS + pDOS->e_lfanew);
PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOptional = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSections = (PIMAGE_SECTION_HEADER)((DWORD)pOptional + pFile->SizeOfOptionalHeader);
//定位到新增节
PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER)((DWORD)pSections + (pFile->NumberOfSections - 1) * 40);

moveExportDir(lpModifyBuffer, pFile->NumberOfSections - 1, 0);
moveRelocDir(lpModifyBuffer, pFile->NumberOfSections - 1, exportSize);

FILE *fp;
fopen_s(&fp, OUTFILEPATH, "wb+");
if (!fp) {
printf("fopen error\n");
getchar();
return 0;
}
fseek(fp, 0, SEEK_SET);

DWORD count = fwrite(lpModifyBuffer, pNewSection->PointerToRawData + pNewSection->SizeOfRawData, 1, fp);

if (count) {
printf("写入成功!!\n");
getchar();
}



fclose(fp);

free(lpFileBuffer);
free(lpModifyBuffer);

}