背景
最近工作中用到了gdb修改二进制指令,浅浅记录一下,分享给大家。
原理
golang例子:
package main
func main() {
go loop()
for {
x = make([]byte, 10000)
}
}
var x []byte
var a [1000]*int
var b [1000]*int
var c [1000]*int
func loop() {
for {
assign()
}
}
//go:noinline
func assign() {
a = [1000]*int{}
b = [1000]*int{}
c = [1000]*int{}
}
缺省情况下,gdb是以只读方式加载程序的。需先指定加载方式为可写, 再通过file
命令加载二进制文件:
(gdb) set write on
(gdb) show write
Writing into executable and core files is on.
(gdb) file /usr1/src/main
...
(gdb) b main.main
Breakpoint 1 at 0x459c80: file /usr1/src/main.go, line 3.
(gdb) disassemble /mr main.assign
Dump of assembler code for function main.assign:
25 func assign() {
0x0000000000459d40 <+0>: 49 3b 66 10 cmp 0x10(%r14),%rsp
0x0000000000459d44 <+4>: 0f 86 8c 00 00 00 jbe 0x459dd6 <main.assign+150>
0x0000000000459d4a <+10>: 48 83 ec 18 sub $0x18,%rsp
0x0000000000459d4e <+14>: 48 89 6c 24 10 mov %rbp,0x10(%rsp)
0x0000000000459d53 <+19>: 48 8d 6c 24 10 lea 0x10(%rsp),%rbp
0x0000000000459dd6 <+150>: 44 9d rex.R popfq
0x0000000000459dd8 <+152>: 45 00 ff add %r15b,%r15b
0x0000000000459ddb <+155>: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0x0000000000459de0 <+160>: e9 5b ff ff ff jmpq 0x459d40 <main.assign>
26 a = [1000]*int{}
0x0000000000459d58 <+24>: 83 3d e1 b4 09 00 00 cmpl $0x0,0x9b4e1(%rip) # 0x4f5240 <runtime.writeBarrier>
0x0000000000459d5f <+31>: 90 nop
0x0000000000459d60 <+32>: 75 31 jne 0x459d93 <main.assign+83>
0x0000000000459d62 <+34>: 48 8d 3d 97 8b 06 00 lea 0x68b97(%rip),%rdi # 0x4c2900 <main.a>
0x0000000000459d69 <+41>: b9 e8 03 00 00 mov $0x3e8,%ecx
0x0000000000459d6e <+46>: 31 c0 xor %eax,%eax
0x0000000000459d70 <+48>: f3 48 ab rep stos %rax,%es:(%rdi)
0x0000000000459d91 <+81>: eb 39 jmp 0x459dcc <main.assign+140>
0x0000000000459d93 <+83>: 48 8d 05 c6 46 00 00 lea 0x46c6(%rip),%rax # 0x45e460
0x0000000000459d9a <+90>: 48 8d 1d 5f 8b 06 00 lea 0x68b5f(%rip),%rbx # 0x4c2900 <main.a>
0x0000000000459da1 <+97>: e8 fa 61 fb ff callq 0x40ffa0 <runtime.typedmemclr>
27 b = [1000]*int{}
0x0000000000459d73 <+51>: 48 8d 3d c6 aa 06 00 lea 0x6aac6(%rip),%rdi # 0x4c4840 <main.b>
0x0000000000459d7a <+58>: b9 e8 03 00 00 mov $0x3e8,%ecx
0x0000000000459d7f <+63>: f3 48 ab rep stos %rax,%es:(%rdi)
0x0000000000459da6 <+102>: 48 8d 05 b3 46 00 00 lea 0x46b3(%rip),%rax # 0x45e460
0x0000000000459dad <+109>: 48 8d 1d 8c aa 06 00 lea 0x6aa8c(%rip),%rbx # 0x4c4840 <main.b>
0x0000000000459db4 <+116>: e8 e7 61 fb ff callq 0x40ffa0 <runtime.typedmemclr>
28 c = [1000]*int{}
0x0000000000459d82 <+66>: 48 8d 3d f7 c9 06 00 lea 0x6c9f7(%rip),%rdi # 0x4c6780 <main.c>
0x0000000000459d89 <+73>: b9 e8 03 00 00 mov $0x3e8,%ecx
0x0000000000459d8e <+78>: f3 48 ab rep stos %rax,%es:(%rdi)
0x0000000000459db9 <+121>: 48 8d 05 a0 46 00 00 lea 0x46a0(%rip),%rax # 0x45e460
0x0000000000459dc0 <+128>: 48 8d 1d b9 c9 06 00 lea 0x6c9b9(%rip),%rbx # 0x4c6780 <main.c>
0x0000000000459dc7 <+135>: e8 d4 61 fb ff callq 0x40ffa0 <runtime.typedmemclr>
29 }
0x0000000000459dcc <+140>: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp
0x0000000000459dd1 <+145>: 48 83 c4 18 add $0x18,%rsp
0x0000000000459dd5 <+149>: c3 retq
End of assembler dump.
我想修改的指令是这一条:
0x0000000000459d44 <+4>: 0f 86 8c 00 00 00 jbe 0x459dd6 <main.assign+150>
我想让jbe跳转到自己,也就是应该改成
0x0000000000459d44 <+4>: 0f 86 8c 00 00 00 jbe 0x459d44 <main.assign+150>
应该要怎么做呢? 汇编代码每一条指令的本质就是二进制,我们只需改变指令所在地址的二进制的值,就可以实现汇编指令的修改,以得到不同的程序执行结果。
可以看到jbe的指令编码是这样的:0f 86 8c 00 00 00。前面的 0f 86 是操作码,不用动,后面的 8c 00 00 00 是距离 jbe 指令的下一条指令位置的偏移,是一个4字节的按小端序存储的整数。jbe 这条指令的下一条指令的地址是 0x459d4a,加上 8c 正好是 jbe 要跳转的地址 0x459dd6。
如果要做到jbe自己跳自己,那么操作码后面的偏移量应该是-6,以补码的形式存储。-6的补码的十六进制是 FF FF FF FA(4个字节),所以我们应该把原本 8c 00 00 00 改成 FA FF FF FF(注意是小端序)。
(gdb) set {byte}0x459d49=0xff
(gdb) set {byte}0x459d48=0xff
(gdb) set {byte}0x459d47=0xff
(gdb) set {byte}0x459d46=0xfa
改完后的结果:
(gdb) disassemble /mr main.assign
Dump of assembler code for function main.assign:
25 func assign() {
0x0000000000459d40 <+0>: 49 3b 66 10 cmp 0x10(%r14),%rsp
0x0000000000459d44 <+4>: 0f 86 fa ff ff ff jbe 0x459d44 <main.assign+4> // jbe自己跳转到自己,死循环
0x0000000000459d4a <+10>: 48 83 ec 18 sub $0x18,%rsp
0x0000000000459d4e <+14>: 48 89 6c 24 10 mov %rbp,0x10(%rsp)
0x0000000000459d53 <+19>: 48 8d 6c 24 10 lea 0x10(%rsp),%rbp
0x0000000000459dd6 <+150>: 44 9d rex.R popfq
0x0000000000459dd8 <+152>: 45 00 ff add %r15b,%r15b
0x0000000000459ddb <+155>: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0x0000000000459de0 <+160>: e9 5b ff ff ff jmpq 0x459d40 <main.assign>