[Crypto] nanoDiamond-rev 题目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 # from Crypto.
[Reverse] Archgame load_code处对bin文件进行了一个解密
1 2 3 for ( i = 0LL; i < size; ++i ) { g_code_data[i] ^= *((_BYTE *)&global_key + (i & 3)); } round()函数有两个switch,手动修复一下,大概逻辑是这个样子。
是一些关于unicorn虚拟机的操作,查一下unicorn引擎的文档可以得到函数作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 __int64 round() { const char *v0; // rax const char *v1; // rax const char *v2; // rax int errorcode2; // eax unsigned int roundkey; // [rsp+0h] [rbp-30h] BYREF unsigned int errorcode1; // [rsp+4h] [rbp-2Ch] __int64 uc_engine; // [rsp+8h] [rbp-28h] BYREF char v8; // [rsp+10h] [rbp-20h] BYREF __int64 v9; // [rsp+18h] [rbp-18h] BYREF __int64 v10[2]; // [rsp+20h] [rbp-10h] BYREF v10[1] = __readfsqword(0x28u); errorcode1 = uc_open((unsigned int)g_arch, (unsigned int)g_mode, &uc_engine); //创建虚拟机实例 /* Error Handler */ errorcode1 = uc_mem_map(uc_engine, 0LL, 655360LL, 7LL); //创建从地址0开始 长度655360的内存,权限为RWX /* Error Handler */ uc_mem_write(uc_engine, 0LL, g_code_data, 655360LL); //向内存地址0处写入bin文件解密结果 errorcode1 = uc_mem_map(uc_engine, 0x70000000LL, 0x4000LL, 7LL); //创建从地址0x70000000开始 长度0x4000的内存,权限为RWX /* Error Handler */ uc_mem_write(uc_engine, 0x70000000LL, input_area, 0x4000LL); //向地址0x70000000写入输入的数据,长度为0x4000 errorcode2 = uc_mem_map(uc_engine, 0x20000000LL, 0x8000LL, 7LL); //创建从地址0x20000000开始 长度0x8000的内存,权限为RWX v9 = 536903424LL; v10[0] = 1879048448LL; switch ( g_arch ) { case 1: //ARM uc_reg_write(uc_engine, 12LL, &v9); uc_reg_write(uc_engine, 10LL, v10); //写寄存器 break; case 2: // ARM-64 uc_reg_write(uc_engine, 4LL, &v9); uc_reg_write(uc_engine, 2LL, v10); break; case 3: // Mips uc_reg_write(uc_engine, 31LL, &v9); uc_reg_write(uc_engine, 33LL, v10); break; case 5: // PowerPC uc_reg_write(uc_engine, 3LL, &v9); uc_reg_write(uc_engine, 74LL, v10); break; case 8: // RISCV uc_reg_write(uc_engine, 3LL, &v9); uc_reg_write(uc_engine, 2LL, v10); break; } uc_hook_add(uc_engine, (unsigned int)&v8, 1008, (unsigned int)hook_mem, 0, 1, 0LL); //hook了一些非法操作,看起来像是异常处理 hook_mem 是nop函数 errorcode1 = uc_emu_start(uc_engine, 0LL, v9, 0LL, 0LL); //从地址0执行到536903424 switch ( g_arch ) { case 1u: uc_reg_read(uc_engine, 66, (__int64)&roundkey); //读寄存器 break; case 2u: uc_reg_read(uc_engine, 199, (__int64)&roundkey); break; case 3u: uc_reg_read(uc_engine, 4, (__int64)&roundkey); break; case 5u: uc_reg_read(uc_engine, 5, (__int64)&roundkey); break; case 8u: uc_reg_read(uc_engine, 11, (__int64)&roundkey); break; default: break; } uc_close(uc_engine); return roundkey; } 程序的逻辑整体是把challs.
打开是一个德扑小游戏,根据已知信息找开源代码。
https://github.com/XanderUZZZER/Blackjack-master
比较关心的是原开源代码中没有的对gamemessage文件的处理,可以注意到一个类似作弊码的goldFunc函数。
整理一下删除没用的部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 private static void goldFunc(ArrayList input, Blackjack.
Reverse 手不务正业实录
Crypto-p or s 题目脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 from secret import keys, flag from Crypto.
模糊测试(Fuzz)介绍 模糊测试是一种漏洞发现的手段,通过用一些方式构造一些输入数据自动化地发送给程序,同时监测程序是否出现异常,将造成crash的输入数据返回给测试人员以达到发现漏洞的目的。
这里仅讨论白盒测试。
LLVM简介 LLVM是一款非常流行的开源编译器框架,支持多种语言和底层硬件。
在使用 LLVM 进行代码优化以及插桩时,我们必须要先了解 LLVM 的基础架构。 经典编译器架构主要分为前端、中间层和后端三个部分。而我们常用的 GCC 在设计之初就导致前后端耦合度非常高,因此支持一个新的架构或编程语言对 GCC 来说都是非常难的一件事。 为了避免强耦合的情况发生,LLVM 采用了非常简洁明了的三段式设计,架构如下所示:
其中LLVM的前端会对高级语言进行编译,生成能被LLVM解析并利用的中间件LLVM-IR。该IR在经过LLVM优化器进行一定程度的优化之后, 被送到LLVM的后端,根据处理器的不同最终编译成可被执行的二进制文件。
目前而言的大部分研究都会以 LLVM IR 作为工具进行程序代码的静态分析。我们知道 LLVM IR 会在优化阶段进行相应的优化,LLVM 也在优化阶段允许用户自定义一些对 IR 的操作,从而达到静态分析的效果,这种自定义模块叫做 LLVM Pass。
传统Fuzz常用工具介绍与工作原理分析 AFL/AFL++(American Fuzzy Loop) 整体架构 在Fuzz开始前AFL首先通过afl-gcc/afl-clang等编译器的wrapper来对待测程序源代码进行插桩并编译。其中插桩用于记录分支信息(如被触发次数等),用于进一步分析。
整体工作流程图如下:
首先AFL从用户提供的一组输入开始,并尝试对输入进行一些变异(详见下文)。若这些变异之后的输入数据触发了新的执行路径,则加入“输入队列”,成为新的输入数据并重复上述过程。
输入变异策略 (其实是按顺序进行的)
一些不具有随机性的操作 bitflip bitflip按照一些的步长对bit进行一些翻转。
在这个过程中,AFL同时会生成token和effector map。
>>token
判断规则:如果连续多个bytes的最低位被翻转后,程序的执行路径都未变化,而且与原始执行路径不一致,那么就把这一段连续的bytes判断是一条token。
例如,众所周知,在PNG文件中使用IHDR作为一个起始块的标识。当翻转I的最高位时,该标识被破坏,此时程序的执行路径必定与原本不相同。这样AFL就得到了一个可能的token:IHDR,为后面的变异做准备。
>>effector map
说人话就是判断有效字节。
具体地,在对每个byte进行翻转时,如果其造成执行路径与原始路径不一致,就将该byte在effector map中标记为1,即“有效”的,否则标记为0,即“无效”的。
如果一个byte完全翻转,都无法带来执行路径的变化,那么这个byte很可能是“无效”的,对整个fuzzing的意义不大。所以,在随后的一些变异中,会参考effector map,跳过那些“无效”的byte,从而节省了执行资源。
ret2libc 题目:ciscn_2019_c_1
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) 分析程序,发现encrypt函数存在危险函数gets可以造成栈溢出。程序开了NX保护,没有现成可供使用的shell代码,考虑使用ret2libc
首先使用ROPgadget找到需要的gadget:pop rdi; ret和ret
python ROPgadget.py --binary ciscn --only "pop|ret"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Gadgets information ============================================================ 0x0000000000400c7c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400c7e : pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400c80 : pop r14 ; pop r15 ; ret 0x0000000000400c82 : pop r15 ; ret 0x0000000000400c7b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400c7f : pop rbp ; pop r14 ; pop r15 ; ret 0x00000000004007f0 : pop rbp ; ret 0x0000000000400aec : pop rbx ; pop rbp ; ret 0x0000000000400c83 : pop rdi ; ret 0x0000000000400c81 : pop rsi ; pop r15 ; ret 0x0000000000400c7d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006b9 : ret 0x00000000004008ca : ret 0x2017 0x0000000000400962 : ret 0x458b 0x00000000004009c5 : ret 0xbf02 Unique gadgets found: 15 利用encrypt函数中的puts泄露出puts函数在内存中的地址,并重新调用执行main函数。