3

I would like to create an execution trace of a binary. To be precise, I would like to record every executed assembly instruction, together with possible memory accesses. By memory accesses I mean reading from, or writing to, memory. For these accesses I would like to record where the instruction reads from (resp. writes to) and what value it reads (resp. what value it writes).

If possible, I would prefer a solution which works on Windows and Linux, as well as for different kinds of CPUs.

The performance does not matter for the moment, i.e. a debugger script would be okay.

I tried several things, but I did not manage to get what I wanted:

  • IDA: I could not find a straight forward way of getting the memory accesses with content (there are obscure functions such as idc.GetTevRegMem (see here) but to reach my goal things get very very complex.
  • Valgrind: valgrind --tool=lackey --trace-mem=yes gave me the accesses, but not the content, see here for lackey
  • GDB: Using watchpoints is not an option as their number is restricted

For the moment, PANDA seems to be the most promising choice (see comment of PANDA author here). Yet, I don't know PANDA and thus cannot tell how complex the creation of a suitable PANDA plugin will get.

As my goal seems not very exotic to me, I figured there must exist something already or there must be an easy way. For example, some lines of debugger script code, a PIN tool (yet Intel only), or a qemu argument or the like. Does it?

  • 1
    I'd definitely go with a DBI framework. PIN can do that easily (although you'll be restricted to x86 / x64). Check the pinatrace example source. Use PIN_SafeCopy (not used in the example) to access the memory, and you're done. – Neitsa Mar 21 '16 at 2:41
  • I think of DBI also, but PIN (and DynamoRIO) cannot trace kernel space accesses. – Ta Thanh Dinh Mar 21 '16 at 2:58
  • You can consult this (PDF) thesis report, specifically chapter 3 – 0xec Mar 21 '16 at 9:10
  • @ExtremeCoders thanks, they are also using PANDA. Really seems like the way to go if one wants everything: different CPUs, kernel+userspace, different OSes. – stackoverflowwww Mar 21 '16 at 10:06
  • As valgrind is open source, i'd guess it can't be too hard to add memory content to the --trace-mem=yes output. – Guntram Blohm Mar 21 '16 at 17:48
1

I know that this question was asked some time ago, but here comes the solution working on Windows.

Note: See the last section of this answer to get the solution for other systems and architectures.

x86 32 bit files

OllyDbg2 is the tool that can be used for logging every single assembly instruction along with memory reads and writes. A short guide how to log it is presented below:

  1. Run OllyDbg.
  2. Select Options->Options... (or use Alt+o shortcut). The options window will show up.
  3. Search for Run trace section and select options specified on image below: olly_options_run_trace

Of course, if you don't want Olly to log all string instructions or code in system DLLs, you may just select two relevant boxes. Additionally, you may choose smaller trace buffer size if you want to dump the log to a file.

  1. Now, open your target application, but don't start it yet.
  2. Select View->Run trace.
  3. Right click on a window that appeared and select memory and register options you want to have in the trace.Olly_run_trace_options
  4. Right click on that window once again and select Log to file... option and choose the file where you want to write entire run trace.
  5. Press Ctrl+F11 to start tracing.
  6. When you want it to stop, click pause button (or F12) to pause execution.
  7. You will see the instructions logged in Run trace window.
  8. Right click on Run trace window and select Stop logging option. This will close and save the trace file.

That's it! You may now open and analyse the file (it will be probably huge nonetheless).

