动态调试elf文件的几种方法

admin 2022年4月9日01:26:40评论176 views字数 18995阅读63分19秒阅读模式
动态调试elf文件的几种方法

动态调试elf文件的几种方法


最近在刷题的时候遇到了很多elf文件,虽然可以通过ida分析伪代码解出来,但是发现有些通过动态调试的方式可以直接找到flag,这样简单了不少,因为之前接触的linux下的逆向题目比较少,所以通过这次刷题也记录一下动态调试elf文件的几种方式。

0x01 ida动态调试


ida不光可以静态分析函数伪代码,也可以通过动态调试的方式来分析linux下的elf文件。
首先将ida/dbgsrv/路径下的linux_server/linux_serverx64文件复制到linux下,两个文件分别是调试32位和64位程序使用的:

动态调试elf文件的几种方法

在linux下启动对应的文件:

动态调试elf文件的几种方法

把我们要调试的文件放到相应的文件夹中,这里我放到/homt/test/文件夹中。

在ida中选择Debugger-Run-Remote linux debugger

动态调试elf文件的几种方法

动态调试elf文件的几种方法

在弹出的对话框中,Application填写文件存放的位置和文件名,Directory中填写文件存放的路径,Parameters是指传递的参数,如果程序运行需要传参的话可以在这填入,Hostname就是linux的ip地址,Port一般都是默认的23946,如果设置了password在下方填入,没有就空着:

动态调试elf文件的几种方法

进入ida动态调试界面,默认情况下界面大概分为6个区域(通用寄存器和标志寄存器窗口为1个),模块列表和线程列表我不怎么看,主要是看反汇编和寄存器还有堆栈。
ida在动态调试的时候和OD差不多,F9继续运行,F7步入,F8步过,F2下断点。
通过Debugger-Breakpoints-Breakpoint list或者Ctrl+Alt+B来查看已经下的断点:

动态调试elf文件的几种方法

也可以通过Debugger-Debugger windows-Stack trace或者Ctrl+Alt+S查看是谁调用了当前的函数:

动态调试elf文件的几种方法

虽然感觉没有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]> iIarch x86baddr 0x400000binsz 9449bintype elfbits 64canary trueclass ELF64compiler GCC: (Ubuntu 5.4.1-2ubuntu1~14.04) 5.4.1 20160904 GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4crypto falseendian littlehavecode trueintrp /lib64/ld-linux-x86-64.so.2laddr 0x0lang clinenum truelsyms truemachine AMD x86-64 architecturemaxopsz 16minopsz 1nx trueos linuxpcalign 0pic falserelocs truerelro partialrpath NONEsanitiz falsestatic falsestripped falsesubsys linuxva true

在了解程序的基本信息后你还可以通过iiiE命令来看下程序的导入和导出函数,从而能够更好的了解程序实现的功能

[0x00400660]> ii[Imports]nth vaddr bind type lib name―――――――――――――――――――――――――――――――――――――1 0x004005c0 GLOBAL FUNC remove2 0x004005d0 GLOBAL FUNC fclose3 0x004005e0 GLOBAL FUNC strlen4 0x004005f0 GLOBAL FUNC __stack_chk_fail5 0x00400600 GLOBAL FUNC fputc6 0x00400610 GLOBAL FUNC __libc_start_main7 0x00400620 GLOBAL FUNC fprintf8 0x00400630 WEAK NOTYPE __gmon_start__9 0x00400640 GLOBAL FUNC fseek10 0x00400650 GLOBAL FUNC fopen
[0x00400660]> iE[Exports]
nth paddr vaddr bind type size lib name――――――――――――――――――――――――――――――――――――――――――――――――――――――45 0x00000960 0x00400960 GLOBAL FUNC 2 __libc_csu_fini49 0x000010e0 0x006010e0 GLOBAL OBJ 44 t50 ---------- 0x0060120c GLOBAL NOTYPE 0 _edata51 0x00001160 0x00601160 GLOBAL OBJ 172 p53 0x00000964 0x00400964 GLOBAL FUNC 0 _fini58 0x00001080 0x00601080 GLOBAL NOTYPE 0 __data_start61 0x00001088 0x00601088 GLOBAL OBJ 0 __dso_handle62 0x00000970 0x00400970 GLOBAL OBJ 4 _IO_stdin_used63 0x000008f0 0x004008f0 GLOBAL FUNC 101 __libc_csu_init64 ---------- 0x00601210 GLOBAL NOTYPE 0 _end65 0x00000660 0x00400660 GLOBAL FUNC 0 _start67 0x000010a0 0x006010a0 GLOBAL OBJ 33 s68 0x00001120 0x00601120 GLOBAL OBJ 44 u69 ---------- 0x0060120c GLOBAL NOTYPE 0 __bss_start70 0x00000756 0x00400756 GLOBAL FUNC 407 main73 ---------- 0x00601210 GLOBAL OBJ 0 __TMC_END__75 0x00000590 0x00400590 GLOBAL FUNC 0 _init

你还可以通过afl命令查看程序存在的函数,从而找到main函数,定位到对应的位置,查看反汇编代码

[0x00400660]> afl0x00400660 1 41 entry00x00400610 1 6 sym.imp.__libc_start_main0x00400690 4 50 -> 41 sym.deregister_tm_clones0x004006d0 4 58 -> 55 sym.register_tm_clones0x00400710 3 28 sym.__do_global_dtors_aux0x00400730 4 38 -> 35 entry.init00x00400960 1 2 sym.__libc_csu_fini0x00400964 1 9 sym._fini0x004008f0 4 101 sym.__libc_csu_init0x00400756 12 407 main0x00400590 3 26 sym._init0x00400630 1 6 loc.imp.__gmon_start__0x004005c0 1 6 sym.imp.remove0x004005d0 1 6 sym.imp.fclose0x004005e0 1 6 sym.imp.strlen0x004005f0 1 6 sym.imp.__stack_chk_fail0x00400600 1 6 sym.imp.fputc0x00400620 1 6 sym.imp.fprintf0x00400640 1 6 sym.imp.fseek0x00400650 1 6 sym.imp.fopen

