Article 1Identify the source code error line through the crash address only
As a programmer, what do we worry most about? Is memory leakage? Is the interface hard to see ?...... Wrong! I believe that there will be no objection in my opinion-that is, the program has crashed! "This program has performed illegal operations and is about to be closed. Please contact your software supplier .", Well, I am afraid this M $ famous saying is something programmers are most worried about. Sometimes, your program runs well on your machine, but it crashes on another machine; sometimes you may encounter illegal operations while writing and testing, but you cannot determine which line of the source code is the cause ...... Is it painful? It doesn't matter. This article can help you out of this dilemma, and even you can proudly ask the user to tell you the crash address, then you can precisely locate the line with errors in the source code. (Amazing, right? Haha .) First of all, I must emphasize that this method can be used on any compiler on the market. However, I am only familiar with the VC and MASM of M $, so the subsequent sections only introduce how to implement them in these two compilers. please familiarize yourself with the methods used in other compilers. Well, let's get started! :) First, the map file of the program must be generated. What is a map file? In short, a map file is the unique text representation of the program's global symbols, source files, and code line numbers. It can be used anywhere and anytime, no additional programs are required. Moreover, this is the only savior that can find the place where the program crashes. Well, since the map file is so amazing, how should we generate it? In VC, we can press Alt + F7 to open the "Project Settings" option page, select the C/C ++ tab, and enter/ZD in the following project options, select the link tab and enter:/MapInfo: lines and/map: project_name.map in the following project options. Finally, press F7 to compile and generate the EXE executable file and map file. In MASM, we need to set compilation and connection parameters. I usually do this: RC % 1.rc ML/C/COFF/ZD % 1.asm Link/subsystem: Windows/MapInfo: exports/MapInfo: lines/map: % 1.map % 1.obj % 1.res Save it as makem. bat, and you can input makem filename in the command line to compile and generate the EXE executable file and map file. Here I will explain the meaning of the added parameters: /ZD indicates generating row information during compilation /Map [: Filename] indicates the path and file name of the generated map file /MapInfo: lines indicates that line information is added when a map file is generated. /MapInfo: exports indicates that exported functions is added when the map file is generated. (If the DLL file is generated, this option must be added) OK. We have obtained the map file through the above steps. How can we use it? Let's start with a simple instance. Open your VC and create a new file: 01 //************************************* *************************** 02 // program name: demonstrate how to identify the source code error line through the crash address 03 // Author: Luo Cong 04 // Date: 2003-2-7 05 // Source: http://www.luocong.com (laoluo's colorful world) 06 // This program will generate an "except 0 error", so that the "invalid operation" dialog box will pop up. 07 // "except 0 errors" will only be generated in the debug version. This program is simplified as much as possible for demonstration. 08 // Note: If you want to reprint it, please keep the program complete and note: 09 // reposted from "Old Luo's colorful world" (http://www.luocong.com) 10 //************************************* *************************** 11 12 Void crash (void) 13 { 14 int I = 1; 15 Int J = 0; 16 I/= J; 17} 18 19 void main (void) 20 { 21 crash (); 22} Obviously, this program has a "Division by 0 error". If it is compiled in debug mode, it will certainly produce "invalid operations" during runtime ". Okay. Let's run it. The "invalid operation" dialog box appears. Then, click "details, record the address that caused the crash -- on my machine it is 0x0040104a. Let's take a look at its map file: (because the file content is too long, I omitted useless parts in the middle) Crashdemo Timestamp is 3e0000a76 (Fri Feb 07 09:23:02 2003) Preferred load address is 00400000 Start length name class 0001:00000000 0000de04h. Text code 0001: include04 0001000ch. textbss code 000:00000000 00001346 H. RDATA 000:00001346 00000000 H. edata 0003:00000000 00000104 H. CRT $ xca data 0003:00000104 00000104 H. CRT $ xcz data 0003:00000208 00000104 H. CRT $ Xia data 0003: 0000030c 00000109 H. CRT $ xic data 0003:00000418 00000104 H. CRT $ xiz data 0003: 0000051c 00000104 H. CRT $ xpa data 0003: 00000620 00000104 H. CRT $ xpx data 0003: 00000724 00000104 H. CRT $ xpz data 0003: 00000828 00000104 H. CRT $ xta data 0003: 0000092c 00000104 H. CRT $ xtz data 0003: 00000a30 00000b93h. Data data 0003: 201715c4 00001974 H. BSS data 0004:00000000 00000014 H. idata $2 Data 0004:00000014 00000014 H. idata $3 data 0004:00000028 00000110 H. idata $4 Data 0004:00000138 00000110 H. idata $5 Data 0004:00000248 000004afh. idata $6 data Address publics by value RVA + base Lib: Object 0001:00000020? Crash @ yaxxz 00401020 F crashdemo. OBJ 0001: 00000070 _ main 00401070 F crashdemo. OBJ 0004:00000000 _ import_descriptor_kernel32 00424000 Kernel32: kernel32.dll 0004:00000014 _ null_import_descriptor 00424014 Kernel32: kernel32.dll 0004:00000138 _ imp _ getcommandlinea @ 0 00424138 Kernel32: kernel32.dll 0004: 0000013c _ imp _ getversion @ 0 0020.13c Kernel32: kernel32.dll 0004:00000140 _ imp _ exitprocess @ 4 00424140 Kernel32: kernel32.dll 0004:00000144 _ imp _ debugbreak @ 0 00424144 Kernel32: kernel32.dll 0004:00000148 _ imp _ getstdhandle @ 4 00424148 Kernel32: kernel32.dll 0004: 0000014c _ imp _ writefile @ 20 0020.14c Kernel32: kernel32.dll 0004:00000150 _ imp _ interlockeddecrement @ 4 00424150 Kernel32: kernel32.dll 0004:00000154 _ imp _ outputdebugstringa @ 4 00424154 Kernel32: kernel32.dll 0004:00000158 _ imp _ getprocaddress @ 8 00424158 Kernel32: kernel32.dll 0004: 0000015c _ imp _ loadlibrarya @ 4 0020.15c Kernel32: kernel32.dll 0004: 00000160 _ imp _ interlockedincrement @ 4 00424160 Kernel32: kernel32.dll 0004: 00000164 _ imp _ getmodulefilenamea @ 12 00424164 Kernel32: kernel32.dll 0004: 00000168 _ imp _ terminateprocess @ 8 00424168 Kernel32: kernel32.dll 0004: 0000016c _ imp _ getcurrentprocess @ 0 0020.16c Kernel32: kernel32.dll 0004: 00000170 _ imp _ unhandledexceptionfilter @ 4 00424170 Kernel32: kernel32.dll 0004: 00000174 _ imp _ freeenvironmentstringsa @ 4 00424174 Kernel32: kernel32.dll 0004: 00000178 _ imp _ freeenvironmentstringsw @ 4 00424178 Kernel32: kernel32.dll 0004: 0000017c _ imp _ widechartomultibyte @ 32 0020.17c Kernel32: kernel32.dll 0004: 00000180 _ imp _ getenvironmentstrings @ 0 00424180 Kernel32: kernel32.dll 0004: 00000184 _ imp _ getenvironmentstringsw @ 0 00424184 Kernel32: kernel32.dll 0004: 00000188 _ imp _ sethandlecount @ 4 00424188 Kernel32: kernel32.dll 0004: 0000018c _ imp _ getfiletype @ 4 0020.18c Kernel32: kernel32.dll 0004: 00000190 _ imp _ getstartupinfoa @ 4 00424190 Kernel32: kernel32.dll 0004: 00000194 _ imp _ heapdestroy @ 4 00424194 Kernel32: kernel32.dll 0004: 00000198 _ imp _ heapcreate @ 12 00424198 Kernel32: kernel32.dll 0004: 0000019c _ imp _ heapfree @ 12 0020.19c Kernel32: kernel32.dll 0004: 000001a0 _ imp _ virtualfree @ 12 001_1a0 Kernel32: kernel32.dll 0004: 000001a4 _ imp _ rtlunwind @ 16 0020.1a4 Kernel32: kernel32.dll 0004: 000001a8 _ imp _ getlasterror @ 0 001_1a8 Kernel32: kernel32.dll 0004: 000001ac _ imp _ setconsolectrlhandler @ 8 001_1ac Kernel32: kernel32.dll 0004: 000001b0 _ imp _ isbadwriteptr @ 8 001_1b0 Kernel32: kernel32.dll 0004: 000001b4 _ imp _ isbadreadptr @ 8 001_1b4 Kernel32: kernel32.dll 0004: 000001b8 _ imp _ heapvalidate @ 12 001_1b8 Kernel32: kernel32.dll 0004: 000001bc _ imp _ getcpinfo @ 8 0020.1bc Kernel32: kernel32.dll 0004: 000001c0 _ imp _ getacp @ 0 001_1c0 Kernel32: kernel32.dll 0004: 000001c4 _ imp _ getoemcp @ 0 0000001c4 Kernel32: kernel32.dll 0004: 000001c8 _ imp _ heapalloc @ 12 001_1c8 Kernel32: kernel32.dll 0004: 000001cc _ imp _ virtualalloc @ 16 0020.1cc Kernel32: kernel32.dll 0004: 000001d0 _ imp _ heaprealloc @ 16 001_1d0 Kernel32: kernel32.dll 0004: 000001d4 _ imp _ multibytetowidechar @ 24 0000001d4 Kernel32: kernel32.dll 0004: 000001d8 _ imp _ lcmapstringa @ 24 001_1d8 Kernel32: kernel32.dll 0004: 000001dc _ imp _ lcmapstringw @ 24 001_1dc Kernel32: kernel32.dll 0004: 000001e0 _ imp _ getstringtypea @ 20 001_1e0 Kernel32: kernel32.dll 0004: 000001e4 _ imp _ getstringtypew @ 16 001_1e4 Kernel32: kernel32.dll 0004: 000001e8 _ imp _ setfilepointer @ 16 001_1e8 Kernel32: kernel32.dll 0004: 000001ec _ imp _ setstdhandle @ 8 001_1ec Kernel32: kernel32.dll 0004: 000001f0 _ imp _ flushfilebuffers @ 4 001_1f0 Kernel32: kernel32.dll 0004: 000001f4 _ imp _ closehandle @ 4 0000001f4 Kernel32: kernel32.dll 0004: 000001f8/177kernel32_null_thunk_data 001_1f8 Kernel32: kernel32.dll Entry point at 0001: 000000f0 Line numbers for./debug/crashdemo. OBJ (D:/msdev/myprojects/crashdemo. cpp) segment. Text 13 0001:00000020 14 0001:00000038 15 0001: 0000003f 16 0001:00000046 17 0001:00000050 20 0001: 00000070 21 0001: 00000088 22 0001: 0000008d If you carefully read the RVA + base column, you will find that the first function address is 0x00401070 larger than the crashed address 0x0040104a, so the entry before the address 0x00401070 is the function that generates the crash, that is, this line: 0001:00000020? Crash @ yaxxz 00401020 F crashdemo. OBJ So what is the function that crashes? Crash @ yaxxz. All function names starting with question marks are c ++ modified names. In our source program, this is the crash () subfunction. OK. Now we can easily see the name of the function that crashed. Are you very excited? Well, don't be busy first. Next, more advanced moves will be available. Pay attention to line numbers information, the last part of the map file, which is displayed in this form: 13 0001:00000020 The first number indicates the code line number in the source code, and the second number indicates the offset of the code line in the code segment to which it belongs. To find the code row number, use the following formula to perform hexadecimal subtraction: Crash line offset = crash address-base address (imagebase address)-0x1000 Why? Careful friends may be aware of the RVA + base column. The crash address we get is obtained from the offset address (RVA) + base address (base, therefore, when calculating the row number, subtract the base address. Generally, the base address value is 0x00400000. In addition, because the code segments of a PE file generally start from 0x1000 offset, 0x1000 must be subtracted. Now that we understand this, we can perform the primary subtraction calculation: Crash line offset = 0x0040104a-0x00400000-0x1000 = 0x4a If you browse the code line information of the map file, you will see that the calculation result is no more than, but the nearest number is in the crashdemo. cpp file: 16 0001:00000046 That is to say, in the source code of line 16th, let's look at the source code: 16 I/= J; Ha !!! That's exactly 16th lines! Excited? The same is true for me! :) The method has been introduced. From now on, we can precisely locate the crash line in the source code, and as long as the compiler can generate Map Files (including VC, MASM, VB, BCB, Delphi ......), This method is applicable. We often complain about how the product of M $ is poor, but in fact M $ still intentionally or unintentionally provides a lot of valuable information to us, but we often don't know how to use it ...... I believe that in this way, you will be more comfortable with the "illegal operations" prompt. You can even ask the user to provide the address for the crash, and then you can sit at home and comfortably find the wrong line and make corrections. Is it nice? :) |
Article 2
Tips for using map file to remove objects
Last update: 03/11/2004
The main materials: http://www.codeproject.com/debug/mapfile.asp
Introduction
What is the greatest division of the program when it is used? The cause is that the program cannot be found after the program is completed. Most people simply use the splitter. When the splitter cannot be found, they have to use the printf trace outputdebugstring method, but none of them work, the true reliability depends on map file for guidance. In addition, if the program has already entered the release mode, security is also in the hands of the customer. If you unfortunately want to delete the program, you only need to ask for map file.
Map file is used by linker to convert object files into binary (exe, DLL ,...) the corresponding table generated by the system, that is, how the linker loader of the system uploads data to the two-element image's dynamic table. With this map generator, coupled with the Assembly source code generated by compiler, it becomes the best tool for finding a program as a machine.
Set up a map file
The hypervisor of the top column refers to the vc6 setting method. In fact, vc7 is similar. In the Properties dialog of the project, choose "linker-> debugging ".
- Generate map file-> Yes
- Map exports-> Yes
- Map lines-> Yes
I will change the handler output to assembly, machine code, and source (/FACS) in C/C ++> output files ), this is more comprehensive than other users.
How to Use Map File to remove objects
There is a program under the false settings:
<font face="Courier New" size="3">// main.cpp void main(){ char* pEmpty = NULL; *pEmpty = 'x';}</font> |
This program will generate two types, Main. COD and Main. map. An Error Reporting screen appears in Windows 2 k/XP, and the response in the dialog box is used to view the response, it tells you that when modname: main.exe, modver: 0.0.0.0, offset 00011cc8, there is a minidump (but following the instructions, this minidump cannot be copied/paste... you can only copy the case to the path listed by him ).
If you attempt to write a row in the VC, the VC ide will tell you that an unhandled exception occurs in 0x00411cc8.
We are interested in how to calculate the line of the queue from the address 00011cc8 or 00411cc8.
First look at the top of the map file. We want to find the base address:
map_debug Timestamp is 404eb1b2 (Tue Mar 09 22:12:02 2004) Preferred load address is 00400000 Start Length Name Class 0001:00000000 00010000H .textbss DATA 0002:00000000 00012492H .text CODE 0003:00000000 00002687H .rdata DATA 0003:00002688 0000014aH .rdata$debug DATA |
We are interested in the length and length of all sections before the load address 00400000 and code segment. In this example, the length of code segment is 0x00010000, remember the useful information.
Address Publics by Value Rva+Base Lib:Object 0000:00000000 __except_list 00000000 <absolute></absolute> 0000:00000000 ___safe_se_handler_table 00000000 <absolute></absolute> 0000:00000000 ___safe_se_handler_count 00000000 <absolute></absolute> 0001:00000000 __enc$textbss$begin 00401000 <linker-defined></linker-defined> 0001:00010000 __enc$textbss$end 00411000 <linker-defined></linker-defined> 0002:00000a50 __RTC_InitBase 00411a50 f LIBCD:init.obj 0002:00000a90 __RTC_Shutdown 00411a90 f LIBCD:init.obj 0002:00000ab0 _mainCRTStartup 00411ab0 f LIBCD:crt0.obj 0002:00000ca0 _main 00411ca0 f main.obj |
In the next section, we need to remember the offset (RVA + base value) of our program function. It is usually faster to search for it from LIB: object. In this example, we only have one function, and its RVA + base is 00411ca0.
Because the program in this example is too small, linker will not generate the third map file, that is, the address of the original program line response, as described in the textbook, this doesn't matter much because we have a more refined experience. cod available.
In Windows report, the offset value is calculated, while in VC report, the offset value is not calculated. Therefore, you can find that if the value of VC report is reset to preferred load address, you can get the windows report value.
00411cc8-00400000 = 00011cc8
This offset refers to the offset of the entire binary image, and there is a fixed length before the code segment, so we need to merge it, to know the offset of code segment.
00011cc8-00010000 = 100001cc8
Next, calculate the Offset Value of the main function:
00411ca0-00400000-00010000 = 100001ca0
So we can find the offset between the place where the sending cursor is located and the main function is:
20171cc8-20171ca0 = 28
Then we can find out main. COD.
; 3 : char* pEmpty = 0; 0001ec7 45 f8 00 0000 00 mov DWORD PTR _pEmpty$[ebp], 0 ; 4 : *pEmpty = 'x'; 000258b 45 f8 mov eax, DWORD PTR _pEmpty$[ebp] 00028c6 00 78 mov BYTE PTR [eax], 120; 00000078H |
Where offset 28 is the issue instruction (mov byte PTR [eax], 120 ), we can push back the problem with 4th lines of the program with COD statistics.
How does GCC generate map file?
Using GCC to generate a. COD attack will be slightly troublesome:
G ++-C-g-wa,-a,-ad main. cpp> main. COD
However, you should find that there is-G, so the. O records produced by it cannot be linked. We have run the-wa,-a,-ad command to notify all debug ctives ves of the as queue. However, if you want to keep the debugging rules, use G ++-s main. CPP produces main. check whether the Assembly of the production code is the same. The. COD segment generated in this example is as follows:
31 main: 32 0000 55 pushl %ebp 33 0001 89E5 movl %esp,%ebp 34 0003 83EC18 subl $24,%esp 1:main.c **** int main() 2:main.c **** { 36 .LM1: 3:main.c **** char* p = 0; 38 .LM2: 39 .LBB2: 40 0006 C745FC00 movl $0,-4(%ebp) 40 000000 4:main.c **** *p = 'x'; 42 .LM3: 43 000d 8B45FC movl -4(%ebp),%eax 44 0010 C60078 movb $120,(%eax) 5:main.c **** } |
Map workers are produced by LD, But we generally submit them to GCC for replacement:
G ++-O main. O-wl,-map, Main. Map
When the program fails, you can use GDB to check the core information:
GDB main core.19320
If there is no debug info, GDB will only tell you that #0 0x080483d0 in main () is better, because if your program is handled using strip-name, you cannot see main! Next, let's take a look at the map file. Because the ELF format is better than the PE format used in windows, there is not much problem with the offset keyword, you only need to find the base address of main:
.text 0x080482d0 0x24 /usr/lib/crt1.o 0x080482d0 _start .text 0x080482f4 0x24 /usr/lib/crti.o *fill* 0x08048318 0x8 2425393296 .text 0x08048320 0xa0 /usr/lib/gcc-lib/i686-pc-linux-gnu/2.95.3/crtbegin.o .text 0x080483c0 0x18 main.o 0x080483c0 main *fill* 0x080483d8 0x8 2425393296 |
Therefore, the data address of the main function 0x080483c0 is known, and 0x080483d0 is dropped. Therefore, if the offset is 0x00000010, the correct location can be quickly found in. COD memory.