How to read CPU registers in Ubuntu?

We can inspect CPU registers in a context of a running program. We will compile a simple C program and we will load the binary in gdb command line debugger. Then, we will set a breakpoint in the program. Breakpoint can be thought of halting an executing program. When we run our program, the execution will completely halt at the breakpoint. At that time, we will inspect what our CPU registers contain.

gdb’s full name is GNU debugger which comes by default in Ubuntu (and in most unix flavors). For more information on gdb, you can read gdb’s wiki page.

Let’s make a very simple C program that prints “Hello World”. I saved this program as registers.c.

#include<stdio.h>
#include<stdio.h>
void main()
{
printf("Hello world\n");
}

Now let’s compile our program.

gcc registers.c -o registers

Make sure the program doesn’t have any errors by running it by command “./registers”

Let’s load the program in gdb. This can be done by command: “gdb binary_name”.
You should see the gdb console like this.

hardik@admin-VM:~/Desktop/info$ gdb registers
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from registers...(no debugging symbols found)...done.
(gdb)

First, we need to setup a breakpoint in the program. Breakpoint can be setup by specifying a function name, a line number, or an address of an instruction. We will set a breakpoint at main.

(gdb) break main
Breakpoint 1 at 0x8048419
(gdb)

We have our breakpoint. Now if we run the program, program will halt at main. We will execute our program by typing “run”. At this point we can inspect our CPU registers by typing “info registers”.

(gdb) run
Starting program: /home/hardik/Desktop/info/registers

Breakpoint 1, 0x08048419 in main ()
(gdb) info registers
eax            0xb7fb8dbc	-1208250948
ecx            0xbffff050	-1073745840
edx            0xbffff074	-1073745804
ebx            0x0	0
esp            0xbffff034	0xbffff034
ebp            0xbffff038	0xbffff038
esi            0xb7fb7000	-1208258560
edi            0xb7fb7000	-1208258560
eip            0x8048419	0x8048419 <main+14>
eflags         0x282	[ SF IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb)

You can see that gdb will show values in all general purpose registers,eflags, eip and segment registers.
You can also read individual registers by “display /x register” command. “/x” will show the register value in hex format.

(gdb) display /x $eax
3: /x $eax = 0xb7fb8dbc
(gdb) display /x $ax
4: /x $ax = 0x8dbc
(gdb) display /x $al
5: /x $al = 0xbc
(gdb) display /x $esp
6: /x $esp = 0xbffff034
(gdb) display /x $cx
7: /x $cx = 0xf050
(gdb)

Also, note that gdb makes us easy for us to see which flags are enabled in eflags register. More info on eflags register can be found here. For our program, SF and IF flags are enabled.

By default, gdb will now show all the register (FPU, XMM). To check all the registers we can type “info all-registers”.

(gdb) info all-registers
eax            0xb7fb8dbc	-1208250948
ecx            0xbffff050	-1073745840
edx            0xbffff074	-1073745804
ebx            0x0	0
esp            0xbffff034	0xbffff034
ebp            0xbffff038	0xbffff038
esi            0xb7fb7000	-1208258560
edi            0xb7fb7000	-1208258560
eip            0x8048419	0x8048419 <main+14>
eflags         0x282	[ SF IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
st0            0	(raw 0x00000000000000000000)
st1            0	(raw 0x00000000000000000000)
st2            0	(raw 0x00000000000000000000)
st3            0	(raw 0x00000000000000000000)
st4            0	(raw 0x00000000000000000000)
st5            0	(raw 0x00000000000000000000)
st6            0	(raw 0x00000000000000000000)
st7            0	(raw 0x00000000000000000000)
fctrl          0x37f	895
fstat          0x0	0
ftag           0xffff	65535
fiseg          0x0	0
fioff          0x0	0
foseg          0x0	0
fooff          0x0	0
fop            0x0	0
mxcsr          0x1f80	[ IM DM ZM OM UM PM ]
ymm0           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0,
0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {0x0, 0x0, 0x0, 0x0}, v2_int128 = {
0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
---Type <return> to continue, or q <return> to quit---
ymm1           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
ymm2           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
ymm3           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
ymm4           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
ymm5           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
ymm6           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
ymm7           {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_double = {0x0, 0x0, 0x0, 0x0}, v32_int8 = {0x0 <repeats 32 times>}, v16_int16 = {0x0 <repeats 16 times>}, v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int64 = {
0x0, 0x0, 0x0, 0x0}, v2_int128 = {0x00000000000000000000000000000000, 0x00000000000000000000000000000000}}
mm0            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm1            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm2            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm3            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm4            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm5            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm6            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
mm7            {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
(gdb)

The assembly code can be shown in either Intel OR AT&T Syntax. By default, Ubuntu will show AT&T syntax. But I like Intel syntax so I will change that by “set disassembly-flavor intel” command in gdb console.
Now, if you type “disassemble” in gdb console, you will see disassembly in Intel syntax.

(gdb) set disassembly-flavor intel
(gdb) disassemble
Dump of assembler code for function main:
0x0804840b <+0>:	lea    ecx,[esp+0x4]
0x0804840f <+4>:	and    esp,0xfffffff0
0x08048412 <+7>:	push   DWORD PTR [ecx-0x4]
0x08048415 <+10>:	push   ebp
0x08048416 <+11>:	mov    ebp,esp
0x08048418 <+13>:	push   ecx
=> 0x08048419 <+14>:	sub    esp,0x4
0x0804841c <+17>:	sub    esp,0xc
0x0804841f <+20>:	push   0x80484c0
0x08048424 <+25>:	call   0x80482e0 <puts@plt>
0x08048429 <+30>:	add    esp,0x10
0x0804842c <+33>:	nop
0x0804842d <+34>:	mov    ecx,DWORD PTR [ebp-0x4]
0x08048430 <+37>:	leave
0x08048431 <+38>:	lea    esp,[ecx-0x4]
0x08048434 <+41>:	ret
End of assembler dump.
(gdb)

One thought on “How to read CPU registers in Ubuntu?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s