在找到程序内存在的函数后,可以使用axt命令来查看函数调用

[0x00400660]> axt mainentry0 0x40067d [DATA] mov rdi, main

在找到main函数后,使用s命令定位到main函数,pdf命令来查看函数的反汇编代码,s命令后可以加地址或者函数名

[0x00400660]> s main[0x00400756]> pdf ; DATA XREF from entry0 @ 0x40067d407: 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-0x180x00400756 55 push rbp0x00400757 4889e5 mov rbp, rsp0x0040075a 53 push rbx0x0040075b 4883ec38 sub rsp, 0x380x0040075f 64488b042528. mov rax, qword fs:[0x28]0x00400768 488945e8 mov qword [canary], rax0x0040076c 31c0 xor eax, eax0x0040076e 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], rax0x004007d5 c745d8672e74. mov dword [var_28h], 0x78742e67 ; 'g.tx'0x004007dc 66c745dc7400 mov word [var_24h], 0x74 ; 't' ; 1160x004007e2 488d45d0 lea rax, [filename]0x004007e6 be74094000 mov esi, 0x400974 ; const char *mode0x004007eb 4889c7 mov rdi, rax ; const char *filename0x004007ee e85dfeffff call sym.imp.fopen ; file*fopen(const char *filename, const char *mode)0x004007f3 488945c8 mov qword [stream], rax0x004007f7 488b45c8 mov rax, qword [stream]0x004007fb ba20116000 mov edx, obj.u ; 0x601120 ; "*******************************************" ; ...0x00400800 be76094000 mov esi, 0x400976 ; const char *format0x00400805 4889c7 mov rdi, rax ; FILE *stream0x00400808 b800000000 mov eax, 00x0040080d 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 *stream0x004008bc e80ffdffff call sym.imp.fclose ; int fclose(FILE *stream)│ 0x004008c1 488d45d0 lea rax, [filename]│ 0x004008c5 4889c7 mov rdi, rax ; const char *filename0x004008c8 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, 0x380x004008ea 5b pop rbx0x004008eb 5d pop rbp0x004008ec c3 ret

如果觉得看反汇编不过瘾也可以通过px命令查看十六进制内容

[0x00400756]> px- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF0x00400756 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....`..[...H0x00400786 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 [email protected]...]...H.E0x004007f6 c848 8b45 c8ba 2011 6000 be76 0940 0048 .H.E.. .`[email protected]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.H0x00400836 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 c61b68366edeb7bdce3c6820314b74981 0x000010e0 0x006010e0 43 44 .data ascii SharifCTF{????????????????????????????????}2   0x00001120 0x00601120 43  44   .data   ascii *******************************************

字符串和函数一样,可以使用axt命令查看是哪里把它打印出来的

[0x00400756]> axt 0x6010a0main 0x40077b [DATA] mov edi, obj.smain 0x400795 [DATA] movzx eax, byte [rax + obj.s]

r2还提供了可视模式,可以更直观的分析函数的调用,vv命令进入

动态调试elf文件的几种方法

以上是使用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]> dbi0 0x00400756 E:1 T:0[0x7f53c8464050]> dchit breakpoint at: 0x400756[0x00400756]> dbt0 0x400756 sp: 0x0 0 [main] main entry.init0+38[0x00400756]> drrax = 0x00400756rbx = 0x004008f0rcx = 0x7f53c8443738rdx = 0x7ffcee042568r8 = 0x00000000r9 = 0x7f53c84731f0r10 = 0xfffffffffffffb8cr11 = 0x7f53c829c730r12 = 0x00400660r13 = 0x00000000r14 = 0x00000000r15 = 0x00000000rsi = 0x7ffcee042558rdi = 0x00000001rsp = 0x7ffcee042468rbp = 0x00000000rip = 0x00400756rflags = 0x00000246orax = 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, "%sn", u); for ( j = 0; j < strlen(&t); ++j ) { fseek(stream, p[j], 0); fputc(*(&t + p[j]), stream); fseek(stream, 0LL, 0); fprintf(stream, "%sn", u); } fclose(stream); remove(filename); return 0;}

本来这道题目应该是写逆向算法得到flag,但是经过分析和与反汇编代码对比,发现flag应该是被存在了eax里面

动态调试elf文件的几种方法

在调试程序的时候,我们可以ida配合gdb来说使用,虽然ida本身也有动态调试的功能,但是我推荐用ida来分析反汇编和伪代码,然后通过gdb或者r2来进行动态调试查看寄存器的值,或者使用r2反编译和反汇编代码对比分析,这样就可以方便不少,这里猜测flag应该存在eax中,可以通过gdb动态调试程序,在0x400832处下断点,让程序运行

动态调试elf文件的几种方法

运行程序到断点处,查看寄存器存储的地址的值,就能得到flag

动态调试elf文件的几种方法

这样通过动态调试的方式,省了很多事。

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.html

E

N

D



Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章370余篇,自研平台达到26个,目有15个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们

动态调试elf文件的几种方法

原文始发于微信公众号(Tide安全团队):动态调试elf文件的几种方法

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月9日01:26:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   动态调试elf文件的几种方法http://cn-sec.com/archives/890398.html

发表评论

匿名网友 填写信息