我们的计算机架构教授给了我们一个示例程序,要求输入密码。任务是在比较输入的密码并确定它是否正常后更改跳转操作码。 我编写了一个程序,可以更改给定二进制文件中特定位置的任何字节。
以下是密码程序的代码:
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");
}
所以我在控制台中输入了$ objdump -d task
并得到了这个:
...
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 ||添加$ 0x58,%rsp
40071b:|| 5b || pop%rbx
...
74
是 je 的字节,我想将 jne 更改为75
。
如何在相应的二进制文件中获取74
字节的确切位置,以便将其更改为新值?
答案 0 :(得分:1)
正如Hans所说,内存位置与它无关。您需要做的是找到文件中字节(或字节)的位置。为此,一个非常好的反汇编程序(这将是IDA-Pro)让你活得很轻松。可悲的是,IDA的价格曲线陡峭,因此我们将坚持使用您可以轻松获得的工具。
在Linux机器上进行了以下操作,使用gcc版本4.8.2和在Ubuntu 14.04上运行的gdb 7.7
使用gcc -g -ansi -pedantic -Wall
编译代码。这不是一个问题,但main
应该有一个返回值。
将程序加载到gdb中,在main处放置一个断点然后运行程序。当我达到断点时,我使用gdb' disass
命令获取反汇编列表,如下所示:
(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.
看起来很不祥,我知道但是请花一点时间环顾四周并注意一些事情; 一个。尖括号(,&lt; + 97&gt;)中的数字给出了指令从函数开始处所在的字节数。在您的情况下,您需要的操作码是从函数开头起的97个字节 湾前9个字节(前4个指令)通常看起来像我们这里的,它们是函数的序言,它们正在为函数设置激活(或堆栈)框架。
现在,我们需要找到您的可执行文件中的main。这往往是操作系统和编译器特定的。在我的情况下,在Linux主机上工作,我知道代码存储在文件的 .text 部分,我可以使用工具readelf
获取该位置,如图所示下面;
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)
从上面我们可以看到 .text 部分将从内存位置0x400590开始,结束位置为0x400792(基于0x202的大小)。此外,我们可以看到 .text 部分在文件0x590中有一个偏移量。回顾一下我们的反汇编,我们可以看到main
从0x0040067d开始,该范围在 .text 范围内(只是一个完整性检查)。
我们现在有了应用程序的入口点( .text 部分的起始内存地址,如果我们从入口点减去main
的起点,我们应获取main
相对于代码映射位置的偏移量(在内存中)。最后将此值添加到可执行文件中代码的偏移量给我们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[]
作为一个快速的理智检查,记得我提到过main的反汇编中main的前几个字节是相当的样板,如果我们看一下它们并将它们组装到我们的机器中我们得到:
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
鉴于上述字节序列与步骤5中的字节序列匹配,我们在可执行文件中找到main
的位置是一个相当安全的假设。现在剩下要做的就是将偏移量0x6de处的字节从0x74修改为0x73。
N.B。 IDA确实有一个免费版本,有很多限制,因此值得您花时间去抓它并玩它。
希望这个帮助, Ť
答案 1 :(得分:0)
使用readelf
实用程序并查找LOAD
程序标题。他们在地址objdump
向您显示(VirtAddr
,又称p_vaddr
)和文件中的偏移(PhysAddr
,又名p_paddr
)之间提供映射。