0xgame re wk2 writeup
0xgame re wk2 wp
FirstSight-Pyc
直接在线反编译。
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.8
import hashlib
user_input = input('请输入神秘代号:')
if user_input != 'Ciallo~':
print('代号不是这个哦')
exit()
input_hash = hashlib.md5(user_input.encode()).hexdigest()
input_hash = list(input_hash)
for i in range(len(input_hash)):
if ord(input_hash[i]) in range(48, 58):
original_num = int(input_hash[i])
new_num = (original_num + 5) % 10
input_hash[i] = str(new_num)
continue
input_hash = ''.join(input_hash)
print('0xGame{{{}}}'.format(input_hash))
return None
发现了Ciallo~发现好像跟逆向已经没什么关系了,只要改一改,正确输入,让程序跑出0xgame{xxx}就行了。
BabyUPX
upx -d脱壳,再打开可以看到加密,直接正向爆破。
ida encode部分:
__int64 __fastcall encode(_BYTE *a1)
{
_BYTE *v2; // [rsp+8h] [rbp-8h]
v2 = a1;
if ( !*a1 )
return 0xFFFFFFFFi64;
do
{
*v2 = (16 * *v2) | (*v2 >> 4);
++v2;
}
while ( *v2 );
return 0i64;
}
爆破:
#include<bits/stdc++.h>
using namespace std;
char encdata[] =
{
0x03, 0x87, 0x74, 0x16, 0xD6, 0x56, 0xB7, 0x63, 0x83, 0x46,
0x66, 0x66, 0x43, 0x53, 0x83, 0xD2, 0x23, 0x93, 0x56, 0x53,
0xD2, 0x43, 0x36, 0x36, 0x03, 0xD2, 0x16, 0x93, 0x36, 0x26,
0xD2, 0x93, 0x73, 0x13, 0x66, 0x56, 0x36, 0x33, 0x33, 0x83,
0x56, 0x23, 0x66, 0xD7
};
char enc(char ch)
{
return (ch*16) | (ch>>4);
}
int main()
{
for(int i=0;i<44;i++)
for(char ch=33;ch<=126;ch++)
if(enc(ch)==encdata[i])
{
printf("%c",ch);
break;
}
return 0;
}
//0xGame{68dff458-29e5-4cc0-a9cb-971fec338e2f}
注意ida导出的时候使用的是usigned char,要改成char,不然部分字符没法显示。
FirstSight-Jar
java逆向,用jadx打开看。加密部分如下:
private static String encrypt(String str) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
int indexOf = Alphabat.indexOf(str.charAt(i));
if (indexOf < 0) {
sb.append(str.charAt(i));
} else {
sb.append(Alphabat.charAt(((indexOf * 5) + 3) % 16));
}
}
return sb.toString();
}
Alphabat就是"0123456789abcdef",大意就是如果str的这一项不在Alphabat中就保留,否则加密,且加密后的字符也在Alphabat中。因此对str中所有在Alphabat中的字符进行爆破即可,将结果输入java程序他会用0xGame将其包裹。
#include<bits/stdc++.h>
using namespace std;
string en="ab50e920-4a97-70d1-b646-cdac5c873376";
char alp[]="0123456789abcdef";
int main()
{
for(int i=0;i<en.length();i++)
if(strchr(alp,en[i])==NULL)
cout<<en[i];
else
{
for(int j=0;j<strlen(alp);j++)
if(alp[((j * 5) + 3) % 16]==en[i])
{
cout<<alp[j];
break;
}
}
return 0;
}
//b8a9fe39-dbe4-4926-87d7-52b5a5140047
ZzZ
无壳,ida打开发现一大堆函数。这种情况下一般Shift+F12去找关键字符串,注意到Please enter your flag,X交叉引用来到关键代码:
往下看,可以看出下面有一系列对v11,v10,v12的约束,应该是判断flag的关键,但是前面sub_14001128F有点意义不明。结合%ll,%s格式控制符,猜测v13,v5,v6,v7,v14是flag中不同类型的部分(长整型和字符串)。
有了这个猜测再去看下面的约束,发现两个长整型部分已经给出,所以主要是解中间的三个字符串。这种比较复杂的约束一般考虑z3求解:
from z3 import *
v11 = BitVec('v11', 64)
v10 = BitVec('v10', 64)
v12 = BitVec('v12', 64)
constraints = [
11 * v11 + 14 * v10 - v12 == 0x48FB41DDD,
9 * v10 - 3 * v11 + 4 * v12 == 0x2BA692AD7,
((v12 - v11) >> 1) + (v10 ^ 0x87654321) == 3451779756
]
solver = Solver()
solver.add(constraints)
if solver.check() == sat:
m = solver.model()
print(f"v11 = {m[v11]}")
print(f"v10 = {m[v10]}")
print(f"v12 = {m[v12]}")
'''
v11 = 862073908
v10 = 842086455
v12 = 1681208161
'''
注意需要定义BitVec型未知数,不然右移和异或操作会报错。
当然这样还不算完,因为我们求解的是整型,需要转成字节串:
from Crypto.Util.number import long_to_bytes
v10 = 842086455
v11 = 862073908
v12 = 1681208161
print(long_to_bytes(v10)[::-1])
print(long_to_bytes(v11)[::-1])
print(long_to_bytes(v12)[::-1])
#有小端序存储转序问题,一开始没发现
'''
b'7812'
b'44b3'
b'a35d'
'''
自此就得到了flag的所有部分:
0xGame{e544267d-7812-44b3-a35d-d085a85201a4}
Xor::Ramdom
学习windows下C生成随机数rand(),srand(),其实是一种伪随机,当种子相同的时候生成的随机数实际上是一样的。
End
——《月色真美》