IDA打开发现一大堆模板很丑,但仔细看一下其实只有一堆变量来回操作而已
基本上就是各种STL和vector的用法,算法名都保留下来了所以难度下降很多
基本流程是接收输入、生成斐波那契数列的十六项
然后对输入依次使用transform和accumulate算法
分别是遍历vector中的一元运算和二元运算
运算都是自定义的方法,双击算法进去可以看到lambda函数
transform的运算是累加第0个元素
accumulate则是通过back_inster迭代器将容器逆序
所以最终结果只要保证生成数组等于fib数列即可
但其实直接动态调试,黑盒的方法很容易看出来两次算法变换的内容,而无需去关注复杂的STL
fib = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987][::-1]
x = fib[0]
for i in range(len(fib)):
print(0xffffffff&(fib[i]-fib[0]))
核心函数中混有一个没有用的数组,最后比较仅用到另一个数组
生成方法是逐个字节遍历,每个字节模58的余数放在结果数组中,再整除58后移一位,直到整除58为0为止
最后用长为58的表转为可见字符
这里的算法有点绕,最好自己动态调试跟着走一遍,观察变量的值,自己写算法复刻一下比较
这里的d1是结果数组,d2没有意义无需看
关键是v21和break的条件
复刻出来长这样
比赛的时候想解方程结果眼瞎输错了数字,抱着侥幸心理试了一下base58,结果直接就出来了
>>>
base58.b58decode(b"D9cS9N9iHjMLTdA8YSMRMp")
b’base58_is_boring’
后来回头观察了一下发现是眼瞎的问题,重修了一下方程就OK了
这里根据数据可以发现第一轮循环中倒数三个数分别为t=a[0]
t%58
, t/58%58
, t/58/58%58
第二轮则可化简为t = a[0]<<8 + a[1]
的状态下
t%58
, t/58%58
, t/58/58%58
以此类推,最终结果就是所有Input拼接起来,满足r[x]==input/(58^x)%58
于是列出方程求解得到input
r = [6, 4, 20, 6, 20, 38, 23, 50, 11, 3, 47, 1, 49, 26, 55, 37, 18, 55, 31, 40, 1, 20]
r = o2
from z3 import *
x = Int("x")
s = Solver()
s.add(x>0)
for i in range(len(r)):
s.add((x%58)==r[len(r)-1-i])
# print(s)
x = x/58
print(s.check())
print(s.model())
要求输入38个字符,花指令破坏了函数结构
由于时间关系无暇去花,直接动态调试跟随,发现检查长度并memcpy输入到新的表以后就用key循环生成了一个新256字节的表,而这是RC4的标志。往后看发现有异或,于是直接解密,发现得到可见字符串,提交失败,再次调试发现存在逆序操作,遂将结果逆序完成
d = [0x5B, 0xD6, 0xD0, 0x26, 0xC8, 0xDD, 0x19, 126, 110, 62, -53, 22, -111, 125, -1, -81, -35, 118, 100, -80, -9, -27, -119, 87, -126, -97, 12, 0, -98, -48, 69, -6]
import sys, os, hashlib, time, base64
def rc4(plain, key):
box = list(range(256))
j = 0
for i in range(256):
j = (j + box[i] + ord(key[i % len(key)])) % 256
box[i], box[j] = box[j], box[i]
print(box)
# print(type(box)) #for_test
# return box
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr((s ^ k)&0xff))
return res
r = rc4(d, "qwertyuiop")
r = [(i) for i in r]
print("".join(r[::-1]))
回头看了一下花指令都长的一个样子,连正则表达式都没必要上,直接去除固定字节流就可以了
from ida_bytes import get_bytes, patch_bytes
import re
addr = 0x12a2400
end = 0x12a3000
buf = get_bytes(addr, end-addr)
def handler1(s):
s = s.group(0)
print("".join(["%02x"%ord(i) for i in s]))
s = "\x90"*len(s)
return s
p = r"\xe8\x00\x00\x00\x00.*?\xc3.*?\xc3"
buf = re.sub(p, handler1, buf, flags=re.I)
patch_bytes(addr, buf)
print("Done")
搞完以后注意函数里面的栈帧还是按照原来指令的,最简单的方式就是直接删除function再重新按p来create,我记得好像在哪里见过重新分析的按钮来着 没找到2333有大哥记得评论分享一下撒~
去完花以后的F5看起来非常明朗,显而易见的RC4
然后逆序的小阴谋藏在这里
去花以后这个题目其实非常简单~签到题的难度了
重要比赛时没时间了2333放题太晚 misc3的APK太坑人惹