首页
社区
课程
招聘
[原创]2020网鼎杯玄武组_babyvm
发表于: 2020-5-24 18:13 6497

[原创]2020网鼎杯玄武组_babyvm

2020-5-24 18:13
6497

这个组,是朋友赛后让我看的这个题,个人并没有参赛,当时也没给做出来,后续断断续续的的调试,工作,好久好久才做出来,贼鸡儿菜,打吉尔比赛。

IDA分析,发现没有符号,有点函数跟的很深具体也不知到什么作用,这是结合动态调试,看看函数的输入输出,可以大概知道函数做了什么,并把函数根据功能进行重命名

这里再正确flag的上面有一个函数,显然即使check,传入的就是str->大写hex的值

下面进入check分析

分析check时,这里还是通过动态调试,确定了几个函数

结果表的值:

[0x0c5d83690,0x1f7902cc,0x978162ea,0x2fae3d15,0x1932a96c,0xceebfe91,0x4222669,0xaff6af42]

一共是8*4个字节

下面把chek中的基本的函数就分析大概流程分析完了,下面总结一下check的基本流程:

check的流程分析结束,下面就要还原算法了,

根据结果表,提出来md5正确的输入

[0x0c5d83690,0x978162ea,0x1932a96c,0x4222669]

MD5如何保证固定?难道要撞嘛?

{0x1F7902CC,0x2FAE3D15,0x0CEEBFE91,0x0AFF6AF42}

先分析vm的流程,首先要知道传入vm中的表的结构,这里只能动态调试,把之前的反调试patch掉,开始调试,再array的地方下断点,观察array的内存变化

array的结构如下

opcode表如下,并不会根据输入的不同而不同,基本上固定的操作路径,vm计算产生的中间值其实就是放到array中,和array+8的指针中的

最后9ea4eff588保存的内容如下,第一个是用户输入,第二个给是用户输入产生的MD5,第三个是用户输入得到的100byte的md5

知道了传入vm的array的结构,下面分析vm的流程,人肉调试还原

总体的结构是这样的,吐了。。。。。

根据下面的分析,每次寻址是*(array + (array + 20)),可以知道*array就是opcode,*(array+20)则是偏移量,由此可知,opcode的寻址方式,可以发现每个case中让array移动都是操作*(array+20)

我的方法是,再每个case的偏移加上注释,动态调试一边关注array的变化,结合静态分析推断处每个case的作用,再在x64debug的对应位置做好标注,进行跟踪就很容易看出来规律了。

下面给一些重要的流程函数标记出来,然后跟踪找规律

下面重点的按照处理流程顺序还原几个重要的case

4.下面再次一直运行可以看到其他的case并没有操作存在与array中的结果,而都是操作的操作表的索引,或者其他的,一直调式运行分析发现法律,一直在调用上面的3个case,并更新着结果值,分析还原整体的流程,小流程做了8轮(0~7)大流程做了两轮

继续往下跟

这里跟了很多次,因为每一次位运算产生的结果会参与到不同流程的计算,这里要十分仔细,我当时就是拿着纸笔一条一条的记录数据。

最终的操作如下:

获取步骤4产生的值(记作re)

~((re | md5) ^ (md5_table & md5) ^ (re & md5_table) ^ (re & md5_table & md5) ^ (re | md5_table | md5))

根据标注,其实追踪也能发现规律。

进入sub_140075159,首先初始化一个array结构,再将array传入vm函数进行vm流程的计算

还原vm流程,先进行r2l,xor,l2r共2*8==16轮

还原vm流程,获取r2l,xor,l2r共2*8==16轮的结果(记作re),j进行下面的计算

这里经过大佬的指点可以这样化简,原来想的是爆破

(re | md5) ^ (md5_table & md5) ^ (re & md5_table) ^ (re & md5_table & md5) ^ (re | md5_table | md5)

re == A

md5 == B

md5_table == C

(A|B) ^ (C&B) ^ (A&C) ^ (A & C & B) ^ (A | C | B) == A ^ B ^ C

最终产生的就是vm的运算结果

(input_md5,vm,input_md5+4,vm+4,....,input_md5+12,vm+12)

vm的结果因该是:

{0x1F7902CC,0x2FAE3D15,0x0CEEBFE91,0x0AFF6AF42}

input_md5的结果因该是

{0x0c5d83690,0x978162ea,0x1932a96c,0x4222669}

已经有了md5的正确结果,以及input的正确结果,但是md5_table还不知道

下面分析一下md5_table的产生:

table的产生gettable

100比特的表进入md5 就获取了md5_table,由于table有有限个可能,那么md5_table也就有有限个可能

