目录
记一次Linux简单的逆向工程实验
逆向工程,通过反汇编、反编译和动态跟踪等方法,分析出其动画效果的实现过程,这种行为就是逆向工程。但逆向工程不仅仅是反编译,而且还要推倒出设计,并且文档化,逆向别人的软件时要遵循相关的条约,逆向软件工程的目的是使软件得以维护。
我的实验环境
系统:Ubuntu 16.04 STL (4.15.0-88-generic)
CPU架构:x86_64
工具:Vim 7.4、Gedit 3.18.3、GCC (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0、Objdump (GNU Binutils for Ubuntu) 2.26.1
0x0 准备工作
因为是实验,所以我写了一个名为"test"的简单C程序来作为本次实验的逆向目标。
#include <stdlib.h>
int main(int argc, char* argv[])
{
int a = 1;
a += 1;
while (1) {
if (a > 1) {
a -= 1;
}
else {
break;
}
}
exit(0);
}
之后我们编译运行一次。
编译和运行没有任何问题,之后我们使用 objdump -d test > test_rev.s 来反汇编得到 test 的反汇编代码,并将它重定向到一个名为"test_rev.s"的文件里,再用 gedit 将它打开,打开后我们得到如下内容,这就是 test 的反汇编代码了(我这里使用的是AT&T的汇编格式,如果想看Intel的汇编格式可以在使用objdump的时候加上 -M intel 就可以看到Intel格式的汇编了)。
test: file format elf64-x86-64
Disassembly of section .init:
00000000004003c8 <_init>:
4003c8: 48 83 ec 08 sub $0x8,%rsp
4003cc: 48 8b 05 25 0c 20 00 mov 0x200c25(%rip),%rax # 600ff8 <_DYNAMIC+0x1d0>
4003d3: 48 85 c0 test %rax,%rax
4003d6: 74 05 je 4003dd <_init+0x15>
4003d8: e8 43 00 00 00 callq 400420 <exit@plt+0x10>
4003dd: 48 83 c4 08 add $0x8,%rsp
4003e1: c3 retq
Disassembly of section .plt:
00000000004003f0 <__libc_start_main@plt-0x10>:
4003f0: ff 35 12 0c 20 00 pushq 0x200c12(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
4003f6: ff 25 14 0c 20 00 jmpq *0x200c14(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
4003fc: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400400 <__libc_start_main@plt>:
400400: ff 25 12 0c 20 00 jmpq *0x200c12(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
400406: 68 00 00 00 00 pushq $0x0
40040b: e9 e0 ff ff ff jmpq 4003f0 <_init+0x28>
0000000000400410 <exit@plt>:
400410: ff 25 0a 0c 20 00 jmpq *0x200c0a(%rip) # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
400416: 68 01 00 00 00 pushq $0x1
40041b: e9 d0 ff ff ff jmpq 4003f0 <_init+0x28>
Disassembly of section .plt.got:
0000000000400420 <.plt.got>:
400420: ff 25 d2 0b 20 00 jmpq *0x200bd2(%rip) # 600ff8 <_DYNAMIC+0x1d0>
400426: 66 90 xchg %ax,%ax
Disassembly of section .text:
0000000000400430 <_start>:
400430: 31 ed xor %ebp,%ebp
400432: 49 89 d1 mov %rdx,%r9
400435: 5e pop %rsi
400436: 48 89 e2 mov %rsp,%rdx
400439: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40043d: 50 push %rax
40043e: 54 push %rsp
40043f: 49 c7 c0 d0 05 40 00 mov $0x4005d0,%r8
400446: 48 c7 c1 60 05 40 00 mov $0x400560,%rcx
40044d: 48 c7 c7 26 05 40 00 mov $0x400526,%rdi
400454: e8 a7 ff ff ff callq 400400 <__libc_start_main@plt>
400459: f4 hlt
40045a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
0000000000400460 <deregister_tm_clones>:
400460: b8 3f 10 60 00 mov $0x60103f,%eax
400465: 55 push %rbp
400466: 48 2d 38 10 60 00 sub $0x601038,%rax
40046c: 48 83 f8 0e cmp $0xe,%rax
400470: 48 89 e5 mov %rsp,%rbp
400473: 76 1b jbe 400490 <deregister_tm_clones+0x30>
400475: b8 00 00 00 00 mov $0x0,%eax
40047a: 48 85 c0 test %rax,%rax
40047d: 74 11 je 400490 <deregister_tm_clones+0x30>
40047f: 5d pop %rbp
400480: bf 38 10 60 00 mov $0x601038,%edi
400485: ff e0 jmpq *%rax
400487: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40048e: 00 00
400490: 5d pop %rbp
400491: c3 retq
400492: 0f 1f 40 00 nopl 0x0(%rax)
400496: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40049d: 00 00 00
00000000004004a0 <register_tm_clones>:
4004a0: be 38 10 60 00 mov $0x601038,%esi
4004a5: 55 push %rbp
4004a6: 48 81 ee 38 10 60 00 sub $0x601038,%rsi
4004ad: 48 c1 fe 03 sar $0x3,%rsi
4004b1: 48 89 e5 mov %rsp,%rbp
4004b4: 48 89 f0 mov %rsi,%rax
4004b7: 48 c1 e8 3f shr $0x3f,%rax
4004bb: 48 01 c6 add %rax,%rsi
4004be: 48 d1 fe sar %rsi
4004c1: 74 15 je 4004d8 <register_tm_clones+0x38>
4004c3: b8 00 00 00 00 mov $0x0,%eax
4004c8: 48 85 c0 test %rax,%rax
4004cb: 74 0b je 4004d8 <register_tm_clones+0x38>
4004cd: 5d pop %rbp
4004ce: bf 38 10 60 00 mov $0x601038,%edi
4004d3: ff e0 jmpq *%rax
4004d5: 0f 1f 00 nopl (%rax)
4004d8: 5d pop %rbp
4004d9: c3 retq
4004da: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
00000000004004e0 <__do_global_dtors_aux>:
4004e0: 80 3d 51 0b 20 00 00 cmpb $0x0,0x200b51(%rip) # 601038 <__TMC_END__>
4004e7: 75 11 jne 4004fa <__do_global_dtors_aux+0x1a>
4004e9: 55 push %rbp
4004ea: 48 89 e5 mov %rsp,%rbp
4004ed: e8 6e ff ff ff callq 400460 <deregister_tm_clones>
4004f2: 5d pop %rbp
4004f3: c6 05 3e 0b 20 00 01 movb $0x1,0x200b3e(%rip) # 601038 <__TMC_END__>
4004fa: f3 c3 repz retq
4004fc: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400500 <frame_dummy>:
400500: bf 20 0e 60 00 mov $0x600e20,%edi
400505: 48 83 3f 00 cmpq $0x0,(%rdi)
400509: 75 05 jne 400510 <frame_dummy+0x10>
40050b: eb 93 jmp 4004a0 <register_tm_clones>
40050d: 0f 1f 00 nopl (%rax)
400510: b8 00 00 00 00 mov $0x0,%eax
400515: 48 85 c0 test %rax,%rax
400518: 74 f1 je 40050b <frame_dummy+0xb>
40051a: 55 push %rbp
40051b: 48 89 e5 mov %rsp,%rbp
40051e: ff d0 callq *%rax
400520: 5d pop %rbp
400521: e9 7a ff ff ff jmpq 4004a0 <register_tm_clones>
0000000000400526 <main>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: 48 83 ec 20 sub $0x20,%rsp
40052e: 89 7d ec mov %edi,-0x14(%rbp)
400531: 48 89 75 e0 mov %rsi,-0x20(%rbp)
400535: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp)
40053c: 83 45 fc 01 addl $0x1,-0x4(%rbp)
400540: 83 7d fc 01 cmpl $0x1,-0x4(%rbp)
400544: 7e 06 jle 40054c <main+0x26>
400546: 83 6d fc 01 subl $0x1,-0x4(%rbp)
40054a: eb f4 jmp 400540 <main+0x1a>
40054c: 90 nop
40054d: bf 00 00 00 00 mov $0x0,%edi
400552: e8 b9 fe ff ff callq 400410 <exit@plt>
400557: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40055e: 00 00
0000000000400560 <__libc_csu_init>:
400560: 41 57 push %r15
400562: 41 56 push %r14
400564: 41 89 ff mov %edi,%r15d
400567: 41 55 push %r13
400569: 41 54 push %r12
40056b: 4c 8d 25 9e 08 20 00 lea 0x20089e(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>
400572: 55 push %rbp
400573: 48 8d 2d 9e 08 20 00 lea 0x20089e(%rip),%rbp # 600e18 <__init_array_end>
40057a: 53 push %rbx
40057b: 49 89 f6 mov %rsi,%r14
40057e: 49 89 d5 mov %rdx,%r13
400581: 4c 29 e5 sub %r12,%rbp
400584: 48 83 ec 08 sub $0x8,%rsp
400588: 48 c1 fd 03 sar $0x3,%rbp
40058c: e8 37 fe ff ff callq 4003c8 <_init>
400591: 48 85 ed test %rbp,%rbp
400594: 74 20 je 4005b6 <__libc_csu_init+0x56>
400596: 31 db xor %ebx,%ebx
400598: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40059f: 00
4005a0: 4c 89 ea mov %r13,%rdx
4005a3: 4c 89 f6 mov %r14,%rsi
4005a6: 44 89 ff mov %r15d,%edi
4005a9: 41 ff 14 dc callq *(%r12,%rbx,8)
4005ad: 48 83 c3 01 add $0x1,%rbx
4005b1: 48 39 eb cmp %rbp,%rbx
4005b4: 75 ea jne 4005a0 <__libc_csu_init+0x40>
4005b6: 48 83 c4 08 add $0x8,%rsp
4005ba: 5b pop %rbx
4005bb: 5d pop %rbp
4005bc: 41 5c pop %r12
4005be: 41 5d pop %r13
4005c0: 41 5e pop %r14
4005c2: 41 5f pop %r15
4005c4: c3 retq
4005c5: 90 nop
4005c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4005cd: 00 00 00
00000000004005d0 <__libc_csu_fini>:
4005d0: f3 c3 repz retq
Disassembly of section .fini:
00000000004005d4 <_fini>:
4005d4: 48 83 ec 08 sub $0x8,%rsp
4005d8: 48 83 c4 08 add $0x8,%rsp
4005dc: c3 retq
可以看到反汇编代码里面有很多的与初始化、操作系统相关的代码段,每个段的含义我就不在这里讲了,我们直接找 test 程序的 main 函数,因为这个实验程序很简单,所以我们忽略一些初始化相关的段之后,剩下的就是程序里编写的函数代码了,我们看到在地址 400526 的地方出现了名为 main 的段名,那么这就是这个实验程序的入口 main 函数了(真实情况下的逆向不会这么简单的,但因为是实验,所以这个程序写的很简单!),看到它的反汇编代码后,我们就要开始分析它的反汇编代码了。
0x1 开始逆向工程
如前所诉,我们截取了 400526 地址位置的 main 函数段里的汇编代码,得到如下。
0000000000400526 <main>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: 48 83 ec 20 sub $0x20,%rsp
40052e: 89 7d ec mov %edi,-0x14(%rbp)
400531: 48 89 75 e0 mov %rsi,-0x20(%rbp)
400535: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp)
40053c: 83 45 fc 01 addl $0x1,-0x4(%rbp)
400540: 83 7d fc 01 cmpl $0x1,-0x4(%rbp)
400544: 7e 06 jle 40054c <main+0x26>
400546: 83 6d fc 01 subl $0x1,-0x4(%rbp)
40054a: eb f4 jmp 400540 <main+0x1a>
40054c: 90 nop
40054d: bf 00 00 00 00 mov $0x0,%edi
400552: e8 b9 fe ff ff callq 400410 <exit@plt>
400557: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40055e: 00 00
首先从第一条指令开始看。。。我们可以看到它先把 rbp 的值 push 入栈,然后将 rsp 的值传给 rbp (Intel和AT&T格式有一些不同,AT&T格式的源操作数和目标操作数与Intel是相反的),然后将栈顶向下减少 0x20 个字节,之后将 di 和 si 的值分别存入栈中,然后我们看到地址 400535 的这个位置将一个值 1 存入了栈中,并且栈向下减少了 0x4 个字节,由此可以推断这里应该是给一个变量赋值位 1,并且因为栈向下减小了 0x4 个字节,所以我们可以推断这个变量的类型应该是一个 4字节 类型的变量,在这里我们给他假定一个名字 "a",然后再看 40053c 这个地址执行了 add 指令,这里 add 指令让变量 a 增加了 1,再看 400540 这个地址他执行了 cmp 指令,这说明代码里应该有一个 if,来用变量 a 对比 1,具体对比条件我们要看下一条 jle 指令,从 jle 指令我们就能知道这个 cmp 的对比条件是小于等于,也就是说 a 是否小于等于 1,反过来说就是 1 是否大于 a,而且我们还从 jle 这个指令的功能码中知道这个变量 a 是一个有符号数,如果条件,成立,那么变量 a 将会减 1,然后 jmp 无条件跳转到 400540 这个地址,也就是说再执行这个地址的 cmp 指令,那么可以推断程序代码里应该有一个循环,而且 if 是嵌在循环里的,而且这个循环没有直接的循环条件,那我们可以推断出它应该是一个无限循环,退出循环必须依靠 if 来退出,那么我们可以推断出 if 有 else,并且这个 else 里面应该有一个 break 用来跳出循环,然后的 40054d-400552 这两个地址位置显而易见是调用了 exit 系统调用,而这个时候我们也可以从 40054d 这个地址的 mov 指令的目标操作数 edi 得出返回值类型应该是 32位 的数,而这个数正是 0,至此我们的分析就完成了,但是这个也仅仅是一次很简单的实验,并不能代表什么,真正的逆向工程比这个要复杂得多得多!那么综上所述,我们可以写一个函数来把我们分析出来的代码写出来。
// 因为有exit系统调用的原因,
// 所以我们要包含stdlib.h这个头文件。
#include <stdlib.h>
// 刚刚我们分析了返回值的类型是32位的,一般情况下
// 在C语言当中int类型是32位的,所以返回值自然是int
// 函数名我们随便取一个,我们再代码当中并没有见到将
// 寄存器直接拿来用或者是pop指令,所以这个函数应该没有使用形参
int test(void)
{
// 用我们刚刚假定的变量a
// 我们也推断出它的类型应该是32位的,所以我们也使用int
// 而且它有一个赋值的操作
int a = 1;
// 有一个加1的操作
a++;
// 有一个无限循环,并且循环里嵌入了一个if
while (1) {
// if判断a是否小于等于1
if (a <= 1) {
// a小于等于1的话退出循环
break;
}
else {
// 否则a减1
a--;
}
}
// 有exit系统调用,参数是0
exit(0);
}
0x2 结尾
做这个实验的目的是为了巩固我自己所学的知识和帮助一些初学者认识一下到底什么是"逆向工程",由于小弟我实力不佳,如有错误,还请各位大佬尽情评论或者私信我!我的邮箱veidongray@qq.com。至此感谢你的阅读!