CISCN 2019 reverse Writeup

栏目: 服务器 · 发布时间: 5年前

内容简介:正常的逆向(大雾)好像就只有两个。。另外还有一个从题目就可以看出来这是个go语言的题目,打开一看是去符号表的逆向,IDA里面通过脚本恢复符号表开始看代码

正常的逆向(大雾)好像就只有两个。。另外还有一个 apk 、一个 wasm 、还有一个 MBR 。。。这三个后续慢慢来补,需要的知识可能有点多,先把两个相对简单的题写一下。

easygo

从题目就可以看出来这是个 go 语言的题目,打开一看是去符号表的逆向,IDA里面通过脚本恢复符号表开始看代码

https://github.com/sibears/IDAGolangHelper

跟到 main.main 看到关键的比较函数:

CISCN 2019 reverse Writeup

gdb跟一下,在 0x495318 地址处下断点即可获得明文的flag:

CISCN 2019 reverse Writeup

bbvvmm

一道让人头疼的题目,根据题目名称大概猜到是一道vm的题目,也就是在前一天刚刚正在研究 reversing.kr 上的另一道vm题目,有点头大。先说说这个题目把。

拖进ida里面,先分析一下:

CISCN 2019 reverse Writeup

整个程序我们分为两部分来看,一部分校验用户名,结果存储在 v5 中,一部分校验密码,结果存储在 *(ptr+25)v8 里面。最后两者检验均通过之后链接上服务器就能打印 flag

先看对用户名的验证,简单跟一下我们的输入经过了这么些函数 sub_4066C0(encode_hex) -> sub_4018C4(sm4_en) -> sub_4067BD(encode_hex) -> sub_400AA6(base64) -> strcmp

首先 sub_4066C0 将我们的输入进行了 encode('hex') 操作,然后使用sm4加密算法进行加密,而密钥就是中间一长串赋值操作,从 v17-v32 ,共16字节的秘钥,然后加密之后进入了一个魔改的 base64 函数,方式没变,替换了原本 base64 的编码表。然后和 RVYtG85NQ9OPHU4uQ8AuFM+MHVVrFMJMR8FuF8WJQ8Y= 进行比较,那我们就可以逆着来就能得到用户名,脚本如下:

import binascii
base64_charset = 'IJLMNOPKABDEFGHCQRTUVWXSYZbcdefa45789+/6ghjklmnioprstuvqwxz0123y'
def b64_decode(base64_str):

    base64_bytes = ['{:0>6}'.format(str(bin(base64_charset.index(s))).replace('0b', '')) for s in base64_str if
                    s != '=']
    resp = bytearray()
    nums = len(base64_bytes) // 4
    remain = len(base64_bytes) % 4
    integral_part = base64_bytes[0:4 * nums]

    while integral_part:
        tmp_unit = ''.join(integral_part[0:4])
        tmp_unit = [int(tmp_unit[x: x + 8], 2) for x in [0, 8, 16]]
        for i in tmp_unit:
            resp.append(i)
        integral_part = integral_part[4:]

    if remain:
        remain_part = ''.join(base64_bytes[nums * 4:])
        tmp_unit = [int(remain_part[i * 8:(i + 1) * 8], 2) for i in range(remain - 1)]
        for i in tmp_unit:
            resp.append(i)

    return resp

from sm4 import encrypt, decrypt
from Crypto.Util.number import bytes_to_long, long_to_bytes
mk=0xda98f1da312ab753a5703a0bfd290dd6

data=int("0x"+b64_decode('RVYtG85NQ9OPHU4uQ8AuFM+MHVVrFMJMR8FuF8WJQ8Y=').decode('utf-8'),16)
print long_to_bytes(decrypt(data,mk)).decode('hex')

其中的sm4就是从 https://github.com/yang3yen/pysm4 这里随便拿的一个sm4.py脚本。

得到用户名为 badrer12

用户名的逆向相对简单,但是密码的逆向就麻烦了。main函数调用了 sub_405B25 ,初始化了一个 handler ,这里一个byte对应一个函数

