├── .gitignore ├── example ├── fmt │ ├── fmt64 │ ├── fmt32_pie │ ├── fmt32_nopie │ └── fmt.c ├── uaf │ ├── uaf32 │ ├── uaf64 │ ├── uaf.c │ └── .gdb_history ├── arm │ ├── vuln64 │ ├── vuln32_pie │ ├── vuln32_nopie │ ├── vuln.c │ └── patch.py ├── mips │ ├── vuln64 │ ├── vuln32_pie │ ├── vuln32_nopie │ ├── patch.py │ └── vuln.c ├── x86 │ ├── vuln64 │ ├── patch.py │ └── vuln.c └── overflow │ ├── overflow32 │ ├── overflow64 │ ├── .gdb_history │ └── overflow.c ├── README.md ├── AwdPwnPatcher.py └── Tutorial.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.pyc 4 | -------------------------------------------------------------------------------- /example/fmt/fmt64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/fmt/fmt64 -------------------------------------------------------------------------------- /example/uaf/uaf32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/uaf/uaf32 -------------------------------------------------------------------------------- /example/uaf/uaf64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/uaf/uaf64 -------------------------------------------------------------------------------- /example/arm/vuln64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/arm/vuln64 -------------------------------------------------------------------------------- /example/mips/vuln64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/mips/vuln64 -------------------------------------------------------------------------------- /example/x86/vuln64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/x86/vuln64 -------------------------------------------------------------------------------- /example/arm/vuln32_pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/arm/vuln32_pie -------------------------------------------------------------------------------- /example/fmt/fmt32_pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/fmt/fmt32_pie -------------------------------------------------------------------------------- /example/arm/vuln32_nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/arm/vuln32_nopie -------------------------------------------------------------------------------- /example/fmt/fmt32_nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/fmt/fmt32_nopie -------------------------------------------------------------------------------- /example/mips/vuln32_pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/mips/vuln32_pie -------------------------------------------------------------------------------- /example/mips/vuln32_nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/mips/vuln32_nopie -------------------------------------------------------------------------------- /example/overflow/overflow32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/overflow/overflow32 -------------------------------------------------------------------------------- /example/overflow/overflow64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aftern00n/AwdPwnPatcher/HEAD/example/overflow/overflow64 -------------------------------------------------------------------------------- /example/overflow/.gdb_history: -------------------------------------------------------------------------------- 1 | start 2 | ni 3 | ni 4 | stack 40 5 | ni 6 | q 7 | start 8 | ni 9 | aaaaaaaaaaaaaaaaaaa 10 | ni 11 | q 12 | -------------------------------------------------------------------------------- /example/overflow/overflow.c: -------------------------------------------------------------------------------- 1 | //gcc overflow.c -o overflow64 -fno-stack-protector 2 | //gcc overflow.c -o overflow32 -fno-stack-protector -no-pie -m32 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | char a[32]; 9 | read(0, a, 0x100); 10 | puts(a); 11 | return 0; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /example/uaf/uaf.c: -------------------------------------------------------------------------------- 1 | //gcc fmt.c -o uaf64 2 | //gcc fmt.c -o uaf32 -no-pie -m32 3 | #include 4 | #include 5 | 6 | void * p[10]; 7 | 8 | int main() 9 | { 10 | unsigned int index; 11 | printf("Input index: "); 12 | scanf("%u", &index); 13 | if(index<10){ 14 | p[index] = malloc(0x40); 15 | free(p[index]); 16 | } 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /example/fmt/fmt.c: -------------------------------------------------------------------------------- 1 | //gcc fmt.c -o fmt64 -fno-builtin-printf 2 | //gcc fmt.c -o fmt32_nopie -fno-builtin-printf -no-pie -m32 3 | //gcc fmt.c -o fmt32_pie -fno-builtin-printf -m32 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | char * p; 11 | p = (char *)malloc(0x40); 12 | read(0, p, 0x40); 13 | printf(p); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /example/uaf/.gdb_history: -------------------------------------------------------------------------------- 1 | start 2 | ni 3 | ni 4 | ni 5 | start 6 | ni 7 | q 8 | start 9 | ni 10 | ni 11 | heap 12 | ni 13 | heap 14 | bins 15 | heapinfo 16 | ni 17 | p &p 18 | x /10xg 0x804a060 19 | x /10xw 0x804a060 20 | ni 21 | x /10xw 0x804a060 22 | ni 23 | x /10xw 0x804a060 24 | ni 25 | q 26 | start 27 | ni 28 | ni 29 | ni 30 | ni 31 | p &p 32 | q 33 | start 34 | ni 35 | ni 36 | nini 37 | ni 38 | q 39 | start 40 | ni 41 | ni 42 | x /10xg 0x555555601040 43 | q 44 | b * 0x555555400833 45 | r 46 | ni 47 | ni 48 | q 49 | b * 0x555555400833 50 | r 51 | ni 52 | x 10xg 0x555555601040 53 | x /10xg 0x555555601040 54 | ni 55 | x /10xg 0x555555601040 56 | q 57 | -------------------------------------------------------------------------------- /example/x86/patch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from AwdPwnPatcher import * 5 | 6 | binary = "./vuln64" 7 | awd_pwn_patcher = AwdPwnPatcher(binary) 8 | fmt_offset = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 9 | assembly = """ 10 | mov rsi, rax 11 | lea rdi, qword ptr [{}] 12 | """.format(hex(fmt_offset)) 13 | awd_pwn_patcher.patch_by_jmp(0xbd4, jmp_to=0xbdc, assembly=assembly) 14 | 15 | assembly = """ 16 | mov edx, 0x20 17 | """ 18 | awd_pwn_patcher.patch_origin(0x9db, end=0x9e0, assembly=assembly) 19 | 20 | assembly = """ 21 | lea rax, qword ptr [rdx + rax] 22 | mov rdi, qword ptr [rax] 23 | mov qword ptr [rax], 0 24 | """ 25 | awd_pwn_patcher.patch_by_jmp(0xb7e, jmp_to=0xb85, assembly=assembly) 26 | awd_pwn_patcher.save() -------------------------------------------------------------------------------- /example/mips/patch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from AwdPwnPatcher import * 5 | 6 | # 32 mips nopie 7 | awd_pwn_patcher = AwdPwnPatcher("./vuln32_nopie") 8 | fmt_addr = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 9 | assembly = """ 10 | lw $v0, 0($v0) 11 | move $a1, $v0 12 | li $a0, {} 13 | """.format(hex(fmt_addr)) 14 | awd_pwn_patcher.patch_by_jmp(0x400E38, jmp_to=0x400E40, assembly=assembly) 15 | 16 | assembly = """ 17 | li $a2, 0x20 18 | """ 19 | awd_pwn_patcher.patch_origin(0x4009fc, assembly=assembly) 20 | 21 | assembly = """ 22 | lw $v1, -0x7fd8($gp) 23 | lw $v0, 0x1c($fp) 24 | sll $v0, 2 25 | addu $v0, $v1, $v0 26 | sw $zero, 0($v0) 27 | """ 28 | awd_pwn_patcher.patch_by_jmp(0x400Da0, jmp_to=0x400dc4, assembly=assembly) 29 | awd_pwn_patcher.save() 30 | 31 | 32 | # 32 mips pie 33 | awd_pwn_patcher = AwdPwnPatcher("./vuln32_pie") 34 | fmt_addr = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 35 | save_fmt_addr = awd_pwn_patcher.add_constant_in_ehframe(p32(fmt_addr)) 36 | assembly = """ 37 | lw $a1, 0($v0) 38 | lw $a0, -{}($gp) 39 | addu $a0, $a0, $gp 40 | subu $a0, $a0, 0x1a030 41 | """.format(hex(0x1a030-save_fmt_addr)) 42 | awd_pwn_patcher.patch_by_jmp(0x1004, jmp_to=0x100c, assembly=assembly) 43 | 44 | assembly = """ 45 | li $a2, 0x20 46 | """ 47 | awd_pwn_patcher.patch_origin(0xb9c, assembly=assembly) 48 | 49 | assembly = """ 50 | lw $v1, -0x7fc0($gp) 51 | lw $v0, 0x1c($fp) 52 | sll $v0, 2 53 | addu $v0, $v1, $v0 54 | sw $zero, 0($v0) 55 | """ 56 | awd_pwn_patcher.patch_by_jmp(0xf60, jmp_to=0xf84, assembly=assembly) 57 | awd_pwn_patcher.save() 58 | 59 | # 64 mips 60 | awd_pwn_patcher = AwdPwnPatcher("./vuln64") 61 | fmt_addr = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 62 | assembly = """ 63 | ld $a1, 0($v0) 64 | move $a0, $gp 65 | dsubu $a0, 0x8000 66 | dsubu $a0, 0x8000 67 | dsubu $a0, 0x5000 68 | dsubu $a0, 0x5030 69 | daddiu $a0, $a0, {} 70 | """.format(hex(fmt_addr)) 71 | awd_pwn_patcher.patch_by_jmp(0x13f0, jmp_to=0x13f8, assembly=assembly) 72 | 73 | assembly = """ 74 | li $a2, 0x20 75 | """ 76 | awd_pwn_patcher.patch_origin(0xfcc, assembly=assembly) 77 | 78 | assembly = """ 79 | ld $v1, -0x7f80($gp) 80 | lw $v0, 0xc($fp) 81 | dsll $v0, 3 82 | daddu $v0, $v1, $v0 83 | sw $zero, 0($v0) 84 | """ 85 | awd_pwn_patcher.patch_by_jmp(0x1354, jmp_to=0x1374, assembly=assembly) 86 | awd_pwn_patcher.save() -------------------------------------------------------------------------------- /example/arm/vuln.c: -------------------------------------------------------------------------------- 1 | //arm-linux-gnueabi-gcc vuln.c -o vuln32_nopie 2 | //arm-linux-gnueabi-gcc vuln.c -o vuln32_pie -fPIE -pie 3 | //aarch64-linux-gnu-gcc vuln.c -o vuln64 4 | #include 5 | #include 6 | #include 7 | 8 | char content[] = "Please tell us your name: "; 9 | char * book[10]; 10 | 11 | void init() 12 | { 13 | char name[0x20]; 14 | setvbuf(stdout, 0LL, 2, 0LL); 15 | setvbuf(stdin, 0LL, 1, 0LL); 16 | setvbuf(stderr, 0LL, 2, 0LL); 17 | puts(content); 18 | read(0, name, 0x100); 19 | 20 | } 21 | 22 | void menu(){ 23 | puts("------------------------"); 24 | puts("1: add. "); 25 | puts("2: delete. "); 26 | puts("3: show. "); 27 | puts("4: exit. "); 28 | puts("------------------------"); 29 | printf("which command?\n> "); 30 | } 31 | 32 | int read_int() 33 | { 34 | char buf[4]; 35 | read(0,buf,4); 36 | return atoi(buf); 37 | } 38 | 39 | void add(){ 40 | int index; 41 | char * ptr; 42 | puts("Please input index: "); 43 | index = read_int(); 44 | if (index>=0 && index<10){ 45 | puts("Please input book name: "); 46 | ptr = (char *)malloc(0x20); 47 | read(0, ptr, 0x20); 48 | book[index] = ptr; 49 | } 50 | else { 51 | puts("Invalid index!"); 52 | } 53 | 54 | 55 | } 56 | 57 | void del(){ 58 | int index; 59 | puts("Please input index: "); 60 | index = read_int(); 61 | if (index>=0 && index<10){ 62 | free(book[index]); 63 | } 64 | else { 65 | puts("Invalid index!"); 66 | } 67 | } 68 | 69 | void show(){ 70 | int index; 71 | puts("Please input index: "); 72 | index = read_int(); 73 | printf(book[index]); 74 | 75 | } 76 | 77 | void bye_bye(){ 78 | puts("bye bye~!"); 79 | exit(0); 80 | } 81 | 82 | int main() 83 | { 84 | init(); 85 | while(1){ 86 | int choose; 87 | menu(); 88 | choose = read_int(); 89 | switch(choose){ 90 | case 1: 91 | add(); 92 | break; 93 | case 2: 94 | del(); 95 | break; 96 | case 3: 97 | show(); 98 | break; 99 | case 4: 100 | bye_bye(); 101 | break; 102 | default: 103 | break; 104 | } 105 | } 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /example/mips/vuln.c: -------------------------------------------------------------------------------- 1 | //mipsel-linux-gnu-gcc vuln.c -o vuln32_nopie 2 | //mipsel-linux-gnu-gcc vuln.c -o vuln32_pie -fPIE -pie 3 | //mips64el-linux-gnuabi64-gcc vuln.c -o vuln64 -fPIE -pie 4 | #include 5 | #include 6 | #include 7 | 8 | char content[] = "Please tell us your name: "; 9 | char * book[10]; 10 | 11 | void init() 12 | { 13 | char name[0x20]; 14 | setvbuf(stdout, 0LL, 2, 0LL); 15 | setvbuf(stdin, 0LL, 1, 0LL); 16 | setvbuf(stderr, 0LL, 2, 0LL); 17 | puts(content); 18 | read(0, name, 0x100); 19 | 20 | } 21 | 22 | void menu(){ 23 | puts("------------------------"); 24 | puts("1: add. "); 25 | puts("2: delete. "); 26 | puts("3: show. "); 27 | puts("4: exit. "); 28 | puts("------------------------"); 29 | printf("which command?\n> "); 30 | } 31 | 32 | int read_int() 33 | { 34 | char buf[4]; 35 | read(0,buf,4); 36 | return atoi(buf); 37 | } 38 | 39 | void add(){ 40 | int index; 41 | char * ptr; 42 | puts("Please input index: "); 43 | index = read_int(); 44 | if (index>=0 && index<10){ 45 | puts("Please input book name: "); 46 | ptr = (char *)malloc(0x20); 47 | read(0, ptr, 0x20); 48 | book[index] = ptr; 49 | } 50 | else { 51 | puts("Invalid index!"); 52 | } 53 | 54 | 55 | } 56 | 57 | void del(){ 58 | int index; 59 | puts("Please input index: "); 60 | index = read_int(); 61 | if (index>=0 && index<10){ 62 | free(book[index]); 63 | } 64 | else { 65 | puts("Invalid index!"); 66 | } 67 | } 68 | 69 | void show(){ 70 | int index; 71 | puts("Please input index: "); 72 | index = read_int(); 73 | printf(book[index]); 74 | 75 | } 76 | 77 | void bye_bye(){ 78 | puts("bye bye~!"); 79 | exit(0); 80 | } 81 | 82 | int main() 83 | { 84 | init(); 85 | while(1){ 86 | int choose; 87 | menu(); 88 | choose = read_int(); 89 | switch(choose){ 90 | case 1: 91 | add(); 92 | break; 93 | case 2: 94 | del(); 95 | break; 96 | case 3: 97 | show(); 98 | break; 99 | case 4: 100 | bye_bye(); 101 | break; 102 | default: 103 | break; 104 | } 105 | } 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /example/x86/vuln.c: -------------------------------------------------------------------------------- 1 | //gcc vuln.c -o vuln32_nopie -fno-builtin-printf -no-pie -m32 2 | //gcc vuln.c -o vuln32_pie -fno-builtin-printf -fPIE -pie -m32 3 | //gcc vuln.c -o vuln64 -fno-builtin-printf -fPIE -pie 4 | #include 5 | #include 6 | #include 7 | 8 | char content[] = "Please tell us your name: "; 9 | char * book[10]; 10 | 11 | void init() 12 | { 13 | char name[0x20]; 14 | setvbuf(stdout, 0LL, 2, 0LL); 15 | setvbuf(stdin, 0LL, 1, 0LL); 16 | setvbuf(stderr, 0LL, 2, 0LL); 17 | puts(content); 18 | read(0, name, 0x100); 19 | 20 | } 21 | 22 | void menu(){ 23 | puts("------------------------"); 24 | puts("1: add. "); 25 | puts("2: delete. "); 26 | puts("3: show. "); 27 | puts("4: exit. "); 28 | puts("------------------------"); 29 | printf("which command?\n> "); 30 | } 31 | 32 | int read_int() 33 | { 34 | char buf[4]; 35 | read(0,buf,4); 36 | return atoi(buf); 37 | } 38 | 39 | void add(){ 40 | int index; 41 | char * ptr; 42 | puts("Please input index: "); 43 | index = read_int(); 44 | if (index>=0 && index<10){ 45 | puts("Please input book name: "); 46 | ptr = (char *)malloc(0x20); 47 | read(0, ptr, 0x20); 48 | book[index] = ptr; 49 | } 50 | else { 51 | puts("Invalid index!"); 52 | } 53 | 54 | 55 | } 56 | 57 | void del(){ 58 | int index; 59 | puts("Please input index: "); 60 | index = read_int(); 61 | if (index>=0 && index<10){ 62 | free(book[index]); 63 | } 64 | else { 65 | puts("Invalid index!"); 66 | } 67 | } 68 | 69 | void show(){ 70 | int index; 71 | puts("Please input index: "); 72 | index = read_int(); 73 | printf(book[index]); 74 | 75 | } 76 | 77 | void bye_bye(){ 78 | puts("bye bye~!"); 79 | exit(0); 80 | } 81 | 82 | int main() 83 | { 84 | init(); 85 | while(1){ 86 | int choose; 87 | menu(); 88 | choose = read_int(); 89 | switch(choose){ 90 | case 1: 91 | add(); 92 | break; 93 | case 2: 94 | del(); 95 | break; 96 | case 3: 97 | show(); 98 | break; 99 | case 4: 100 | bye_bye(); 101 | break; 102 | default: 103 | break; 104 | } 105 | } 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /example/arm/patch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from AwdPwnPatcher import * 4 | 5 | # arm 32 nopie 6 | awd_pwn_patcher = AwdPwnPatcher("./vuln32_nopie") 7 | fmt_addr = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 8 | save_fmt_addr = awd_pwn_patcher.add_constant_in_ehframe(p32(fmt_addr)) 9 | patch_start_addr = awd_pwn_patcher.get_next_patch_start_addr() 10 | assembly = """ 11 | mov r1, r3 12 | ldr r0, [pc, #{}] 13 | """.format(hex(save_fmt_addr-patch_start_addr-0xc)) 14 | awd_pwn_patcher.patch_by_jmp(0x10900, jmp_to=0x10904, assembly=assembly) 15 | 16 | assembly = """ 17 | mov r2, 0x20 18 | """ 19 | awd_pwn_patcher.patch_origin(0x106cc, assembly=assembly) 20 | 21 | book_ptr = awd_pwn_patcher.add_constant_in_ehframe(p32(0x2107C)) 22 | patch_start_addr = awd_pwn_patcher.get_next_patch_start_addr() 23 | assembly = """ 24 | ldr r2, [pc, #{}] 25 | ldr r3, [r11, #-8] 26 | mov r1, 0 27 | str r1, [r2, r3, LSL#2] 28 | """.format(hex(book_ptr-patch_start_addr-8)) 29 | awd_pwn_patcher.patch_by_jmp(0x108B4, jmp_to=0x108c0, assembly=assembly) 30 | awd_pwn_patcher.save() 31 | 32 | # arm 32 pie 33 | awd_pwn_patcher = AwdPwnPatcher("./vuln32_pie") 34 | fmt_addr = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 35 | save_fmt_addr = awd_pwn_patcher.add_constant_in_ehframe(struct.pack(" Patch program -> Apply patches to input file才能使修改生效,可能会因为忘记使用该操作而导致修改未生效 12 | - IDA修改原文件时,可以选择将原文件备份成.bak,然后在原文件上进行修改,此时可能产生的影响是,如果多次使用Apply patches to input file,你只会保留前一个修改后的副本,这时你想回退到之前的副本时,几乎不可能。 13 | - 用IDA在patch的时候,如果patch失败了想回退,除非你之前有保存过快照,即View -> Database snapshot manager,否则你只能选择关闭IDA重新打开。 14 | - lief在给程序patch的时候,前后文件改动会比较大,往往很难过check 15 | 16 | 因此,诞生了AwdPwnPatcher这款工具,该工具基于pwntools+keystone来辅助二进制选手进行patch,你可以将所有的patch代码以汇编的形式写进脚本里然后一键patch,这样如果哪一处patch有问题,直接修改对应的代码即可,而不会造成混乱。 17 | 18 | 该工具目前适用x86、arm和mips架构,包括32位和64位,该脚本patch的思路主要是通过jmp跳转到eh_frame段执行patch代码,然后再跳回程序原逻辑。当然也支持patch call指令(只针对x86,arm和mips暂未添加),改为调用在eh_frame段添加的函数,但这种方式很有可能在比赛中过不了check,比方UAF漏洞,不允许patch call free。 19 | 20 | ## 依赖安装 21 | 22 | 支持python2/python3环境: 23 | 24 | ``` 25 | sudo pip install pwntools 26 | sudo pip install keystone-engine 27 | ``` 28 | 29 | ## 使用说明 30 | 31 | 添加AwdPwnPatcher脚本所在目录至环境变量: 32 | 33 | ``` 34 | export PYTHONPATH=/path/to/AwdPwnPatcher:$PYTHONPATH 35 | ``` 36 | 37 | 引用: 38 | 39 | ``` 40 | from AwdPwnPatcher import * 41 | ``` 42 | 43 | 加载目标程序: 44 | 45 | ``` 46 | binary = "./test" 47 | awd_pwn_patcher = AwdPwnPatcher(binary) 48 | ``` 49 | 50 | 然后通过调用类成员函数,对目标程序进行patch,一些关键函数如下: 51 | 52 | **add_patch_in_ehframe(self, assembly="", machine_code=[])** 53 | 54 | - 作用:在eh_frame段中添加patch代码 55 | - 参数 56 | - assembly:要添加的汇编代码,与machine_code二选一 57 | - machine_code:要添加的机器码,类型为整数列表,与assembly二选一 58 | 59 | **patch_origin(self, start, end=0, assembly="", machine_code=[], string="")** 60 | 61 | - 作用:主要针对在原地址处修改指令或字符串的patch 62 | - 参数: 63 | - start:原程序待patch的起始指令地址 64 | - end:原程序待patch的结束指令地址,如果不为0,则会要求assembly翻译成机器码的长度或者machine_code的长度必须小于等于end-start,小于的时候会用nop指令填充 65 | - assembly:要添加的汇编代码,与machine_code二选一 66 | - machine_code:要添加的机器码,类型为整数列表,与assembly二选一 67 | - string:要修改成的字符串,也可以用来修改整数,整数需要转成字符串,该参数与汇编的两个参数二选一 68 | 69 | **patch_by_jmp(self, jmp_from, jmp_to=0, assembly="", machine_code=[])** 70 | 71 | - 作用:通过jmp指令修改原程序逻辑,使得跳转到eh_frame段处的patch代码,执行完patch代码后再跳转回去 72 | - 参数 73 | - jmp_from:地址,表示从原程序哪一条指令jmp到我们的patch代码 74 | - jmp_to:地址,表示patch代码结束后,需要跳转的目标指令地址,该参数不为0的情况下,patch_by_jmp函数会自动在patch代码的最后添加jmp语句,为0默认表示用户已经在原有的assembly或machine_code中考虑了跳转情况。 75 | - assembly:要添加的汇编代码,与machine_code二选一 76 | - machine_code:要添加的机器码,类型为整数列表,与assembly二选一 77 | 78 | **patch_by_call(self, call_from, assembly="", machine_code=[])** 79 | 80 | - 作用:通过修改call指令,使其调用在eh_frame段添加的函数,目前只适用x86架构 81 | - 参数 82 | - call_from:要patch的call指令地址 83 | - assembly:要在eh_frame段插入的汇编代码,记得包含ret,与machine_code二选一 84 | - machine_code:要添加的机器码,类型为整数列表,与assembly二选一 85 | 86 | **add_constant_in_ehframe(self, string)** 87 | 88 | - 作用:在eh_frame段中添加常量,如整数、字符串等。 89 | - 参数: 90 | - string:常量,类型为str,比如添加整数0xffff,则值为'\xff\xff\x00\x00' 91 | - 返回值:常量的起始地址 92 | 93 | **get_next_patch_start_addr(self)** 94 | 95 | - 作用:获取下一段patch代码的起始地址 96 | 97 | **save(self, save_path="")** 98 | 99 | - 作用:当执行完所有的patch后,通过save函数将结果保存到二进制文件中,在不提供save_patch参数的时候,默认文件以_patch为后缀名。在保存的时候,会自动修改eh_frame段为可执行。 100 | 101 | ## 教程样例 102 | 103 | 程序和代码见example文件夹,详细教程见[Tutorial](./Tutorial.md)。 104 | 105 | ## 更新日志 106 | 107 | ### 2022-10-17 108 | 109 | - 修复patch_by_jump函数逻辑问题:当同时提供jmp_to和machine_code两个参数时,machine_code不会写入。 110 | - 增加对python3的支持 111 | 112 | ### 2021-11-19 113 | 114 | 修复patch_by_jmp针对mips架构的时候,对跳转指令翻译有误问题。keystone在翻译b跳转指令的时候,偏移计算的时候是以ks.asm参数addr作为基准,因此如果此时翻译的指令有好几条汇编,b跳转指令并不是第一条的时候,翻译b指令计算相对偏移的时候,并不会自动以b指令对应的地址作为基准,而是直接以参数addr计算,所以跳转的地址翻译就会有偏差,因此针对这种情况,会重新翻译b指令,把之前的覆盖。 115 | 116 | ### 2021-11-16 117 | 118 | 支持mips32、mips64、arm和aarch64,由于arm和mips架构的程序eh_frame的size都比较小,无法直接用,但其实eh_frame后面有一段部分内存是可用的且没有其他数据存在,因此修改prgrame header table将这段内存空间也映射到虚拟空间里来。 119 | 120 | ### 2021-09-27 121 | 122 | save函数自动将ehframe段修改为可执行 123 | 124 | ### 2021-09-22 125 | 126 | - 更新README的使用说明 127 | - 添加样例教程 128 | -------------------------------------------------------------------------------- /AwdPwnPatcher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from pwn import * 5 | import keystone 6 | import ctypes 7 | import shutil 8 | import struct 9 | import sys 10 | 11 | PYTHON_VERSION = sys.version_info[0] 12 | 13 | class AwdPwnPatcher: 14 | def __init__(self, path, adjust_eh_frame_size=True): 15 | self.path = path 16 | self.save_path = path + "_patch" 17 | self.binary = ELF(self.path) 18 | self.bits = self.binary.bits 19 | self.pie = self.binary.pie 20 | self.endian = self.binary.endian 21 | self.arch = self.binary.arch 22 | if self.bits != 32 and self.bits != 64: 23 | print("Sorry, the architecture of program is neither 32-bit or 64-bit.") 24 | quit() 25 | if self.arch == "arm": 26 | self.ks_arch = keystone.KS_ARCH_ARM 27 | self.ks_mode = keystone.KS_MODE_ARM 28 | elif self.arch == "aarch64": 29 | self.ks_arch = keystone.KS_ARCH_ARM64 30 | self.ks_mode = 0 31 | elif self.arch == "i386" or self.arch == "amd64": 32 | self.ks_arch = keystone.KS_ARCH_X86 33 | self.ks_mode = keystone.KS_MODE_32 if self.bits == 32 else keystone.KS_MODE_64 34 | elif self.arch == "mips" or self.arch == "mips64": 35 | self.ks_arch = keystone.KS_ARCH_MIPS 36 | self.ks_mode = keystone.KS_MODE_MIPS32 if self.bits == 32 else keystone.KS_MODE_MIPS64 37 | else: 38 | self.ks_mode = 0 39 | self.ks_arch = 0 40 | if self.endian == "little": 41 | self.ks_mode |= keystone.KS_MODE_LITTLE_ENDIAN 42 | else: 43 | self.ks_mode |= keystone.KS_MODE_BIG_ENDIAN 44 | if self.ks_arch != 0: 45 | self.ks = keystone.Ks(self.ks_arch, self.ks_mode) 46 | self.eh_frame_section = self.binary.get_section_by_name(".eh_frame") 47 | self.eh_frame_addr = self.eh_frame_section.header.sh_addr 48 | self.eh_frame_size = self.eh_frame_section.header.sh_size 49 | self.offset = 0 50 | if adjust_eh_frame_size: 51 | self.adjust_eh_frame_size() 52 | 53 | def adjust_eh_frame_size(self): 54 | if self.arch == "arm" or self.arch == "aarch64" or self.arch == "mips" or self.arch == "mips64": 55 | PAGE_SIZE = 0x1000 56 | for i in range(self.binary.num_sections()): 57 | section = self.binary.get_section(i) 58 | if self.binary._get_section_name(section) == ".eh_frame": 59 | break 60 | if self.arch == "mips64": 61 | self.note_section = self.binary.get_section(i+1) 62 | self.ctors_section = self.binary.get_section(i+2) 63 | self.offset = self.eh_frame_size + self.note_section.header.sh_size 64 | self.eh_frame_next_section = self.ctors_section 65 | else: 66 | self.eh_frame_next_section = self.binary.get_section(i+1) 67 | self.eh_frame_section_header_offset = self.binary._section_offset(i) 68 | actual_size = self.eh_frame_next_section.header.sh_offset - self.eh_frame_section.header.sh_offset 69 | self.eh_frame_end_addr = self.eh_frame_addr + self.eh_frame_size 70 | if (self.eh_frame_end_addr % PAGE_SIZE) != 0: 71 | self.eh_frame_end_addr_align = (self.eh_frame_end_addr + PAGE_SIZE) & ctypes.c_uint32(~PAGE_SIZE + 1).value 72 | self.old_eh_frame_size = self.eh_frame_size 73 | if self.eh_frame_addr + actual_size > self.eh_frame_end_addr_align: 74 | self.eh_frame_size = self.eh_frame_end_addr_align - self.eh_frame_addr 75 | else: 76 | self.eh_frame_size = actual_size 77 | load_segment = self.binary.get_segment_for_address(self.eh_frame_addr) 78 | for i in range(self.binary.num_segments()): 79 | segment = self.binary.get_segment(i) 80 | if segment.header.p_vaddr == load_segment.header.p_vaddr: 81 | break 82 | self.load_segment_header_offset = self.binary._segment_offset(i) 83 | if self.endian == "little": 84 | endian_fmt = "<" 85 | else: 86 | endian_fmt = ">" 87 | new_size = self.eh_frame_size - self.old_eh_frame_size + load_segment.header.p_filesz 88 | shutil.copy2(self.path, self.save_path) 89 | self.bin_file = open(self.save_path, "rb+") 90 | if self.bits == 32: 91 | self.bin_file.seek(self.load_segment_header_offset+16) 92 | self.bin_file.write(struct.pack(endian_fmt+"I", new_size)) 93 | self.bin_file.write(struct.pack(endian_fmt+"I", new_size)) 94 | else: 95 | self.bin_file.seek(self.load_segment_header_offset+32) 96 | self.bin_file.write(struct.pack(endian_fmt+"Q", new_size)) 97 | self.bin_file.write(struct.pack(endian_fmt+"Q", new_size)) 98 | self.bin_file.close() 99 | self.binary = ELF(self.save_path) 100 | 101 | print("old eh_frame_size: %#x" % self.old_eh_frame_size) 102 | print("eh_frame_size: %#x" % self.eh_frame_size) 103 | 104 | def patch_file(self, offset, content, save_path=""): 105 | if len(save_path) != 0: 106 | shutil.copy2(self.path, save_path) 107 | self.bin_file = open(save_path, "rb+") 108 | else: 109 | shutil.copy2(self.path, self.save_path) 110 | self.bin_file = open(self.save_path, "rb+") 111 | self.bin_file.seek(offset) 112 | self.bin_file.write(content) 113 | self.bin_file.close() 114 | 115 | def generate_shellcode(self, assembly, base_addr): 116 | shellcode, count = self.ks.asm(assembly, addr=base_addr) 117 | shellcode = "".join([chr(x) for x in shellcode]) 118 | return shellcode 119 | 120 | def add_patch_in_ehframe(self, assembly="", machine_code=[]): 121 | patch_start_addr = self.eh_frame_addr + self.offset 122 | if len(assembly) != 0 : 123 | shellcode, count = self.ks.asm(assembly, addr=patch_start_addr) 124 | shellcode = "".join([chr(x) for x in shellcode]) 125 | elif len(machine_code) != 0: 126 | shellcode = "".join([chr(x) for x in machine_code]) 127 | else: 128 | shellcode = "" 129 | 130 | if len(shellcode) == 0: 131 | return 0 132 | 133 | self.offset += len(shellcode) 134 | assert(self.offset <= self.eh_frame_size) 135 | if PYTHON_VERSION == 3: 136 | shellcode = shellcode.encode("latin-1") 137 | self.binary.write(patch_start_addr, shellcode) 138 | return patch_start_addr 139 | 140 | def patch_origin(self, start, end=0, assembly="", machine_code=[], string=""): 141 | if len(assembly) != 0: 142 | shellcode, count = self.ks.asm(assembly, addr=start) 143 | shellcode = "".join([chr(x) for x in shellcode]) 144 | elif len(machine_code) != 0: 145 | shellcode = "".join([chr(x) for x in machine_code]) 146 | elif len(string) != 0: 147 | shellcode = string 148 | else: 149 | shellcode = "" 150 | if end != 0: 151 | assert(len(shellcode) <= (end-start)) 152 | shellcode = shellcode.ljust(end - start, "\x90") 153 | if PYTHON_VERSION == 3: 154 | shellcode = shellcode.encode("latin-1") 155 | self.binary.write(start, shellcode) 156 | 157 | def patch_by_jmp(self, jmp_from, jmp_to=0, assembly="", machine_code=[]): 158 | if self.arch == "i386" or self.arch == "amd64": 159 | jmp_ins = "jmp" 160 | elif self.arch == "arm" or self.arch == "aarch64": 161 | jmp_ins = "b" 162 | elif self.arch == "mips" or self.arch =="mips64": 163 | if self.pie: 164 | jmp_ins = "b" 165 | else: 166 | jmp_ins = "j" 167 | if jmp_to: 168 | payload = "{} {}".format(jmp_ins, hex(jmp_to)) 169 | if len(assembly) != 0: 170 | assembly += "\n" + payload 171 | else: 172 | addr = self.get_next_patch_start_addr() + len(machine_code) 173 | shellcode, count = self.ks.asm(payload, addr=addr) 174 | machine_code += shellcode 175 | patch_start_addr = self.add_patch_in_ehframe(assembly=assembly, machine_code=machine_code) 176 | if jmp_to: 177 | # fix translation bug of mips jump code: when keystone translates jmp code, it treats the value of argument start as the base address, 178 | # rather than the address of jump code. 179 | # FYI: shellcode, count = self.ks.asm(assembly, addr=patch_start_addr) 180 | if self.arch == "mips" or self.arch == "mips64": 181 | next_patch_addr = self.get_next_patch_start_addr() 182 | payload = "{} {}".format(jmp_ins, hex(jmp_to)) 183 | # why - 8? because a nop code will be added automatically after jmp code. 184 | self.patch_origin(next_patch_addr-8, assembly=payload) 185 | 186 | if patch_start_addr == 0: 187 | return 0 188 | payload = "{} {}".format(jmp_ins, hex(patch_start_addr)) 189 | self.patch_origin(jmp_from, assembly=payload) 190 | return patch_start_addr 191 | 192 | def patch_by_call(self, call_from, assembly="", machine_code=[]): 193 | if self.arch != "i386" and self.arch != "amd64": 194 | print("Sorry, patch_by_call only support x86 architecture!") 195 | quit() 196 | patch_start_addr = self.add_patch_in_ehframe(assembly=assembly, machine_code=machine_code) 197 | if patch_start_addr == 0: 198 | return 0 199 | 200 | payload = "call {}".format(hex(patch_start_addr)) 201 | self.patch_origin(call_from, assembly=payload) 202 | return patch_start_addr 203 | 204 | def add_constant_in_ehframe(self, string): 205 | patch_start_addr = self.eh_frame_addr + self.offset 206 | if PYTHON_VERSION == 3: 207 | string = string.encode("latin-1") 208 | self.binary.write(patch_start_addr, string) 209 | self.offset += len(string) 210 | return patch_start_addr 211 | 212 | def patch_fmt_by_call(self, call_from): 213 | if self.arch != "i386" and self.arch != "amd64": 214 | print("Sorry, patch_fmt_by_call only support x86 architecture!") 215 | quit() 216 | fmt_addr = self.add_constant_in_ehframe("%s\x00\x00") 217 | patch_start_addr = self.eh_frame_addr + self.offset 218 | 219 | printf_addr = (call_from + 5 + u32(self.binary.read(call_from+1, 4))) & 0xffffffff 220 | if self.bits == 32 and not self.pie: 221 | assembly = """ 222 | mov eax, dword ptr [esp+4] 223 | push eax 224 | lea eax, dword ptr [{0}] 225 | push eax 226 | call {1} 227 | add esp, 0x8 228 | ret 229 | """.format(hex(fmt_addr), hex(printf_addr)) 230 | elif self.bits == 32 and self.pie: 231 | assembly = """ 232 | call {0} 233 | mov eax, dword ptr [esp+8] 234 | push eax 235 | mov eax, dword ptr [esp+4] 236 | sub eax, {0} 237 | add eax, {1} 238 | push eax 239 | call {2} 240 | add esp, 0xc 241 | ret 242 | """.format(hex(patch_start_addr+5), fmt_addr, hex(printf_addr)) 243 | else: 244 | assembly = """ 245 | mov rsi, rdi 246 | lea rdi, qword ptr [{0}] 247 | call {1} 248 | ret 249 | """.format(hex(fmt_addr), hex(printf_addr)) 250 | self.patch_by_call(call_from, assembly=assembly) 251 | 252 | 253 | def save(self, save_path="", fix_eh_frame_flags=True): 254 | if fix_eh_frame_flags: 255 | self.fix_eh_frame_flags() 256 | if len(save_path) != 0: 257 | self.binary.save(save_path) 258 | else: 259 | self.binary.save(self.save_path) 260 | 261 | def get_next_patch_start_addr(self): 262 | return self.eh_frame_addr + self.offset 263 | 264 | def fix_eh_frame_flags(self): 265 | e_phnum = self.binary.header.e_phnum 266 | e_phoff = self.binary.header.e_phoff 267 | phdr_size = 32 if self.bits == 32 else 56 268 | p_flags_offset = 24 if self.bits == 32 else 4 269 | for i in range(0, e_phnum): 270 | phdr = self.binary.get_segment(i).header 271 | page_start = int((phdr.p_vaddr / 0x1000) * 0x1000) 272 | page_end = phdr.p_vaddr + phdr.p_memsz 273 | if page_end % 0x1000 != 0: 274 | page_end = (page_end / 0x1000) * 0x1000 + 0x1000 275 | page_end = int(page_end) 276 | if phdr.p_type == "PT_LOAD" and page_start <= self.eh_frame_addr and page_end >= self.eh_frame_addr + self.eh_frame_size: 277 | print("fix_eh_frame_flags:\npage_start: {} page_end: {} eh_frame_addr: {} eh_frame_size: {} origin phdr.p_flags: {}" 278 | .format(hex(page_start), hex(page_end), hex(self.eh_frame_addr), hex(self.eh_frame_size), str(phdr.p_flags))) 279 | flags = chr(phdr.p_flags | 1) 280 | if PYTHON_VERSION == 3: 281 | flags = flags.encode("latin-1") 282 | self.binary.write(e_phoff + phdr_size * i + p_flags_offset, flags) 283 | 284 | -------------------------------------------------------------------------------- /Tutorial.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # AwdPwnPatcher教程 4 | 5 | ## 格式化字符串 6 | 7 | 程序源码: 8 | 9 | ```C 10 | //gcc fmt.c -o fmt64 -fno-builtin-printf 11 | //gcc fmt.c -o fmt32_nopie -fno-builtin-printf -no-pie -m32 12 | //gcc fmt.c -o fmt32_pie -fno-builtin-printf -m32 13 | #include 14 | #include 15 | #include 16 | 17 | int main() 18 | { 19 | char * p; 20 | p = (char *)malloc(0x40); 21 | read(0, p, 0x40); 22 | printf(p); 23 | return 0; 24 | } 25 | ``` 26 | 27 | 在AWD比赛中,printf格式化漏洞修复不能简简单单地将printf改为puts,会多一个换行符从而过不了check,一般通过增加"%s"参数进行patch。 28 | 29 | ### 32位不开PIE 30 | 31 | ```assembly 32 | 08048486
: 33 | 8048486: 8d 4c 24 04 lea ecx,[esp+0x4] 34 | 804848a: 83 e4 f0 and esp,0xfffffff0 35 | 804848d: ff 71 fc push DWORD PTR [ecx-0x4] 36 | 8048490: 55 push ebp 37 | 8048491: 89 e5 mov ebp,esp 38 | 8048493: 53 push ebx 39 | 8048494: 51 push ecx 40 | 8048495: 83 ec 10 sub esp,0x10 41 | 8048498: e8 23 ff ff ff call 80483c0 <__x86.get_pc_thunk.bx> 42 | 804849d: 81 c3 63 1b 00 00 add ebx,0x1b63 43 | 80484a3: 83 ec 0c sub esp,0xc 44 | 80484a6: 6a 40 push 0x40 45 | 80484a8: e8 93 fe ff ff call 8048340 46 | 80484ad: 83 c4 10 add esp,0x10 47 | 80484b0: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 48 | 80484b3: 83 ec 04 sub esp,0x4 49 | 80484b6: 6a 40 push 0x40 50 | 80484b8: ff 75 f4 push DWORD PTR [ebp-0xc] 51 | 80484bb: 6a 00 push 0x0 52 | 80484bd: e8 5e fe ff ff call 8048320 53 | 80484c2: 83 c4 10 add esp,0x10 54 | 80484c5: 83 ec 0c sub esp,0xc 55 | 80484c8: ff 75 f4 push DWORD PTR [ebp-0xc] 56 | 80484cb: e8 60 fe ff ff call 8048330 57 | 80484d0: 83 c4 10 add esp,0x10 58 | 80484d3: b8 00 00 00 00 mov eax,0x0 59 | 80484d8: 8d 65 f8 lea esp,[ebp-0x8] 60 | 80484db: 59 pop ecx 61 | 80484dc: 5b pop ebx 62 | 80484dd: 5d pop ebp 63 | 80484de: 8d 61 fc lea esp,[ecx-0x4] 64 | 80484e1: c3 ret 65 | ``` 66 | 67 | 使用patch_fmt_by_call函数修补很简单,只需要提供call printf这条指令的地址即可,即0x80484cb: 68 | 69 | ```python 70 | binary = "./fmt32_nopie" 71 | awd_pwn_patcher = AwdPwnPatcher(binary) 72 | awd_pwn_patcher.patch_fmt_by_call(0x80484cb) 73 | awd_pwn_patcher.save() 74 | ``` 75 | 76 | 使用patch_by_jmp函数进行patch,需要将0x80484cb地址之前的一条指令修改为jmp,并且离call printf指令的距离要保证至少有5字节的空间,这里选择从0x80484c5地址开始,patch过程分为三个步骤: 77 | 78 | - 在eh_frame添加"%s"字符串 79 | - 在eh_frame添加patch代码 80 | - 调用完printf后对栈进行调整 81 | 82 | ```python 83 | binary = "./fmt32_nopie" 84 | awd_pwn_patcher = AwdPwnPatcher(binary) 85 | fmt_addr = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 86 | assembly = """ 87 | sub esp, 0xc 88 | push dword ptr [ebp - 0xc] 89 | lea eax, dword ptr [{}] 90 | push eax 91 | """.format(fmt_addr) 92 | awd_pwn_patcher.patch_by_jmp(0x80484c5, jmp_to=0x80484cb, assembly=assembly) 93 | assembly = "add esp, 0x14" 94 | awd_pwn_patcher.patch_origin(0x80484d0, assembly=assembly) 95 | awd_pwn_patcher.save() 96 | ``` 97 | 98 | 99 | 100 | ### 32位开PIE 101 | 102 | ```assembly 103 | 0000057d
: 104 | 57d: 8d 4c 24 04 lea ecx,[esp+0x4] 105 | 581: 83 e4 f0 and esp,0xfffffff0 106 | 584: ff 71 fc push DWORD PTR [ecx-0x4] 107 | 587: 55 push ebp 108 | 588: 89 e5 mov ebp,esp 109 | 58a: 53 push ebx 110 | 58b: 51 push ecx 111 | 58c: 83 ec 10 sub esp,0x10 112 | 58f: e8 ec fe ff ff call 480 <__x86.get_pc_thunk.bx> 113 | 594: 81 c3 3c 1a 00 00 add ebx,0x1a3c 114 | 59a: 83 ec 0c sub esp,0xc 115 | 59d: 6a 40 push 0x40 116 | 59f: e8 6c fe ff ff call 410 117 | 5a4: 83 c4 10 add esp,0x10 118 | 5a7: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 119 | 5aa: 83 ec 04 sub esp,0x4 120 | 5ad: 6a 40 push 0x40 121 | 5af: ff 75 f4 push DWORD PTR [ebp-0xc] 122 | 5b2: 6a 00 push 0x0 123 | 5b4: e8 37 fe ff ff call 3f0 124 | 5b9: 83 c4 10 add esp,0x10 125 | 5bc: 83 ec 0c sub esp,0xc 126 | 5bf: ff 75 f4 push DWORD PTR [ebp-0xc] 127 | 5c2: e8 39 fe ff ff call 400 128 | 5c7: 83 c4 10 add esp,0x10 129 | 5ca: b8 00 00 00 00 mov eax,0x0 130 | 5cf: 8d 65 f8 lea esp,[ebp-0x8] 131 | 5d2: 59 pop ecx 132 | 5d3: 5b pop ebx 133 | 5d4: 5d pop ebp 134 | 5d5: 8d 61 fc lea esp,[ecx-0x4] 135 | 5d8: c3 ret 136 | ``` 137 | 138 | 用patch_fmt_by_call修补很简单,这里只介绍patch_by_jmp的方式。32位程序无法像64位程序一样通过lea指令取相对地址,所以在开了PIE的情况下,需要先获取程序地址,这里通过call+pop的方式取到程序地址: 139 | 140 | ```python 141 | binary = "./fmt32_pie" 142 | awd_pwn_patcher = AwdPwnPatcher(binary) 143 | fmt_offset = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 144 | patch_start_addr = awd_pwn_patcher.get_next_patch_start_addr() 145 | assembly = """ 146 | call {0} 147 | pop eax 148 | sub eax, {0} 149 | add eax, {1} 150 | sub esp, 0xc 151 | push dword ptr [ebp - 0xc] 152 | push eax 153 | """.format(hex(patch_start_addr+5), fmt_offset) 154 | awd_pwn_patcher.patch_by_jmp(0x5bc, jmp_to=0x5c2, assembly=assembly) 155 | assembly = "add esp, 0x14" 156 | awd_pwn_patcher.patch_origin(0x5c7, assembly=assembly) 157 | awd_pwn_patcher.save() 158 | ``` 159 | 160 | ### 64位 161 | 162 | ``` 163 | 00000000000006da
: 164 | 6da: 55 push rbp 165 | 6db: 48 89 e5 mov rbp,rsp 166 | 6de: 48 83 ec 10 sub rsp,0x10 167 | 6e2: bf 40 00 00 00 mov edi,0x40 168 | 6e7: e8 c4 fe ff ff call 5b0 169 | 6ec: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 170 | 6f0: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 171 | 6f4: ba 40 00 00 00 mov edx,0x40 172 | 6f9: 48 89 c6 mov rsi,rax 173 | 6fc: bf 00 00 00 00 mov edi,0x0 174 | 701: e8 9a fe ff ff call 5a0 175 | 706: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 176 | 70a: 48 89 c7 mov rdi,rax 177 | 70d: b8 00 00 00 00 mov eax,0x0 178 | 712: e8 79 fe ff ff call 590 179 | 717: b8 00 00 00 00 mov eax,0x0 180 | 71c: c9 leave 181 | 71d: c3 ret 182 | ``` 183 | 184 | 64位程序通过patch_by_jmp修补的话,还不需要考虑恢复栈平衡的情况,比32位要简单: 185 | 186 | ```python 187 | binary = "./fmt64" 188 | awd_pwn_patcher = AwdPwnPatcher(binary) 189 | fmt_offset = awd_pwn_patcher.add_constant_in_ehframe("%s\x00\x00") 190 | assembly = """ 191 | mov rsi, qword ptr [rbp-0x8] 192 | lea rdi, qword ptr [{}] 193 | """.format(hex(fmt_offset)) 194 | awd_pwn_patcher.patch_by_jmp(0x706, jmp_to=0x712, assembly=assembly) 195 | awd_pwn_patcher.save() 196 | ``` 197 | 198 | ## UAF 199 | 200 | 程序源码: 201 | 202 | ```c 203 | //gcc fmt.c -o uaf64 204 | //gcc fmt.c -o uaf32 -no-pie -m32 205 | #include 206 | #include 207 | 208 | void * p[10]; 209 | 210 | int main() 211 | { 212 | unsigned int index; 213 | printf("Input index: "); 214 | scanf("%u", &index); 215 | if(index<10){ 216 | p[index] = malloc(0x40); 217 | free(p[index]); 218 | } 219 | return 0; 220 | } 221 | ``` 222 | 223 | 一般严格的AWD比赛中,都不允许修改call free这条指令,否则直接nop即可,这种情况有两种修复方法: 224 | 225 | - 在call free前插入jmp 226 | - 在call free后插入jmp 227 | 228 | ### 32位 229 | 230 | ```assembly 231 | 08048536
: 232 | ... ... 233 | 8048597: e8 44 fe ff ff call 80483e0 234 | 804859c: 83 c4 10 add esp,0x10 235 | 804859f: 89 c2 mov edx,eax 236 | 80485a1: c7 c0 60 a0 04 08 mov eax,0x804a060 237 | 80485a7: 89 14 b0 mov DWORD PTR [eax+esi*4],edx 238 | 80485aa: 8b 55 e0 mov edx,DWORD PTR [ebp-0x20] 239 | 80485ad: c7 c0 60 a0 04 08 mov eax,0x804a060 240 | 80485b3: 8b 04 90 mov eax,DWORD PTR [eax+edx*4] 241 | 80485b6: 83 ec 0c sub esp,0xc 242 | 80485b9: 50 push eax 243 | 80485ba: e8 01 fe ff ff call 80483c0 244 | 80485bf: 83 c4 10 add esp,0x10 245 | 80485c2: b8 00 00 00 00 mov eax,0x0 246 | 80485c7: 8b 4d e4 mov ecx,DWORD PTR [ebp-0x1c] 247 | 80485ca: 65 33 0d 14 00 00 00 xor ecx,DWORD PTR gs:0x14 248 | 80485d1: 74 05 je 80485d8 249 | 80485d3: e8 88 00 00 00 call 8048660 <__stack_chk_fail_local> 250 | 80485d8: 8d 65 f4 lea esp,[ebp-0xc] 251 | 80485db: 59 pop ecx 252 | 80485dc: 5b pop ebx 253 | 80485dd: 5e pop esi 254 | 80485de: 5d pop ebp 255 | 80485df: 8d 61 fc lea esp,[ecx-0x4] 256 | 80485e2: c3 ret 257 | ``` 258 | 259 | 260 | 261 | 这里通过在call free之后插入jmp的方式进行patch,从0x80485bf开始跳转,patch代码结束后再跳回0x80485c7: 262 | 263 | ```python 264 | binary = "./uaf32" 265 | awd_pwn_patcher = AwdPwnPatcher(binary) 266 | assembly = """ 267 | add esp, 0x10 268 | mov eax, 0 269 | mov edx, dword ptr [ebp - 0x20] 270 | mov eax, 0x804a060 271 | lea eax, dword ptr [eax + edx*4] 272 | mov dword ptr [eax], 0 273 | """ 274 | awd_pwn_patcher.patch_by_jmp(0x80485bf, jmp_to=0x80485c7, assembly=assembly) 275 | awd_pwn_patcher.save() 276 | ``` 277 | 278 | 279 | 280 | ### 64位 281 | 282 | ```assembly 283 | 00000000000007aa
: 284 | ... ... 285 | 7fb: e8 70 fe ff ff call 670 286 | 800: 48 89 c1 mov rcx,rax 287 | 803: 89 d8 mov eax,ebx 288 | 805: 48 8d 14 c5 00 00 00 lea rdx,[rax*8+0x0] 289 | 80c: 00 290 | 80d: 48 8d 05 2c 08 20 00 lea rax,[rip+0x20082c] # 201040

