com.re.wp
p0iL blog: http://poilzero.cn/
难度确认
- easyasm:1 asm
- flagchecker:1.5 apk
- crackme:2 tea
- 猫头鹰不是猫:5 z3
easyasm
涉及移位操作,为了避免数据宽度的问题,使用z3
dic = '91 61 01 C1 41 A0 60 41 D1 21 14 C1 41 E2 50 E1 E2 54 20 C1 E2 60 14 30 D1 51 C0 17 00 00 00 00'.split()
dic = list(map(lambda x:int(x, 16),dic))
print(dic)
ans = ''
from z3 import *
for x in dic:
ic= BitVec('ic', 8)
s = Solver()
s.add( ((ic<<4)+(ic>>4))^0x17==x )
assert s.check()==sat
m = s.model()
print("traversing model...")
for i in m:
print("{} = {}".format(i, m[i]))
ans += chr(int('%s'%m[i]))
print(ans) # hgame{welc0me_to_4sm_w0rld}
Flag Checker
java的byte是有符号的,而c/c++,python均为无符号,因此需要使用java进行编写
from base64 import *
str = 'mg6CITV6GEaFDTYnObFmENOAVjKcQmGncF90WhqvCFyhhsyqq1s='
str = b64decode(str)
for x in str:
print('(byte){}, '.format(x), end='')
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
class Test_1 {
public static byte[] encrypt(byte[] arg4, String arg5) throws Exception {
SecretKeySpec v0 = new SecretKeySpec(arg5.getBytes(), 0, arg5.length(), "RC4");
Cipher v5 = Cipher.getInstance("RC4");
v5.init(1, v0);
return v5.doFinal(arg4);
}
public static void main(String[] args) {
byte[] cipher = {(byte)154, (byte)14, (byte)130, (byte)33, (byte)53, (byte)122, (byte)24, (byte)70, (byte)133, (byte)13, (byte)54, (byte)39, (byte)57, (byte)177, (byte)102, (byte)16, (byte)211, (byte)128, (byte)86, (byte)50, (byte)156, (byte)66, (byte)97, (byte)167, (byte)112, (byte)95, (byte)116, (byte)90, (byte)26, (byte)175, (byte)8, (byte)92, (byte)161, (byte)134, (byte)204, (byte)170, (byte)171, (byte)91};
byte[] v2 = encrypt(cipher, "carol");
String plain = new String(v2);
System.out.println(plain);
}
}
//hgame{weLC0ME_To-tHE_WORLD_oF-AnDr0|D}
crackme
logic
输入32个字符,分四组每组八个字符,进行tea加密
delta修改了,key是固定的从dic中取,直接求逆即可
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // edx
int i; // esi
unsigned int v5; // edi
unsigned int v6; // ebx
int v7; // esi
int v8; // esi
_DWORD v10[17]; // [esp+Ch] [ebp-8Ch] BYREF
__int128 v11[2]; // [esp+50h] [ebp-48h]
char Arglist[32]; // [esp+70h] [ebp-28h] BYREF
int v13; // [esp+90h] [ebp-8h]
int v14; // [esp+94h] [ebp-4h]
memset(Arglist, 0, sizeof(Arglist));
sub_40103A("%s", (char)Arglist);
strcpy((char *)v10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
v3 = 0;
v14 = 0;
for ( i = 0; i < 32; v14 = i )
{
v5 = *(_DWORD *)&Arglist[i];
v6 = *(_DWORD *)&Arglist[i + 4];
v13 = 0;
v7 = 32;
do
{
v3 += 305419896;
v5 += v3 ^ (v3 + v6) ^ (v10[2] + 16 * v6) ^ (v10[3] + (v6 >> 5));
v6 += v3 ^ (v3 + v5) ^ (v10[0] + 16 * v5) ^ (v10[1] + (v5 >> 5));
--v7;
}
while ( v7 );
v8 = v14;
v3 = 0;
*(_DWORD *)&Arglist[v14] = v5;
*(_DWORD *)&Arglist[v8 + 4] = v6;
i = v8 + 8;
}
v11[0] = xmmword_402180;
v11[1] = xmmword_402170;
while ( Arglist[v3] == *((_BYTE *)v11 + v3) )
{
if ( ++v3 >= 32 )
{
puts("right!", v10[0]);
return 0;
}
}
puts("wrong!", v10[0]);
return 0;
}
expolit
数据处理(ida识别的v11是数字,大小端转换一下 以及 处理把每组加密结果数据列出来)
def mem2char(data='9c6f6424 8651502a'):
print(data)
data = data.split()
for x in data:
x = '{:0>8}'.format(x)
# print(x)
for i in range(len(x)):
if i%2==0:
print(x[i:i+2], chr(int(x[i:i+2], 16)))
def l22b(string = 'ED9CE5ED52EB78C2030C144C48D93488', INFO=False):
# ED9CE5ED 52EB78C2 030C144C 48D93488
collection = ''
for th in range(len(string)-1,-1,-1):
if th%2==0:
per = string[th:th+2]
collection+=per
return collection
print('> result arglist(big-side):')
x0 = 'ED9CE5ED52EB78C2030C144C48D93488'
x1 = '65E0F2E3CF9284AABA5A126DAE1FEDE6'
print(x0+x1)
print('=>')
arglist = l22b(x0) + l22b(x1)
print(arglist)
print('> get each group(little-size):')
fm = []
for i in range(len(arglist)):
if i%16==0:
ag = arglist[i:i+8]
bg = arglist[i+8:i+16]
print(ag, bg, end='=>')
ag = l22b(ag)# to little(for num counting)
bg = l22b(bg)
print(ag, bg)
fm.append([ag, bg])
print('c style: ')
for x in fm:
print(', {0x%s, 0x%s}'% (x[0],x[1]) )
'''
> result arglist(big-side):
ED9CE5ED52EB78C2030C144C48D9348865E0F2E3CF9284AABA5A126DAE1FEDE6
=>
8834D9484C140C03C278EB52EDE59CEDE6ED1FAE6D125ABAAA8492CFE3F2E065
> get each group(little-size):
8834D948 4C140C03=>48D93488 030C144C
C278EB52 EDE59CED=>52EB78C2 ED9CE5ED
E6ED1FAE 6D125ABA=>AE1FEDE6 BA5A126D
AA8492CF E3F2E065=>CF9284AA 65E0F2E3
c style:
, {0x48D93488, 0x030C144C}
, {0x52EB78C2, 0xED9CE5ED}
, {0xAE1FEDE6, 0xBA5A126D}
, {0xCF9284AA, 0x65E0F2E3}
'''
解密程序
#include<Windows.h>
#include<bits/stdc++.h>
#include<stdint.h>
using namespace std;
const char* dic="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
void decrypt_tea(DWORD *v5,DWORD *v6){
int32_t sum = 0x12345678*32;
for(int i=0; i<32; i++){
*v6 -= sum ^ (sum + *v5) ^ (((DWORD *)dic)[0] + 16 * (*v5)) ^ (((DWORD *)dic)[1] + ((*v5) >> 5));
*v5 -= sum ^ (sum + *v6) ^ (((DWORD *)dic)[2] + 16 * (*v6)) ^ (((DWORD *)dic)[3] + ((*v6) >> 5));
sum -= 0x12345678;
}
}
int main(){
puts("> decrypto");
DWORD cipher[4][2]={
{0x48D93488, 0x030C144C}
, {0x52EB78C2, 0xED9CE5ED}
, {0xAE1FEDE6, 0xBA5A126D}
, {0xCF9284AA, 0x65E0F2E3}
};
for(int i=0; i<4; i++){
DWORD v5 = cipher[i][0];
DWORD v6 = cipher[i][1];
decrypt_tea(&v5, &v6);
printf("%-4.4s%-4.4s", (char *)&v5, (char *)&v6);//默认右对齐(+),左对齐为-;因为没有0所以用4.4截断4个字符
}//hgame{H4ppy_v4c4ti0n!}
return 0;
}
summary
出错点总结:
IDA的识别有误,根据汇编代码,此处的 v10定义是char但是实际运算的时候,v10是先转换为了(DWORD*)后进行的调值
strcpy((char *)v10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
v3 += 0x12345678;
v5 += v3 ^ (v3 + v6) ^ (v10[2] + 16 * v6) ^ (v10[3] + (v6 >> 5));
v6 += v3 ^ (v3 + v5) ^ (v10[0] + 16 * v5) ^ (v10[1] + (v5 >> 5));
正确c代码应该是:((DWORD *)v10)[2]
详细见exp
猫头鹰不是猫
logic
分析可知enc的密钥a2是4096长度的字符(64位的超宽字符size==4096*4),程序将输入进行两次加密,后与长度256(char窄字符)的结果比对。
- 04040h-04140h:结果
- 04140h+4000h+4000h:第一二次加密密钥
unsigned __int64 __fastcall enc(__int64 a1, __int64 a2)
{
int v3; // [rsp+18h] [rbp-128h]
int i; // [rsp+1Ch] [rbp-124h]
int j; // [rsp+20h] [rbp-120h]
int k; // [rsp+24h] [rbp-11Ch]
int l; // [rsp+28h] [rbp-118h]
int m; // [rsp+2Ch] [rbp-114h]
int v9[66]; // [rsp+30h] [rbp-110h] BYREF
unsigned __int64 v10; // [rsp+138h] [rbp-8h]
v10 = __readfsqword(0x28u);
memset(v9, 0, 0x100uLL);
for ( i = 0; i <= 63; ++i )
{
for ( j = 0; j <= 63; ++j ) // int32[] matrix[64][64]
*(_DWORD *)(((__int64)i << 8) + a2 + 4LL * j) = *(_DWORD *)(a2 + ((__int64)i << 8) + 4LL * j) / 10;
}
for ( k = 0; k <= 63; ++k )
{
v3 = 0;
for ( l = 0; l <= 63; ++l )
v3 += *(_DWORD *)(4LL * l + a1) * *(_DWORD *)(a2 + ((__int64)l << 8) + 4LL * k);
v9[k] = v3;
}
for ( m = 0; m <= 63; ++m )
*(_DWORD *)(a1 + 4LL * m) = v9[m];
return __readfsqword(0x28u) ^ v10;
}
expolit
dump一下便于读取数据
# coding:utf8
'''
type:
1 static dump from pe file
2 dynamic dump from memory
'''
size = (63<<8)+63*4 + 4 # 0x3ffc+4
type = 1
start_address = 0x04040
length = 0x100 + size*2
outputfile = "C:\\Users\\15426\\Desktop\\hgame2022\\crackme.dump" #需对应修改
import idaapi
if type==1:
data = idaapi.get_bytes(start_address, length)
elif type==2:
data = idaapi.dbg_read_memory(start_address, length)
with open(outputfile, 'wb') as fp:
fp.write(data)
fp.close()
print('[+] written %s bytes'%hex(len(data)))
数据处理+z3
'''
data handle
step1: get a2(//10)
'''
size = (63<<8)+63*4 + 4 # 0x3ffc+4
with open('./crackme.dump', 'rb+') as cf:
data =cf.read(0x100)
data0=cf.read(size)
data1=cf.read(size)
cipher = []
for i in range(len(data)):
if i%4==0:
# python style, if in python2 we can struct.unpack('<h', bytes_num)
cipher.append(int.from_bytes(data[i:i+4], byteorder='little'))
key0 = []
for i in range(len(data0)):
if i%4==0:
key0.append(data0[i]//10)
key1 = []
for i in range(len(data1)):
if i%4==0:
key1.append(data1[i]//10)
# print(len(key0), len(key1), len(cipher))
assert len(key0)==len(key1)==4096
assert key0[0]==0x10 and key1[-1]==0x19
# print(cipher[0])
print("[+] data handled successfully")
'''
step2: z3
x0*c0_0 + x1*c0_1 +... + x63*c0_63 == cipher0
...
x0*c63_0 + x1*c63_1 +... + x63*c63_63 == cipher63
'''
with open('./crackme_code.py', 'wt+') as of:
for i in range(64):
of.write("x{} = Int('x{}')\n".format(i, i))
for i in range(64):
line = ""
for j in range(64):
line += " x{}*key[{}*64+{}] +".format(j, j, i)
line = line[:-1] + " == cipher[{}]".format(i)
of.write("s.add({})\n".format(line))
print('writed {} generated code'.format(hex(of.tell())))
from z3 import *
def decrypt(key, debug=False):
global cipher
s = Solver()
with open('./crackme_code.py', 'rt+') as rf:
data = rf.read()
exec(data)
assert s.check()==sat
sm = s.model()
if debug==True:
for k in sm:
print("{} = {}".format(k, sm[k]))
cipher = [0]*64
for k in sm:
tmpV = int('{}'.format(sm[k]))
tmpK = int('{}'.format(k)[1:])
cipher[tmpK] = tmpV
decrypt(key1), print('[+] first decrypto successfully')
decrypt(key0), print('[+] second decrypto successfully')
plain = ''.join(list(map(lambda x:chr(x), cipher)))
print(plain) # hgame{100011100000110000100000000110001010110000100010011001111}
One comment
博主真是太厉害了!!!