Our professor in computer-architecture gave us a sample program which asks for a password. The task is to change the jump-opcode after it compares the entered password and decides if it is okay or not. I wrote a program which can change any byte at a specific place in a given binary file.
Here's the code of the pasword-program:
int main(int argc, char* argv[]){
char *pw = "12441233";
char pass[32];
printf("Enter password:\n");
scanf("%s", pass);
if(strncmp(pass,pw,8))
{
printf("Password wrong\n");
exit(-1);
}
printf("Welcome\n");
}
So I entered $ objdump -d task
in the console and got this:
...
400703: || 48 8b 5d e8 || mov -0x18(%rbp),%rbx
400707: || 64 48 33 1c 25 28 00 || xor %fs:0x28,%rbx
40070e: || 00 00
400710: || 74 05 || je 400717
400712: || e8 29 fe ff ff || callq 400540 <__stack_chk_fail@plt>
400717: || 48 83 c4 58 || add $0x58,%rsp
40071b: || 5b || pop %rbx
...
74
is the byte for je which I want to change to 75
for jne.
How can I get the exact position of my 74
byte in the corresponding binary file so that I can change it to a new value?
As Hans noted, the memory location has nothing to do with it. What you need to do is find where the byte (or bytes) are located in the file. For this a really good disassembler (this would be IDA-Pro) makes you live very easy. Sadly, there is a steep price curve for IDA, so we will stick to tools that you can get easily.
What follows was done on a Linux machine, using gcc version 4.8.2 and gdb 7.7 running on an Ubuntu 14.04
Compiled the code using gcc -g -ansi -pedantic -Wall
. Not really an issue, but main
should have a return value.
Loaded the program into gdb, put a break point at main and then ran the program. When I hit the break point, I used gdb's disass
command to get a disassembly listing as shown below:
(gdb) disass
Dump of assembler code for function main:
0x000000000040067d <+0>: push %rbp
0x000000000040067e <+1>: mov %rsp,%rbp
0x0000000000400681 <+4>: push %rbx
0x0000000000400682 <+5>: sub $0x58,%rsp
0x0000000000400686 <+9>: mov %edi,-0x54(%rbp)
0x0000000000400689 <+12>: mov %rsi,-0x60(%rbp)
=> 0x000000000040068d <+16>: mov %fs:0x28,%rax
0x0000000000400696 <+25>: mov %rax,-0x18(%rbp)
0x000000000040069a <+29>: xor %eax,%eax
[ ... ]
0x00000000004006cc <+79>: mov $0x8,%edx
0x00000000004006d1 <+84>: mov %rcx,%rsi
0x00000000004006d4 <+87>: mov %rax,%rdi
0x00000000004006d7 <+90>: callq 0x400520 <strncmp@plt>
0x00000000004006dc <+95>: test %eax,%eax
0x00000000004006de <+97>: je 0x4006f4 <main+119>
0x00000000004006e0 <+99>: mov $0x4007c0,%edi
0x00000000004006e5 <+104>: callq 0x400530 <puts@plt>
[ ... ]
0x0000000000400718 <+155>: retq
End of assembler dump.
Pretty ominous looking, I know but take a second and look around and notice a few things;
a. The numbers in the angle-brackets (i.e. <+97>) give the number of bytes that the instruction is located from the start of the function. In your case the the op-code that you need is 97 bytes from the beginning of the function.
b. The first 9 bytes (the first four instructions) will normally look like what we have here, they are the prolog for the function, and they are setting up the activation (or stack) frame for the function.
Now, we need to find where main is in your executable. This tends to be OS and compiler specific. In my case, working on a Linux host, I know that the code is stored in the .text section of the file, and I can use the tool readelf
to get that location as shown below;
readelf --wide -S task
There are 35 section headers, starting at offset 0x1478:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 00001c 00 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 0002b8 0000c0 18
[ .... ]
[13] .text PROGBITS 0000000000400590 000590 000202 00 AX 0 0 16
[14] .fini PROGBITS 0000000000400794 000794 000009 00 AX 0 0 4
[15] .rodata PROGBITS 00000000004007a0 0007a0 000037 00
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
From the above, we can see that the .text section will start at a memory location of 0x400590 and an ending location of 0x400792 (based on a size of 0x202). Additionally, we can see the the .text section has an offset in the file of 0x590. Looking back at our disassembly, we can see that main
starts at 0x0040067d, which is in the range of .text (just a sanity check).
We now have the entry point for the application (the starting memory address of the .text section, and if we subtract the starting point of main
from the entry point, we should get the offset (in memory) for main
relative to where the code is mapped. Finally adding this value to the offset of the code in the executable should give us the file location of main
:
000067d: 5548 89e5 5348 83ec 5889 7dac 4889 75a0 UH..SH..X.}.H.u.
000068d: 6448 8b04 2528 0000 0048 8945 e831 c048 dH..%(...H.E.1.H
000069d: c745 b8a4 0740 00bf ad07 4000 e882 feff .E...@....@.....
00006ad: ff48 8d45 c048 89c6 bfbd 0740 00b8 0000 .H.E.H.....@....
00006bd: 0000 e8ac feff ff48 8b4d b848 8d45 c0ba .......H.M.H.E..
00006cd: 0800 0000 4889 ce48 89c7 e844 feff ff85 ....H..H...D....
00006dd: c074 14bf c007 4000 e846 feff ffbf ffff .t....@..F......
00006ed: ffff e88c feff ffbf cf07 4000 e832 feff ..........@..2..
00006fd: ff48 8b5d e864 4833 1c25 2800 0000 7405 .H.].dH3.%(...t.
000070d: e82e feff ff48 83c4 585b 5d .....H..X[]
As a quick sanity check, remember I mentioned that the first few bytes of main from the disassembly of main were fairly boiler-plate, if we look at them and assemble them into we machine we get:
push %rbp 0x55
mov %rsp,%rbp 0x48 0x89 0xe5
push %rbx 0x53
sub $0x58,%rsp 0x48 0x83 0xec 0x58
mov %edi,-0x54(%rbp) 0x89 0x7d 0xac
Given that the above sequences of bytes matches the begging sequence of bytes from step 5, it is a fairly safe assumption that we have found the location of main
in the executable. Now all that is left to do is to modify the byte at offset 0x6de from 0x74 to 0x73.
N.B. IDA does have a free version, with a number of restrictions on it, so it would be worth your time to grab it and play with it.
Hope this help, T