291 | 814: 48 89 0c 02 mov QWORD PTR [rdx+rax*1],rcx 292 | 818: 8b 45 e4 mov eax,DWORD PTR [rbp-0x1c] 293 | 81b: 89 c0 mov eax,eax 294 | 81d: 48 8d 14 c5 00 00 00 lea rdx,[rax*8+0x0] 295 | 824: 00 296 | 825: 48 8d 05 14 08 20 00 lea rax,[rip+0x200814] # 201040

297 | 82c: 48 8b 04 02 mov rax,QWORD PTR [rdx+rax*1] 298 | 830: 48 89 c7 mov rdi,rax 299 | 833: e8 08 fe ff ff call 640 300 | 838: b8 00 00 00 00 mov eax,0x0 301 | 83d: 48 8b 75 e8 mov rsi,QWORD PTR [rbp-0x18] 302 | 841: 64 48 33 34 25 28 00 xor rsi,QWORD PTR fs:0x28 303 | 848: 00 00 304 | 84a: 74 05 je 851 305 | 84c: e8 ff fd ff ff call 650 <__stack_chk_fail@plt> 306 | 851: 48 83 c4 18 add rsp,0x18 307 | 855: 5b pop rbx 308 | 856: 5d pop rbp 309 | 857: c3 ret 310 | ``` 311 | 312 | 在0x838指令出插入jmp,然后再跳回0x83d地址处: 313 | 314 | ```python 315 | binary = "./uaf64" 316 | awd_pwn_patcher = AwdPwnPatcher(binary) 317 | assembly = """ 318 | mov eax, 0 319 | mov eax, dword ptr [rbp - 0x1c] 320 | cdqe 321 | lea rdx, qword ptr [0x201040] 322 | lea rax, qword ptr [rdx + rax*8] 323 | mov qword ptr [rax], 0 324 | """ 325 | awd_pwn_patcher.patch_by_jmp(0x838, jmp_to=0x83d, assembly=assembly) 326 | awd_pwn_patcher.save() 327 | ``` 328 | 329 | 330 | 331 | ## 栈溢出 332 | 333 | 程序源码: 334 | 335 | ```C 336 | //gcc overflow.c -o overflow64 -fno-stack-protector 337 | //gcc overflow.c -o overflow32 -fno-stack-protector -no-pie -m32 338 | #include 339 | #include 340 | 341 | int main() 342 | { 343 | char a[32]; 344 | read(0, a, 0x100); 345 | puts(a); 346 | return 0; 347 | } 348 | ``` 349 | 350 | 351 | 352 | ### 32位 353 | 354 | ```C 355 | 08048456

