Watchpoint doesn't work when reverse executing - c

Here's a debugging scenario:
Create start breakpoint A and finish breakpoint B.
Start recording. Continue.
Reach breakpoint B.
Set watchpoint to watch writes to some piece of memory.
Reverse continue until watchpoint breaks execution.
Let's suppose that setting the watchpoint is only possible in step 4, not earlier, since the location of memory that should be watched is only known at that point.
Here's a simple example.
main.c:
int main(void)
{
int num;
num = 5;
return 0;
}
Debugging session:
$ cc main.c
$ gdb -q -nx -ex 'set disassembly-flavor intel' ./a.out
Reading symbols from ./a.out...
(No debugging symbols found in ./a.out)
(gdb) b *main + 0
Breakpoint 1 at 0x1129
(gdb) disas main
Dump of assembler code for function main:
0x0000000000001129 <+0>: push rbp
0x000000000000112a <+1>: mov rbp,rsp
0x000000000000112d <+4>: mov DWORD PTR [rbp-0x4],0x5
0x0000000000001134 <+11>: mov eax,0x0
0x0000000000001139 <+16>: pop rbp
0x000000000000113a <+17>: ret
End of assembler dump.
(gdb) b *main + 16
Breakpoint 2 at 0x1139
(gdb) r
Starting program: /var/tmp/test/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, 0x0000555555555129 in main ()
(gdb) record
(gdb) c
Continuing.
Breakpoint 2, 0x0000555555555139 in main ()
(gdb) p/x $rbp-0x4
$1 = 0x7fffffffe63c
(gdb) watch *(int*)0x7fffffffe63c
Hardware watchpoint 3: *(int*)0x7fffffffe63c
(gdb) reverse-continue
Continuing.
No more reverse-execution history.
0x0000555555555129 in main ()
(gdb)
The last command shows that reverse execution didn't stop where it is supposed to stop, that is *main+4. Instead, it stopped where the recording has started, ignoring any memory writes.
My simple experiment was motivated by that video at the CppCon 2015, highlighting usefulness of the record & replay concept. Really, I don't know what I've done wrong, because for me it seems that I've repeated every step from the video.
Also, reading gdb docs didn't help either.
So, why the watchpoint has been ignored and how can it be prevented?

Try telling gdb to use a software breakpoint instead of the hardware one through the command set can-use-hw-watchpoints 0.
It seems that the hardware watchpoint doesn't work here because when executing back and forth through reverse-execution history, no traditional process execution takes place, it's only gdb changing it's internal memory to make appearance that a program executed backwards.
In other words, we should use hardware watchpoints when the real process executes on a CPU, and software watchpoints when gdb makes an emulation through its recording history.
Thanks to Peter Cordes for the explanation in the comments.

Related

Overwriting the stored return address on the stack doesn't work

I'm trying to take control of a C program's execution by overwriting the stored return address (saved eip) on the stack:
(gdb) info frame
Stack level 0, frame at 0xbffff550:
eip = 0x8048831 in main (6.c:52); saved eip = 0xbffffdef
source language c.
Arglist at 0xbffff538, args: argc=6, argv=0xbffff5e4
Locals at 0xbffff538, Previous frame's sp is 0xbffff550
Saved registers:
ebx at 0xbffff534, ebp at 0xbffff538, eip at 0xbffff54c
The value 0xbffffdef is the address of a "Hello, world!" shellcode which has been assembled, checked for any null bytes and put in an environment variable SHELLCODE:
(gdb) x/s *((char **)environ + 7)
0xbffffdef: "SHELLCODE=\353\023Y1\300\260\004\061\333C1Ҳ\017̀\260\001K̀\350\350\377\377\377Hello, world!\n\r"
Unfortunately the program fails to print the expected greeting:
Program received signal SIGSEGV, Segmentation fault.
0xbffffdfd in ?? ()
Why did it crash and how to solve the problem?
Notes:
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
// program was compiled with the following flags:
-m32 -fno-stack-protector -z execstack -fno-PIE -no-pie -g
program source: link
Credits to #Nate Eldredge
The reason behind this specific crash is the incorrect return address 0xbffffdef. The location of the environment variable SHELLCODE actually points to the beginning of the string "SHELLCODE=..."
(gdb) x/s *((char **)environ + 7)
0xbffffdef: "SHELLCODE=\353\023Y1\300\260\004\061\333C1Ҳ\017̀\260\001K̀\350\350\377\377\377Hello, world!\n\r"
In order to fix the issue, we have to adjust the address by skipping the first 10 characters and point to the "\353\023Y1\300...":
0xbffffdef + 0xa = 0xbffffdf9
When overwriting the stored return address with this value 0xbffffdf9, the program is coerced into greeting the world:
(gdb) info frame
Stack level 0, frame at 0xbffff550:
eip = 0x8048831 in main (6.c:52); saved eip = 0xbffffdf9
source language c.
Arglist at 0xbffff538, args: argc=6, argv=0xbffff5e4
Locals at 0xbffff538, Previous frame's sp is 0xbffff550
Saved registers:
ebx at 0xbffff534, ebp at 0xbffff538, eip at 0xbffff54c
(gdb) cont
Continuing.
Hello, world!
[Inferior 1 (process 28591) exited normally]

