📊
CTF-All-In-One
  • 简介
  • 前言
  • 一、基础知识篇
    • 1.1 CTF 简介
    • 1.2 学习方法
    • 1.3 Linux 基础
    • 1.4 Web 安全基础
      • 1.4.1 HTML 基础
      • 1.4.2 HTTP 协议基础
      • 1.4.3 JavaScript 基础
      • 1.4.4 常见 Web 服务器基础
      • 1.4.5 OWASP Top Ten Project 漏洞基础
      • 1.4.6 PHP 源码审计基础
    • 1.5 逆向工程基础
      • 1.5.1 C/C++ 语言基础
      • 1.5.2 汇编基础
      • 1.5.3 Linux ELF
      • 1.5.4 Windows PE
      • 1.5.5 静态链接
      • 1.5.6 动态链接
      • 1.5.7 内存管理
      • 1.5.8 glibc malloc
      • 1.5.9 Linux 内核
      • 1.5.10 Windows 内核
      • 1.5.11 jemalloc
    • 1.6 密码学基础
      • 1.6.1 密码学导论
      • 1.6.2 流密码
      • 1.6.3 分组密码
      • 1.6.4 公钥密码
      • 1.6.5 消息认证和哈希函数
      • 1.6.6 数字签名
      • 1.6.7 密码协议
      • 1.6.8 密钥分配与管理
      • 1.6.9 数字货币
    • 1.7 Android 安全基础
      • 1.7.1 Android 环境搭建
      • 1.7.2 Dalvik 指令集
      • 1.7.3 ARM 汇编基础
      • 1.7.4 Android 常用工具
  • 二、工具篇
    • 虚拟化分析环境
      • 2.1.1 VirtualBox
      • 2.1.2 QEMU
      • 2.1.3 Docker
      • 2.1.4 Unicorn
    • 静态分析工具
      • 2.2.1 radare2
      • 2.2.2 IDA Pro
      • 2.2.3 JEB
      • 2.2.4 Capstone
      • 2.2.5 Keystone
      • 2.2.6 Ghidra
    • 动态分析工具
      • 2.3.1 GDB
      • 2.3.2 OllyDbg
      • 2.3.3 x64dbg
      • 2.3.4 WinDbg
      • 2.3.5 LLDB
    • 其他工具
      • 2.4.1 pwntools
      • 2.4.2 zio
      • 2.4.3 metasploit
      • 2.4.4 binwalk
      • 2.4.5 Burp Suite
      • 2.4.6 Wireshark
      • 2.4.7 Cuckoo Sandbox
  • 三、分类专题篇
    • Pwn
      • 3.1.1 格式化字符串漏洞
      • 3.1.2 整数溢出
      • 3.1.3 栈溢出
      • 3.1.4 返回导向编程(ROP)(x86)
      • 3.1.5 返回导向编程(ROP)(ARM)
      • 3.1.6 Linux 堆利用(一)
      • 3.1.7 Linux 堆利用(二)
      • 3.1.8 Linux 堆利用(三)
      • 3.1.9 Linux 堆利用(四)
      • 3.1.10 内核 ROP
      • 3.1.11 Linux 内核漏洞利用
      • 3.1.12 Windows 内核漏洞利用
      • 3.1.13 竞争条件
      • 3.1.14 虚拟机逃逸
    • Reverse
      • 3.2.1 patch 二进制文件
      • 3.2.2 脱壳技术(PE)
      • 3.2.3 脱壳技术(ELF)
      • 3.2.4 反调试技术(PE)
      • 3.2.5 反调试技术(ELF)
      • 3.2.6 指令混淆
    • Web
      • 3.3.1 SQL 注入利用
      • 3.3.2 XSS 漏洞利用
    • Crypto
    • Misc
      • 3.5.1 Lsb
    • Mobile
  • 四、技巧篇
    • 4.1 Linux 内核调试
    • 4.2 Linux 命令行技巧
    • 4.3 GCC 编译参数解析
    • 4.4 GCC 堆栈保护技术
    • 4.5 ROP 防御技术
    • 4.6 one-gadget RCE
    • 4.7 通用 gadget
    • 4.8 使用 DynELF 泄露函数地址
    • 4.9 shellcode 开发
    • 4.10 跳转导向编程(JOP)
    • 4.11 利用 mprotect 修改栈权限
    • 4.12 利用 __stack_chk_fail
    • 4.13 利用 _IO_FILE 结构
    • 4.14 glibc tcache 机制
    • 4.15 利用 vsyscall 和 vDSO
  • 五、高级篇
    • 5.0 软件漏洞分析
    • 5.1 模糊测试
      • 5.1.1 AFL fuzzer
      • 5.1.2 libFuzzer
    • 5.2 动态二进制插桩
      • 5.2.1 Pin
      • 5.2.2 DynamoRio
      • 5.2.3 Valgrind
    • 5.3 符号执行
      • 5.3.1 angr
      • 5.3.2 Triton
      • 5.3.3 KLEE
      • 5.3.4 S²E
    • 5.4 数据流分析
      • 5.4.1 Soot
    • 5.5 污点分析
      • 5.5.1 TaintCheck
    • 5.6 LLVM
      • 5.6.1 Clang
    • 5.7 程序切片
    • 5.8 SAT/SMT
      • 5.8.1 Z3
    • 5.9 基于模式的漏洞分析
    • 5.10 基于二进制比对的漏洞分析
    • 5.11 反编译技术
      • 5.11.1 RetDec
  • 六、题解篇
    • Pwn
      • 6.1.1 pwn HCTF2016 brop
      • 6.1.2 pwn NJCTF2017 pingme
      • 6.1.3 pwn XDCTF2015 pwn200
      • 6.1.4 pwn BackdoorCTF2017 Fun-Signals
      • 6.1.5 pwn GreHackCTF2017 beerfighter
      • 6.1.6 pwn DefconCTF2015 fuckup
      • 6.1.7 pwn 0CTF2015 freenote
      • 6.1.8 pwn DCTF2017 Flex
      • 6.1.9 pwn RHme3 Exploitation
      • 6.1.10 pwn 0CTF2017 BabyHeap2017
      • 6.1.11 pwn 9447CTF2015 Search-Engine
      • 6.1.12 pwn N1CTF2018 vote
      • 6.1.13 pwn 34C3CTF2017 readme_revenge
      • 6.1.14 pwn 32C3CTF2015 readme
      • 6.1.15 pwn 34C3CTF2017 SimpleGC
      • 6.1.16 pwn HITBCTF2017 1000levels
      • 6.1.17 pwn SECCONCTF2016 jmper
      • 6.1.18 pwn HITBCTF2017 Sentosa
      • 6.1.19 pwn HITBCTF2018 gundam
      • 6.1.20 pwn 33C3CTF2016 babyfengshui
      • 6.1.21 pwn HITCONCTF2016 Secret_Holder
      • 6.1.22 pwn HITCONCTF2016 Sleepy_Holder
      • 6.1.23 pwn BCTF2016 bcloud
      • 6.1.24 pwn HITCONCTF2016 House_of_Orange
      • 6.1.25 pwn HCTF2017 babyprintf
      • 6.1.26 pwn 34C3CTF2017 300
      • 6.1.27 pwn SECCONCTF2016 tinypad
      • 6.1.28 pwn ASISCTF2016 b00ks
      • 6.1.29 pwn Insomni'hack_teaserCTF2017 The_Great_Escape_part-3
      • 6.1.30 pwn HITCONCTF2017 Ghost_in_the_heap
      • 6.1.31 pwn HITBCTF2018 mutepig
      • 6.1.32 pwn SECCONCTF2017 vm_no_fun
      • 6.1.33 pwn 34C3CTF2017 LFA
      • 6.1.34 pwn N1CTF2018 memsafety
      • 6.1.35 pwn 0CTF2018 heapstorm2
      • 6.1.36 pwn NJCTF2017 messager
      • 6.1.37 pwn sixstarctf2018 babystack
      • 6.1.38 pwn HITCONCMT2017 pwn200
      • 6.1.39 pwn BCTF2018 house_of_Atum
      • 6.1.40 pwn LCTF2016 pwn200
      • 6.1.41 pwn PlaidCTF2015 PlaidDB
      • 6.1.42 pwn hacklu2015 bookstore
      • 6.1.43 pwn 0CTF2018 babyheap
      • 6.1.44 pwn ASIS2017 start_hard
      • 6.1.45 pwn LCTF2016 pwn100
    • Reverse
      • 6.2.1 re XHPCTF2017 dont_panic
      • 6.2.2 re ECTF2016 tayy
      • 6.2.3 re CodegateCTF2017 angrybird
      • 6.2.4 re CSAWCTF2015 wyvern
      • 6.2.5 re PicoCTF2014 Baleful
      • 6.2.6 re SECCONCTF2017 printf_machine
      • 6.2.7 re CodegateCTF2018 RedVelvet
      • 6.2.8 re DefcampCTF2015 entry_language
    • Web
      • 6.3.1 web HCTF2017 babycrack
    • Crypto
    • Misc
    • Mobile
  • 七、实战篇
    • CVE
      • 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
      • 7.1.2 CVE-2015-0235 glibc __nss_hostname_digits_dots 堆溢出漏洞
      • 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
      • 7.1.4 CVE-2017-13089 wget skip_short_body 栈溢出漏洞
      • 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
      • 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
      • 7.1.7 CVE-2018-6323 GNU binutils elf_object_p 整型溢出漏洞
      • 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
      • 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
    • Malware
  • 八、学术篇
    • 8.1 The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)
    • 8.2 Return-Oriented Programming without Returns
    • 8.3 Return-Oriented Rootkits: Bypassing Kernel Code Integrity Protection Mechanisms
    • 8.4 ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks
    • 8.5 Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks
    • 8.7 What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses
    • 8.9 Symbolic Execution for Software Testing: Three Decades Later
    • 8.10 AEG: Automatic Exploit Generation
    • 8.11 Address Space Layout Permutation (ASLP): Towards Fine-Grained Randomization of Commodity Softwa
    • 8.13 New Frontiers of Reverse Engineering
    • 8.14 Who Allocated My Memory? Detecting Custom Memory Allocators in C Binaries
    • 8.21 Micro-Virtualization Memory Tracing to Detect and Prevent Spraying Attacks
    • 8.22 Practical Memory Checking With Dr. Memory
    • 8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses
    • 8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization
    • 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis
    • 8.26 Driller: Augmenting Fuzzing Through Selective Symbolic Execution
    • 8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware
    • 8.28 Cross-Architecture Bug Search in Binary Executables
    • 8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data
    • 8.30 Preventing brute force attacks against stack canary protection on networking servers
    • 8.33 Under-Constrained Symbolic Execution: Correctness Checking for Real Code
    • 8.34 Enhancing Symbolic Execution with Veritesting
    • 8.38 TaintEraser: Protecting Sensitive Data Leaks Using Application-Level Taint Tracking
    • 8.39 DART: Directed Automated Random Testing
    • 8.40 EXE: Automatically Generating Inputs of Death
    • 8.41 IntPatch: Automatically Fix Integer-Overflow-to-Buffer-Overflow Vulnerability at Compile-Time
    • 8.42 Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits
    • 8.43 DTA++: Dynamic Taint Analysis with Targeted Control-Flow Propagation
    • 8.44 Superset Disassembly: Statically Rewriting x86 Binaries Without Heuristics
    • 8.45 Ramblr: Making Reassembly Great Again
    • 8.46 FreeGuard: A Faster Secure Heap Allocator
    • 8.48 Reassembleable Disassembling
  • 九、附录
    • 9.1 更多 Linux 工具
    • 9.2 更多 Windows 工具
    • 9.3 更多资源
    • 9.4 Linux 系统调用表
    • 9.5 python2到3字符串转换
    • 9.6 幻灯片
