24年ciscn初赛Re(部分)
24年ciscn初赛Re(部分)
一、asm_re
一开始还想还原这个ida工程文件hhh,发现根本做不到,后面纯看arm汇编代码,直接手撕就好,加密逻辑在这儿,密文一开始还找半天,后面发现应该是存在变量unk_100003F10里面
搓出脚本之后也还是卡了一小会儿,最后反应过来大小端的问题,改一下小端就好了,爆破一下直接出
exp:
1 | k = [ |
二、whereThel1b
还真是第一次遇见这种,给了个so和一个py文件,一开始的想法是能不能给so解包之类的,因为py文件里面密文给了,就差一个加密逻辑,找了一大圈还是没找到,最后还是想到了调一下so文件,像调安卓那样
动调起来锁定出了两个函数,得知输入的数据先经过base64编码之后再进行的异或
加密逻辑知道了,但是不知道异或的值是什么,一开始以为是存在r18里面的,最后调了一下找不到规律,最后想到重新写一份密文输入,然后把加密之后的数据输出一下,前后异或得到所需异或的值,想办法输入一个输构造出经过base64编码之后长度为56的数
exp:
其中aa是上图构造的“55555555555555555555555555555555555555555555”的base64之后的值,然后bb是运行上图之后得到的异或之后的值,最后运行出来的结果解一下base64就行
1 | encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26] |
三、gdb_debug
进入主函数之后逻辑还是相当清楚的,锁定了一下伪随机数
动调跑起来取出随机数
1 | 0xd9, 0x0f, 0x18, 0xBD, 0xC7, 0x16, 0x81, 0xbe, 0xf8, 0x4A, 0x65, 0xf2, 0x5D, 0xab, 0x74, 0x33, 0xd4, 0xa5, 0x67, 0x98, 0x9f, 0x7E, 0x2B, 0x5D, 0xc2, 0xaf, 0x8e, 0x3A, 0x4C, 0xa5, 0X75, 0X25, 0xb4, 0x8d, 0xe3, 0X7B, 0xa3, 0x64 |
然后直接从后往前逆就好
exp:
1 |
|
四、rust_baby(复现)
经典Rust终于还是出现了,比赛时候属于是草草看一眼就直接过了,结束之后复现一下,直接生逆太复杂了歪日,选择用frida插桩进行字节爆破来解。
大概逛了一下程序流程,直接从main里面锁定了目标函数如下图:
跟进之后发现巨长,又加上是rust,瞬间不想看加密逻辑了,先寻找check flag的地方,然后往前回溯,此处下面有判断,然后还有个错误提示,所以锁定这块儿应该就是check部分
然后走流程图来看,循环取值作比较处的逻辑如下,其中cmp rcx, rdx
属于是长度的check,如果长度对劲的话直接走右边的逻辑,其中rcx中存储的长度为8C,即为密文长度,为什么这里不用check值是否对就直接走右边的正确逻辑了呢,因为check值是在左边下面的流程做的,所以直接将桩子插在 0x3EA8处,后续脚本里面做一个次数的比较,每尝试爆破一个字符的时候,如果该处的暗桩被打到的次数大于之前的次数的话即说明该字符可取,并将新的次数存起来用于下一轮的爆破。
下图标签处即为左边逻辑进行flag check的地方,其中mov r8b, [rax+rdx]
即是取出 input 经过加密之后的内容,cmp r8b, [r14+rdx]
即是正常与密文进行比较操作,特别注意此处属于是单字节check吧,所以完全可以单字节爆破一手
下面是check之后的走向红线即是比较没过,直接判错了,然后直接走nonono的逻辑,过了的话继续走绿线循环这块儿的整个个判断逻辑
明确可以字节爆破之后,修改控制流走一下右边的逻辑看一下程序正确输入之后的提示输出是什么,correct flag
将会作为结束爆破的信息,彼时爆破进程会以一个异常跳出循环
先写一个frida的hook插桩脚本(就在对应调用的地方插了个桩,然后每次调用有一个累加的过程,最后将数字传给主脚本):
1 | var number = 0 |
EXP:
1 | import subprocess |