How to "unwind" a core dump in gdb

I'm not sure if this is the correct wording of the issue, but let's take the following example where I have a program that will crash/abort:
#include <assert.h>
int main(void)
{
int z=2;
assert (z>5);
}
And if I compile it with debugging and then run it:
$ gcc -ggdb3 a.c -o a.o && ./a.o
a.o: a.c:8: main: Assertion `z>5' failed.
Aborted (core dumped)
Now I'll open it up in gdb to see if I can inspect the program:
$ gdb a.o core
Core was generated by `./a.o'.
Program terminated with signal SIGABRT, Aborted.
#0 __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
If I now "run" the program with r I will get something like this (from gdb-dashboard viewer):
My question is the stack is now pretty deep into the C runtime / linux:
─── Stack ──────────────────────────────────────────────────────────────────────────────────────────
[0] from 0x00007ffff7a22f47 in __GI_raise+199 at ../sysdeps/unix/sysv/linux/raise.c:51
[1] from 0x00007ffff7a248b1 in __GI_abort+321 at abort.c:79
[2] from 0x00007ffff7a1442a in __assert_fail_base+330 at assert.c:92
[3] from 0x00007ffff7a144a2 in __GI___assert_fail+66 at assert.c:101
[4] from 0x00005555555546ce in main+52 at a.c:8
Is it possible that I can unwind the stack to where the error was triggered:
[4] from 0x00005555555546ce in main+52 at a.c:8
So that I can see what the registers, variables, etc. were at that point? Another way to phrase the question is "How do I ignore things outside my code when inspecting a core dump / gdb" ?
Here's a stab at this:
To travel up or down the callstack, use up|down. In this case we do up 4 to get back to main:
>>> up 4
#4 0x000055555555467e in main () at a.c:7
7 assert (z>5);
info frame and info locals can tell us high-level information about the function:
>>> info locals
z = 2
__PRETTY_FUNCTION__ = "main"
>>> info frame
Stack level 4, frame at 0x7fffffffe0e0:
rip = 0x55555555467e in main (a.c:7); saved rip = 0x7ffff7a05b97
caller of frame at 0x7fffffffe0c0
source language c.
Arglist at 0x7fffffffe0d0, args:
Locals at 0x7fffffffe0d0, Previous frame's sp is 0x7fffffffe0e0
Saved registers:
rbp at 0x7fffffffe0d0, rip at 0x7fffffffe0d8
For example, given the above, we can make a guess that the un-optimized assembly would put z into %rbp-4 and we can examine its value there:
>>> x/d $rbp-4
0x7fffffffe0cc: 2
# or, in long-form to ensure our rbp address above from `info` is the same:
>>> x/d 0x7fffffffe0d0-4
0x7fffffffe0cc: 2

Understanding OpenSSL's Assembly version of AES decryption implementation