CISCN 2019 reverse Writeup

然后 sub_406607 开始扫描执行,执行的就是从 0x6090e0 开始的这部分代码:

CISCN 2019 reverse Writeup

而在这些函数中势必有读取我们输入的密码,

CISCN 2019 reverse Writeup

所以需要开始分析虚拟机指令对应的函数。不过学弟他们是用angr一会儿就能跑出来。另外想了下似乎可以patch掉 system('exit') ,然后让它返回到输入来,这样就是本地70亿次的爆破,似乎是可行的。

那既然是复现还是分析下虚拟机。

一下午加一晚上过去了,我分析虚拟机回来了。。。。

分析的脑壳都大了,就是得耐着性子挨个指令看,vm的题做的比较少,所以这道题多花了心血认真分析了。几个比较核心的点,首先 0x609240 处存储目前的栈顶指针, 0x60a4f0 则是直接的栈底指针,核心的handler的结构体 0x60a010 ,结果存储在 *(0x60a010+0x24)+0x64 的地方,也就是最后验证的结果。

整个逻辑分析下来很简单,就是六位输入满足 x1^0x78+x2^0x79+x3^0x7a+x4^0x7b+x5^0x7c+x6^0x7d 即可,所以 x1-x6 分别为 0x78-0x7d 即可,所以输入为 xyz{|}

贴一下笔记吧:

//part 0 
B0 19 00 00 00 
B5 0A
B2 0B
B4 09
B0 1A 00 00 00 
B5 0A 
04 0B 09 
B0 1A 00 00 00 
B5 0A 
B2 0B  
B4 09 
90 C2 00 00 00 91   //jmp part 2

//part 1
01 1A 00 00 00 0A       // a1+0x28 = 0x1A
02 09 00                //*(a1)=*(*(a1+0x24)+0x68)
10 09 30 00 00 00 01    // judge(*a1+0x24)!=0 ? *(a1+4)=0x0060b5c0 : *(a1+4)=0x0060b530 
B2 01                   //*a1+4 入栈 
B2 00                   //*a1 入栈
C0                      //judge(*a1)!=0,*a1=4**a1+*a1+4 : *a1+=*a1+4
B5 00                   //*a1 <- 栈顶
B0 F4 FF FF FF          // -c 入栈
B5 0A                   //a1+0x28= -c 
B1 00                   // password逐个取到栈顶
B5 01                   // a1+0x4=password
01 1A 00 00 00 0A       // a1+0x28 = 0x1A
B1 09                   // *[*(a1+0x24)+0x68] 入栈
B5 00                   //*a1 <- 栈顶
10 00 78 00 00 00 00    //judge(*a1)!=0? *a1+=4*0x78:*a1+=0x78;
70 00 FF 00 00 00 00    //*a1=*a1 & 0xff
50 00 18 00 00 00 00    //*a1=*a1 << 0x18
B2 00                   // *a1 入栈
B0 18 00 00 00          // 0x18 入栈
C8                      // *a1 >> 0x18 
B5 00                   // *a1 <- 栈顶
B2 01                   // *a1+0x4 入栈(password)
B2 00                   // *a1 入栈
C3                      // *a1 ^ *a1+0x4
B5 00                   // *a1 <- 栈顶 
50 00 18 00 00 00 00    // *a1=*a1 << 0x18
B2 00                   // *a1 入栈
B0 18 00 00 00          // 0x18 入栈
C8                      // *a1 >> 0x18
B5 00                   // *a1 <- 栈顶 
70 00 FF 00 00 00 01    //*a1+4=*a1 & 0xff 
01 19 00 00 00 0A       //*a1+0x28=0x19
02 09 00                //*(a1)=*(*(a1+0x24)+0x64)
11 01 00 00             // judge(*(a1+4))!=0? *a1=*(a1+4) + 4**a1: *a1=*(a1+4) +*a1
B0 19 00 00 00          // 0x19 入栈
B5 0A                   // *a1+0x28 = 0x19
B2 00                   // *a1 入栈
B4 09                   // *[*(a1+0x24)+0x64]=栈顶
01 1A 00 00 00 0A       // a1+0x28 = 0x1A
B1 09                   //*[*(a1+0x24)+0x68] 入栈
B5 00                   // *a1 <- 栈顶
10 00 01 00 00 00 00    // judge(*a1)!=0? *a1+=4*0x01:*a1+=0x01;
01 1A 00 00 00 0A       // a1+0x28 = 0x1A
04 00 09                //*[*(a1+0x24)+0x68]=*a1