The small excerpt of such a file is presented below (it shows only modified registers and doesn't enter system DLLs):

main  <ModuleEntryPoint>          JMP SHORT 00401012
main  00401012                    MOV EAX,[DWORD DS:4F61EF]               [004F61EF]=0                EAX=00000000
main  00401017                    SHL EAX,2
main  0040101A                    MOV [DWORD DS:4F61F3],EAX               [004F61F3]=0
main  0040101F                    PUSH EDX                                [0019FF80]=0                ESP=0019FF80
main  00401020                    PUSH 0                                  [0019FF7C]=0                ESP=0019FF7C
main  00401022                    CALL <JMP.&KERNEL32.GetModuleHandleA>                               EAX=00400000, ECX=DC5CD787, ESP=0019FF80
main  00401027                    MOV EDX,EAX                                                         EDX=00400000
main  00401029                    CALL 004E7210                                                       ESP=0019FF7C
main  004E7210                    MOV EAX,EDX
main  004E7212                    CMP [BYTE DS:4F61E0],0                  [004F61E0]=00
main  004E7219                    JNE SHORT 004E7240
main  004E721B                    CMP [BYTE DS:4F61E1],0                  [004F61E1]=00
main  004E7222                    JE SHORT 004E7238
main  004E7238                    MOV ECX,[DWORD DS:57D7D4]               [0057D7D4]=ollydbg.0061B108 ECX=0061B108
main  004E723E                    MOV [DWORD DS:ECX],EAX                  [0061B108]=0
main  004E7240                    MOV EAX,[DWORD DS:57D7D8]               [0057D7D8]=ollydbg.0061B131 EAX=0061B131
main  004E7245                    MOV [BYTE DS:EAX],1                     [0061B131]=00

DOS executables

DOSBox Debugger will log everything you want when you are analysing DOS executable.

  1. Download it from here for example.
  2. Start application by dragging and dropping the executable on DOSBox Debugger icon.
  3. Press Alt+Pause at the moment you want to start logging the execution trace - application will freeze.
  4. Switch to debugger window. You will see something like this: DOSBoxDebugger
  5. Type logl n, where n is a (hexadecimal) number of instructions to log; for example: logl ffff.
  6. The log file has been created and should be located in the same directory as DOSBox debugger - it will have LOGCPU.txt name.

Several lines of resulting file are given below:

01A2:00004654  mov  ax,si                                              8B C6                 EAX:0000002A EBX:0000002A ECX:00000A00 EDX:00000000 ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:1 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007293 CR0:00000000
01A2:00004656  mov  dx,000E                                            BA 0E 00              EAX:00000004 EBX:0000002A ECX:00000A00 EDX:00000000 ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:1 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007293 CR0:00000000
01A2:00004659  imul dx                                                 F7 EA                 EAX:00000004 EBX:0000002A ECX:00000A00 EDX:0000000E ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:1 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007293 CR0:00000000
01A2:0000465B  mov  bx,ax                                              8B D8                 EAX:00000038 EBX:0000002A ECX:00000A00 EDX:00000000 ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:0 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007296 CR0:00000000
01A2:0000465D  cmp  word [bx+56F6],0001        ds:[572E]=0000          83 BF F6 56 01        EAX:00000038 EBX:00000038 ECX:00000A00 EDX:00000000 ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:0 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007296 CR0:00000000
01A2:00004662  jne  00004678 ($+14)            (down)                  75 14                 EAX:00000038 EBX:00000038 ECX:00000A00 EDX:00000000 ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:1 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007296 CR0:00000000
01A2:00004678  inc  si                                                 46                    EAX:00000038 EBX:00000038 ECX:00000A00 EDX:00000000 ESI:00000004 EDI:00000004 EBP:0000FFE2 ESP:0000FFDE DS:26EF ES:A000 FS:0000 GS:0000 SS:26EF CF:1 ZF:0 SF:1 OF:0 AF:1 PF:1 IF:1 TF:0 VM:0 FLG:00007296 CR0:00000000

Every or almost every architectures and systems

radare2 is the tool that may be used for your purpose regardless the system and architecture (complete list of them is given here).

  1. Run r2 -c aei -d programToDebug.
  2. Put a breakpoint at the address where you want to start tracing (using db address_in_hex for example).
  3. Run dc to continue until the breakpoint is hit.
  4. Type e dbg.trace=1 and press enter.
  5. Run des N to step N instructions (for example des 10).
  6. Use dtd > log1 to print instructions traced to log1 file.
  7. Use dte > log2 to print all memory and register accesses to log2 file. Sample contents of these files are given below:

Instructions: r2TraceInstructions And some corresponding memory accesses: r2TraceMemory

Of course, it will be nicer to have these outputs listed in such a way as OllyDbg does, for example, but it just requires to write a script that links these two files together and displays the information in more convenient way. Like so, for instance: r2PrettyLog

Python script for creating such an output from these files is available here. Usage:

./prettyTraceLog.py file1 file2

, where log1 and log2 are default values. If you find any bug, or want to modify it for some other reason, feel free to do it.

Thanks @pancake for telling me how to do tracing in radare2.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.