menu 牢记自己是菜
高校战“疫”网络安全分享赛 re天津垓
3665 浏览 | 2020-03-11 | 阅读时间: 约 4 分钟 | 分类: 高校战“疫”网络安全分享赛 | 标签:
请注意,本文编写于 1519 天前,最后修改于 1491 天前,其中某些信息可能已经过时。

0x1天津垓re主要考点

比赛有回顾,地址点这里
静态分析加一些反调试措施和加壳的保护
比赛的时候只做到了Part1,完成了Key1=Caucasus@s_ability的求解,完全没有发现part2的存在。最后神经病的去看假面骑士了,研究了这几个主人公之间的关系,属实蠢驴。


0x2 Part1

首先查壳,拖入IDA,查看字符串,查找引用,进入Part1主体函数,菜鸡五连。这个就不给具体过程了,上几张图(毕竟作为一个re菜鸡也只会这样了)。逻辑很简单,上面那一片的数字转为字符,是一个倒叙的小端序。正过来代入加密程序,解除我们的Key1=Caucasus@s_ability


这是我们队伍大佬给出来的题解中的python关与Part1的脚本,当时比赛的时候我用的C++,没有保存代码,就用这个代替一下吧,之后还是要慢慢习惯用python解决问题了。

在这里尝试一下,发现不是flag,联想到之前的出题规则,要求出题人要在五分钟之内给到选手正确的线索,所以这个Key1一定是一个有用的东西,x键,查看str的交叉引用,果然,在这段下方还有两个地方用到的这个字符串,跳转过去。



跳转过去,找到一个函数,和一大堆字节数据,我一直都觉得这个是一个字节流数据,但是这是一段没有被IDA反汇编的汇编代码。这里就到了我纠结了很久的地方,我不太明白这个函数与这一大串代码的关系,在这位大佬博客里我找到了答案,这是一段被加密后的程序段,而Key1作为这个加密的要是,第二个函数作为加密时候使用的方法。所以我们要做的就是把这段数据流进行解密,然后F5,分析里面的程序。

0x3 Part2

第二部分,这个就是我的知识盲区了,对于IDA自带的Python脚本我基本上没怎么用过,整个过程我也是参考大佬博客才完成的,但是这个方法我还是要好好学习一下。

import idc
from binascii import *
s = "Caucasus@s_ability"
def main():
    count = 1045  //设置读取大小
    start = 0x10040164D  //设置开始读取位置,byte_10040164D
    addr = start
    fp = open('origin','wb')
    for i in range(0,1045):
        c = s[i%18]
        addr = start + i;
        res = idc.Byte(addr)
        res ^= ord(c)
        fp.write(a2b_hex(hex(res)[2:].rjust(2,'0')))
    fp.close()
if __name__ == '__main__':
    main()

地址0x164d是被加壳的内容,大小为1045。sub_1506函数在解壳,之后在第5行即可直接调用unk_164d()函数。其中第18行就是解码过程,其中(lpAddress+i)就是unk_164d[i],然后(v8+i%18)就是str[i%18],可以先把解码之后的内容dump出来。生成一个origin的二进制文件,用IDA打开,看见这段代码虽然没办法反汇编,但是已经里答案很近了,我们需要用这个代码去替换掉之前程序中的数据。

再将这些数据写回,定义一个rewrite函数,然后在IDA下面python处调用函数。

def rewrite():
    fp = open('tjh.exe','rb')
    fp2 = open('origin','rb')
    data2 = fp2.read()
    data=fp.read()
    idx = data.find('\x16\x29\xf4\x8f') #是地址0x164d的内容,找到index,可以准确替换
    out_data = data[:idx]
    for i in range(0,1045):
        out_data+=data2[i]
    out_data += data[idx+1045:]
    fp3 = open('out.exe','wb')
    fp3.write(out_data)
    fp3.close()

函数结束后,会在根目录下生成一个out.exe,拖入IDA,发现这个exe就是之前两个二进制文件的组合。

这里要注意一个问题,就是IDA中会对中文目录报错,所以我们先修改exe的名称,再进行连接。
之后拖入IDA进行反编译,F5,出现最后的加密逻辑。

这个逻辑很简单,与Part1类似。上脚本

s=r"""
v9 = 2007666;
v10 = 2125764;
v11 = 1909251;
v12 = 2027349;
v13 = 2421009;
v14 = 1653372;
v15 = 2047032;
v16 = 2184813;
v17 = 2302911;
v18 = 2263545;
v19 = 1909251;
v20 = 2165130;
v21 = 1968300;
v22 = 2243862;
v23 = 2066715;
v24 = 2322594;
v25 = 1987983;
v26 = 2243862;
v27 = 1869885;
v28 = 2066715;
v29 = 2263545;
v30 = 1869885;
v31 = 964467;
v32 = 944784;
v33 = 944784;
v34 = 944784;
v35 = 728271;
v36 = 1869885;
v37 = 2263545;
v38 = 2283228;
v39 = 2243862;
v40 = 2184813;
v41 = 2165130;
v42 = 2027349;
v43 = 1987983;
v44 = 2243862;
v45 = 1869885;
v46 = 2283228;
v47 = 2047032;
v48 = 1909251;
v49 = 2165130;
v50 = 1869885;
v51 = 2401326;
v52 = 1987983;
v53 = 2243862;
v54 = 2184813;
v55 = 885735;
v56 = 2184813;
v57 = 2165130;
v58 = 1987983;
v59 = 2460375;
"""
def solve():
    res = s.split('\n')
    n_list = []
    for item in res[1:-1]:
        number = item.split('=')[1].strip(';')
        n_list.append(int(number))
    v61 = 19683
    v62 = 0x8000000b
    flag = ""
    for i in range(0,51):
        for c in range(0x20,0x7f):
            t = v61 * (c%v62)
            if t == n_list[i]:
                flag += chr(c)
    print(flag)

最后得出flag为为flag{Thousandriver_is_1000%_stronger_than_zero-one}


0x4自我反思

整道题考察了两个点,一个是静态代码的分析,一道是部分代码的壳加密。值得注意的的是,在题目的函数中,有一个反调试的函数,禁止运行一切调试程序。在我没有想明白这个题目的最终解法之前,我看见了一篇wp,进入了IDA的那个函数,强行修改了反调试函数,将其汇编指令改为了nop,然后进行了动态调试。当调试道了那个加密函数的时候,自动完成了加密与解密,打上断点,就可以直接分析到我们最后手动解密的函数。附上这位大佬的wp,但是在我实际操作的过程中,发现我使用的IDA对汇编进行修补后,保存的exe,X64debug不识别,提示损坏的PE文件,没有办法进行动态调试。这里先存疑,应该是我那里操作不当,但是这的确是一种更加nb的方法。总的来说,在反汇编函数中发现反调试代码时,一定要留个神,因为对这个代码动态分析很有可能是一种捷径,换句话说可能在内部自己自制了一种可对称的加密算法。还有一个是,在一道逆向题目没有想法的时候,尝试对关键字符X获取交叉引用,比去看假面骑士有用的多。

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!