//part 2
B0 1A 00 00 00          // 0x1A 入栈
B5 0A                   // *a1+0x28 = 0x1A
02 09 00                // *(a1)=*(*(a1+0x24)+0x64)
86 00 06 00 00 00 00    // *a1 = len < 6 ;限定长度为6,即循环6次就ok
88 00 26 00 00 00 91    //jmp part 1
FF
B0:   *tmp=*(pc+1);tmp+=4;pc+=5;    push 立即数指令
B5:   *(a1+4*op1)=*tmp;tmp-=4;pc+=2;    pop 指令
B2:   *tmp=*(a1+4*op1);tmp+=4;pc+=2;    push 指令
B4:   *(a1+0x28)<=1000 ? tmp-=4; *[*(a1+4*op1)  + 4 * [*(a1+0x28)] ] = *tmp   : *[*(a1+4*op1) - [*(a1+0x28)] ] =*tmp   ;   pop指令
04:  *(a1+0x28)<=1000 ? *[a1+4*op2 + 4*[*(a1+0x28)]]=*(a1+4*op1):*[*(a1+4*op2) - 4*[*(a1+0x28)]]=*(a1+4*op1)
90:   pc+=*(a1+0x30)+op1
02:   *(a1+0x28)<=1000 ? *(a1+4*op2)=*[*(a1+4*op1) + 4*[*(a1+0x28)]] :*(a1+4*op2)=*[*(a1+4*op1) - 4*[*(a1+0x28)]]
86:   *(a1+4*op6)=*(a1+4*op1) < op2
88:   *(a1+4*op1) != 0 ? pc = *(a1+0x30+4*op1) : pc=op6
01:   *(a1+4*op5)=op1
10:   judge(*(a1+4*op1))!=0 ? *(a1+4*op6)=*(a1+4*op1)+4*op2 : *(a1+4*op6)=*(a1+4*op1)+op2 
C0:   judge(*(tmp-1))!=0 ? *(tmp-1)=4* *(tmp-1) + *(tmp-2) : *(tmp-1)=*(tmp-1) + *(tmp-2) 
B1:   *(a1+0x28)<=1000 ? tmp=*[a1+4*op1  + 4 * [*(a1+0x28)] ]   : tmp=*[a1+4*op1 - 4*[*(a1+0x28)] ]   ;tmp+=4; 
70:   *(a1+4*op6)=*(a1+4*op1) & op2
50:   *(a1+4*op6)=*(a1+4*op1) << op2
C8:   *(tmp-1) = *(tmp-2) >> *(tmp-1);
C3:   *(tmp-1) ^= *(tmp-2); 
11:   judge(*(a1+4*op1))!=0 ? *(a1+4*op3)=*(a1+4*op1)+4**(a1+4*op2) : *(a1+4*op3)=*(a1+4*op1)+*(a1+4*op2)

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

特斯拉之父

特斯拉之父

竹内一正 / 千太阳 / 中信出版社 / 2014-12 / 39.00

马斯克的成就前无古人地跨越了各个领域,曾大起大落,成为亿万富翁后,又曾濒临破产。他凭借极强的控制欲、坚强的意志力把人生浓缩得异常精彩,拓展了人类对自身智力与能力限度的想象。乔布斯离开了,马斯克来了,后者离人更远,离神更近。 他的创业故事就是一部真实的好莱坞大片 美国《财富》杂志 “2013年度商业人物” 史上最富激情、传奇、未来感的企业家 他是个外表优雅的生意人、太空的挑战......一起来看看 《特斯拉之父》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具