144_[XMAN2018排位赛]easyvm
虚拟机的题很少,感觉两个都差不多,不过这个确实感觉麻烦了点。
没有删除符号表这让处理起来方便了不少。代码除循环和结束命令外都是3字节,1字节命令2字节数据。
printf("Please input your flag:");
flag = malloc(0x20u);
scanf("%32s", flag);
v3[1] = (int)&flag;
memcpy(cipher, &unk_3E75, sizeof(cipher));
v3[0] = (int)cipher;
v2[1] = (int)v3;
v2[0] = 0xEFBEADDE;
R[0] = (char **)&v17;
R[1] = (char **)&v11;
R[2] = (char **)&v10;
R[3] = (char **)&v16;
R[4] = (char **)&v15; // R4,V15存计算结果
R[5] = &v7;
R[6] = (char **)&v9;
R[7] = (char **)&v8;
R[8] = (char **)&v14;
R[9] = (char **)&v13;
R[10] = (char **)&v12;
R[11] = (char **)&flag;
R[12] = (char **)v3; // cipher
R[13] = (char **)v2;
while ( 1 )
{
v22 = read_code(code, &ptr); // pop
v21 = v22 & 0xFE;
v20 = v22 & 1;
switch ( v22 & 0xFE )
{
case 0:
v6 = read_code(code, &ptr); // op a b : mov R[a],(char)R[b] mov R[b],(char)b
v5 = read_code(code, &ptr);
if ( v20 )
{
if ( v20 == 1 )
mov((int *)R[v6], (char)*R[v5]);
}
else
{
mov((int *)R[v6], v5);
}
break;
case 2:
v6 = read_code(code, &ptr); // op a b : mov R[a],(int)R[b] mov R[b],(int)b
v5 = read_code(code, &ptr);
if ( v20 )
{
if ( v20 == 1 )
mov32((int *)R[v6], (int)*R[v5]);
}
else
{
mov32((int *)R[v6], v5);
}
break;
case 4:
循环没有开始结束标志,只是个二选一第一次进第二次出,所以没有嵌套
case 0x28:
if ( inloop ) // 单字节指令 循环
{
if ( v9 )
ptr = loopstart;
else
inloop = 0;
}
else
{
inloop = 1;
loopstart = ptr;
}
break;
操作命令一共20个,包含赋值,加,移位,逻辑运算,栈运算等。先把操作命令按这些功能打印出来(不大准确,只为方便观看)
def printcode(code):
i = 0
while i<246:
op = code[i]; i+=1
v1 = op&0xfe
v2 = op&1
if v1 < 0x28:
a = code[i]; i+=1
b = code[i]; i+=1
if v1 == 0 and v2 == 1:
print(f'mov R[{a}] R[{b}]')
if v1 == 0 and v2 == 0:
print(f'mov R[{a}] {b}')
if v1 == 2 and v2 == 1:
print(f'mov32 R[{a}] R[{b}]')
if v1 == 2 and v2 == 0:
print(f'mov32 R[{a}] {b}')
if v1 == 4 and v2 == 1:
print(f'lea_ch R[{a}] R[{b}]')
if v1 == 6 and v2 == 1:
print(f'lea_int R[{a}] R[{b}]')
if v1 == 8 and v2 == 1:
print(f'ldr_int R[{a}] R[{b}]')
if v1 == 10 and v2 == 1:
print(f'ldr_ch R[{a}] R[{b}]')
if v1 == 12 and v2 == 1:
print(f'add R[{a}] R[{b}]')
if v1 == 12 and v2 == 0:
print(f'add R[{a}] {b}')
if v1 == 14 and v2 == 1:
print(f'add_pint R[{a}] R[{b}]')
if v1 == 14 and v2 == 0:
print(f'add_pint R[{a}] {b}')
if v1 == 16 and v2 == 1:
print(f'add_pch R[{a}] R[{b}]')
if v1 == 16 and v2 == 0:
print(f'add_pcj R[{a}] {b}')
if v1 == 18 and v2 == 1:
print(f'xor R[{a}] R[{b}]')
if v1 == 18 and v2 == 0:
print(f'xor R[{a}] {b}')
if v1 == 20 and v2 == 0:
print(f'mod R[{a}] {b}')
if v1 == 22 and v2 == 1:
print(f'or R[{a}] R[{b}]')
if v1 == 22 and v2 == 0:
print(f'or R[{a}] {b}')
if v1 == 24 and v2 == 1:
print(f'and R[{a}] R[{b}]')
if v1 == 24 and v2 == 0:
print(f'and R[{a}] {b}')
if v1 == 26 and v2 == 1:
print(f'push R[{a}] R[{b}]')
if v1 == 26 and v2 == 0:
print(f'push R[{a}] {b}')
if v1 == 28 and v2 == 1:
print(f'pop R[{a}] R[{b}]')
if v1 == 30 and v2 == 1:
print(f'shr R[{a}] R[{b}]')
if v1 == 30 and v2 == 0:
print(f'shr R[{a}] {b}')
if v1 == 32 and v2 == 1:
print(f'shl R[{a}] R[{b}]')
if v1 == 32 and v2 == 0:
print(f'shl R[{a}] {b}')
if v1 == 34 and v2 == 1:
print(f'ror R[{a}] R[{b}]')
if v1 == 34 and v2 == 0:
print(f'ror R[{a}] {b}')
if v1 == 36 and v2 == 1:
print(f'cmp_l R[{a}] R[{b}]')
if v1 == 36 and v2 == 0:
print(f'cmp_l R[{a}] {b}')
if v1 == 38 and v2 == 1:
print(f'cmp_eq R[{a}] R[{b}]')
if v1 == 38 and v2 == 0:
print(f'cmp_eq R[{a}] {b}')
if v1 == 40:
print('loop')
if v1 == 42:
print('end')
return
code = open('easyvm', 'rb').read()[0x2e95: 0x2e95+246]
print(list(code))
printcode(code)
然后对输出的结果一点点啃。大概分成4段,
第一段是对输入的flag进行换位
'''
lea_ch R[1] R[11] #R1 = flag
xor R[3] R[3]
xor R[0] R[0]
xor R[4] R[4]
'''
#第1段换位置
j=0
for i in range(32):
j = (j + 51)%32
stack[i] = flag[j]
'''
loop
add R[0] 51
mod R[0] 32
lea_ch R[9] R[1]
add_pch R[9] R[0]
ldr_ch R[10] R[9]
mov R[4] R[10]
push R[5] R[4]
add R[3] 1
cmp_l R[3] 32
loop
'''
第二段是将每个字符分成前3后5然后用后一个的后5和前一个的前3拼成新的字节
#第1字符的前3位
'''
xor R[0] R[0]
lea_int R[8] R[5]
add_pint R[8] 224
lea_int R[2] R[8]
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 224
shr R[0] 5
mov R[4] R[0]
xor R[3] R[3]
'''
#第2段,前5后3组合
[((flag[i]<<3)|(flag[(i-1) %32]>>5))&0xff for i in range(32)]
'''
loop
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 31
shl R[0] 3 # a&0x1f;a<<=3
push R[5] R[0]
lea_int R[8] R[5]
add_pint R[8] 224
lea_int R[2] R[8]
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 224 # a&0xe0;a>>5
shr R[0] 5
pop R[5] R[10]
add R[10] R[0]
push R[5] R[10]
add R[3] 1
cmp_l R[3] 31
loop
'''
#最后一个
'''
ldr_int R[10] R[2]
mov R[0] R[10]
and R[0] 31
shl R[0] 3
add R[0] R[4]
push R[5] R[0]
'''
第3段是先把key和第i%4位与i相加后再与原文作异或
#第3段 与key循环+序号 异或
def ror8(a):
return (a>>i)|((a&0xff)<<24)
r3=0
r4=0xEFBEADDE
for i in range(32):
stack[i] = flag[i]^(r4+i)
r4 = ror8(r4)
'''
xor R[3] R[3]
mov32 R[4] R[13] #0xEFBEADDE
loop
lea_int R[8] R[5]
add_pint R[8] 224
lea_int R[2] R[8]
ldr_int R[10] R[2]
mov R[0] R[10]
push R[5] R[0]
mov R[0] R[4]
add R[0] R[3]
pop R[5] R[10]
xor R[10] R[0]
push R[5] R[10]
ror R[4] 8
add R[3] 1
cmp_l R[3] 32
loop
'''
第4段看了半天没看懂,大概意思是没有,因为先是pop后边马上就push了,似乎没有作什么改动
#似乎没用,并没有改变数据
'''
xor R[3] R[3]
xor R[4] R[4]
lea_ch R[1] R[12]
loop
lea_ch R[9] R[1]
add_pch R[9] R[3]
ldr_ch R[10] R[9]
mov R[0] R[10]
push R[5] R[0]
lea_int R[8] R[5]
add_pint R[8] 223
ldr_int R[10] R[8]
pop R[5] R[0] #pop后直接push
push R[5] R[0]
cmp_eq R[0] R[10] #v8 = (r0 != r10)? 1: 0; r7=v8
or R[4] R[7]
add R[3] 1
cmp_l R[3] 32
loop
end
'''
根据这个反过来写出3句来解密
cipher = open('easyvm', 'rb').read()[0x2e75: 0x2e75+32]
#print(cipher)
key = [0xDE, 0xAD, 0xBE, 0xEF]
m = [cipher[i]^(key[i%4] + i) for i in range(32)] #3
v = [( (m[(i-1)%32]<<5) | (m[i]>>3) )&0xff for i in range(32)] #2
flag = [0]*32 #1
p = 0
for i in range(32):
p = (p+51)%32
flag[p] = v[i]
print(bytes(flag))
#xman{ae791f19bdf77357ff10bb6b0e}
#flag{ae791f19bdf77357ff10bb6b0e}
147_[XNUCA2018]Code_Interpreter
流程基本一样,主程序里输入3个数,还有3个要求就是数的第几位是5e然后就进虚拟机运行处理
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v4; // [rsp+14h] [rbp-Ch]
FILE *stream; // [rsp+18h] [rbp-8h]
if ( a1 > 1 )
{
stream = fopen(a2[1], "rb");
if ( !stream )
{
fprintf(stderr, "Could not open file %s\n", a2[1]);
exit(1);
}
fseek(stream, 0LL, 2);
v4 = ftell(stream);
fseek(stream, 0LL, 0);
ptr = malloc(v4);
if ( fread(ptr, v4, 1uLL, stream) != 1 )
{
fprintf(stderr, "Error reading file %s\n", a2[1]);
exit(1);
}
puts("Play a game, prepare three number: ");
printf("First: ");
__isoc99_scanf("%d", &num1);
printf("Second: ");
__isoc99_scanf("%d", &num2);
printf("Third: ");
__isoc99_scanf("%d", &num3);
sub_400806((__int64)ptr); //进虚拟机检查
if ( !dword_6024B0 && (unsigned __int8)num1 == 0x5E && (num2 & 0xFF0000) == 0x5E0000 && (unsigned __int8)num3 == 94 ) //限制条件
printf("Congratulations! \nflag is: X-NUCA{%x%x%x}\n", (unsigned int)num1, (unsigned int)num2, (unsigned int)num3);
else
puts("Try again.");
return 0LL;
}
else
{
printf("usage: ./%s <code>\n", *a2);
return 0xFFFFFFFFLL;
}
}
一共11个命令也比较容易理解
dword_6020A0[0] = num1;
dword_6020A4 = num2;
result = (unsigned int)num3;
dword_6020A8 = num3;
k = 0;
j = 2;
i = 0;
v5 = 1;
while ( v5 )
{
switch ( *(_BYTE *)((unsigned int)i + a1) )
{
case 0:
v5 = 0;
break;
case 1: // read int:4 data[++j] = int
v2 = ++i;
++i;
v14 = *(unsigned __int8 *)(v2 + a1); // +1
v3 = i++;
v15 = (*(unsigned __int8 *)(v3 + a1) << 8) + v14;// +2
v4 = i++;
v16 = (*(unsigned __int8 *)((unsigned int)i + a1) << 24) + (*(unsigned __int8 *)(v4 + a1) << 16) + v15;// +4,+3,+2,+1
dword_6020A0[++j] = v16;
break;
case 2:
--j;
break;
case 3: // a,b data[a]+=data[b]
v6 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v6] += dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
break;
case 4: // a,b data[a]-=data[b]
v7 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v7] -= dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
break;
case 5: // a,b data[a]*=b
v8 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v8] *= *(unsigned __int8 *)((unsigned int)++i + a1);
break;
case 6: // a,b data[a]>>=b
v9 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v9] = (unsigned int)dword_6024A0[v9] >> *(_BYTE *)((unsigned int)++i + a1);
break;
case 7: // a,b data[a]=data[b]
v10 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v10] = dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
break;
case 8: // a,b data[a]=data[k+b]
v11 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v11] = dword_6020A0[k + *(unsigned __int8 *)((unsigned int)++i + a1)];
break;
case 9: // a,b data[a]^=data[b]
v12 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v12] ^= dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
break;
case 0xA: // a,b data[a]|=data[b]
v13 = *(_BYTE *)((unsigned int)++i + a1);
dword_6024A0[v13] |= dword_6024A0[*(unsigned __int8 *)((unsigned int)++i + a1)];
break;
default:
fprintf(stderr, "Invalid opcode. %d\n", *(unsigned __int8 *)((unsigned int)i + a1));
exit(1);
}
result = (unsigned int)++i;
}
return result;
跟上一题一样,第一步先把命令文件翻译成好自己理解的方式
from pwn import u32
code = open('code', 'rb').read()
data = [0]*4
i=0
j=2
k=0
v5=1
while (v5 == 1):
if code[i] == 0:
break
elif code[i] == 1:
v = u32(code[i+1: i+5])
print(f'data[{j}] ={v}');i+=5;j+=1
elif code[i] == 2:
i +=1; j-=1
elif code[i] == 3:
print(f'data[{code[i+1]}] += data[{code[i+2]}]');i+=3
elif code[i] == 4:
print(f'data[{code[i+1]}] -= data[{code[i+2]}]');i+=3
elif code[i] == 5:
print(f'data[{code[i+1]}] *= {code[i+2]}');i+=3
elif code[i] == 6:
print(f'data[{code[i+1]}] >>= {code[i+2]}');i+=3
elif code[i] == 7:
print(f'data[{code[i+1]}] = data[{code[i+2]}]');i+=3
elif code[i] == 8:
print(f'data[{code[i+1]}] = num[{k} + {code[i+2]}]');i+=3
elif code[i] == 9:
print(f'data[{code[i+1]}] ^= data[{code[i+2]}]');i+=3
elif code[i] == 10:
print(f'data[{code[i+1]}] |= data[{code[i+2]}]');i+=3
else:
print('err {code[i]}')
然后一段一段的啃,这个理解的不大对,因为确实后边两句整不清了,但是前边两句一个是运算,另一个就是一个数字,估计是相等,不然还会怎么着呢。
data = [n1,n2,n3]
d0 = (n1>>4)*21 - n3
d2 = 494849131
d0 -= n3
d4 = (n1>>4)*21 - n3 - n4
'''
data[4] ^= data[4]
data[0] ^= data[0]
data[1] = num[0 + 0]
data[2] = num[0 + 1]
data[3] = num[0 + 2]
data[1] >>= 4
data[1] *= 21
data[0] = data[1]
data[0] -= data[3]
data[2] =494849131
data[1] = num[0 + 3]
data[0] -= data[1]
data[4] |= data[0]
'''
d0 = (n3>>8)*3 + n2
d2 = 1618573692
d1 = n3
d0 = (n3>>8)*3 + n2 - n4
d4 |= d0
'''
data[0] ^= data[0]
data[1] = num[0 + 0]
data[2] = num[0 + 1]
data[3] = num[0 + 2]
data[3] >>= 8
data[3] *= 3
data[0] = data[3]
data[0] += data[2]
data[2] =1618573692
data[1] = num[0 + 3]
data[0] -= data[1]
data[4] |= data[0]
'''
d0 = (n1>>8) + n2
d2 = 1606204861
d0 = (n1>>8) + n2 - n4
d4 |= d0
'''
data[0] ^= data[0]
data[1] = num[0 + 0]
data[2] = num[0 + 1]
data[3] = num[0 + 2]
data[1] >>= 8
data[0] = data[1]
data[0] += data[2]
data[2] =1606204861
data[1] = num[0 + 3]
data[0] -= data[1]
data[4] |= data[0]
'''
先整理出3个等式
(n1>>4)*21 - n3 == 494849131
(n3>>8)*3 + n2 == 1618573692
(n1>>8) + n2 == 1606204861
由于n2和n3都可以用n1表示,所以只需要爆破n1的前3个字节,然后比对就可以了
#n1 = xxx5e n2 = x 5e x x n3 = x x x 5e
for n1 in range(0x5e, 0x100000000, 0x100):
n2 = (1606204861 - (n1>>8))& 0xffffffff
n3 = ((n1>>4)*21 - 494849131) & 0xffffffff
if (n2&0xff0000) == 0x5e0000 and (n3&0xff == 0x5e) and (n3>>8)*3 + n2 == 1618573692:
print('flag{'+hex(n1)[2:] + hex(n2)[2:] + hex(n3)[2:] + '}')
break
#flag{5e5f5e5e5f5e5e5f5e5e5f5e}