Powered by GitBook
On this page
  • 题目解析
  • 参考资料

Was this helpful?

  1. 六、题解篇
  2. Reverse

6.2.3 re CodegateCTF2017 angrybird

Previous6.2.2 re ECTF2016 tayyNext6.2.4 re CSAWCTF2015 wyvern

Last updated 3 years ago

Was this helpful?

题目解析

看题目就知道,这是一个会让我们抓狂的程序,事实也确实如此。

$ file angrybird_org
angrybird: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=089c3a14bcd7ffb08e94645cea46f1162b171445, stripped
$ ./angrybird_org
$

一运行就退出,应该是需要程序流上有问题。

main 函数的开头有一些坑需要 patch,才能使程序正常运行,然后经过很多很多轮的运算和判断,可以看到 main 函数长达 18555 行:

[0x00400600]> pd 60 @ main
/ (fcn) main 18555
|   main ();
|       :      ; DATA XREF from 0x0040061d (entry0)
|       :   0x00400761      55             push rbp
|       :   0x00400762      4889e5         mov rbp, rsp
|       :   0x00400765      4883c480       add rsp, 0xffffffffffffff80
|       :   0x00400769      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=-1 ; '(' ; 40
|       :   0x00400772      488945f8       mov qword [local_8h], rax
|       :   0x00400776      31c0           xor eax, eax                 ; 将 eax 置 0
|       :   0x00400778      83f800         cmp eax, 0                   ; 比较 eax 和 0
|       `=< 0x0040077b      0f845ffeffff   je sym.imp.exit              ; eax == 0 时退出,所以需要将 je 换成 jne,或者把上一行的 0 换成 1
|           0x00400781      48c745901860.  mov qword [local_70h], reloc.strncmp_24 ; 0x606018
|           0x00400789      48c745982060.  mov qword [local_68h], reloc.puts_32 ; 0x606020
|           0x00400791      48c745a02860.  mov qword [local_60h], reloc.__stack_chk_fail_40 ; 0x606028
|           0x00400799      48c745a83860.  mov qword [local_58h], reloc.__libc_start_main_56 ; 0x606038
|           0x004007a1      b800000000     mov eax, 0
|           0x004007a6      e84bffffff     call sub.you_should_return_21_not_1_:__6f6   ; 该函数中需要返回 21
|           0x004007ab      89458c         mov dword [local_74h], eax                   ; [local_74] = 21
|           0x004007ae      b800000000     mov eax, 0
|           0x004007b3      e854ffffff     call sub.stack_check_70c                     ; 栈检查函数,直接 nop 掉,或者进入函数修改逻辑
|           0x004007b8      b800000000     mov eax, 0
|           0x004007bd      e868ffffff     call sub.hello_72a
|           0x004007c2      488b15a75820.  mov rdx, qword [obj.stdin]  ; [0x606070:8]=0 ; 从标准输入读入
|           0x004007c9      8b4d8c         mov ecx, dword [local_74h]
|           0x004007cc      488d45b0       lea rax, [local_50h]
|           0x004007d0      89ce           mov esi, ecx                 ; esi = 21
|           0x004007d2      4889c7         mov rdi, rax
|           0x004007d5      e8f6fdffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)   ; patch 成功后就能调用 fgets
|           0x004007da      0fb655b0       movzx edx, byte [local_50h]  ; 读入的第一个字符
|           0x004007de      0fb645b1       movzx eax, byte [local_4fh]  ; 读入的第二个字符
|           0x004007e2      31d0           xor eax, edx
|           0x004007e4      8845d0         mov byte [local_30h], al
|           0x004007e7      0fb645d0       movzx eax, byte [local_30h]
|           0x004007eb      3c0f           cmp al, 0xf                 ; 15 ; 对处理后的输入字符做判断
|       ,=< 0x004007ed      7f14           jg 0x400803                      ; 若不满足条件,跳转失败,程序退出
|       |   0x004007ef      bf94504000     mov edi, str.melong         ; 0x405094 ; "melong"
|       |   0x004007f4      e897fdffff     call sym.imp.puts           ; int puts(const char *s)
|       |   0x004007f9      bf01000000     mov edi, 1
|       |   0x004007fe      e8ddfdffff     call sym.imp.exit           ; void exit(int status)
|       |      ; JMP XREF from 0x004007ed (main)
|       `-> 0x00400803      0fb655b0       movzx edx, byte [local_50h]  ; 第二轮运算
|           0x00400807      0fb645b1       movzx eax, byte [local_4fh]
|           0x0040080b      21d0           and eax, edx
|           0x0040080d      8845d0         mov byte [local_30h], al
|           0x00400810      0fb645d0       movzx eax, byte [local_30h]
|           0x00400814      3c50           cmp al, 0x50                ; 'P' ; 80
|       ,=< 0x00400816      7e14           jle 0x40082c
|       |   0x00400818      bf94504000     mov edi, str.melong         ; 0x405094 ; "melong"
|       |   0x0040081d      e86efdffff     call sym.imp.puts           ; int puts(const char *s)
|       |   0x00400822      bf01000000     mov edi, 1
|       |   0x00400827      e8b4fdffff     call sym.imp.exit           ; void exit(int status)
|       |      ; JMP XREF from 0x00400816 (main)
|       `-> 0x0040082c      c645d000       mov byte [local_30h], 0      ; 第三轮运算
|           0x00400830      0fb645d0       movzx eax, byte [local_30h]
|           0x00400834      3c01           cmp al, 1                   ; 1
|       ,=< 0x00400836      7e14           jle 0x40084c
|       |   0x00400838      bf94504000     mov edi, str.melong         ; 0x405094 ; "melong"
|       |   0x0040083d      e84efdffff     call sym.imp.puts           ; int puts(const char *s)
|       |   0x00400842      bf01000000     mov edi, 1
|       |   0x00400847      e894fdffff     call sym.imp.exit           ; void exit(int status)
|       |      ; JMP XREF from 0x00400836 (main)
|       `-> 0x0040084c      0fb655c2       movzx edx, byte [local_3eh]  ; 第 n 轮运算
|           0x00400850      0fb645b1       movzx eax, byte [local_4fh]
|           0x00400854      21d0           and eax, edx
|           0x00400856      8845d0         mov byte [local_30h], al
|           0x00400859      0fb645d0       movzx eax, byte [local_30h]

