ctfshow-逆向签到题re1(linux) linux 可执行文件在内存中运行时,如下几个段:
内存段
说明
BSS段
存储未初始化的全局变量或者是默认初始化为0的全局变量
data段
用于存储初始化的全局变量,初始化为 0 的全局变量处于编译优化的策略还是被保存在BSS段中
.rodata
该段也叫常量区,用于存放常量数据,ro(Read Only)只读,注意:不是所有的常量都是放在常量数据段中
text段
用于存放程序代码
stack段
栈段,用于存储参数变量及局部变量
heap段
堆,由用户申请和释放内存空间
查看文件类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 root@kali:/opt/C/CTF arch x86baddr 0x0 binsz 6585 bintype elf bits 64 canary true class ELF64 compiler GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 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 true relocs true relro full rpath NONE sanitiz false static false stripped false subsys linux va true
执行程序判断大体架构
1 2 3 4 5 6 7 8 root@kali:/opt/C/CTF plz input the key: jflsdfs key error 1、打印字符串:"plz input the key:" 2、获取用户输入 3、通过判断输出信息:错误"key error"
查看 .rodata段的常量
1 2 3 4 5 6 root@kali:/opt/C/CTF# rabin2 -z re1 [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x00000884 0x00000884 16 17 (.rodata) ascii flag{7u jm8ikhy6}001 0x00000895 0x00000895 18 19 (.rodata) ascii plz input the key:002 0x000008ab 0x000008ab 9 10 (.rodata) ascii key error
获得 flag{7ujm8ikhy6}
注:签到题一般就是把 flag 放在常量区。
静态分析
使用 r2 的 -A
选项分析代码,并进入程序入口点
1 2 3 4 5 6 7 8 9 10 11 root@kali:/opt/C/CTF# r2 -A re1 [Cannot analyze at 0x00000650 g with sym. and entry0 (aa) [x] Analyze all flags starting with sym. and entry0 (aa) [Cannot analyze at 0x00000650ac) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [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.
查看 main 反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [0x00000660 ]> pdf @ sym.main / (fcn) main 146 | int main (int argc, char **argv, char **envp) ; | ; var char *s1 @ rbp-0x78 | ; var char *s2 @ rbp-0x70 | ; var int32_t canary @ rbp-0x8 | ; DATA XREF from entry0 @ 0x67d | 0x0000076a 55 push rbp | 0x0000076b 4889e5 mov rbp, rsp | 0x0000076e 4883 c480 add rsp, -0x80 | 0x00000772 64488b 042528. mov rax, qword fs:[0x28 ] | 0x0000077b 488945f 8 mov qword [canary], rax | 0x0000077f 31 c0 xor eax, eax | 0x00000781 488 d05fc0000. lea rax, qword str.flag_7ujm8ikhy6 ; 0x884 ; "flag{7ujm8ikhy6}" | 0x00000788 48894588 mov qword [s1], rax | 0x0000078c 488 d3d020100. lea rdi, qword str.plz_input_the_key: ; 0x895 ; "plz input the key:" ; const char *s | 0x00000793 e878feffff call sym.imp.puts ; int puts (const char *s) | 0x00000798 488d4590 lea rax, qword [s2] | 0x0000079c 4889c6 mov rsi, rax | 0x0000079f 488d3d020100. lea rdi, qword [0x000008a8] ; "%s" ; const char *format | 0x000007a6 b800000000 mov eax, 0 | 0x000007ab e890feffff call sym.imp.__isoc99_scanf ; int scanf (const char *format) | 0x000007b0 488d5590 lea rdx, qword [s2] | 0x000007b4 488b4588 mov rax, qword [s1] | 0x000007b8 4889d6 mov rsi, rdx ; const char *s2 | 0x000007bb 4889 c7 mov rdi, rax ; const char *s1 | 0x000007be e86dfeffff call sym.imp.strcmp ; int strcmp (const char *s1, const char *s2) | 0x000007c3 85c0 test eax, eax | ,=< 0x000007c5 750 e jne 0x7d5 | | 0x000007c7 488b 4588 mov rax, qword [s1] | | 0x000007cb 4889 c7 mov rdi, rax ; const char *s | | 0x000007ce e83dfeffff call sym.imp.puts ; int puts (const char *s) | ,==< 0x000007d3 eb0c jmp 0x7e1 | || ; CODE XREF from main @ 0x7c5 | |`-> 0x000007d5 488 d3dcf0000. lea rdi, qword str.key_error ; 0x8ab ; "key error" ; const char *s | | 0x000007dc e82ffeffff call sym.imp.puts ; int puts (const char *s) | | ; CODE XREF from main @ 0x7d3 | `--> 0x000007e1 b800000000 mov eax, 0 | 0x000007e6 488b 4df8 mov rcx, qword [canary] | 0x000007ea 6448330 c2528. xor rcx, qword fs:[0x28 ] | ,=< 0x000007f3 7405 je 0x7fa | | 0x000007f5 e826feffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void ) | | ; CODE XREF from main @ 0x7f3 | `-> 0x000007fa c9 leave \ 0x000007fb c3 ret
pdf表示p(打印)d(反汇编)f(函数), @表示取地址, sym.main为函数符号。
开辟栈空间 1 2 3 | 0x0000076a 55 push rbp |保存栈底 | 0x0000076b 4889e5 mov rbp, rsp |提升栈底 | 0x0000076e 4883 c480 add rsp, -0x80 |提升栈顶
设置 canary 1 2 | 0x00000772 64488b 042528. mov rax, qword fs:[0x28 ] | | 0x0000077b 488945f 8 mov qword [canary], rax |开启 canary 机制
局部变量
[rbp-0x8]
1 2 | 0x00000772 64488b 042528. mov rax, qword fs:[0x28 ] | 0x0000077b 488945f 8 mov qword [canary], rax
[rbp-0x08] 是开启 canary 机制后,生成的随机验证码。程序自动分析后,定义为 canary
[rbp-0x78]
1 2 3 | 0x0000077f 31 c0 xor eax, eax | 0x00000781 488 d05fc0000. lea rax, qword str.flag_7ujm8ikhy6 ; 0x884 ; "flag{7ujm8ikhy6}" | 0x00000788 48894588 mov qword [s1], rax
把 flag 存到了 [rbp-0x78] 中。
这里 r2 对程序自动分析,将 [rbp-0x78] 定义为 s1
[rbp-0x70]
1 2 3 4 5 | 0x00000798 488 d4590 lea rax, qword [s2] | 0x0000079c 4889 c6 mov rsi, rax | 0x0000079f 488 d3d020100. lea rdi, qword [0x000008a8 ] ; "%s" ; const char *format | 0x000007a6 b800000000 mov eax, 0 | 0x000007ab e890feffff call sym.imp.__isoc99_scanf ; int scanf (const char *format)
[rbp-0x70] 是输入字符串。程序自动分析后,定义为 s2
观察程序功能用到了哪些局部变量
从后面的程序来看,只用到了[rbp-0x70] 和 [rbp-0x78] 这两个局部变量。我们分别重命名为 input 和 flag
分析程序功能
内置函数分析
1 2 3 4 5 6 7 8 9 10 11 12 | 0x0000078c 488 d3d020100. lea rdi, qword str.plz_input_the_key: | 0x00000793 e878feffff call sym.imp.puts ; puts ("plz input the key:" ) | 0x00000798 488 d4590 lea rax, qword [s2] | 0x0000079c 4889 c6 mov rsi, rax | 0x0000079f 488 d3d020100. lea rdi, qword [0x000008a8 ] ; | 0x000007a6 b800000000 mov eax, 0 | 0x000007ab e890feffff call sym.imp.__isoc99_scanf ; scanf ("%s" ,input) | 0x000007b0 488 d5590 lea rdx, qword [s2] | 0x000007b4 488b 4588 mov rax, qword [s1] | 0x000007b8 4889 d6 mov rsi, rdx ; | 0x000007bb 4889 c7 mov rdi, rax ; | 0x000007be e86dfeffff call sym.imp.strcmp ; int strcmp (flag,input)
JCC 判断分析
1 2 3 4 5 6 7 8 9 | 0x000007c3 85 c0 test eax, eax | ,=< 0x000007c5 750 e jne 0x7d5 | | 0x000007c7 488b 4588 mov rax, qword [s1] | | 0x000007cb 4889 c7 mov rdi, rax ; | | 0x000007ce e83dfeffff call sym.imp.puts ; int puts (flag) | ,==< 0x000007d3 eb0c jmp 0x7e1 | || ; CODE XREF from main @ 0x7c5 | |`-> 0x000007d5 488 d3dcf0000. lea rdi, qword str.key_error ; | | 0x000007dc e82ffeffff call sym.imp.puts ; int puts ("key error" )
从这题结构来看,这是一个 if…else 语句。test eax,eax 是将返回值只作为判断条件。
代码分析
1 2 3 4 5 6 7 8 if (strcmp (flag,input) == 0 ){ puts (flag); } else { puts ("key error" ); }
main 函数结尾部分 1 2 3 4 5 6 7 8 | `--> 0x000007e1 b800000000 mov eax, 0 | 0x000007e6 488b 4df8 mov rcx, qword [canary] | 0x000007ea 6448330 c2528. xor rcx, qword fs:[0x28 ] | ,=< 0x000007f3 7405 je 0x7fa | | 0x000007f5 e826feffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void ) | | ; CODE XREF from main @ 0x7f3 | `-> 0x000007fa c9 leave \ 0x000007fb c3 ret
代码还原 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main () { char flag[]={0 }; strcpy (flag,"flag{7ujm8ikhy6}" ); char input[20 ]; scanf ("%s" ,input); if (strcmp (flag,input) == 0 ) { puts (flag); } else { puts ("key error" ); } }
知识补充
Canary 机制
Canary 机制是一种栈溢出保护机制,栈溢出保护是一种缓冲区溢出攻击环节手段(只是环节机制,不能彻底阻止)。当弃用栈保护后,函数开始执行的时候会先往栈底插入 cookie 信息,当函数真正返回的时候会验证 cookie 信息是否合法(栈帧销毁前测试该值是否被改变),如果不合法就停止程序运行(栈溢出发生)。
gcc 相关参数及意义
1 2 3 4 5 -fstack-protector 启用保护,不过只为局部变量中含有数组的函数插入保护 -fstack-protector-all 启用保护,为所有函数插入保护 -fstack-protector-strong -fstack-protector-explicit 只对有明确 stack_protect attribute 的函数开启保护 -fno-stack -protector 禁用保护
Canary 机制插入结构
1 2 3 4 5 6 7 8 9 10 11 mov rax,QWORD PTR fs:0x28 mov QWORD PTR [rbp-0x8 ],rax mov rax,QWORD PTR [rbp-0x8 ] sub rax,QWORD PTR fs:0x28 je 0x1161 <test+44 > call 0x1030 <__stack_chk_fail@plt>
攻防世界-insanity(linux)
查看文件类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 root@kali:/opt/C/CTF arch x86baddr 0x8048000 binsz 5915 bintype elf bits 32 canary false class ELF32 compiler GCC: (Debian 4.7.2-5) 4.7.2/GCC: (Debian 4.4.7-2) 4.4.7 crypto false endian little havecode true intrp /lib/ld-linux.so.2 laddr 0x0 lang c linenum true lsyms true machine Intel 80386 maxopsz 16 minopsz 1 nx true os linux pcalign 0 pic false relocs true relro no rpath NONE sanitiz false static false stripped false subsys linux va true
执行程序判断程序架构
1 2 3 4 5 6 7 root@kali:/opt/C/CTF Reticulating splines, please wait .. Your ability to hack is about as good as my ability to have free will. 1、 打印字符串:"Reticulating splines, please wait.." 2、 等待了几秒 3、 打印字符串:"Your ability to hack is about as good as my ability to have free will."
查看常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 root@kali:/opt/C/CTF [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x000005d0 0x080485d0 35 36 (.rodata) ascii Reticulating splines, please wait .. 001 0x000005f4 0x080485f4 63 64 (.rodata) ascii If you're pretending to suck, you just passed that Turing test. 002 0x00000634 0x08048634 69 70 (.rodata) ascii There aren' t enough bits in my memory to represent how hard you fail.003 0x0000067c 0x0804867c 70 71 (.rodata) ascii Your ability to hack is about as good as my ability to have free will. 004 0x000006c4 0x080486c4 77 78 (.rodata) ascii Have you considered becoming a vacuum cleaner? You're pretty good at sucking. 005 0x00000714 0x08048714 69 70 (.rodata) ascii I' ve got a good feeling about this one..... wait no. Maybe next time.006 0x0000075c 0x0804875c 41 42 (.rodata) ascii Knock knock..\nWho's there?\nUDP.\nUDP who?\n 007 0x00000788 0x08048788 20 21 (.rodata) ascii 9447{This_is_a_flag} 008 0x0000079d 0x0804879d 27 28 (.rodata) ascii Congrats, you hacked me!\n$ 009 0x000007b9 0x080487b9 28 29 (.rodata) ascii rm -rf / : Permission denied 010 0x000007d6 0x080487d6 29 30 (.rodata) ascii #define YOU "massive failure"
获取 flag:9447{This_is_a_flag}
静态分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 root@kali:/opt/C/CTF# r2 -A insanity [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 objc references [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. [0x08048450]> pdf @ sym.main ;-- section..text: ;-- .text: / (fcn) main 96 | int main (int argc, char **argv, char **envp) ; | ; DATA XREF from entry0 @ 0x8048467 | 0x080483f0 55 push ebp ; [14 ] -r-x section size 448 named .text | 0x080483f1 89e5 mov ebp, esp | 0x080483f3 83e4 f0 and esp, 0xfffffff0 | 0x080483f6 83 ec10 sub esp, 0x10 | 0x080483f9 c70424d08504. mov dword [esp], str.Reticulating_splines__please_wait.. ; [0x80485d0 :4 ]=0x69746552 ; "Reticulating splines, please wait.." ; const char *s | 0x08048400 e89bffffff call sym.imp.puts ; int puts (const char *s) | 0x08048405 c70424050000. mov dword [esp], 5 ; int s | 0x0804840c e87fffffff call sym.imp.sleep ; int sleep (int s) | 0x08048411 c70424000000. mov dword [esp], 0 ; time_t *timer | 0x08048418 e863ffffff call sym.imp.time ; time_t time (time_t *timer) | 0x0804841d 890424 mov dword [esp], eax ; int seed | 0x08048420 e89bffffff call sym.imp.srand ; void srand (int seed) | 0x08048425 e8b6ffffff call sym.imp.rand ; int rand (void ) | 0x0804842a bacdcccccc mov edx, 0xcccccccd | 0x0804842f 89c1 mov ecx, eax ; | 0x08048431 f7e2 mul edx ;eax*edx | 0x08048433 c1ea03 shr edx, 3 ;edx 逻辑右移 3 位(edx=ecx/10 ) | 0x08048436 8 d0492 lea eax, dword [edx + edx*4 ];eax = edx*5 | 0x08048439 01 c0 add eax, eax ;eax=edx*10 | 0x0804843b 29 c1 sub ecx, eax ;ecx=ecx-eax | 0x0804843d 8b 048dc09904. mov eax, dword [ecx*4 + obj.strs]; | 0x08048444 890424 mov dword [esp], eax ; const char *s | 0x08048447 e854ffffff call sym.imp.puts ; int puts (const char *s) | 0x0804844c 31c0 xor eax, eax | 0x0804844e c9 leave \ 0x0804844f c3 ret
这段程序如何执行的还没看懂,暂时就放一放不分析了。
知识点补充
取余算法的优化
1 2 3 4 5 6 7 8 9 10 11 12 13 mov edx, 0xcccccccd mov ecx, eax ;ecx == 0xcccccccd mul edx ;eax*edx shr edx, 3 ;edx 逻辑右移 3 位 lea eax, dword [edx + edx*4 ];eax = edx*5 add eax, eax ;eax=edx*10 sub ecx, eax ;ecx=ecx-eax
示例:
1 2 3 101/10=10 10*10=100 101-100=1
攻防世界-python-trade 对于 pyc 文件的反汇编成源码,可以使用 pip 模块 “uncompyle6” 进行反汇编
反汇编成源码,并分析程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@kali:/opt/C/CTF import base64 def encode(message): s = '' for i in message: x = ord(i) ^ 32 x = x + 16 s += chr(x) return base64.b64encode(s) correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt' flag = '' print 'Input flag:' flag = raw_input() if encode(flag) == correct: print 'correct' else : print 'wrong'
通过逆算法,计算出 flag
逆算法
base64 解码成 flag1
flag2=(ord(flag)-16) ^ 32:将字符串转换成十进制,并进行逆向运算
flag3 = str(flag2)
1 2 3 4 5 6 7 8 9 10 1 2 import base64 3 4 flag = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt' 5 flag1 = base64.b64decode(flag) 6 flag2 = '' 7 for i in flag1: 8 flag2 += chr ((ord (i)-16 )^32 ) 9 10 print (flag2)
执行程序,获得flag
1 2 root@kali:/opt/C/CTF nctf{d3c0mpil1n9_PyC}
获得 flag:nctf{d3c0mpil1n9_PyC}
攻防世界-re1(windows)
PEID 查看文件信息
这是一个 32 位的程序,没有壳
执行程序,分析程序的功能
1 2 3 4 5 6 打印 "欢迎来到DUTCTF呦" 打印 "这是一道很可爱很简单的逆向题呦" 打印 "输入flag吧:" 获取用户输入:"fucker" 打印 "flag不太对呦,再试试呗,加油呦" system("pause") "请按任意键继续. . ."
使用 x64dbg 加载程序到主模块
注:主模块的入口点 EntryPoint 是 main 函数执行前的一些初始化的操作,并不是 main 函数的入口点。
查找 main 函数入口点
右键–>搜索–>当前模块–>跨模块调用,来查找程序使用了哪些 windows API
搜索 GetCommandLine 函数
双击 GetCommandLine 函数,定位到反汇编窗口
查找调用了 3 个参数的函数,该函数很有可能就是 main 函数
左击该函数,然后按 Enter,进入函数入口点
如上图所示,已经定位到了 main 函数。从 main 函数开头就已经看到了 flag。不过还是要静态分析,提升自己的分析能力。
win32 程序大多是以 push 指令来传递参数的
程序静态分析 开辟栈空间 1 2 3 00641000 <re1.sub_641000> | 55 | push ebp |保存栈底00641001 | 8B EC | mov ebp,esp |提升栈底00641003 | 83 EC 44 | sub esp,44 |提升栈顶
设置 security_cookie 1 2 3 00641006 | A1 00506500 | mov eax,dword ptr ds:[655000 ] |0064100B | 33 C5 | xor eax,ebp |0064100 D | 8945 FC | mov dword ptr ss:[ebp-4 ],eax |___security_cookie
分析局部变量 局部变量常以:[ebp-xxx] 的形式出现
[ebp-44]
1 2 3 4 5 6 [ebp-44 ]="DUTCTF{We1c0met0" movdqu xmm0,xmmword ptr ds:[653E34 ] | "DUTCTF{We1c0met0DUTCTF}" movdqu xmmword ptr ss:[ebp-44 ],xmm0 | "DUTCTF{We1c0met0"
[ebp-2C]
1 2 3 4 [ebp-2 C] = 0 xor eax,eax | mov dword ptr ss:[ebp-2 C],eax
[ebp-34]
1 2 3 4 [ebp-44 ]+[ebp-34 ] = "DUTCTF{We1c0met0DUTCTF}" movq xmm0,qword ptr ds:[653E44 ] | "DUTCTF}}" movq qword ptr ss:[ebp-34 ],xmm0 | "DUTCTF}}"
[ebp-28]
1 2 [ebp-28 ] = 0 mov word ptr ss:[ebp-28 ],ax
[ebp-24]
1 2 3 4 5 6 这是 scanf 函数反汇编特征,所以这里 [ebp-24 ] 是我们输入的 flag lea eax,dword ptr ss:[ebp-24 ] | push eax | Arg2 push re1.653E8 C | Arg1 = "%s" ==L"猥" call <re1.sub_6410D1> | sub_8410D1
观察程序功能用到了哪些局部变量
从后面的程序中,只看到了 [ebp-44](包括 [ebp-34]) 和 [ebp-24] 这两各个局部变量。其他的都没用到,那么我们把 [ebp-44] 和 [ebp-34] 定义为局部变量:flag、input
至此局部边变量已经分析结束。这里把汇编代码的顺序改了一下,方便看懂。我们分析局部变量的时候,不需要太过关注这些汇编指令的顺序。我们只需要知道这个局部变量存的值是什么,它在哪里被到了,分析它到底是个什么东西。
分析程序功能
内置函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 push re1.653E4 C | Arg1 = "欢迎来到DUTCTF呦\n" call <re1.sub_64127B> | printf ("欢迎来到DUTCTF呦\n" ),调用上面的 push 参数 push re1.653E60 | Arg1 = "这是一道很可爱很简单的逆向题呦\n" call <re1.sub_64127B> | printf ("这是一道很可爱很简单的逆向题呦\n" ) push re1.653E80 | Arg1 = "输入flag吧:" call <re1.sub_64127B> | printf ("输入flag吧:" ) lea eax,dword ptr ss:[ebp-24 ] | push eax | Arg2 push re1.653E8 C | Arg1 = "%s" ==L"猥" call <re1.sub_6410D1> | scanf ("%s" ,input) add esp,14 | 堆栈平衡
循环代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 00641062 | 8 D45 DC | lea eax,dword ptr ss:[ebp-24 ] | 获取 input 的内存地址00641065 | 8 D4D BC | lea ecx,dword ptr ss:[ebp-44 ] | 获取 flag 的内存地址00641068 | 8 A11 | mov dl,byte ptr ds:[ecx] |0064106 A | 3 A10 | cmp dl,byte ptr ds:[eax] | 判断 flag 和 input 是否相等0064106 C | 75 1 A | jne re1.641088 | 不相等就跳到 641088 0064106 E | 84 D2 | test dl,dl | test 指令用得是 and 运算,即 flag and input 。如果等于0 ,就说明我的输入为 0 ,或是循环结束为 0 。00641070 | 74 12 | je re1.641084 | 如果等于 0 就跳出循环00641072 | 8 A51 01 | mov dl,byte ptr ds:[ecx+1 ] |00641075 | 3 A50 01 | cmp dl,byte ptr ds:[eax+1 ] | 继续循环比较00641078 | 75 0 E | jne re1.641088 | 如果等于 0 就跳到 641088 0064107 A | 83 C1 02 | add ecx,2 |0064107 D | 83 C0 02 | add eax,2 | 内存地址+2 00641080 | 84 D2 | test dl,dl | 再次测试循环是否结束00641082 | 75 E4 | jne re1.641068 | 不等于 0 ,就跳到 641068 继续比较字符串00641084 | 33 C0 | xor eax,eax | 字符串比较结束,返回 0 00641088 | 1B C0 | sbb eax,eax |0064108 A | 83 C8 01 | or eax,1 | 返回 -1
这段分析完成:这段代码就是将两个字符串进行比较,并返回值 0 , -1 。这段代码既然是比较字符串的代码,所以我们可以用 strcmp 进行代替。这段代码就分析为:strcmp(flag,input)
JCC 比较分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 00641086 | EB 05 | jmp re1.64108 D | 字符串相等0064108 D | 85 C0 | test eax,eax | and 运算之前的返回值,判断返回值是不是等于 0 0064108F | 75 07 | jne re1.641098 | 不等于就报错,等于就打印 flag_get00641091 | 68 903E6500 | push re1.653E90 | 653E90 :"flag get√\n" 00641096 | EB 05 | jmp re1.64109 D |00641098 | 68 9 C3E6500 | push re1.653E9 C | Arg1 = "flag不太对呦,再试试呗,加油呦\n" 0064109 D | E8 D9010000 | call <re1.sub_64127B> | sub_84127B006410 A2 | 83 C4 04 | add esp,4 |006410 A5 | 68 BC3E6500 | push re1.653 EBC | Arg1 = "pause" ==L"慰獵e" 006410 AA | E8 C2000000 | call <re1.sub_641171> | system(pause)
这段分析完成:
1 2 3 4 5 6 7 8 if (strcmp (flag,input) == 0 ){ printf ("flag get√\n" ); } else { printf ("flag不太对呦,再试试呗,加油呦\n" ); }
main 函数结尾部分分析 1 2 3 4 5 6 7 8 9 006410 AF | 8B 4D FC | mov ecx,dword ptr ss:[ebp-4 ] |006410B 2 | 83 C4 04 | add esp,4 |006410B 5 | 33 CD | xor ecx,ebp |006410B 7 | 33 C0 | xor eax,eax |006410B 9 | E8 04000000 | call <re1.sub_6410C2> |__security_check_cookie:cookie 检测006410B E | 8B E5 | mov esp,ebp |降低栈顶006410 C0 | 5 D | pop ebp |降低栈底006410 C1 | C3 | ret |函数返回
代码还原 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int main (int argc, char * argv[]) { char flag[]={0 }; strcpy (flag,"DUTCTF{We1c0met0DUTCTF}" ); char input[23 ]; printf ("欢迎来到DUTCTF呦\n" ); printf ("这是一道很可爱很简单的逆向题呦\n" ); printf ("输入flag吧:" ); scanf ("%s" ,input); if (strcmp (input,flag) == 0 ) { printf ("flag get√\n" ); } else { printf ("flag不太对呦,再试试呗,加油呦\n" ); } system("pause" ); return 0 ; }
知识点补充
if…else 结构
___security_cookie:广义上来讲是栈的保护机制,加上 /GS 编译选项,编译器就会自动添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 mov eax,dword ptr ds:[655000 ] xor eax,ebp mov dword ptr ss:[ebp-4 ],eax mov ecx,dword ptr ss:[ebp-4 ] add esp,4 xor ecx,ebp xor eax,eax call <re1.sub_6410C2>
总结 我们通过学习 C/C++ 反汇编是为了提高逆向的效率,当我们看到没见到的架构类型就需要手动分析。然后把分析出来的汇编总结出来,当以后看到与之类似的汇编的时候,就能够快速分析出这块汇编是干什么的。然后用功能类似的函数来代替,并还原成源码。
reference Linux段管理,BSS段,data段,.rodata段,text段
___security_cookie
c –x86解码汇编函数
Canary机制及绕过策略