内容简介:正常的逆向(大雾)好像就只有两个。。另外还有一个从题目就可以看出来这是个go语言的题目,打开一看是去符号表的逆向,IDA里面通过脚本恢复符号表开始看代码
正常的逆向(大雾)好像就只有两个。。另外还有一个 apk 、一个 wasm 、还有一个 MBR 。。。这三个后续慢慢来补,需要的知识可能有点多,先把两个相对简单的题写一下。
easygo
从题目就可以看出来这是个 go 语言的题目,打开一看是去符号表的逆向,IDA里面通过脚本恢复符号表开始看代码
https://github.com/sibears/IDAGolangHelper
跟到 main.main 看到关键的比较函数:
gdb跟一下,在 0x495318 地址处下断点即可获得明文的flag:
bbvvmm
一道让人头疼的题目,根据题目名称大概猜到是一道vm的题目,也就是在前一天刚刚正在研究 reversing.kr 上的另一道vm题目,有点头大。先说说这个题目把。
拖进ida里面,先分析一下:
整个程序我们分为两部分来看,一部分校验用户名,结果存储在 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对应一个函数
然后 sub_406607 开始扫描执行,执行的就是从 0x6090e0 开始的这部分代码:
而在这些函数中势必有读取我们输入的密码,
所以需要开始分析虚拟机指令对应的函数。不过学弟他们是用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)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
赢在设计
[美] 洛芙迪 (Lance Loveday)、[美] 尼豪斯 (Sandra Niehaus) / 刘淼、枊靖、王卓昊 / 人民邮电出版社 / 2010-8 / 55.00
企业总是面临在网站设计和改进方面进行投资的抉择。怎样才能让有限的资金发挥出最大的效益呢?网站设计不应只是把网站做得赏心悦目,它更应该是提高经济收益和获得竞争优势的战略利器。是时候让网站发挥其潜能,以业务指标为导向来做设计决策,为提升网站收益而设计了。 作者凭借多年为众多网站做咨询工作的经验,为我们揭示了赢在设计的奥秘。它针对目前网站设计中存在的典型问题,先从宏观上探讨解决问题的战略手段,围绕......一起来看看 《赢在设计》 这本书的介绍吧!