第一处 patch,将指令 je 改成 jne:

[0x00400600]> s 0x0040077b
[0x0040077b]> pd 1
|       `=< 0x0040077b      0f845ffeffff   je sym.imp.exit
[0x0040077b]> wx 0f85
[0x0040077b]> pd 1
|       `=< 0x0040077b      0f855ffeffff   jne sym.imp.exit

第二处 patch,函数 sub.you_should_return_21_not_1_:__6f6:

[0x0040077b]> pdf @ sub.you_should_return_21_not_1_:__6f6
/ (fcn) sub.you_should_return_21_not_1_:__6f6 22
|   sub.you_should_return_21_not_1_:__6f6 ();
|              ; CALL XREF from 0x004007a6 (main)
|           0x004006f6      55             push rbp
|           0x004006f7      4889e5         mov rbp, rsp
|           0x004006fa      bf64504000     mov edi, str.you_should_return_21_not_1_:_ ; 0x405064 ; "you should return 21 not 1 :("
|           0x004006ff      e88cfeffff     call sym.imp.puts           ; int puts(const char *s)
|           0x00400704      8b0556592000   mov eax, dword [0x00606060] ; [0x606060:4]=1     ; 修改 [0x606060:4] = 21 = 0x15
|           0x0040070a      5d             pop rbp
\           0x0040070b      c3             ret
[0x0040077b]> ?v 21
0x15
[0x0040077b]> s 0x00606060
[0x00606060]> px 16
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00606060  0100 0000 0000 0000 0000 0000 0000 0000  ................
[0x00606060]> wx 15
[0x00606060]> px 16
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00606060  1500 0000 0000 0000 0000 0000 0000 0000  ................

