本来应该用高级评论将重点高亮的,但不知道cppblog出了什么问题用不了。只能眼睛尖点了。。。
1. test files
module.c 导出1个变量与3个函数
__declspec(dllexport) int error_code;
__declspec(dllexport) int get(void) { return error_code; }
__declspec(dllexport) void set(int x) { error_code = x; }
int main(void) { return 0; }
main.c 输出变量地址、函数地址以及函数包含的指令
#include <stdio.h>
__declspec(dllimport) int error_code;
__declspec(dllimport) int get(void);
__declspec(dllimport) void set(int x);
int main(void)
{
int i;
unsigned char const* p;
printf("%p\n", (void*)&error_code);
p = (unsigned char*)get;
printf("%p:", p);
for (i=0; i<12; ++i) printf(" %02X", p[i]);
printf("\n");
p = (unsigned char*)set;
printf("%p:", p);
for (i=0; i<12; ++i) printf(" %02X", p[i]);
printf("\n");
return 0;
}
2. dll
编译
cl /LD /O1 module.c /link /noentry
查看首选基地址
dumpbin /all module.dll | find /i "image base"
10000000 image base (10000000 to 10004FFF)
查看反汇编与重定项
dumpbin /disasm /relocations module.dll
File Type: DLL
10001000: A1 00 30 00 10 mov eax,dword ptr ds:[10003000h]
10001005: C3 ret
10001006: 8B 44 24 04 mov eax,dword ptr [esp+4]
1000100A: A3 00 30 00 10 mov dword ptr ds:[10003000h],eax
1000100F: C3 ret
BASE RELOCATIONS #4
1000 RVA, C SizeOfBlock
1 HIGHLOW 10003000
B HIGHLOW 10003000
10001000 处(get的第1条)指令的操作数(地址在10001001)是 10003000
1000100A 处(set的第2条)指令的操作数(地址在1000100B)也是 10003000
注意"File Type: DLL",这是根据PE的域来的。
编译并运行得到的输出是
cl main.c module.lib && main.exe
10003000
10001000: A1 00 30 00 10 C3 8B 44 24 04 A3 00
10001006: 8B 44 24 04 A3 00 30 00 10 C3 00 00
error_code的地址和指令中使用的地址是相同的。
3. dll relocation
上面 module.dll 恰好加载在首选基地址,所以没有发生重定项。
要演示重定项发生的情况, 可以将 module.dll 复制一份, 然后用 LoadLibrary 加载。
或者直接首选基地址为一个会冲突的, exe的默认基地址0x400000。
cl /LD /O1 module.c /link /noentry /base:0x400000
dumpbin /all module.dll | find /i "image base"
400000 image base (00400000 to 00404FFF)
dumpbin /disasm /relocations module.dll
00401000: A1 00 30 40 00 mov eax,dword ptr ds:[00403000h]
00401005: C3 ret
00401006: 8B 44 24 04 mov eax,dword ptr [esp+4]
0040100A: A3 00 30 40 00 mov dword ptr ds:[00403000h],eax
0040100F: C3 ret
BASE RELOCATIONS #4
1000 RVA, C SizeOfBlock
1 HIGHLOW 00403000
B HIGHLOW 00403000
cl main.c module.lib && main.exe
00393000
00391000: A1 00 30 39 00 C3 8B 44 24 04 A3 00
00391006: 8B 44 24 04 A3 00 30 39 00 C3 00 00
对比 dumpbin 得到的反汇编与 main.exe 的输出,可以发现指令中的操作数有相应的修改,以正确的使用00393000上的变量error_code。
4. dll fixed
如果链接时选择基地址固定
cl /LD /O1 module.c /link /noentry /base:0x400000 /fixed
产生的dll里就没有重定项信息
dumpbin /relocations module.dll
并且选择的是一个肯定会冲突的基地址,所以加载main.exe就会失败。
main.exe
5. exe export
默认exe是不会包含重定项信息的
cl /O1 module.c && dumpbin /relocations module.exe
File Type: EXECUTABLE IMAGE
注意"File Type: EXECUTABLE IMAGE",这是根据PE的域来的。
并且首选基地址也是冲突的。
dumpbin /all module.exe | find /i "image base"
400000 image base (00400000 to 0040BFFF)
但是让 main.c 链接到 module.exe 可以运行成功(之前dll fixed的情况是加载 main.exe 失败)
cl main.c module.lib & main.exe
0039B700
00391000: A1 00 B7 40 00 C3 8B 44 24 04 A3 00
00391006: 8B 44 24 04 A3 00 B7 40 00 C3 33 C0
注意指令里的操作码,并没有修改为error_code的地址:0039B700。
如果真的调用了get和set,也只是读写了其他的地址,而不是error_code。
bug已经产生了。 没崩只是运气, 那个地址恰好有读写权限。
而且实验代码一般都比较短,跑完马上就退出了,这种意外的写入产生的影响也不一定能发现。
6. exe export with relocation information
可以用 /fixed:no 附带上重定项信息
cl /O1 module.c /link /fixed:no
dumpbin /relocations module.exe 会产生很多输出,因为它还引用了libc。
而让 main.c 链接到 module.exe 并运行的同样不会发生重定项
cl main.c module.lib & main.exe
0039B700
00391000: A1 00 B7 40 00 C3 8B 44 24 04 A3 00
00391006: 8B 44 24 04 A3 00 B7 40 00 C3 33 C0
回复 更多评论