I am trying to understand how OpenSSL implemented AES decryption using Assembly language and for the same I am using gdb debugger.
To understand from very basic concept how AES works - like Byte Substitute, Mix Columns OR how lookup table operations are performed. I am using very old version of OpenSSL-0.9.8.a as it contains only one assembly file ax86-elf.s ( generated from aes-586.pl) whereas latest Openssl contains multiple files (supporting AES-NI, SSE etc).
After understanding with Old OpenSSL, I may try to look into latest OpenSSL version.
Here is what I have done so far.
Generated assembly code of ax86-elf.s can be found here
https://pastebin.com/L3FsbqmK
It has global label AES_Td which points to some long data. I think those long data is the AES look up table data.
.globl AES_Td
.text
.globl _x86_AES_decrypt
.type _x86_AES_decrypt,#function
.align 16
_x86_AES_decrypt:
movl %edi, 12(%esp)
...
...
.align 64
AES_Td:
.long 1353184337,1353184337
.long 1399144830,1399144830
.long 3282310938,3282310938
.long 2522752826,2522752826
.long 3412831035,3412831035
.long 4047871263,4047871263
.long 2874735276,2874735276
.long 2466505547,2466505547
.long 1442459680,1442459680
To debug openssl, I have used gdb.
gdb /usr/local/ssl/bin/openssl
(gdb) set args enc -d -aes-128-cbc -in ciphertext2.bin -out plaintext2.txt -K 0123456789abcdef0123456789abcdef -iv 00000000000000000000000000000000
(gdb) set logging on
Copying output to gdb.txt.
(gdb) b main
Breakpoint 1 at 0x8055591
(gdb) b _x86_AES_decrypt
Function "_x86_AES_decrypt" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
(gdb) run
Starting program: /usr/local/ssl/bin/openssl enc -d -aes-128-cbc -in ciphertext2.bin -out plaintext2.txt -K 0123456789abcdef0123456789abcdef -iv 00000000000000000000000000000000
Breakpoint 1, 0x08055591 in main ()
(gdb) s
Single stepping until exit from function main,
which has no line number information.
__GI_getenv (name=0x8090a32 "OPENSSL_DEBUG_MEMORY") at getenv.c:35
35 getenv.c: No such file or directory.
(gdb)
__x86.get_pc_thunk.bx () at ../sysdeps/i386/i686/multiarch/strcat.S:55
55 ../sysdeps/i386/i686/multiarch/strcat.S: No such file or directory.
(gdb)
__GI_getenv (name=0x8090a32 "OPENSSL_DEBUG_MEMORY") at getenv.c:36
36 getenv.c: No such file or directory.
(gdb)
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:43
43 ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
Now this shows getenv.c is called, then ../sysdeps/i386/i686/multiarch/../../i586/strlen.S is called (although I am not able to view source code due to non-availablity).
I am wondering this way I would be able to understand the code as it shows so many calls of functions whose source code is not available.
So I follow this second approach, without putting any break-point at main.
gdb /usr/local/ssl/bin/openssl
(gdb) set args enc -d -aes-128-cbc -in ciphertext2.bin -out plaintext2.txt -K 0123456789abcdef0123456789abcdef -iv 00000000000000000000000000000000
(gdb) set logging on
Copying output to gdb.txt.
(gdb) b _x86_AES_decrypt
Function "_x86_AES_decrypt" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_x86_AES_decrypt) pending.
(gdb) run
Starting program: /usr/local/ssl/bin/openssl enc -d -aes-128-cbc -in ciphertext2.bin -out plaintext2.txt -K 0123456789abcdef0123456789abcdef -iv 00000000000000000000000000000000
Breakpoint 1, 0xb7e57590 in _x86_AES_decrypt ()
from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb) s
Single stepping until exit from function _x86_AES_decrypt,
which has no line number information.
0xb7e586d0 in AES_cbc_encrypt () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb) s
Single stepping until exit from function AES_cbc_encrypt,
which has no line number information.
Breakpoint 1, 0xb7e57590 in _x86_AES_decrypt ()
from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
(gdb)
Single stepping until exit from function _x86_AES_decrypt,
which has no line number information.
0xb7e586d0 in AES_cbc_encrypt () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
Single stepping until exit from function AES_cbc_encrypt,
which has no line number information.
0xb7ea380e in aes_128_cbc_cipher () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
Single stepping until exit from function aes_128_cbc_cipher,
which has no line number information.
0xb7ea182d in EVP_EncryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
Single stepping until exit from function EVP_EncryptUpdate,
which has no line number information.
0xb7ea1a39 in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
Single stepping until exit from function EVP_DecryptUpdate,
which has no line number information.
__memcpy_ssse3_rep () at ../sysdeps/i386/i686/multiarch/memcpy-ssse3-rep.S:111
111 ../sysdeps/i386/i686/multiarch/memcpy-ssse3-rep.S: No such file or directory.
I have following doubts -
I am not able to understand where AES LOOKUP table operations are performed ? How to find them ?
Why AES_cbc_encrypt, EVP_EncryptUpdate functions are called although I am performing decryption operations?
How to debug functions EVP_DecryptUpdate() to understand how the function is executed ? Simply putting breakpoint at the function like b EVP_DecryptUpdate is not giving any insight.
Here is what I am getting while putting breakpoint
(gdb) b EVP_DecryptUpdate
Breakpoint 3 at 0xb7ea19e0
(gdb) run
Breakpoint 3, 0xb7ea19e0 in EVP_DecryptUpdate ()
from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb) si
0xb7ea19e1 in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb) si
0xb7ea19e2 in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
0xb7ea19e3 in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
0xb7ea19e4 in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
0xb7e372c0 in __x86.get_pc_thunk.bx ()
from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
0xb7e372c3 in __x86.get_pc_thunk.bx ()
from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
0xb7ea19e9 in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
0xb7ea19ef in EVP_DecryptUpdate () from /usr/local/ssl/lib/libcrypto.so.0.9.8
(gdb)
EVP_DecryptUpdate() function is defined inside crypto/evp/evp_enc.c
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl)
{
How to display the corresponding C instructions while debugging using gdb ?
I am not sure, but it may be possible OpenSSL use various C programs as well as assembly to decrypt the AES encrypted ciphertext. How to find the flow of evaluation ?
I am using Debian linux, 32bit, gdb .
Any help to understand the above flow or any link to understand will be a great help. Thanks in advance.

Trying to understand example char_array2.c from "the art of exploitation"

OK so I am really trying to understand what's going on this example of "The art of exploitation" second edition. I am trying to see exactly what is going on with the example by closely following the output of GDB on the book. My greatest problem with this is the last part, I included the whole thing so that everyone can see what's going on. Granted I only have very(very) basic knowledge of assembly code. I do understand basic C.
In the last part the author says that there is a minor difference in the second run of the program from the last one in the address that strcpy() points to and I just can't see it.
The program is simply
#include<stdio.h>
#include<string.h>
int main() {
char str_a[20];
strcpy(str_a, "Hello, world!\n");
printf(str_a);
}
After I compile it with the necessary options to be able to debug it I load it on
GDB and include the following:
(gdb) break 6
Breakpoint 1 at 0x80483c4: file char_array2.c, line 6.
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) break 8
Breakpoint 3 at 0x80483d7: file char_array2.c, line 8.
(gdb)
I have no problem with this, it is to my understanding that the
debugger can only do this sort of things with user defined functions. I also know how to go around this problem with gcc options.
I also know that when the program runs the strcpy breakpoint is resolved. Let me continue.
(gdb) run
Starting program: /home/reader/booksrc/char_array2
Breakpoint 4 at 0xb7f076f4
Pending breakpoint "strcpy" resolved
Breakpoint 1, main() at char_array2.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) i r eip
eip 0x80483c4 0x80483c4 <main+16>
(gdb) x/5i $eip
0x80483c4 <main+16>: mov DWORD PTR [esp+4],0x80484c4
0x80483cc <main+24>: lea eax,[ebp-40]
0x80483cf <main+27>: mov DWORD PTR [esp],eax
0x80483d2 <main+30>: call 0x80482c4 <strcpy#plt>
0x80483d7 <main+35>: lea eax,[ebp-40]
(gdb) continue
Continuing.
Breakpoint 4, 0xb7f076f4 in strcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb) i r eip
eip 0xb7f076f4 0xb7f076f4 <strcpy+4>
(gdb) x/5i $eip
0xb7f076f4 <strcpy+4>: mov esi,DWORD PTR [ebp+8]
0xb7f076f7 <strcpy+7>: mov eax,DWORD PTR [ebp+12]
0xb7f076fa <strcpy+10>: mov ecx,esi
0xb7f076fc <strcpy+12>: sub ecx,eax
0xb7f076fe <strcpy+14>: mov edx,eax
(gdb) continue
Continuing.
Breakpoint 3, main () at char_array2.c:8
8
printf(str_a);
(gdb) i r eip
eip 0x80483d7 0x80483d7 <main+35>
(gdb) x/5i $eip
0x80483d7 <main+35>: lea eax,[ebp-40]
0x80483da <main+38>: mov DWORD PTR [esp],eax
0x80483dd <main+41>: call 0x80482d4 <printf#plt>
0x80483e2 <main+46>: leave
0x80483e3 <main+47>: ret
(gdb)
This is the second run of the program in which supposedly the address to strcpy is different from the other address.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/reader/booksrc/char_array2
Error in re-setting breakpoint 4:
Function "strcpy" not defined.
Breakpoint 1, main () at char_array2.c:7
7
strcpy(str_a, "Hello, world!\n");
(gdb) bt
#0 main () at char_array2.c:7
(gdb) cont
Continuing.
Breakpoint 4, 0xb7f076f4 in strcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb) bt
#0 0xb7f076f4 in strcpy () from /lib/tls/i686/cmov/libc.so.6
#1 0x080483d7 in main () at char_array2.c:7
(gdb) cont
Continuing.
Breakpoint 3, main () at char_array2.c:8
8
printf(str_a);
(gdb) bt
#0 main () at char_array2.c:8
(gdb)
Where is the difference? am I wrong for thinking that 0xb7f076f4 is the address of strcpy? On the second run if I am correct everything indicates that the address is 0xb7f076f4.
Also, what is ? I can't find the explanation for this anywhere earlier in the book. If someone could be kind enough to explain this from the top down to me I would appreciate it so much being that I don't know any expert in real life that could help me. I find the explanations to be vague, he explains variables and loops like if he was explaining it to a 5 year old, but leaves much of the assembly code for us to figure out by ourselves, I have not been very successful at this.
Any help would be greatly appreciated.
Apparently gdb turns off ASLR for the debugged process to make (session-to-session) debugging easier.
From https://sourceware.org/gdb/current/onlinedocs/gdb/Starting.html
set disable-randomization
set disable-randomization on
This option (enabled by default in GDB) will turn off the native
randomization of the virtual address space of the started program.
This option is useful for multiple debugging sessions to make the
execution better reproducible and memory addresses reusable across
debugging sessions.
Set set disable-randomization off in gdb or in a .gdbinit file and try it again. Libc should now get loaded at a different address each time you run the binary.
Running watch -n 1 cat /proc/self/maps also is nice to see how the binary and the libraries are mapped at 'random' addresses.
As #Voo said in his comment above, the book probably refers to ASLR (Address Space Layout Randomization) which is a security feature. It changes how the address space is used for each execution so you can't rely on finding things always in the same place.
If you don't see it in gdb that means you have ASLR turned off. Either globally or locally in gdb. You can check the former using cat /proc/sys/kernel/randomize_va_space and the latter using show disable-randomization command at the gdb prompt.

