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}
Last modification:January 27, 2022
如果觉得我的文章对你有用,请随意赞赏。咖啡(12RMB)进度+100%,一块巧克力(1RMB)进度+6%。
(赞赏请备注你的名称哦!后台记录中来自理工小菜狗)