快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Xp sp3 / Vs2008
欢迎转载,但请保留作者信息
这个节看起来很容易理解,不就是存放代码的嘛,看看头信息:
SECTION HEADER #2
.text name
3482 virtual size
11000 virtual address (00411000 to 00414481)
3600 size of raw data
400 file pointer to raw data (00000400 to 000039FF)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
60000020 flags
Code
Execute Read
比较.textbss可以看到这一节是只读的,但.textbss则是可以读写的。
下面讨论一下几个比较有意思的问题。
查看符号表,可以发现从.text节的开始到第一个函数代码中间有一段空间:
0002:000003a0 ?add@@YAHHH@Z 004113a0 f demo.obj
根据.textbss节中获取的信息,我们可以知道当启用增量链接时,这一段空间用于存放ILT表。
在符号表中还有这样一些有趣的符号:
0002:000024e2 _GetCurrentProcessId@0 004134e2 f kernel32:KERNEL32.dll
0002:000024e8 _GetSystemTimeAsFileTime@4 004134e8 f kernel32:KERNEL32.dll
这些函数应该是在DLL中实现的,为何会出现符号表中呢?而且每个函数都只有6个字节,在反汇编中看看到底是什么:
GetCurrentProcessId:
004134E2 jmp dword ptr [__imp__GetCurrentProcessId@0 (4181BCh)]
GetSystemTimeAsFileTime:
004134E8 jmp dword ptr [__imp__GetSystemTimeAsFileTime@4 (4181B8h)]
又是jmp,又是ILT?关闭增量链接,再看这段地址,依然存放,很不幸地猜想失败。
在函数中加上对此函数的调用,在符号表中可以看到GetCurrentProcessId的地址发生了变化:
0002:00000446 _GetCurrentProcessId@0 00411446 f kernel32:KERNEL32.dll
但反汇编的结果显示它的内容并没有变化:
GetCurrentProcessId:
00411446 jmp dword ptr [__imp__GetCurrentProcessId@0 (418198h)]
再看调用者的反汇编代码:
int n = GetCurrentProcessId();
004113FE mov esi,esp
00411400 call dword ptr [__imp__GetCurrentProcessId@0 (418198h)]
00411406 cmp esi,esp
00411408 call @ILT+315(__RTC_CheckEsp) (411140h)
0041140D mov dword ptr [n],eax
这里直接调用的是__imp__GetCurrentProcessId@0函数,那么为什么还要生成前面那个jmp代码呢?据说这个问题深究起来比较复杂,先放过它,到DLL中学习它。
使用
#pragma code_seg("code1")
可以自定义一个code section,节的名字就是code1,需要注意的是这个语句的作用范围为当前文件。
试试看:
#pragma code_seg("code1")
int add(int a, int b)
{
return a + b + 10;
}
int _tmain(int argc, _TCHAR* argv[])
{
add(3, 4);
return 0;
}
用dumpbin查看生成的头信息,发现多了一个节:
SECTION HEADER #1
code1 name
77 virtual size
1000 virtual address (00401000 to 00401076)
200 size of raw data
400 file pointer to raw data (00000400 to 000005FF)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
60000020 flags
Code
Execute Read
对照符号表中这一节的符号:
0001:00000000 ?add@@YAHHH@Z 00401000 f demo.obj
0001:00000030 _main 00401030 f demo.obj
非常符合我们的期望。
下面尝试改变code1节中main函数与add函数的顺序。
在code1这个节名称的后面加上尾缀:
#pragma code_seg("code1$b") int add(int a, int b) { return a + b + 10; } #pragma code_seg("code1$a") int _tmain(int argc, _TCHAR* argv[]) { add(3, 4); return 0; }
VS将$之前的部分视为节的名称,再根据$后的字母进行排序。
再看生成的符号表:
0001:00000000 _main 00401000 f demo.obj
0001:00000040 ?add@@YAHHH@Z 00401040 f demo.obj
符合我们的期望。
当不加$及之后的尾缀时,默认放在最前。
利用链接器的这种特性,我们很容易就可以获取一个节的起始位置和结束位置。
.text用于默认存放代码,VS并没有象GNU TOOLCHAIN那样提供链接器的符号,因而只有自己动态查询,就像这样的:
PIMAGE_FILE_HEADER pfh;
PIMAGE_OPTIONAL_HEADER poh;
PIMAGE_SECTION_HEADER psh;
HMODULE hModule;
MODULEINFO modinfo;
DWORD dwBase, cbNeeded;
BYTE* lpbuf;
HANDLE hProcess = ::GetCurrentProcess();
EnumProcessModules(hProcess, &hModule, sizeof(HMODULE), &cbNeeded);
GetModuleInformation( hProcess, hModule, &modinfo, sizeof(MODULEINFO) );
// 根据lpBaseOfDll得到其它的数据
lpbuf = (BYTE*)modinfo.lpBaseOfDll;
//取得节数目
pfh = (PIMAGE_FILE_HEADER) ((LPVOID)((BYTE *)(lpbuf)+((PIMAGE_DOS_HEADER)(lpbuf))-> e_lfanew+sizeof(DWORD)));
int nSectionCount = pfh-> NumberOfSections;
poh = (PIMAGE_OPTIONAL_HEADER)(pfh + 1);
psh = (PIMAGE_SECTION_HEADER) ((LPVOID)((BYTE *)pfh + sizeof(IMAGE_FILE_HEADER) + pfh->SizeOfOptionalHeader));
dwBase = 0;
for(int i = 0; i < nSectionCount; i++)
{
if(strcmp( ".text", (char *)psh->Name) == 0)
{
dwBase = (DWORD)modinfo.lpBaseOfDll + psh->VirtualAddress;
}
psh++;
}