#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
char* ptr = NULL;
*ptr = 'A';
return 0;
}
这段程序bug很显见,要定位bug也不难。但是,如果把这代码发布出去,直接运行会有这样的错误,最多看到汇编指令:
这时怎么定位程序?
首先0x00CA1378,这地址看着有点高,一般程序加载在0x00400000,看着就是随机地址加载。怎么验证是随机地址加载?程序的bug只有一处,每次运行都应该在同一处发生bug。但是再次运行程序,bug的地址发生了变化:
这就可以确定是随机地址加载。因此首先在属性-链接器-高级选项中关闭随机基址和打开固定基址。
这之后,每次发生bug的地址都是固定的,到此可以确定bug地址。
但是,源码不在发布现场,现场也没调试器。定位代码还是不方便。有什么办法可以大概的定位到哪条语句?这时就如文章标题所说用到Map文件和List文件:
产生List文件:属性-c/c++-输出文件-汇编程序输出:程序集、机器码和源代码 (/FAcs)
产生Map文件:属性-连接器-调试-生成映射文件:是 (/MAP)
这里解释一下各个文件的意义:
List文件,包含源文件编译后(未连接)的行号和重定位汇编代码,如下:
; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01
TITLE C:\studio\mapAsm\mapAsm\mapAsm\mapAsm.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES
PUBLIC _wmain
EXTRN __RTC_Shutdown:PROC
EXTRN __RTC_InitBase:PROC
; COMDAT rtc$TMZ
; File c:\studio\mapasm\mapasm\mapasm\mapasm.cpp
rtc$TMZ SEGMENT
__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown
rtc$TMZ ENDS
; COMDAT rtc$IMZ
rtc$IMZ SEGMENT
__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase
; Function compile flags: /Odtp /RTCsu /ZI
rtc$IMZ ENDS
; COMDAT _wmain
_TEXT SEGMENT
_ptr$ = -8 ; size = 4
_argc$ = 8 ; size = 4
_argv$ = 12 ; size = 4
_wmain PROC ; COMDAT
; 8 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 81 ec cc 00 00
00 sub esp, 204 ; 000000ccH
00009 53 push ebx
0000a 56 push esi
0000b 57 push edi
0000c 8d bd 34 ff ff
ff lea edi, DWORD PTR [ebp-204]
00012 b9 33 00 00 00 mov ecx, 51 ; 00000033H
00017 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
0001c f3 ab rep stosd
; 9 : char* ptr = NULL;
0001e c7 45 f8 00 00
00 00 mov DWORD PTR _ptr$[ebp], 0
; 10 : *ptr = 'A';
00025 8b 45 f8 mov eax, DWORD PTR _ptr$[ebp]
00028 c6 00 41 mov BYTE PTR [eax], 65 ; 00000041H
; 11 : return 0;
0002b 33 c0 xor eax, eax
; 12 : }
0002d 5f pop edi
0002e 5e pop esi
0002f 5b pop ebx
00030 8b e5 mov esp, ebp
00032 5d pop ebp
00033 c3 ret 0
_wmain ENDP
_TEXT ENDS
END
Map文件:连接后各个符号的链接地址,因为编译器假设连接地址就是加载地址,所以在固定基址的情况下程序会按链接地址加载到内存0x40000;但是启用了随机基址,那就连接地址跟加载地址不同,真心不好定位了。如图,显示了各个文件的链接地址:
Address Publics by Value Rva+Base Lib:Object
0000:00000000 ___safe_se_handler_table 00000000 <absolute>
0000:00000000 ___safe_se_handler_count 00000000 <absolute>
0000:00000000 ___ImageBase 00400000 <linker-defined>
0001:00000000 __enc$textbss$begin 00401000 <linker-defined>
0001:00010000 __enc$textbss$end 00411000 <linker-defined>
0002:00000350 _wmain 00411350 f mapAsm.obj
0002:000003a0 __RTC_InitBase 004113a0 f MSVCRTD:_init_.obj
0002:000003e0 __RTC_Shutdown 004113e0 f MSVCRTD:_init_.obj
0002:00000520 _wmainCRTStartup 00411520 f MSVCRTD:wcrtexe.obj
0002:00000840 _NtCurrentTeb 00411840 f i MSVCRTD:wcrtexe.obj
0002:000008e0 __RTC_NumErrors 004118e0 f MSVCRTD:_userapi_.obj
0002:000008f0 __RTC_GetErrDesc 004118f0 f MSVCRTD:_userapi_.obj
0002:00000910 __RTC_SetErrorType 00411910 f MSVCRTD:_userapi_.obj
0002:00000940 __RTC_SetErrorFunc 00411940 f MSVCRTD:_userapi_.obj
0002:00000970 __RTC_SetErrorFuncW 00411970 f MSVCRTD:_userapi_.obj
程序异常发生在0x411378,正好在mapAsm.obj文件范围内:
异常指令的地址是0x411378,而mapAsm的地址是0x411350,两者相差0x28B。来看下这个偏移上的源文件和汇编代码:
; 10 : *ptr = 'A';
00025 8b 45 f8 mov eax, DWORD PTR _ptr$[ebp]
00028 c6 00 41 mov BYTE PTR [eax], 65 ; 00000041H
果然是给空指针赋值引起~。知道Map文件和List文件的作用了吧。
附注,gcc也有类似功能,可参考如下连接:
http://blog.csdn.net/lanxinju/article/details/5900986
http://blog.chinaunix.net/uid-24567872-id-209622.html