另外该函数结尾处指令是 pop rbp,而不是正确情况下的 leave,我们把它改过来:

[0x00606060]> s 0x0040070a
[0x0040070a]> pd 1
|           0x0040070a      5d             pop rbp
[0x0040070a]> wx c9
[0x0040070a]> pd 1
|           0x0040070a      c9             leave

第三处 patch,将调用 sub.stack_check_70c 的指令直接 nop 掉:

[0x00606060]> pdf @ sub.stack_check_70c
/ (fcn) sub.stack_check_70c 30
|   sub.stack_check_70c ();
|       :      ; CALL XREF from 0x004007b3 (main)
|       :   0x0040070c      55             push rbp
|       :   0x0040070d      4889e5         mov rbp, rsp
|       :   0x00400710      bf82504000     mov edi, str.stack_check    ; 0x405082 ; "stack check"
|       :   0x00400715      e876feffff     call sym.imp.puts           ; int puts(const char *s)
|       :   0x0040071a      678b0424       mov eax, dword [esp]
|       :   0x0040071e      83f800         cmp eax, 0
|       `=< 0x00400721      0f85b9feffff   jne sym.imp.exit
|           0x00400727      90             nop
|           0x00400728      5d             pop rbp
\           0x00400729      c3             ret
[0x00400600]> s 0x004007b3
[0x004007b3]> pd 1
|           0x004007b3      e854ffffff     call sub.stack_check_70c
[0x004007b3]> wx 9090909090
[0x004007b3]> pd 5
|           0x004007b3      90             nop
|           0x004007b4      90             nop
|           0x004007b5      90             nop
|           0x004007b6      90             nop
|           0x004007b7      90             nop

第四处 patch 是将 sub.hello_72a 函数中的 je 改成 jne:

[0x0040077b]> pdf @ sub.hello_72a
/ (fcn) sub.hello_72a 55
|   sub.hello_72a ();
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x004007bd (main)
|           0x0040072a      55             push rbp
|           0x0040072b      4889e5         mov rbp, rsp
|           0x0040072e      4883ec10       sub rsp, 0x10
|           0x00400732      48c745f83860.  mov qword [local_8h], reloc.__libc_start_main_56 ; 0x606038
|           0x0040073a      488b45f8       mov rax, qword [local_8h]
|           0x0040073e      ba05000000     mov edx, 5
|           0x00400743      be8e504000     mov esi, str.hello          ; 0x40508e ; "hello"
|           0x00400748      4889c7         mov rdi, rax
|           0x0040074b      e830feffff     call sym.imp.strncmp        ; int strncmp(const char *s1, const char *s2, size_t n)  ; 如果相等则返回 0
|           0x00400750      85c0           test eax, eax
|       ,=< 0x00400752      740a           je 0x40075e                  ; 如果 eax 为 0,则跳转
|       |   0x00400754      bf01000000     mov edi, 1
|       |   0x00400759      e882feffff     call sym.imp.exit           ; void exit(int status)
|       |      ; JMP XREF from 0x00400752 (sub.hello_72a)
|       `-> 0x0040075e      90             nop
|           0x0040075f      c9             leave
\           0x00400760      c3             ret

