[HWS2022硬件安全 x DAS Jan] EasyVM + BabyVM
### BabyVM进来跟着main一顿跳, 跳到412CC0, 先把花指令去掉
```python
from ida_bytes import *
base = 0x412DBC
to = 0x413937
for i in range(base, to) :
if get_bytes(i, 5) == b'\x74\x03\x75\x01\xe8' :
patch_bytes(i, b'\x90\x90\x90\x90\x90')
```
每条指令用了三个64bit, 按这种格式把数据转换出来方便分析
```cpp
int main() {
unsigned long long * p = (unsigned long long *) ida_chars;
for(int i = 0; i < 888; i += 24) {
printf("0x%llx 0x%llx, 0x%llx\n", *p, *(p+1), *(p+2) != -1ll ? *(p+2) : 0);
p += 3;
}
}
```
```ams
0x12,0x0, 0x0,
略
0x19
```
写了部分脚本之后没找到对比, 那就是还有OPCode, 我太天真了, 以为只用这些, 实际应该是我去花指令之后声明函数没太声明好.下面是提出来的内存, 太长了没必要都挂上, 0x19是每段的结束.
反调太多, 没能拿动调找到, 是通过找交叉引用找到的,其实这些内存都在一起,看到一个就能看到后面的![屏幕截图 2022-01-25 111714](https://tianyu.xin/usr/uploads/2022/01/634141456.png)
sub_6E2CC0是vm函数
```c
0x12,0x2, 0x2,
略
0x19, 0, 0,
0x12,0x2, 0x2,
略
0x19, 0, 0,
0x6,0x0, 0x0,
略
0x19,0xffffffffffffffff, 0x0,
略
0x19,0xffffffffffffffff, 0x0
```
三个64bit我分别定义成了opcode, rs, rt, 差不多就是mips那种
这个分析的时候没把寄存器看成寄存器, 实际的寄存器就是这个mem1和mem2, 下面会解释 ,核心vm的操作都写在下面的脚本里了,我主要解释一下命名,可以结合你的IDA对照着看, 因为这边IDA数据库改掉之后没有原始哑名了
![屏幕截图 2022-01-25 112135](https://tianyu.xin/usr/uploads/2022/01/2293258022.png)
Mem_table是传入参数,也是Code_Segment,
mem1和mem2共同构成了脚本中的mem,你可能看到mem1和mem2使用的偏移都在基础寻址上乘了2, 这是一种类似储存器位扩展的形式,它是这样的
```c
-----------------------------------
-------------------------------
^ ^
| |
m1m2
```
m1永远奇数, m2永远偶数, 由此两个int就可以交叉成一个64位, 我觉得它更像一个寄存器的结构, 我们可以分别读取高低32位
这里的mem3是一个类似栈的结构,在下面的脚本中被我称为了mem64, 原因是原vm代码中它都是以64位而非上面的交叉结构存在的
脚本中还有一个mem2, 它不同于IDA里被我改名的mem2, 是case 0x5, 0x6中 出现的另一段内存, 实际上,它的性质才更像内存. 但是改命名太麻烦了,能看懂就好
另外还有两个寄存器, 分别被我命名成了 cnt 和 ZF/ZF1, 前者是栈寄存器,后者是判断寄存器.
非常传统的vm题, 我没有其他可以说的了
```python
CS = [
略
]
cnt = 0
for IP in range(0, len(CS), 3) :
RS = CS
RT = CS
print(cnt, end = " : ")
if CS == 1 :
print("mem[", RS, "] = ", RT, "")
elif CS == 5 :
print("mem64[++cnt] = mem[", RS, "]")
elif CS == 7 :
print("mem[", RS, "] += ", RT, "")
elif CS == 0x12 :
print("mem[", RS, "] ^= mem[", RT, "]")
elif CS == 0x17 :
print("mem[", RS, "] = Input()")
elif CS == 0x18 :
print("Print(lo32(mem[", RS, "]))")
elif CS == 0x19 :
print("Exit\nRestart 0x0 :")
cnt = 0
continue
elif CS == 0x1a :
print("ZF = mem[", RS, "] ==", RT)
print("ZF1 = mem[", RS, "] <", RT)
elif CS == 0x1e :
print("JZ1", RS)
elif CS == 0 :
print("mem2] = ", RT )
elif CS == 3 :
print("mem[", RS, "] = mem2]")
elif CS == 9 :
print("mem[", RS, "] -= ", RT)
elif CS == 4 :
print("mem2] = mem[", RT, "]")
elif CS == 6 :
print("mem[", RS, "] = mem64")
elif CS == 0x1C :
print("JZ", RS)
elif CS == 0x1d :
print("JMP", RS)
elif CS == 0x1f :
print("JNZ", RS)
elif CS == 0x11 :
print("mem[", RS, "] ^=", RT)
elif CS == 0xD :
print("mem[", RS, "] <<=", RT)
elif CS == 0x1B :
print("ZF = mem[", RS, "] == mem[", RT, "]")
print("ZF1 = mem[", RS, "] < mem[", RT, "]")
else :
print(hex(CS))
break
cnt += 1
```
```c
0 : mem[ 0 ] ^= mem[ 0 ]
1 : mem[ 1 ] ^= mem[ 1 ]
2 : mem[ 2 ] ^= mem[ 2 ]
3 : mem[ 3 ] ^= mem[ 3 ]
4 : mem[ 6 ] ^= mem[ 6 ]
5 : mem[ 7 ] ^= mem[ 7 ]
6 : mem[ 0 ] =105
7 : mem[ 1 ] =110
8 : mem[ 2 ] =112
9 : mem[ 3 ] =117
10 : mem[ 6 ] =116
11 : mem[ 7 ] =32
12 : Print(lo32(mem[ 0 ]))
13 : Print(lo32(mem[ 1 ]))
14 : Print(lo32(mem[ 2 ]))
15 : Print(lo32(mem[ 3 ]))
16 : Print(lo32(mem[ 6 ]))
17 : Print(lo32(mem[ 7 ]))
18 : mem[ 0 ] =102
19 : mem[ 1 ] =108
20 : mem[ 2 ] =97
21 : mem[ 3 ] =103
22 : mem[ 6 ] =58
23 : mem[ 7 ] =32
24 : Print(lo32(mem[ 0 ]))
25 : Print(lo32(mem[ 1 ]))
26 : Print(lo32(mem[ 2 ]))
27 : Print(lo32(mem[ 3 ]))
28 : Print(lo32(mem[ 6 ]))
29 : Print(lo32(mem[ 7 ]))
30 : mem[ 1 ] ^= mem[ 1 ]
31 : mem[ 0 ] = Input()
32 : mem64[++cnt] = mem[ 0 ]
33 : mem[ 1 ] +=1
34 : ZF = mem[ 1 ] == 38
ZF1 = mem[ 1 ] < 38
35 : JZ1 31
36 : Exit
Restart 0x0 :
0 : mem[ 2 ] ^= mem[ 2 ]
1 : mem2] =255
2 : mem[ 2 ] +=1
3 : mem2] =547
4 : mem[ 2 ] +=1
5 : mem2] =571
6 : mem[ 2 ] +=1
7 : mem2] =567
8 : mem[ 2 ] +=1
9 : mem2] =567
10 : mem[ 2 ] +=1
11 : mem2] =587
12 : mem[ 2 ] +=1
13 : mem2] =555
14 : mem[ 2 ] +=1
15 : mem2] =251
16 : mem[ 2 ] +=1
17 : mem2] =555
18 : mem[ 2 ] +=1
19 : mem2] =547
20 : mem[ 2 ] +=1
21 : mem2] =591
22 : mem[ 2 ] +=1
23 : mem2] =239
24 : mem[ 2 ] +=1
25 : mem2] =567
26 : mem[ 2 ] +=1
27 : mem2] =239
28 : mem[ 2 ] +=1
29 : mem2] =591
30 : mem[ 2 ] +=1
31 : mem2] =591
32 : mem[ 2 ] +=1
33 : mem2] =547
34 : mem[ 2 ] +=1
35 : mem2] =547
36 : mem[ 2 ] +=1
37 : mem2] =571
38 : mem[ 2 ] +=1
39 : mem2] =567
40 : mem[ 2 ] +=1
41 : mem2] =255
42 : mem[ 2 ] +=1
43 : mem2] =563
44 : mem[ 2 ] +=1
45 : mem2] =563
46 : mem[ 2 ] +=1
47 : mem2] =563
48 : mem[ 2 ] +=1
49 : mem2] =567
50 : mem[ 2 ] +=1
51 : mem2] =587
52 : mem[ 2 ] +=1
53 : mem2] =563
54 : mem[ 2 ] +=1
55 : mem2] =591
56 : mem[ 2 ] +=1
57 : mem2] =555
58 : mem[ 2 ] +=1
59 : mem2] =555
60 : mem[ 2 ] +=1
61 : mem2] =587
62 : mem[ 2 ] +=1
63 : mem2] =239
64 : mem[ 2 ] +=1
65 : Exit
Restart 0x0 :
0 : mem[ 2 ] ^= mem[ 2 ]
1 : mem[ 0 ] = mem2]
2 : mem[ 0 ] -=99
3 : mem2] = mem[ 0 ]
4 : mem[ 2 ] +=1
5 : ZF = mem[ 2 ] == 32
ZF1 = mem[ 2 ] < 32
6 : JZ1 1
7 : Exit
Restart 0x0 :
0 : mem[ 0 ] = mem64
1 : ZF = mem[ 0 ] == 125
ZF1 = mem[ 0 ] < 125
2 : JZ 18
3 : mem[ 0 ] =119
4 : mem[ 1 ] =114
5 : mem[ 2 ] =111
6 : mem[ 3 ] =110
7 : mem[ 6 ] =103
8 : mem[ 7 ] =33
9 : Print(lo32(mem[ 0 ]))
10 : Print(lo32(mem[ 1 ]))
11 : Print(lo32(mem[ 2 ]))
12 : Print(lo32(mem[ 3 ]))
13 : Print(lo32(mem[ 6 ]))
14 : Print(lo32(mem[ 7 ]))
15 : mem[ 0 ] =10
16 : Print(lo32(mem[ 0 ]))
17 : Exit
Restart 0x0 :
0 : mem[ 8 ] =256
1 : ZF = mem[ 8 ] == 225
ZF1 = mem[ 8 ] < 225
2 : JZ1 25
3 : mem[ 0 ] = mem64
4 : mem2] = mem[ 0 ]
5 : mem[ 8 ] -=1
6 : JMP 19
7 : mem[ 0 ] = mem64
8 : ZF = mem[ 0 ] == 123
ZF1 = mem[ 0 ] < 123
9 : JNZ 3
10 : mem[ 0 ] = mem64
11 : ZF = mem[ 0 ] == 103
ZF1 = mem[ 0 ] < 103
12 : JNZ 3
13 : mem[ 0 ] = mem64
14 : ZF = mem[ 0 ] == 97
ZF1 = mem[ 0 ] < 97
15 : JNZ 3
16 : mem[ 0 ] = mem64
17 : ZF = mem[ 0 ] == 108
ZF1 = mem[ 0 ] < 108
18 : JNZ 3
19 : mem[ 0 ] = mem64
20 : ZF = mem[ 0 ] == 102
ZF1 = mem[ 0 ] < 102
21 : JNZ 3
22 : mem[ 9 ] ^= mem[ 9 ]
23 : mem[ 10 ] =225
24 : mem[ 7 ] = mem2]
25 : mem[ 6 ] = mem2]
26 : mem[ 6 ] ^= 66
27 : mem[ 6 ] <<= 2
28 : ZF = mem[ 6 ] == mem[ 7 ]
ZF1 = mem[ 6 ] < mem[ 7 ]
29 : JNZ 3
30 : mem[ 9 ] +=1
31 : mem[ 10 ] +=1
32 : ZF = mem[ 9 ] == 32
ZF1 = mem[ 9 ] < 32
33 : JZ1 42
34 : mem[ 0 ] =99
35 : mem[ 1 ] =111
36 : mem[ 2 ] =114
37 : mem[ 3 ] =114
38 : mem[ 6 ] =101
39 : mem[ 7 ] =99
40 : Print(lo32(mem[ 0 ]))
41 : Print(lo32(mem[ 1 ]))
42 : Print(lo32(mem[ 2 ]))
43 : Print(lo32(mem[ 3 ]))
44 : Print(lo32(mem[ 6 ]))
45 : Print(lo32(mem[ 7 ]))
46 : mem[ 0 ] =116
47 : mem[ 1 ] =108
48 : mem[ 2 ] =121
49 : mem[ 3 ] =33
50 : mem[ 6 ] =10
51 : Print(lo32(mem[ 0 ]))
52 : Print(lo32(mem[ 1 ]))
53 : Print(lo32(mem[ 2 ]))
54 : Print(lo32(mem[ 3 ]))
55 : Print(lo32(mem[ 6 ]))
56 : Exit
Restart 0x0 :
```
先把输入读到mem64, 再在mem2预置一串数, 并全减掉99, flag ^ 66 再 左移2 之后和预置对比
```python
Mem = [
255, 547, 571, 567, 567,
587, 555, 251, 555, 547,
591, 239, 567, 239, 591,
591, 547, 547, 571, 567,
255, 563, 563, 563, 567,
587, 563, 591, 555, 555,
587, 239
]
for i in range(len(Mem)) :
Mem -= 99;
Mem >>= 2;
Mem ^= 66
print(chr(Mem), end = "")
```
### EasyVM
进来没有main, 有两条花指令, nop掉
401610看上去像反调, 把jnz nop掉, 强行返回2.0
4012F0初始化, 把输入扔4011E0里, 返回是分发器的最后一个参数
输入函数一会再看
申请了一段地址, 作为函数表, 接下来一个一个函数分析, 函数分析结果写在下面的C脚本里了
其中this~是AX-CX寄存器
```c
int __thiscall distributer(_DWORD *this, char *src, char *dst, int a4, int a5)
{
this = src;
this = dst;
this = a4;
this = a5;
while ( 2 )
{
switch ( *(_BYTE *)this )
{
case 0xC0:
AX ++;
case 0xC1:
BX ++;
case 0xC2:
CX ++;
case 0xC3:
AX = BX;
case 0xC4:
AX = CX;
case 0xC5:
BX = AX;
case 0xC6:
BX = CX;
case 0xC7:
CX = AX;
case 0xC8:
CX = BX;
case 0xC9:
LOAD((uint32), AX), IP += 4;
case 0xCA:
LOAD((uint32), BX), IP += 4;
case 0xCB:
LOAD((uint32), CX), IP += 4;
case 0xCC:
LOAD(Pre(uint8), AX)
case 0xCD:
LOAD(Pre(uint8), BX)
case 0xCE:
AX ^= BX
case 0xCF:
BX ^= AX
case 0xD0:
if AX == DST :
DX = 1;
else :
AX >= DST ? DX = 2 : DX = 0;
case 0xD1:
if BX == DST :
DX = 1;
else :
BX >= DST ? DX = 2 : DX = 0;
case 0xD2:
if CX == :
DX = 1;
else :
CX >= ? DX = 2 : DX = 0;
IP += 4
case 0xD3:
if DX == 1 :
IP += (uint8);
IP ++;
case 0xD4:
if DX != 1 :
IP += (uint8);
IP ++;
case 0xFE:
return 0;
case 0xFF:
return 1;
default:
print((int)aCmdError);
return 0;
IP ++;
}
}
}
```
输入大概就是B64解码, 返回一个首地址,目的是通过JMP跳到 0xFF
内存拽出来分析成汇编, 汇编逻辑可以直接看下面的脚本
```c
0x0 : LD32 BX, 0
IP += 5
0x5 : LD32 CX, 0
IP += 5
0xa : LD8 AX, Inp
IP += 1
0xb : XOR BX AX
IP += 1
0xc : LD32 AX, 0xEE
IP += 5
0x11 : XOR BX AX
IP += 1
0x12 : CMP BX, TAB
IP += 1
0x13 : JPD 0x1
IP += 2
0x15 : FAILED
IP += 1
0x16 : INC CX
IP += 1
0x17 : CMP CX, 0x39
IP += 5
0x1c : JND 0xEC // 0x1C + 0xEC + 2 & 0xFF = 0xA
IP += 2
0x1e : SUCCESS
IP += 1
```
逻辑就是0x38位每位异或前一位结果, 再异或0xEE要等于明文
脚本
```python
Target = [
0, 0xBE, 0x36, 0xAC, 0x27, 0x99, 0x4F, 0xDE, 0x44, 0xEE, 0x5F,
0xDA, 0x0B, 0xB5, 0x17, 0xB8, 0x68, 0xC2, 0x4E, 0x9C, 0x4A,
0xE1, 0x43, 0xF0, 0x22, 0x8A, 0x3B, 0x88, 0x5B, 0xE5, 0x54,
0xFF, 0x68, 0xD5, 0x67, 0xD4, 0x06, 0xAD, 0x0B, 0xD8, 0x50,
0xF9, 0x58, 0xE0, 0x6F, 0xC5, 0x4A, 0xFD, 0x2F, 0x84, 0x36,
0x85, 0x52, 0xFB, 0x73, 0xD7, 0x0D, 0xE3
]
salt =
for i in range(1, len(Target), 1) :
print(chr(Target ^ 0xEE ^ Target ^ salt), end = "")
print()
#ZmxhZ3syNTg2ZGM3Ni05OGQ1LTQ0ZTItYWQ1OC1kMDZlNjU1OWQ4MmF9
```
注意到input里给连续四位分别异或了ABCD, 解码要异或回来, 解base64即可 不明觉厉,好高的水平 多谢楼主分享!!! 现在再看wp觉得好像不是很难,但当时做的时候倒是做得死去活来的......再反过来想想,果然自己还是太菜了(瘫 迷迷糊糊的呢 学习一下 感谢分享。
页:
[1]