Leahcim
Leahcim

Reputation: 41929

selecting address to change value in memory

This question/answer on SO shows how to use GDB to change a value in memory, but in the example given, it chooses an address to set the value that wasn't previously being used

For example, to change the return value to 22, the author does

set {unsigned char}0x00000000004004b9 = 22

However, why would this address 0x00000000004004b9 be the address to change? If you look at the output of disas/r the address 0x00000000004004b9 isn't being used, so why use this one to set to 22? I'm trying to understand how to know which address needs to be changed to (in this example) change the return value, if the output of disas/r doesn't show it.

code

$ cat t.c
int main()
{
  return 42;
}

$ gcc t.c && ./a.out; echo $?
42

$ gdb --write -q  ./a.out
(gdb) disas/r main
Dump of assembler code for function main:
   0x00000000004004b4 <+0>:     55      push   %rbp
   0x00000000004004b5 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x00000000004004b8 <+4>:     b8 2a 00 00 00  mov    $0x2a,%eax
   0x00000000004004bd <+9>:     5d      pop    %rbp
   0x00000000004004be <+10>:    c3      retq   
End of assembler dump.
(gdb) set {unsigned char}0x00000000004004b9 = 22
(gdb) disas/r main
Dump of assembler code for function main:
   0x00000000004004b4 <+0>:     55      push   %rbp
   0x00000000004004b5 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x00000000004004b8 <+4>:     b8 16 00 00 00  mov    $0x16,%eax  <<< ---changed
   0x00000000004004bd <+9>:     5d      pop    %rbp
   0x00000000004004be <+10>:    c3      retq   
End of assembler dump.
(gdb) q

$ ./a.out; echo $?
22    <<<--- Just as desired

Upvotes: 1

Views: 655

Answers (1)

Employed Russian
Employed Russian

Reputation: 213576

I'm trying to understand how to know which address needs to be changed to (in this example) change the return value, if the output of disas/r doesn't show it.

To understand this, you need to understand instruction encoding. The instruction here is "move immediate 32-bit constant to register". The constant is part of the instruction (that's what "immediate" means). It may be helpful to compile this instead:

int foo() { return 0x41424344; }
int bar() { return 0x45464748; }
int main() { return foo() + bar(); }

When you do compile it, you should see something similar to:

(gdb) disas/r foo
Dump of assembler code for function foo:
   0x00000000004004ed <+0>:     55      push   %rbp
   0x00000000004004ee <+1>:     48 89 e5        mov    %rsp,%rbp
   0x00000000004004f1 <+4>:     b8 44 43 42 41  mov    $0x41424344,%eax
   0x00000000004004f6 <+9>:     5d      pop    %rbp
   0x00000000004004f7 <+10>:    c3      retq   
End of assembler dump.
(gdb) disas/r bar
Dump of assembler code for function bar:
   0x00000000004004f8 <+0>:     55      push   %rbp
   0x00000000004004f9 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x00000000004004fc <+4>:     b8 48 47 46 45  mov    $0x45464748,%eax
   0x0000000000400501 <+9>:     5d      pop    %rbp
   0x0000000000400502 <+10>:    c3      retq   
End of assembler dump.

Now you can clearly see where in the instruction stream each byte of the immediate constant resides (and also that x86 uses little-endian encoding for them).

The standard reference on instruction encoding for x86 is Intel instruction set reference. You can find 0xB8 instruction on page 3-528.

Upvotes: 1

Related Questions