快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Xp sp3 / Vs2008
欢迎转载,但请保留作者信息
使用dumpbin可以看到这一节的基本信息:
SECTION HEADER #1
.textbss name
10000 virtual size
1000 virtual address (00401000 to 00410FFF)
0 size of raw data
0 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
E00000A0 flags
Code
Uninitialized Data
Execute Read Write
很明显,这个节里存放的是代码,而且是未初始化的代码,那么这个节的代码应该是动态写入的,再看一下符号表中的相关定义:
Address Publics by Value Rva+Base Lib:Object
0001:00000000 __enc$textbss$begin 00401000 <linker-defined>
0001:00010000 __enc$textbss$end 00411000 <linker-defined>
这到底是什么东西?
在vs中启动调试器,在内存窗口中查看这一段空间的代码:
0x00401000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00401010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00401020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
全部为0。
根据网上资料,这个节是与增量链接及动态编译相关的,检查链接器参数,确保打开了增量链接,进行验证工作。
在main函数之前插入一个测试函数并在main中调用它:
int add(int a, int b)
{
return a + b;
}
int _tmain(int argc, _TCHAR* argv[])
{
add(3, 4);
return 0;
}
查看符号表文件,可以看到add函数的地址:
Address Publics by Value Rva+Base Lib:Object
0002:00000370 ?add@@YAHHH@Z 00411370 f demo.obj
启动调试器,在反汇编窗口中查看main函数:
int _tmain(int argc, _TCHAR* argv[])
{
004117B0 push ebp
004117B1 mov ebp,esp
004117B3 sub esp,0C0h
004117B9 push ebx
004117BA push esi
004117BB push edi
004117BC lea edi,[ebp-0C0h]
004117C2 mov ecx,30h
004117C7 mov eax,0CCCCCCCCh
004117CC rep stos dword ptr es:[edi]
add(3, 4);
004117CE push 4
004117D0 push 3
004117D2 call add (4111C2h)
004117D7 add esp,8
return 0;
004117DA xor eax,eax
}
注意这里的地址是0x004111c2,而不是add这个函数的地址0x00411370。
在反汇编窗口中查看0x004111c2这个地址:
00411001 int 3
00411002 int 3
00411003 int 3
00411004 int 3
00411005 jmp _setdefaultprecision (411AB0h)
00411014 jmp _RTC_GetErrDesc (411940h)
………………
004111BD jmp _RTC_CheckStackVars (4124C0h)
004111C2 jmp add (411370h)
004111C7 jmp _RTC_CheckStackVars2 (4132A0h)
004111CC jmp _RTC_CheckEsp (4114A0h)
004111D1 int 3
这一段想来就是传说中的ILT表了,可以看到在这里跳转到了add函数所在的位置。
下面试试动态编译:
不退出调试器,修改add函数,将之改为:
int add(int a, int b)
{
return a + b + 10;
}
保存后再单步跟踪,此时VC将动态编译这个文件,但不会退出调试状态,我们再看ILT表的变化:
004111BD jmp _RTC_CheckStackVars (4124C0h)
004111C2 jmp add (401000h)
004111C7 jmp _RTC_CheckStackVars2 (4132A0h)
注意这里的地址已经进行了修改,且指向.textbss的首地址,在动态编译之前,.textbss的内容全部为0,但此时再看:
--- e:\projects\src\cygwin\demo\demo.cpp ---------------------------------------
// demo.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
int add(int a, int b)
{
00401000 push ebp
00401001 mov ebp,esp
00401003 sub esp,0C0h
00401009 push ebx
0040100A push esi
0040100B push edi
0040100C lea edi,[ebp-0C0h]
00401012 mov ecx,30h
00401017 mov eax,0CCCCCCCCh
0040101C rep stos dword ptr es:[edi]
return a + b + 10;
0040101E mov eax,dword ptr [b]
00401021 mov ecx,dword ptr [a]
00401024 lea eax,[ecx+eax+0Ah]
}
00401028 pop edi
00401029 pop esi
0040102A pop ebx
0040102B mov esp,ebp
0040102D pop ebp
0040102E ret
--- 无源文件-----------------------------------------------------------------------
0040102F stos byte ptr es:[edi]
00401030 adc byte ptr [ecx],al
00401033 mov ecx,4110h
00401038 add byte ptr [eax],al
这就是重新编译后生成的代码!
现在我们终于明白了.textbss就是用来存放动态编译生成的代码的。
既然.textbss是为增量链接和动态编译服务的,那么如果关闭增量链接,又会如何呢?
试试关闭增量链接,可以看到在exe文件中已经不再生成.textbss这个节,也不再有ILT。
看看此时符号表中add函数的地址:
Address Publics by Value Rva+Base Lib:Object
0001:00000000 ?add@@YAHHH@Z 00401000 f demo.obj
直接放在了.text段的开头。
再看main函数的反汇编:
int _tmain(int argc, _TCHAR* argv[])
{
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,0C0h
00401039 push ebx
0040103A push esi
0040103B push edi
0040103C lea edi,[ebp-0C0h]
00401042 mov ecx,30h
00401047 mov eax,0CCCCCCCCh
0040104C rep stos dword ptr es:[edi]
add(3, 4);
0040104E push 4
00401050 push 3
00401052 call add (401000h)
00401057 add esp,8
return 0;
0040105A xor eax,eax
}
它直接跳转到了add函数的地址。
如果此时修改add函数的代码并单步跟踪,VC将毫不客气地弹出对话框:
也就是说动态编译的功能将不再可用。