那么就可以编码求出100个md5_table然后参与解密运算中

接下来用枚举出来的md5_table和vm正确的结果,md5的正确结果返推出input。但是这会产生多个input,因为不一定满足md5,这是再次比较md5筛选出真正的input

 
 
 
 
 
 
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-5-24 18:26 被Craft_A编辑 ,原因:
上传的附件:
收藏
免费 3
支持
分享
最新回复 (6)
雪    币: 2
活跃值: (156)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2
师傅牛皮
2020-5-25 10:10
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
请教大神:没有查到壳,查壳工具说这是无效PE文件,但是IDA还是打开了,只是没有看到main函数
2020-5-25 11:34
0
雪    币: 16501
活跃值: (6382)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
4
#python 2.7
import hashlib as H
import struct
from z3 import *
DEBUG=0
def u32(a):
	return struct.unpack('<I',a)[0]
def p32(a):
	return struct.pack('<I',a)
#print hex(ROR32(0x0000000021111112, 7))
def T1(t0):
	a2=[0 for i in range(100)]
	#t0%=0x64
	t2=t0
	for i in range(100):
		t1=0
		t2^=0xc3
		a2[i]=t2 & 0xFF
		for j in range(8):
			t1^=((t2 >> j) & 1)
		t2=t1 | 2*t2
	return a2
def FT(input):
	m=input[0]
	X1=input[1]
	X2=input[2]
	
	for i in range(7,-1,-1):
		m=RotateLeft(RotateRight(m,i)^0x9E3779B9,6)
	for i in range(7,-1,-1):
		m=RotateLeft(RotateRight(m,i)^0x9E3779B9,6)
		
	X0=m
	x4= (X2 & X1) ^ (X0 & X1) ^ (X0 & X2) ^ (X0 & X2 & X1) ^ (X0|X2|X1)
	x5 = ~x4
	
	#print hex(x5 & 0xFFFFFFFF)
	return x5 & 0xFFFFFFFF
X=[0 for i in range(4)]
if DEBUG==0:
	R=[0x1f7902cc,
0x2fae3d15,
0xceebfe91L,
0xaff6af42L
]
else:
	R=[
0x3560d569,
0xe566c865L,
0x1a638fcc,
0x931e84cbL
]
input=[0,0,0]
for t0 in range(0x64):
	if DEBUG==0:
		md5flag='9036D8C5EA6281976CA9321969262204'
	else:
		md5flag='2d1dabc34507bb34cf00ca759fa28faf'
	md5r1=md5flag.decode('hex')
	RT=''.join(map(chr,T1(t0)))
	md5r2=H.md5(RT).hexdigest()
	md5r2=md5r2.decode('hex')
	s=Solver()
	for i in range(4):
		X[i]=BitVec('X'+str(i),32)	
	for i in range(4):
		input[0]=X[i]
		input[1]=u32(md5r1[4*i:4*(i+1)])
		input[2]=u32(md5r2[4*i:4*(i+1)])
		s.add(FT(input)==R[i])
	flag=''
	if s.check()==sat:
		m=s.model()
		for i in range(4):
			flag+=p32(int(str(m[X[i]])))
		print flag.encode('hex')
		if H.md5(flag).hexdigest().upper()==md5flag.upper():
			print "found"
			flag='flag{'+flag.encode('hex')[0:8]+'-'+flag.encode('hex')[8:12]+'-'+flag.encode('hex')[12:16]+'-'+flag.encode('hex')[16:20]+'-'+flag.encode('hex')[20:]+'}'
			print flag
			break
#flag{9e573902-0e31-4837-a337-32a475ca007c}

贴个z3的,貌似z3可以直接把那个vm的部分直接解出来

2020-5-25 12:02
1
雪    币: 1308
活跃值: (469)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
guoguofish 请教大神:没有查到壳,查壳工具说这是无效PE文件,但是IDA还是打开了,只是没有看到main函数
蛤,不是吧,你用的什么pe查看的工具啊,没有加壳,应该编译的时候把符号都去掉了,可以直接搜字符出就定位main函数了。
2020-5-25 22:15
0
雪    币: 1308
活跃值: (469)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
大帅锅 #python&nbsp;2.7 import&nbsp;hashlib&nbsp;as&nbsp;H import&nbsp;struct from&a ...
我一开始想用爆破前一部分结果或者类似求解器的,但没实践,大佬发的z3这个代码我学习到了,谢谢师傅
2020-5-25 22:20
0
雪    币: 259
活跃值: (283)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
666
2020-5-26 09:43
0
游客
登录 | 注册 方可回帖
返回
//