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机制及绕过策略