How can I know how the exit() function work?

I wrote a small program to find how the exit() function works in Linux.
#include <unistd.h>
int main()
{
exit(0);
}
And then I compiled the program with gcc.
gcc -o example -g -static example.c
In gdb, when I set a breakpoint, I got these lines.
Dump of assembler code for function exit:
0x080495a0 <+0>: sub $0x1c,%esp
0x080495a3 <+3>: mov 0x20(%esp),%eax
0x080495a7 <+7>: movl $0x1,0x8(%esp)
0x080495af <+15>: movl $0x80d602c,0x4(%esp)
0x080495b7 <+23>: mov %eax,(%esp)
0x080495ba <+26>: call 0x80494b0 <__run_exit_handlers>
End of assembler dump.
(gdb) b 0x080495a3
Function "0x080495a3" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (0x080495a3) pending.
(gdb) run
Starting program: /home/jack/Documents/overflow/example
[Inferior 1 (process 2299) exited normally]
The program does not stop at the breakpoint. Why? I use -static to compile the program, why does the breakpoint pend until the library loads into the memory?
You're asking gdb to break on a function called 0x080495a3. You'll need to use b *0x080495a3 instead.
(gdb) help break
Set breakpoint at specified line or function.
break [LOCATION] [thread THREADNUM] [if CONDITION]
LOCATION may be a line number, function name, or "*" and an address.
As the help says, The * tells gdb it's an address you want to break on.
From your example:
Function "0x080495a3" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (0x080495a3) pending.
The "pending" means that the breakpoint is waiting until a function called 0x080495a3 is loaded from a shared library.
You might also be interested in break-range:
(gdb) help break-range
Set a breakpoint for an address range.
break-range START-LOCATION, END-LOCATION
where START-LOCATION and END-LOCATION can be one of the following:
LINENUM, for that line in the current file,
FILE:LINENUM, for that line in that file,
+OFFSET, for that number of lines after the current line
or the start of the range
FUNCTION, for the first line in that function,
FILE:FUNCTION, to distinguish among like-named static functions.
*ADDRESS, for the instruction at that address.
The breakpoint will stop execution of the inferior whenever it executes
an instruction at any address within the [START-LOCATION, END-LOCATION]
range (including START-LOCATION and END-LOCATION).
It looks like that you're trying to set a breakpoint at a function named 0x080495a3. Instead try b *0x080495a3 to indicate to GDB that you want to break at a specific address.
0x080495a3 is an address of the line on which you are willing to apply break point. But the format for gdb is b (function name or line number). So You have 2 ways to do this.
1) do an l after your gdb session has started. It will list you the code in C. And then apply a break point using the line number else
2) if you want to use the address, use b *0x080495a3 way to set a break point.
This way you will be able to halt at line
0x080495a3 <+3>: mov 0x20(%esp),%eax

Resources