总的来说就是修改了下面几个地方:

$ radiff2 angrybird_org angrybird_mod
0x0000070a 5d => c9 0x0000070a
0x00000722 85 => 84 0x00000722
0x00000752 74 => 75 0x00000752
0x0000077c 84 => 85 0x0000077c
0x000007b3 e854ffffff => 9090909090 0x000007b3
0x00006060 01 => 15 0x00006060

这样程序的运行就正常了,它从标准输入读入字符,进行一系列的判断,由于程序执行流非常长,我们不可能一个一个地去 patch。radare2 里输入命令 VV @ main 可以看到下面的东西:

不如使用 angr 来解决它,指定好目标地址,让它运行到那儿,在大多数情况下,这种方法都是有效的。

[0x00400761]> pd -20 @ main+18555
|           0x00404f8e      d00f           ror byte [rdi], 1
|           0x00404f90      b645           mov dh, 0x45                ; 'E' ; 69
|           0x00404f92      d03c78         sar byte [rax + rdi*2], 1
|       ,=< 0x00404f95      7e14           jle 0x404fab
|       |   0x00404f97      bf94504000     mov edi, str.melong         ; 0x405094 ; "melong"
|       |   0x00404f9c      e8efb5ffff     call sym.imp.puts           ; int puts(const char *s)
|       |   0x00404fa1      bf01000000     mov edi, 1
|       |   0x00404fa6      e835b6ffff     call sym.imp.exit           ; void exit(int status)
|       |      ; JMP XREF from 0x00404f95 (main)
|       `-> 0x00404fab      488d45b0       lea rax, [local_50h]
|           0x00404faf      4889c6         mov rsi, rax
|           0x00404fb2      bf9b504000     mov edi, str.you_typed_:__s_n ; 0x40509b ; "you typed : %s\n"
|           0x00404fb7      b800000000     mov eax, 0
|           0x00404fbc      e8efb5ffff     call sym.imp.printf         ; int printf(const char *format)
|           0x00404fc1      b800000000     mov eax, 0
|           0x00404fc6      488b4df8       mov rcx, qword [local_8h]
|           0x00404fca      6448330c2528.  xor rcx, qword fs:[0x28]
|       ,=< 0x00404fd3      7405           je 0x404fda
|       |   0x00404fd5      e8c6b5ffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       |      ; JMP XREF from 0x00404fd3 (main)
|       `-> 0x00404fda      c9             leave                   ; 选择一个目标地址
\           0x00404fdb      c3             ret

