最近在刷题的时候遇到了很多elf文件,虽然可以通过ida分析伪代码解出来,但是发现有些通过动态调试的方式可以直接找到flag,这样简单了不少,因为之前接触的linux下的逆向题目比较少,所以通过这次刷题也记录一下动态调试elf文件的几种方式。
0x01 ida动态调试
ida不光可以静态分析函数伪代码,也可以通过动态调试的方式来分析linux下的elf文件。
首先将ida/dbgsrv/路径下的linux_server/linux_serverx64文件复制到linux下,两个文件分别是调试32位和64位程序使用的:
在linux下启动对应的文件:
把我们要调试的文件放到相应的文件夹中,这里我放到/homt/test/文件夹中。
在ida中选择Debugger-Run-Remote linux debugger
在弹出的对话框中,Application填写文件存放的位置和文件名,Directory中填写文件存放的路径,Parameters是指传递的参数,如果程序运行需要传参的话可以在这填入,Hostname就是linux的ip地址,Port一般都是默认的23946,如果设置了password在下方填入,没有就空着:
进入ida动态调试界面,默认情况下界面大概分为6个区域(通用寄存器和标志寄存器窗口为1个),模块列表和线程列表我不怎么看,主要是看反汇编和寄存器还有堆栈。
ida在动态调试的时候和OD差不多,F9继续运行,F7步入,F8步过,F2下断点。 通过Debugger-Breakpoints-Breakpoint list或者Ctrl+Alt+B来查看已经下的断点:
也可以通过Debugger-Debugger windows-Stack trace或者Ctrl+Alt+S查看是谁调用了当前的函数:
虽然感觉没有OD好用,但是实现简单的调试还是没有问题的。
0x02 gdb动态调试
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。
一般在kali中,gdb都是默认安装的。 使用gdb调试可执行文件
`gdb <program> //启动`
`quit //结束`
运行程序
`run:简写为r,运行调试程序,直到断点处`
`stepi:简写为s,执行一条指令,步入函数`
`nexti:简写为n,执行一条指令,步过函数`
`continue:简写为c,继续执行`
`until:在一个循环体内部单步跟踪的时候,可以通过until命令跳出循环`
`until+行号:运行至某行`
断点
`break *address:简写b *address,设置断点`
`info breakpoints:简写为info b,查看断点`
`delete *address:删除断点`
`disable *address:暂停断点`
`enable *address:开启断点`
`delete breakpoints:删除所有断点`
查看
`info register:简写为info r,查看寄存器`
`x/countFormatSize addr:以指定格式Format打印地址address处的指定大小size、指定数量count的对象`
`Size:b(字节)、h(半字)、w(字)、g(8字节)`
`Format:o(八进制)、d(十进制)、x(十六进制)、u(无符号十进制)、t(二进制)、f(浮点数)、a(地 址)、i(指令)、c(字符)、s(字符串)`
`backtrace:打印函数调用栈回溯`
`set $reg=value:修改寄存器`
`set *(type*)(address)=value:修改内存地址addr为value`
我在使用的过程中大概用到的就是这些命令。
0x03 radare2动态调试
radare2是一个开源项目,他可以使用命令直接执行,也有GUI程序叫做Cutter并且兼容多平台。
在kali中r2也是默认安装的,可以直接在控制台运行,进入程序后使用aaa命令分析程序
`$ r2 ./e3dd9674429f4ce1a25c08ea799fc027`
`[0x00400660]> aaa`
`[x] Analyze all flags starting with sym. and entry0 (aa)`
`[x] Analyze function calls (aac)`
`[x] Analyze len bytes of instructions for references (aar)`
`[x] Check for vtables`
`[x] Type matching analysis for all functions (aaft)`
`[x] Propagate noreturn information`
`[x] Use -AA or aaaa to perform additional experimental analysis.`
也可以直接使用r2 -A ./program
来直接分析程序
`$ r2 -A ./e3dd9674429f4ce1a25c08ea799fc027`
`[x] Analyze all flags starting with sym. and entry0 (aa)`
`[x] Analyze function calls (aac)`
`[x] Analyze len bytes of instructions for references (aar)`
`[x] Check for vtables`
`[x] Type matching analysis for all functions (aaft)`
`[x] Propagate noreturn information`
`[x] Use -AA or aaaa to perform additional experimental analysis.`
在分析程序之前,r2的iI
命令可以提供很多有用的信息
`[0x00400660]> iI`
`arch x86`
`baddr 0x400000`
`binsz 9449`
`bintype elf`
`bits 64`
`canary true`
`class ELF64`
`compiler GCC: (Ubuntu 5.4.1-2ubuntu1~14.04) 5.4.1 20160904 GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4`
`crypto false`
`endian little`
`havecode true`
`intrp /lib64/ld-linux-x86-64.so.2`
`laddr 0x0`
`lang c`
`linenum true`
`lsyms true`
`machine AMD x86-64 architecture`
`maxopsz 16`
`minopsz 1`
`nx true`
`os linux`
`pcalign 0`
`pic false`
`relocs true`
`relro partial`
`rpath NONE`
`sanitiz false`
`static false`
`stripped false`
`subsys linux`
`va true`
在了解程序的基本信息后你还可以通过ii
和iE
命令来看下程序的导入和导出函数,从而能够更好的了解程序实现的功能
`[0x00400660]> ii`
`[Imports]`
`nth vaddr bind type lib name`
`―――――――――――――――――――――――――――――――――――――`
`1 0x004005c0 GLOBAL FUNC remove`
`2 0x004005d0 GLOBAL FUNC fclose`
`3 0x004005e0 GLOBAL FUNC strlen`
`4 0x004005f0 GLOBAL FUNC __stack_chk_fail`
`5 0x00400600 GLOBAL FUNC fputc`
`6 0x00400610 GLOBAL FUNC __libc_start_main`
`7 0x00400620 GLOBAL FUNC fprintf`
`8 0x00400630 WEAK NOTYPE __gmon_start__`
`9 0x00400640 GLOBAL FUNC fseek`
`10 0x00400650 GLOBAL FUNC fopen`
`[0x00400660]> iE`
`[Exports]`
`nth paddr vaddr bind type size lib name`
`――――――――――――――――――――――――――――――――――――――――――――――――――――――`
`45 0x00000960 0x00400960 GLOBAL FUNC 2 __libc_csu_fini`
`49 0x000010e0 0x006010e0 GLOBAL OBJ 44 t`
`50 ---------- 0x0060120c GLOBAL NOTYPE 0 _edata`
`51 0x00001160 0x00601160 GLOBAL OBJ 172 p`
`53 0x00000964 0x00400964 GLOBAL FUNC 0 _fini`
`58 0x00001080 0x00601080 GLOBAL NOTYPE 0 __data_start`
`61 0x00001088 0x00601088 GLOBAL OBJ 0 __dso_handle`
`62 0x00000970 0x00400970 GLOBAL OBJ 4 _IO_stdin_used`
`63 0x000008f0 0x004008f0 GLOBAL FUNC 101 __libc_csu_init`
`64 ---------- 0x00601210 GLOBAL NOTYPE 0 _end`
`65 0x00000660 0x00400660 GLOBAL FUNC 0 _start`
`67 0x000010a0 0x006010a0 GLOBAL OBJ 33 s`
`68 0x00001120 0x00601120 GLOBAL OBJ 44 u`
`69 ---------- 0x0060120c GLOBAL NOTYPE 0 __bss_start`
`70 0x00000756 0x00400756 GLOBAL FUNC 407 main`
`73 ---------- 0x00601210 GLOBAL OBJ 0 __TMC_END__`
`75 0x00000590 0x00400590 GLOBAL FUNC 0 _init`
你还可以通过afl
命令查看程序存在的函数,从而找到main函数,定位到对应的位置,查看反汇编代码
`[0x00400660]> afl`
`0x00400660 1 41 entry0`
`0x00400610 1 6 sym.imp.__libc_start_main`
`0x00400690 4 50 -> 41 sym.deregister_tm_clones`
`0x004006d0 4 58 -> 55 sym.register_tm_clones`
`0x00400710 3 28 sym.__do_global_dtors_aux`
`0x00400730 4 38 -> 35 entry.init0`
`0x00400960 1 2 sym.__libc_csu_fini`
`0x00400964 1 9 sym._fini`
`0x004008f0 4 101 sym.__libc_csu_init`
`0x00400756 12 407 main`
`0x00400590 3 26 sym._init`
`0x00400630 1 6 loc.imp.__gmon_start__`
`0x004005c0 1 6 sym.imp.remove`
`0x004005d0 1 6 sym.imp.fclose`
`0x004005e0 1 6 sym.imp.strlen`
`0x004005f0 1 6 sym.imp.__stack_chk_fail`
`0x00400600 1 6 sym.imp.fputc`
`0x00400620 1 6 sym.imp.fprintf`
`0x00400640 1 6 sym.imp.fseek`
`0x00400650 1 6 sym.imp.fopen`
在找到程序内存在的函数后,可以使用axt
命令来查看函数调用
`[0x00400660]> axt main`
`entry0 0x40067d [DATA] mov rdi, main`
在找到main函数后,使用s
命令定位到main函数,pdf
命令来查看函数的反汇编代码,s
命令后可以加地址或者函数名
`[0x00400660]> s main`
`[0x00400756]> pdf`
`; DATA XREF from entry0 @ 0x40067d`
`┌ 407: int main (int argc, char **argv, char **envp);`
`│ ; var int64_t var_40h @ rbp-0x40`
`│ ; var int64_t var_3ch @ rbp-0x3c`
`│ ; var file*stream @ rbp-0x38`
`│ ; var char *filename @ rbp-0x30`
`│ ; var int64_t var_28h @ rbp-0x28`
`│ ; var int64_t var_24h @ rbp-0x24`
`│ ; var int64_t canary @ rbp-0x18`
`│ 0x00400756 55 push rbp`
`│ 0x00400757 4889e5 mov rbp, rsp`
`│ 0x0040075a 53 push rbx`
`│ 0x0040075b 4883ec38 sub rsp, 0x38`
`│ 0x0040075f 64488b042528. mov rax, qword fs:[0x28]`
`│ 0x00400768 488945e8 mov qword [canary], rax`
`│ 0x0040076c 31c0 xor eax, eax`
`│ 0x0040076e c745c0000000. mov dword [var_40h], 0`
`│ ; CODE XREF from main @ 0x4007c5`
`│ ┌─> 0x00400775 8b45c0 mov eax, dword [var_40h]`
`│ ╎ 0x00400778 4863d8 movsxd rbx, eax`
`│ ╎ 0x0040077b bfa0106000 mov edi, obj.s ; 0x6010a0 ; "c61b68366edeb7bdce3c6820314b7498" ; const char *s`
`│ ╎ 0x00400780 e85bfeffff call sym.imp.strlen ; size_t strlen(const char *s)`
`│ ╎ 0x00400785 4839c3 cmp rbx, rax`
`│ ┌──< 0x00400788 733d jae 0x4007c7`
`│ │╎ 0x0040078a 8b45c0 mov eax, dword [var_40h]`
`│ │╎ 0x0040078d 8d500a lea edx, [rax + 0xa]`
`│ │╎ 0x00400790 8b45c0 mov eax, dword [var_40h]`
`│ │╎ 0x00400793 4898 cdqe`
`│ │╎ 0x00400795 0fb680a01060. movzx eax, byte [rax + obj.s] ; [0x6010a0:1]=99 ; "c61b68366edeb7bdce3c6820314b7498"`
`│ │╎ 0x0040079c 89c1 mov ecx, eax`
`│ │╎ 0x0040079e 8b45c0 mov eax, dword [var_40h]`
`│ │╎ 0x004007a1 83e001 and eax, 1`
`│ │╎ 0x004007a4 85c0 test eax, eax`
`│ ┌───< 0x004007a6 7407 je 0x4007af`
`│ ││╎ 0x004007a8 b801000000 mov eax, 1`
`│ ┌────< 0x004007ad eb05 jmp 0x4007b4`
`│ │││╎ ; CODE XREF from main @ 0x4007a6`
`│ │└───> 0x004007af b8ffffffff mov eax, 0xffffffff ; -1`
`│ │ │╎ ; CODE XREF from main @ 0x4007ad`
`│ └────> 0x004007b4 01c8 add eax, ecx`
`│ │╎ 0x004007b6 89c1 mov ecx, eax`
`│ │╎ 0x004007b8 4863c2 movsxd rax, edx`
`│ │╎ 0x004007bb 8888e0106000 mov byte [rax + obj.t], cl ; [0x6010e0:1]=83 ; "SharifCTF{????????????????????????????????}"`
`│ │╎ 0x004007c1 8345c001 add dword [var_40h], 1`
`│ │└─< 0x004007c5 ebae jmp 0x400775`
`│ │ ; CODE XREF from main @ 0x400788`
`│ └──> 0x004007c7 48b82f746d70. movabs rax, 0x616c662f706d742f ; '/tmp/fla'`
`│ 0x004007d1 488945d0 mov qword [filename], rax`
`│ 0x004007d5 c745d8672e74. mov dword [var_28h], 0x78742e67 ; 'g.tx'`
`│ 0x004007dc 66c745dc7400 mov word [var_24h], 0x74 ; 't' ; 116`
`│ 0x004007e2 488d45d0 lea rax, [filename]`
`│ 0x004007e6 be74094000 mov esi, 0x400974 ; const char *mode`
`│ 0x004007eb 4889c7 mov rdi, rax ; const char *filename`
`│ 0x004007ee e85dfeffff call sym.imp.fopen ; file*fopen(const char *filename, const char *mode)`
`│ 0x004007f3 488945c8 mov qword [stream], rax`
`│ 0x004007f7 488b45c8 mov rax, qword [stream]`
`│ 0x004007fb ba20116000 mov edx, obj.u ; 0x601120 ; "*******************************************" ; ...`
`│ 0x00400800 be76094000 mov esi, 0x400976 ; const char *format`
`│ 0x00400805 4889c7 mov rdi, rax ; FILE *stream`
`│ 0x00400808 b800000000 mov eax, 0`
`│ 0x0040080d e80efeffff call sym.imp.fprintf ; int fprintf(FILE *stream, const char *format, ...)`
`│ 0x00400812 c745c4000000. mov dword [var_3ch], 0`
`│ ; CODE XREF from main @ 0x4008b0`
`│ ┌─> 0x00400819 8b45c4 mov eax, dword [var_3ch]`
`│ ╎ 0x0040081c 4863d8 movsxd rbx, eax`
`│ ╎ 0x0040081f bfe0106000 mov edi, obj.t ; 0x6010e0 ; "SharifCTF{????????????????????????????????}" ; const char *s`
`│ ╎ 0x00400824 e8b7fdffff call sym.imp.strlen ; size_t strlen(const char *s)`
`│ ╎ 0x00400829 4839c3 cmp rbx, rax`
`│ ┌──< 0x0040082c 0f8383000000 jae 0x4008b5`
`│ │╎ 0x00400832 8b45c4 mov eax, dword [var_3ch]`
`│ │╎ 0x00400835 4898 cdqe`
`│ │╎ 0x00400837 8b0485601160. mov eax, dword [rax*4 + obj.p] ; [0x601160:4]=30`
`│ │╎ 0x0040083e 4863c8 movsxd rcx, eax`
`│ │╎ 0x00400841 488b45c8 mov rax, qword [stream]`
`│ │╎ 0x00400845 ba00000000 mov edx, 0 ; int whence`
`│ │╎ 0x0040084a 4889ce mov rsi, rcx ; long offset`
`│ │╎ 0x0040084d 4889c7 mov rdi, rax ; FILE *stream`
`│ │╎ 0x00400850 e8ebfdffff call sym.imp.fseek ; int fseek(FILE *stream, long offset, int whence)`
`│ │╎ 0x00400855 8b45c4 mov eax, dword [var_3ch]`
`│ │╎ 0x00400858 4898 cdqe`
`│ │╎ 0x0040085a 8b0485601160. mov eax, dword [rax*4 + obj.p] ; [0x601160:4]=30`
`│ │╎ 0x00400861 4898 cdqe`
`│ │╎ 0x00400863 0fb680e01060. movzx eax, byte [rax + obj.t] ; [0x6010e0:1]=83 ; "SharifCTF{????????????????????????????????}"`
`│ │╎ 0x0040086a 0fbec0 movsx eax, al`
`│ │╎ 0x0040086d 488b55c8 mov rdx, qword [stream]`
`│ │╎ 0x00400871 4889d6 mov rsi, rdx ; FILE *stream`
`│ │╎ 0x00400874 89c7 mov edi, eax ; int c`
`│ │╎ 0x00400876 e885fdffff call sym.imp.fputc ; int fputc(int c, FILE *stream)`
`│ │╎ 0x0040087b 488b45c8 mov rax, qword [stream]`
`│ │╎ 0x0040087f ba00000000 mov edx, 0 ; int whence`
`│ │╎ 0x00400884 be00000000 mov esi, 0 ; long offset`
`│ │╎ 0x00400889 4889c7 mov rdi, rax ; FILE *stream`
`│ │╎ 0x0040088c e8affdffff call sym.imp.fseek ; int fseek(FILE *stream, long offset, int whence)`
`│ │╎ 0x00400891 488b45c8 mov rax, qword [stream]`
`│ │╎ 0x00400895 ba20116000 mov edx, obj.u ; 0x601120 ; "*******************************************" ; ...`
`│ │╎ 0x0040089a be76094000 mov esi, 0x400976 ; const char *format`
`│ │╎ 0x0040089f 4889c7 mov rdi, rax ; FILE *stream`
`│ │╎ 0x004008a2 b800000000 mov eax, 0`
`│ │╎ 0x004008a7 e874fdffff call sym.imp.fprintf ; int fprintf(FILE *stream, const char *format, ...)`
`│ │╎ 0x004008ac 8345c401 add dword [var_3ch], 1`
`│ │└─< 0x004008b0 e964ffffff jmp 0x400819`
`│ │ ; CODE XREF from main @ 0x40082c`
`│ └──> 0x004008b5 488b45c8 mov rax, qword [stream]`
`│ 0x004008b9 4889c7 mov rdi, rax ; FILE *stream`
`│ 0x004008bc e80ffdffff call sym.imp.fclose ; int fclose(FILE *stream)`
`│ 0x004008c1 488d45d0 lea rax, [filename]`
`│ 0x004008c5 4889c7 mov rdi, rax ; const char *filename`
`│ 0x004008c8 e8f3fcffff call sym.imp.remove ; int remove(const char *filename)`
`│ 0x004008cd b800000000 mov eax, 0`
`│ 0x004008d2 488b5de8 mov rbx, qword [canary]`
`│ 0x004008d6 6448331c2528. xor rbx, qword fs:[0x28]`
`│ ┌─< 0x004008df 7405 je 0x4008e6`
`│ │ 0x004008e1 e80afdffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)`
`│ │ ; CODE XREF from main @ 0x4008df`
`│ └─> 0x004008e6 4883c438 add rsp, 0x38`
`│ 0x004008ea 5b pop rbx`
`│ 0x004008eb 5d pop rbp`
`└ 0x004008ec c3 ret`
如果觉得看反汇编不过瘾也可以通过px
命令查看十六进制内容
`[0x00400756]> px`
`- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF`
`0x00400756 5548 89e5 5348 83ec 3864 488b 0425 2800 UH..SH..8dH..%(.`
`0x00400766 0000 4889 45e8 31c0 c745 c000 0000 008b ..H.E.1..E......`
`0x00400776 45c0 4863 d8bf a010 6000 e85b feff ff48 E.Hc....`..[...H`
`0x00400786 39c3 733d 8b45 c08d 500a 8b45 c048 980f 9.s=.E..P..E.H..`
`0x00400796 b680 a010 6000 89c1 8b45 c083 e001 85c0 ....`....E......`
`0x004007a6 7407 b801 0000 00eb 05b8 ffff ffff 01c8 t...............`
`0x004007b6 89c1 4863 c288 88e0 1060 0083 45c0 01eb ..Hc.....`..E...`
`0x004007c6 ae48 b82f 746d 702f 666c 6148 8945 d0c7 .H./tmp/flaH.E..`
`0x004007d6 45d8 672e 7478 66c7 45dc 7400 488d 45d0 E.g.txf.E.t.H.E.`
`0x004007e6 be74 0940 0048 89c7 e85d feff ff48 8945 .t.@.H...]...H.E`
`0x004007f6 c848 8b45 c8ba 2011 6000 be76 0940 0048 .H.E.. .`..v.@.H`
`0x00400806 89c7 b800 0000 00e8 0efe ffff c745 c400 .............E..`
`0x00400816 0000 008b 45c4 4863 d8bf e010 6000 e8b7 ....E.Hc....`...`
`0x00400826 fdff ff48 39c3 0f83 8300 0000 8b45 c448 ...H9........E.H`
`0x00400836 988b 0485 6011 6000 4863 c848 8b45 c8ba ....`.`.Hc.H.E..`
`0x00400846 0000 0000 4889 ce48 89c7 e8eb fdff ff8b ....H..H........`
同时,为了能够更好的理解程序的功能,iz
命令可以查看程序中的字符串
`[0x00400756]> iz`
`[Strings]`
`nth paddr vaddr len size section type string`
`―――――――――――――――――――――――――――――――――――――――――――――――――――――――`
`0 0x000010a0 0x006010a0 32 33 .data ascii c61b68366edeb7bdce3c6820314b7498`
`1 0x000010e0 0x006010e0 43 44 .data ascii SharifCTF{????????????????????????????????}`
`2 0x00001120 0x00601120 43 44 .data ascii *******************************************`
字符串和函数一样,可以使用axt命令查看是哪里把它打印出来的
`[0x00400756]> axt 0x6010a0`
`main 0x40077b [DATA] mov edi, obj.s`
`main 0x400795 [DATA] movzx eax, byte [rax + obj.s]`
r2还提供了可视模式,可以更直观的分析函数的调用,vv
命令进入
以上是使用r2静态分析二进制文件,然后r2的功能不止于此,它还可以动态调试二进制文件甚至给我函数的伪代码
使用r2 -d -A ./program
命令来进入调试器
`$ r2 -d -A ./e3dd9674429f4ce1a25c08ea799fc027`
`[x] Analyze all flags starting with sym. and entry0 (aa)`
`[x] Analyze function calls (aac)`
`[x] Analyze len bytes of instructions for references (aar)`
`[x] Finding and parsing C++ vtables (avrr)`
`[x] Skipping type matching analysis in debugger mode (aaft)`
`[x] Propagate noreturn information (aanr)`
`[x] Use -AA or aaaa to perform additional experimental analysis.`
`[0x7f8186b18050]>`
动态调试的主要命令
`db:断点,db后可以跟函数名或者地址`
`dbi:查看已有断点`
`dc:运行程序`
`dbt:查看堆栈`
`dr:查看寄存器`
`$ r2 -d -A ./e3dd9674429f4ce1a25c08ea799fc027`
`[x] Analyze all flags starting with sym. and entry0 (aa)`
`[x] Analyze function calls (aac)`
`[x] Analyze len bytes of instructions for references (aar)`
`[x] Finding and parsing C++ vtables (avrr)`
`[x] Skipping type matching analysis in debugger mode (aaft)`
`[x] Propagate noreturn information (aanr)`
`[x] Use -AA or aaaa to perform additional experimental analysis.`
`[0x7f53c8464050]> db main`
`[0x7f53c8464050]> dbi`
`0 0x00400756 E:1 T:0`
`[0x7f53c8464050]> dc`
`hit breakpoint at: 0x400756`
`[0x00400756]> dbt`
`0 0x400756 sp: 0x0 0 [main] main entry.init0+38`
`[0x00400756]> dr`
`rax = 0x00400756`
`rbx = 0x004008f0`
`rcx = 0x7f53c8443738`
`rdx = 0x7ffcee042568`
`r8 = 0x00000000`
`r9 = 0x7f53c84731f0`
`r10 = 0xfffffffffffffb8c`
`r11 = 0x7f53c829c730`
`r12 = 0x00400660`
`r13 = 0x00000000`
`r14 = 0x00000000`
`r15 = 0x00000000`
`rsi = 0x7ffcee042558`
`rdi = 0x00000001`
`rsp = 0x7ffcee042468`
`rbp = 0x00000000`
`rip = 0x00400756`
`rflags = 0x00000246`
`orax = 0xffffffffffffffff`
同时,r2可以通过插件实现反编译功能,使用r2pm -l
命令查看当前插件,r2的插件有很多,这里使用r2dec反编译插件来实现对程序的反编译,r2pm -install r2dec
来安装,安装完成后可以直接在r2中使用-A加载程序,然后使用r2dec反汇编程序,反汇编的命令是pdda
`$ r2 -A ./e3dd9674429f4ce1a25c08ea799fc027`
`[x] Analyze all flags starting with sym. and entry0 (aa)`
`[x] Analyze function calls (aac)`
`[x] Analyze len bytes of instructions for references (aar)`
`[x] Check for vtables`
`[x] Type matching analysis for all functions (aaft)`
`[x] Propagate noreturn information`
`[x] Use -AA or aaaa to perform additional experimental analysis.`
`[0x00400660]> s main`
`[0x00400756]> pdda`
`; assembly | /* r2dec pseudo code output */`
`| /* ./e3dd9674429f4ce1a25c08ea799fc027 @ 0x400756 */`
`| #include <stdint.h>`
`|`
`; (fcn) main () | int32_t main (void) {`
`| int64_t var_40h;`
`| int64_t var_3ch;`
`| file* stream;`
`| char * filename;`
`| int64_t var_28h;`
`| int64_t var_24h;`
`| int64_t canary;`
`0x00400756 push rbp |`
`0x00400757 mov rbp, rsp |`
`0x0040075a push rbx |`
`0x0040075b sub rsp, 0x38 |`
`0x0040075f mov rax, qword fs:[0x28] | rax = *(fs:0x28);`
`0x00400768 mov qword [rbp - 0x18], rax | *((rbp - 0x18)) = rax;`
`0x0040076c xor eax, eax | eax = 0;`
`0x0040076e mov dword [rbp - 0x40], 0 | *((rbp - 0x40)) = 0;`
`| do {`
`0x00400775 mov eax, dword [rbp - 0x40] | eax = *((rbp - 0x40));`
`0x00400778 movsxd rbx, eax | rbx = (int64_t) eax;`
`0x0040077b mov edi, 0x6010a0 |`
`0x00400780 call 0x4005e0 | rax = strlen ("c61b68366edeb7bdce3c6820314b7498");`
`0x00400785 cmp rbx, rax |`
`| if (rbx >= rax) {`
`0x00400788 jae 0x4007c7 | goto label_0;`
`| }`
`0x0040078a mov eax, dword [rbp - 0x40] | eax = *((rbp - 0x40));`
`0x0040078d lea edx, [rax + 0xa] | edx = rax + 0xa;`
`0x00400790 mov eax, dword [rbp - 0x40] | eax = *((rbp - 0x40));`
`0x00400793 cdqe | rax = (int64_t) eax;`
`0x00400795 movzx eax, byte [rax + 0x6010a0] | eax = *((rax + obj.s));`
`0x0040079c mov ecx, eax | ecx = eax;`
`0x0040079e mov eax, dword [rbp - 0x40] | eax = *((rbp - 0x40));`
`0x004007a1 and eax, 1 | eax &= 1;`
`0x004007a4 test eax, eax |`
`| if (eax != 0) {`
`0x004007a6 je 0x4007af |`
`0x004007a8 mov eax, 1 | eax = 1;`
`0x004007ad jmp 0x4007b4 |`
`| } else {`
`0x004007af mov eax, 0xffffffff | eax = 0xffffffff;`
`| }`
`0x004007b4 add eax, ecx | eax += ecx;`
`0x004007b6 mov ecx, eax | ecx = eax;`
`0x004007b8 movsxd rax, edx | rax = (int64_t) edx;`
`0x004007bb mov byte [rax + 0x6010e0], cl | *((rax + obj.t)) = cl;`
`0x004007c1 add dword [rbp - 0x40], 1 | *((rbp - 0x40))++;`
`0x004007c5 jmp 0x400775 |`
`| } while (1);`
`| label_0:`
`0x004007c7 movabs rax, 0x616c662f706d742f | rax = 0x616c662f706d742f;`
`0x004007d1 mov qword [rbp - 0x30], rax | *((rbp - 0x30)) = rax;`
`0x004007d5 mov dword [rbp - 0x28], 0x78742e67 | *((rbp - 0x28)) = 0x78742e67;`
`0x004007dc mov word [rbp - 0x24], 0x74 | *((rbp - 0x24)) = 0x74;`
`0x004007e2 lea rax, [rbp - 0x30] | rax = rbp - 0x30;`
`0x004007e6 mov esi, 0x400974 |`
`0x004007eb mov rdi, rax |`
`0x004007ee call 0x400650 | rax = fopen (rax, 0x400974);`
`0x004007f3 mov qword [rbp - 0x38], rax | *((rbp - 0x38)) = rax;`
`0x004007f7 mov rax, qword [rbp - 0x38] | rax = *((rbp - 0x38));`
`0x004007fb mov edx, 0x601120 | edx = "*******************************************";`
`0x00400800 mov esi, 0x400976 |`
`0x00400805 mov rdi, rax |`
`0x00400808 mov eax, 0 | eax = 0;`
`0x0040080d call 0x400620 | fprintf (rax, 0x400976);`
`0x00400812 mov dword [rbp - 0x3c], 0 | *((rbp - 0x3c)) = 0;`
`| do {`
`0x00400819 mov eax, dword [rbp - 0x3c] | eax = *((rbp - 0x3c));`
`0x0040081c movsxd rbx, eax | rbx = (int64_t) eax;`
`0x0040081f mov edi, 0x6010e0 |`
`0x00400824 call 0x4005e0 | rax = strlen ("SharifCTF{????????????????????????????????}");`
`0x00400829 cmp rbx, rax |`
`| if (rbx >= rax) {`
`0x0040082c jae 0x4008b5 | goto label_1;`
`| }`
`0x00400832 mov eax, dword [rbp - 0x3c] | eax = *((rbp - 0x3c));`
`0x00400835 cdqe | rax = (int64_t) eax;`
`0x00400837 mov eax, dword [rax*4 + 0x601160] | eax = *((rax*4 + obj.p));`
`0x0040083e movsxd rcx, eax | rcx = (int64_t) eax;`
`0x00400841 mov rax, qword [rbp - 0x38] | rax = *((rbp - 0x38));`
`0x00400845 mov edx, 0 |`
`0x0040084a mov rsi, rcx |`
`0x0040084d mov rdi, rax |`
`0x00400850 call 0x400640 | fseek (rax, rcx, 0);`
`0x00400855 mov eax, dword [rbp - 0x3c] | eax = *((rbp - 0x3c));`
`0x00400858 cdqe | rax = (int64_t) eax;`
`0x0040085a mov eax, dword [rax*4 + 0x601160] | eax = *((rax*4 + obj.p));`
`0x00400861 cdqe | rax = (int64_t) eax;`
`0x00400863 movzx eax, byte [rax + 0x6010e0] | eax = *((rax + obj.t));`
`0x0040086a movsx eax, al | eax = (int32_t) al;`
`0x0040086d mov rdx, qword [rbp - 0x38] | rdx = *((rbp - 0x38));`
`0x00400871 mov rsi, rdx |`
`0x00400874 mov edi, eax |`
`0x00400876 call 0x400600 | fputc (eax, *((rbp - 0x38)));`
`0x0040087b mov rax, qword [rbp - 0x38] | rax = *((rbp - 0x38));`
`0x0040087f mov edx, 0 | edx = 0;`
`0x00400884 mov esi, 0 |`
`0x00400889 mov rdi, rax |`
`0x0040088c call 0x400640 | fseek (rax, 0, edx);`
`0x00400891 mov rax, qword [rbp - 0x38] | rax = *((rbp - 0x38));`
`0x00400895 mov edx, 0x601120 | edx = "*******************************************";`
`0x0040089a mov esi, 0x400976 |`
`0x0040089f mov rdi, rax |`
`0x004008a2 mov eax, 0 | eax = 0;`
`0x004008a7 call 0x400620 | fprintf (rax, 0x400976);`
`0x004008ac add dword [rbp - 0x3c], 1 | *((rbp - 0x3c))++;`
`0x004008b0 jmp 0x400819 |`
`| } while (1);`
`| label_1:`
`0x004008b5 mov rax, qword [rbp - 0x38] | rax = *((rbp - 0x38));`
`0x004008b9 mov rdi, rax |`
`0x004008bc call 0x4005d0 | fclose (*((rbp - 0x38)));`
`0x004008c1 lea rax, [rbp - 0x30] | rax = rbp - 0x30;`
`0x004008c5 mov rdi, rax |`
`0x004008c8 call 0x4005c0 | remove (rax);`
`0x004008cd mov eax, 0 | eax = 0;`
`0x004008d2 mov rbx, qword [rbp - 0x18] | rbx = *((rbp - 0x18));`
`0x004008d6 xor rbx, qword fs:[0x28] | rbx ^= *(fs:0x28);`
`| if (*((rbp - 0x3c)) != 0) {`
`0x004008df je 0x4008e6 |`
`0x004008e1 call 0x4005f0 | stack_chk_fail ();`
`| }`
`0x004008e6 add rsp, 0x38 |`
`0x004008ea pop rbx |`
`0x004008eb pop rbp |`
`0x004008ec ret | return rax;`
`| }`
这样可以清晰的对比观察汇编代码和反编译后的伪代码,更方便观察。
radare2还有很多插件可以使用,大家有兴趣的话可以在深入研究一下。
0x04 题目实例
getit这是攻防世界的一道re题,这道题拿到手是个elf文件,我首先是放到ida里看了看伪代码
`int __cdecl main(int argc, const char **argv, const char **envp)`
`{`
`char v3; // al`
`int i; // [rsp+0h] [rbp-40h]`
`int j; // [rsp+4h] [rbp-3Ch]`
`FILE *stream; // [rsp+8h] [rbp-38h]`
`char filename[24]; // [rsp+10h] [rbp-30h] BYREF`
`unsigned __int64 v9; // [rsp+28h] [rbp-18h]`
`v9 = __readfsqword(0x28u);`
`for ( i = 0; i < strlen(s); ++i )`
`{`
`if ( (i & 1) != 0 )`
`v3 = 1;`
`else`
`v3 = -1;`
`*(&t + i + 10) = s[i] + v3;`
`}`
`strcpy(filename, "/tmp/flag.txt");`
`stream = fopen(filename, "w");`
`fprintf(stream, "%s\n", u);`
`for ( j = 0; j < strlen(&t); ++j )`
`{`
`fseek(stream, p[j], 0);`
`fputc(*(&t + p[j]), stream);`
`fseek(stream, 0LL, 0);`
`fprintf(stream, "%s\n", u);`
`}`
`fclose(stream);`
`remove(filename);`
`return 0;`
`}`
本来这道题目应该是写逆向算法得到flag,但是经过分析和与反汇编代码对比,发现flag应该是被存在了eax里面
在调试程序的时候,我们可以ida配合gdb来说使用,虽然ida本身也有动态调试的功能,但是我推荐用ida来分析反汇编和伪代码,然后通过gdb或者r2来进行动态调试查看寄存器的值,或者使用r2反编译和反汇编代码对比分析,这样就可以方便不少,这里猜测flag应该存在eax中,可以通过gdb动态调试程序,在0x400832处下断点,让程序运行
运行程序到断点处,查看寄存器存储的地址的值,就能得到flag
这样通过动态调试的方式,省了很多事。
0x05 总结
本文简单介绍了ida、gdb和r2动态调试二进制文件的方法,也是自己最近在做ctf题目的时候使用的,之前只会用od和ida,使用gdb和r2很少,正好借此机会也学习了一下,对于我这种菜鸡来说,在分析二进制文件的时候,3种工具一起使用效果会好一些。
参考文章:https://wizardforcel.gitbooks.io/100-gdb-tips/content/print-registers.htmlhttps://linuxtools-rst.readthedocs.io/zh\_CN/latest/tool/gdb.htmlhttps://linux.cn/article-13074-1.htm
如有侵权,请联系删除
推荐阅读
查看更多精彩内容,还请关注 橘猫学安全:
每日坚持学习与分享,觉得文章对你有帮助可在底部给点个“ 再看 ”