: 356 | 8048456: 8d 4c 24 04 lea ecx,[esp+0x4] 357 | 804845a: 83 e4 f0 and esp,0xfffffff0 358 | 804845d: ff 71 fc push DWORD PTR [ecx-0x4] 359 | 8048460: 55 push ebp 360 | 8048461: 89 e5 mov ebp,esp 361 | 8048463: 53 push ebx 362 | 8048464: 51 push ecx 363 | 8048465: 83 ec 20 sub esp,0x20 364 | 8048468: e8 23 ff ff ff call 8048390 <__x86.get_pc_thunk.bx> 365 | 804846d: 81 c3 93 1b 00 00 add ebx,0x1b93 366 | 8048473: 83 ec 04 sub esp,0x4 367 | 8048476: 68 00 01 00 00 push 0x100 368 | 804847b: 8d 45 d8 lea eax,[ebp-0x28] 369 | 804847e: 50 push eax 370 | 804847f: 6a 00 push 0x0 371 | 8048481: e8 7a fe ff ff call 8048300 372 | 8048486: 83 c4 10 add esp,0x10 373 | 8048489: 83 ec 0c sub esp,0xc 374 | 804848c: 8d 45 d8 lea eax,[ebp-0x28] 375 | 804848f: 50 push eax 376 | 8048490: e8 7b fe ff ff call 8048310 377 | 8048495: 83 c4 10 add esp,0x10 378 | 8048498: b8 00 00 00 00 mov eax,0x0 379 | 804849d: 8d 65 f8 lea esp,[ebp-0x8] 380 | 80484a0: 59 pop ecx 381 | 80484a1: 5b pop ebx 382 | 80484a2: 5d pop ebp 383 | 80484a3: 8d 61 fc lea esp,[ecx-0x4] 384 | 80484a6: c3 ret 385 | ``` 386 | 387 | 将0x8048476地址处的push 0x100改成push 0x20即可: 388 | 389 | ```python 390 | binary = "./overflow32" 391 | awd_pwn_patcher = AwdPwnPatcher(binary) 392 | assembly = ''' 393 | push 0x20 394 | ''' 395 | awd_pwn_patcher.patch_origin(0x8048476, end=0x804847b, assembly=assembly) 396 | awd_pwn_patcher.save() 397 | ``` 398 | 399 | ### 64位 400 | 401 | ```C 402 | 000000000000068a
: 403 | 68a: 55 push rbp 404 | 68b: 48 89 e5 mov rbp,rsp 405 | 68e: 48 83 ec 20 sub rsp,0x20 406 | 692: 48 8d 45 e0 lea rax,[rbp-0x20] 407 | 696: ba 00 01 00 00 mov edx,0x100 408 | 69b: 48 89 c6 mov rsi,rax 409 | 69e: bf 00 00 00 00 mov edi,0x0 410 | 6a3: e8 b8 fe ff ff call 560 411 | 6a8: 48 8d 45 e0 lea rax,[rbp-0x20] 412 | 6ac: 48 89 c7 mov rdi,rax 413 | 6af: e8 9c fe ff ff call 550 414 | 6b4: b8 00 00 00 00 mov eax,0x0 415 | 6b9: c9 leave 416 | 6ba: c3 ret 417 | ``` 418 | 419 | 将0x696地址处的mov edx, 0x100改成mov edx, 0x20即可: 420 | 421 | ```python 422 | binary = "./overflow64" 423 | awd_pwn_patcher = AwdPwnPatcher(binary) 424 | assembly = ''' 425 | mov edx, 0x20 426 | ''' 427 | awd_pwn_patcher.patch_origin(0x696, end=0x69b, assembly=assembly) 428 | awd_pwn_patcher.save() 429 | ``` 430 | 431 | 432 | 433 | --------------------------------------------------------------------------------