因为每次错误退出之前,都会调用 puts 函数,所以应该避免其出现,将地址设置为参数 avoid。

[0x00400600]> is~puts
vaddr=0x00400590 paddr=0x00000590 ord=002 fwd=NONE sz=16 bind=GLOBAL type=FUNC name=imp.puts

对于使用 angr 来说,上面的 patch 完全没有必要,只要选择一个合适的初始化地址,如 0x004007da,也就是 fget 函数的下一条指令,就可以跑出结果:

import angr

main  = 0x004007da
find  = 0x00404fda  # leave;ret
avoid = 0x00400590  # puts@plt

p = angr.Project('./angrybird_org')
init = p.factory.blank_state(addr=main)
pg = p.factory.simgr(init, threads=4)
ex = pg.explore(find=find, avoid=avoid)

final = ex.found[0].state
flag = final.posix.dumps(0)

print "Flag:", final.posix.dumps(1)

Bingo!!!(不能保证每次都有效,多试几次)

$ python2 solve.py
WARNING | 2017-12-03 17:33:58,544 | angr.state_plugins.symbolic_memory | Concretizing symbolic length. Much sad; think about implementing.
Flag: you typed : Im_so_cute&pretty_:)�

然后用我们 patch 过的程序来验证 flag:

$ ./angrybird_mod
you should return 21 not 1 :(
Im_so_cute&pretty_:)
you typed : Im_so_cute&pretty_:)

同样需要一定的运气才能通过,祝好运:)

参考资料

https://ctftime.org/task/3375
下载文件
题目解析
参考资料
img