Hgame2023 Reverse

结城希亚發表於2024-11-15

Hgame 2023

[HGAME 2023 week1]test your IDA

用ida開啟即可

[HGAME 2023 week1]encode

查殼image-20241106135704303

32位windows

加密函式

image-20241106144724863

將輸入的位元組轉高位和低位進行加密,後與byte_403000進行比較

解密指令碼:

這裡採取了爆破

enc = [8, 6, 7, 6, 1, 6, 13, 6, 5, 6, 11, 7, 5, 6, 14, 6, 3, 6, 15, 6, 4, 6, 5, 6, 15, 5, 9, 6, 3, 7, 15, 5, 5, 6, 1, 6, 3, 7, 9, 7, 15, 5, 6, 6, 15, 6, 2, 7, 15, 5, 1, 6, 15, 5, 2, 7, 5, 6, 6, 7, 5, 6, 2, 7, 3, 7, 5, 6, 15, 5, 5, 6, 14, 6, 7, 6, 9, 6, 14, 6, 5, 6, 5, 6, 2, 7, 13, 7]
flag = ''
for j in range(0, len(enc), 2):
    for i in range(32, 126):
        if i & 0xF == enc[j] and (i >> 4) & 0xF == enc[j+1]:
            flag += chr(i)
print(flag)

[HGAME 2023 week2]before_main

查殼:

image-20241106145106892

64位linux無殼

找到主加密程式

image-20241106151208838

大概是個base64加密

點開sub_12EB

image-20241106151233417

直接點開裡面的table表檢視,按a換成字串

image-20241106151251508

可以發現是替換了table表

但拿去解密是錯的

那應該就有可能是base64加密本身邏輯不同,或者有別的table表

觀察加密函式,沒有問題,那應該就是有別的table表了

交叉引用a0cxwsoemvjq4zd

image-20241106151414391

可以看到確實有別的引用了這個table表

跟進

image-20241106152555619

便可以看到別的table表,拿去解密即可

從網上抄的對映指令碼:

import base64  # 匯入base64模組用於解密

s1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'  # 標準表
s2 = 'qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK'  # base64換表
en_text = 'AMHo7dLxUEabf6Z3PdWr6cOy75i4fdfeUzL17kaV7rG='  # 密文

map_text = ''  # 用於存放密文透過換表對映回標準表的字元
for i in en_text:
    if (i != '='):  # 注意密文中存在等號的情況下,不需要替換!
        idx = s2.index(i)  # 獲取每個密文的字元在換表中的索引
        map_text += s1[idx]  # 取出標準表中的該索引的字元,就是正常base64加密的密文
    else:
        map_text += i
print(map_text)  # 可以先看看標準表base64加密的密文
print(base64.b64decode(map_text))  # 直接使用提供的base64解密函式,獲得明文,就是flag

疑問

為什麼會是呼叫這個table表,可以看到程式執行的時候同步執行了init函式,我們跟進init函式即可找到剛剛的table表image-20241106152705919

[HGAME 2023 week1]easyasm

開啟附件是一個txt文字,裡面儲存了一個用匯編程式碼寫的加密程式

image-20241106153651967

可以看到這裡有個異或和加密程式

其實可以直接猜是異或0x33

enc = [0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e]
flag = ''
for i in enc:
    flag += chr(i ^ 0x33)
print(flag)

得到flag

但還是得學著看彙編的hh

我自己的理解:

; void __cdecl enc(char *p)
.text:00401160 _enc            proc near               ; CODE XREF: _main+1B↑p   //定義函式enc
.text:00401160
.text:00401160 i               = dword ptr -4   //定義變數
.text:00401160 Str             = dword ptr  8
.text:00401160
.text:00401160                 push    ebp
.text:00401161                 mov     ebp, esp  //將基址指標ebp
.text:00401163                 push    ecx
.text:00401164                 mov     [ebp+i], 0
.text:0040116B                 jmp     short loc_401176        //跳轉到loc_401176 開始迴圈
.text:0040116D ; ---------------------------------------------------------------------------
.text:0040116D
.text:0040116D loc_40116D:                             ; CODE XREF: _enc+3B↓j
.text:0040116D                 mov     eax, [ebp+i]   //將i載入到eax中
.text:00401170                 add     eax, 1  //將i值加1
.text:00401173                 mov     [ebp+i], eax  //更新i的值
.text:00401176
.text:00401176 loc_401176:                             ; CODE XREF: _enc+B↑j
.text:00401176                 mov     ecx, [ebp+Str]  //將str移動給ecx
.text:00401179                 push    ecx             ; Str  //將str壓棧
.text:0040117A                 call    _strlen  //用strlen獲取剛剛壓棧的str長度
.text:0040117F                 add     esp, 4  
.text:00401182                 cmp     [ebp+i], eax //比較i 與 eax大小
.text:00401185                 jge     short loc_40119D //如果i大於eax則跳轉loc_40119D
.text:00401187                 mov     edx, [ebp+Str]  //將str地址賦值edx
.text:0040118A                 add     edx, [ebp+i] //將str的地址+i的地址,即可實現對str陣列元素的獲取
.text:0040118D                 movsx   eax, byte ptr [edx] //將edx地址中的元素轉byte賦值到eax暫存器
.text:00401190                 xor     eax, 33h //將eax與0x33異或,將異或結果儲存到了eax暫存器中的低地址al中,他只是對eax中的記憶體進行操作
.text:00401193                 mov     ecx, [ebp+Str] //將異或後的值賦值給ecx
.text:00401196                 add     ecx, [ebp+i] //再次載入str的第i個數
.text:00401199                 mov     [ecx], al //將加密後的結果移動到ecx中
.text:0040119B                 jmp     short loc_40116D //跳轉到loc_40116D函式
.text:0040119D ; ---------------------------------------------------------------------------
.text:0040119D
.text:0040119D loc_40119D:                             ; CODE XREF: _enc+25↑j
.text:0040119D                 mov     esp, ebp //回覆棧指標
.text:0040119F                 pop     ebp //恢復基址指標
.text:004011A0                 retn  //結束函式
.text:004011A0 _enc            endp
Input: your flag
Encrypted result: 0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e

[HGAME 2023 week1]easyenc

die查殼

image-20241106171829452

64位win

定位主程式

image-20241106183124094

對v7陣列以byte型別進行操作

編寫指令碼

#include<stdio.h>
void main() {
    int v8[10], v3, v5;
    v8[0] = 167640836;
    v8[1] = 11596545;
    v8[2] = -1376779008;
    v8[3] = 85394951;
    v8[4] = 402462699;
    v8[5] = 32375274;
    v8[6] = -100290070;
    v8[7] = -1407778552;
    v8[8] = -34995732;
    v8[9] = 101123568;
    for (int i = 0; i < 41; i++)
    {
        v5 = (*((char*)v8 + i) + 86) ^ 0x32;
        printf("%c", v5);
    }
}

[HGAME 2023 week1]a_cup_of_tea

查殼

image-20241106183359746

64win無殼

main函式

image-20241106202555776

可以看到是接受引數Buf1和v10來加密然後與buf2進行比較

沒看到哪裡是v10的輸入,但v10和buf1的地址相近,所以v10應該也是buf1的輸入

tea加密

image-20241106203229555

魔改delta的tea加密,進行四輪

返回看函式接收,接受了一個m128i_i32地址

image-20241106203348716

smmword表示一個十六位元組字串

小端序儲存,因此可以推測這就是tea加密的key,當然也可以用動調來看tea加密中變數的賦值

編寫指令碼

#include <iostream>
using namespace std;
int main()
{
	unsigned int enc[8] = { 778273437,-1051836401, -1690714183,1512016660,1636330974,1701168847,-1626976412,0x236A43F6 };
	unsigned int key[4] = { 0x12345678, 0x23456789, 0x34567890, 0x45678901 };
	int i, j;
	for (i = 0;i < 8;i += 2) {
		unsigned int delta = 0x543210DD;
		int sum = -0x543210DD * 32;
		for (j = 0; j < 32; j++) {
			enc[i + 1] -= ((enc[i] << 4) + key[2]) ^ (enc[i] + sum) ^ ((enc[i] >> 5) + key[3]);
			enc[i] -= ((enc[i + 1] << 4) + key[0]) ^ (enc[i + 1] + sum) ^ ((enc[i + 1] >> 5) + key[1]);
			sum += delta;
		}
	}
	printf("%s", enc);
	return 0;
}

用python的ctypes庫

from ctypes import *
from binascii import *
from Crypto.Util import *
key = [0x12345678, 0x23456789, 0x34567890, 0x45678901]
enc = [0x2E63829D, 0xC14E400F, 0x9B39BFB9, 0x5A1F8B14, 0x61886DDE, 0x6565C6CF, 0x9F064F64, 0x236A43F6]
for i in range(0, len(enc), 2):
    v0 = c_uint32(enc[i])
    v1 = c_uint32(enc[i+1])
    delta = 0x543210DD
    sum = c_uint32(-delta * 32)
    r = 32
    for j in range(r):
        v1.value -= (sum.value + v0.value) ^ (key[2] + 16 * v0.value) ^ (key[3] + (v0.value >> 5))
        v0.value -= (sum.value + v1.value) ^ (key[0] + 16 * v1.value) ^ (key[1] + (v1.value >> 5))
        sum.value += delta
    enc[i] = v0.value
    enc[i+1] = v1.value
    print(a2b_hex(hex(enc[i])[2:].encode()).decode()[::-1],end='')
    print(a2b_hex(hex(enc[i+1])[2:].encode()).decode()[::-1], end='')
    
      # print(number.long_to_bytes(enc[i]).decode()[::-1],end="")
    # print(number.long_to_bytes(enc[i+1]).decode()[::-1],end="")
#也可以用Crypto.Util庫的long_to_bytes列印

[HGAME 2023 week2]stream

python編譯的程式,使用pyinstxtractor-master來拆包

image-20241107220021032

stream.pyc反編譯

import base64

def gen(key):
    s = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s[i] + ord(key[i % len(key)])) % 256
        tmp = s[i]
        s[i] = s[j]
        s[j] = tmp
    i = j = 0
    data = []
    for _ in range(50):
        i = (i + 1) % 256
        j = (j + s[i]) % 256
        tmp = s[i]
        s[i] = s[j]
        s[j] = tmp
        data.append(s[(s[i] + s[j]) % 256])
    return data


def encrypt(text, key):
    result = ''
    for c, k in zip(text, gen(key)):
        result += chr(ord(c) ^ k)
    result = base64.b64encode(result.encode()).decode()
    return result

text = input('Flag: ')
key = 'As_we_do_as_you_know'
enc = encrypt(text, key)
if enc == 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl':
    print('yes!')
    return None
None('try again...')

很簡單的python程式寫的異或加密

image-20241107221851175

首先在加密函式中把k列印出來

再編寫指令碼

解密指令碼:

from base64 import *
key = [213, 242, 54, 127, 156, 227, 172, 100, 212, 1, 130, 92, 20, 189, 115, 12, 15, 228, 186, 225, 227, 75, 200, 119, 171, 11, 152, 15, 89, 160, 116, 157, 194, 226, 72, 147, 65, 74, 92, 21, 136, 193, 152, 94, 17, 178, 205, 195, 87, 145]
enc = 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl'
tmp = b64decode(enc.encode()).decode()
flag = ''
for i in range(len(tmp)):
    flag += chr(ord(tmp[i]) ^ key[i])
print(flag)

[HGAME 2023 week2]math

查殼:

image-20241107222041409

64位linux無殼

分析

是一個簡單的矩陣相乘方程

from z3 import *
v12 = [63998, 33111, 67762, 54789, 61979, 69619, 37190, 70162, 53110, 68678, 63339, 30687, 66494, 50936, 60810, 48784, 30188, 60104, 44599, 52265, 43048, 23660, 43850, 33646, 44270]
v10 = [126, 225, 62, 40, 216, 253, 20, 124, 232, 122, 62, 23, 100, 161, 36, 118, 21, 184, 26, 142, 59, 31, 186, 82, 79]
x = [Int(f"x{i}") for i in range(25)]
s = Solver()
for i in range(5):
    for j in range(5):
        tmp = 0
        for k in range(5):
            tmp += x[5 * i + k] * v10[5 * k + j]
        s.add(v12[5 * i + j] == tmp)
if s.check() == sat:
    result = s.model()
    print(result)

[HGAME 2023 week3]kunmusic

用dnspy開啟dll檔案

定位關鍵函式

這裡看入口點

image-20241108211525781

image-20241108191928713

在資源處儲存檔案

image-20241108202017307

轉換文件指令碼

with open(r'D:\CTF\Reverse\Hgame\Hgame2023\[HGAME 2023 week3]kunmusic\kmusic\data', 'rb') as file:
    str = file.read()
result = [0] * len(str)
for i in range(len(str)):
    result[i] = (str[i] ^ 104)
output = bytes(result)
with open(r'D:\CTF\Reverse\Hgame\Hgame2023\[HGAME 2023 week3]kunmusic\kmusic\output.txt', 'wb') as file:
    file.write(output)

丟入dnspy反編譯

image-20241109110631309

找到關鍵加密函式

z3求解

from z3 import *
s = Solver()
num = [BitVec(f'num{i}',8) for i in range(13)]
array = [132, 47, 180, 7, 216, 45, 68, 6, 39, 246, 124, 2, 243, 137, 58, 172, 53, 200, 99, 91, 83, 13, 171, 80, 108, 235, 179, 58, 176, 28, 216, 36, 11, 80, 39, 162, 97, 58, 236, 130, 123, 176, 24, 212, 56, 89, 72]

s.add(num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 0xA114) + num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 0x525F) == 12702282 )
s.add( num[0] - 25228 + (num[1] ^ 0x50DB) + (num[2] ^ 0x1FDE) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 0xBF9F) + num[8] - 7992 + (num[9] ^ 0xE079) + (num[10] ^ 0xE052) + num[11] + 13299 + num[12] - 50966 == 9946829 )
s.add( num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 0x3506) + (num[6] ^ 0x533E) + num[7] + 47366 + num[8] + 41784 + (num[9] ^ 0xD1BA) + num[10] * 58436 + num[11] * 15590 + num[12] + 58225 == 2372055 )
s.add( num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + num[9] + 18568 + num[10] + 12995 + (num[11] ^ 0x995C) + num[12] + 25329 == 6732474 )
s.add( num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 0x3F9C) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 0x483B) + (num[9] ^ 0x12BB) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 == 14020739 )
s.add( num[0] - 10968 + num[1] - 31780 + (num[2] ^ 0x7C71) + num[3] - 61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 0x1B98) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 0x3099) == 14434062 )
s.add( num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 0x59CA) + (num[7] ^ 0x5B35) + (num[8] ^ 0x3FFD) + (num[9] ^ 0x5A85) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 == 7433598 )
s.add( num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4] ^ 0x507) + (num[5] ^ 0x89C8) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + (num[9] ^ 0xAF4A) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404 )
s.add( (num[0] ^ 0x7132) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 0x9977) + num[5] - 48576 + (num[6] ^ 0x6F7E) + num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 == 3504803 )
s.add( num[0] * 22466 + (num[1] ^ 0xDABF) + num[2] - 53658 + (num[3] ^ 0xB838) + (num[4] ^ 0x30DF) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 0x62BF) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 0x4175) == 11003580 )
s.add( num[0] * 57492 + (num[1] ^ 0x346D) + num[2] - 13941 + (num[3] ^ 0xBBDC) + num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 0x4888) + num[11] * 49694 + (num[12] ^ 0x2501) == 25546210 )
s.add( num[0] - 23355 + num[1] * 50164 + (num[2] ^ 0x873A) + num[3] + 52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 0x12FA) + (num[7] ^ 0xA376) + num[8] * 27122 + (num[9] ^ 0xA44A) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836 )
s.add( num[0] * 30523 + (num[1] ^ 0x1F36) + num[2] + 39058 + num[3] * 57549 + (num[4] ^ 0xD0C0) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 0xD88C) + (num[8] ^ 0xA40) + (num[9] ^ 0x3554) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722)
s.add(num[0]==ord('h')^array[0])
s.add(num[1]==ord('g')^array[1])
s.add(num[2]==ord('a')^array[2])
s.add(num[3]==ord('m')^array[3])
s.add(num[4]==ord('e')^array[4])
s.add(num[5]==ord('{')^array[5]) #z3多解,猜測求解
if s.check() == sat:
    result = s.model()
num_values = [result[num[i]].as_long() for i in range(13)]

flag = ''
for i in range(len(array)):
    flag += chr((array[i] ^ num_values[i % len(num_values)] ) & 0x7f )
print(flag)

[HGAME 2023 week2]VidarCamera

找到主加密程式

image-20241109112054883

長度40的位元組

uIntArr是密文

檢視 m8encrypthkIa6DI函式

image-20241109160401129

我改了一下變數名稱(有一說一java太醜了)

image-20241109160421623

可以看到這裡有個很明顯的xtea加密

修改了delta,round,加密過程

(func1和func2點進去檢視是什麼意思,其實就是簡單的索引)

解密函式:

from ctypes import *
from Crypto.Util import *
enc = [0x260202FA, 0x1B451064, 0x867B61F1, 0x228033C5, 0xF15D82DC, 0x9D8430B1, 0x19F2B1E7, 0x2BBA859C, 0x2A08291D, 0xDC707918]
key = [2233, 4455, 6677, 8899]
for i in range(len(enc) - 2, -1, -1):
    v0 = c_uint32(enc[i])
    v1 = c_uint32(enc[i+1])
    delta = 878077251
    r = 33
    sum = c_uint32(r * delta)
    for j in range(r):
        sum.value -= delta
        v1.value -= (key[(sum.value >> 11) & 3] + sum.value) ^ (v0.value + ((v0.value >> 5) ^ (v0.value << 4)))
        v0.value -= (key[sum.value & 3] + sum.value) ^ (v1.value + ((v1.value >> 5) ^ (v1.value << 4))) ^ sum.value
    enc[i] = v0.value
    enc[i+1] = v1.value
for i in range(10):
    print(number.long_to_bytes(enc[i]).decode()[::-1], end='')

[HGAME 2023 week4]vm

image-20241111133632240

點進主要加密函式image-20241111133640284

這裡就是虛擬機器的opcode,a1就是ip

匯入結構體(根據sub_140001000函式)

image-20241111165158083

struct vm
{
  _DWORD r[6];
  _DWORD ip;
  _DWORD esp;
  _BYTE zf;
};

分析vm指令

image-20241112121848666

mov

image-20241112121900440

有賦值操作,即mov

push

image-20241112122024679

點進stack,即可看到一堆未定義的空間

將暫存器的值賦值給這些空間,即可知道這是模擬壓棧操作

pop

image-20241112122138416

與上面的類似,將stack中的值賦值給暫存器,模擬出棧操作(將上面的函式結合其實很容易看出來)

運算

image-20241112122234485

進行了一系列運算,分別是add, sub, mul, xor, shl, shr

cmp

image-20241112122345377

兩個if值的比較,如果比較成功則zf為0不成功則為1,為後面的jz和jnz服務的

jmp

image-20241112122913255

跳轉到下一條opcode指令處,將opcode下一條指令賦值給ip

jz

image-20241112122651008

JZ:jump if zero

如果if為假(即zf為0),則執行下一條指令

jnz

image-20241112123144319

JNZ:jump if not zero

如果if為真(即zf不為0),則執行下一條指令

編寫指令碼

opcode = [ 0x00, 0x03, 0x02, 0x00, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x02, 0x32,
  0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
  0x01, 0x00, 0x00, 0x03, 0x02, 0x64, 0x03, 0x00, 0x02, 0x03,
  0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x00, 0x03,
  0x00, 0x08, 0x00, 0x02, 0x02, 0x01, 0x03, 0x04, 0x01, 0x00,
  0x03, 0x05, 0x02, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x02,
  0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x01, 0x03, 0x00,
  0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x01, 0x28,
  0x04, 0x06, 0x5F, 0x05, 0x00, 0x00, 0x03, 0x03, 0x00, 0x02,
  0x01, 0x00, 0x03, 0x02, 0x96, 0x03, 0x00, 0x02, 0x03, 0x00,
  0x00, 0x00, 0x00, 0x04, 0x07, 0x88, 0x00, 0x03, 0x00, 0x01,
  0x03, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03,
  0x01, 0x28, 0x04, 0x07, 0x63, 0xFF, 0xFF]
i = 0
for j in range(10000):
    if opcode[i] == 0xff:
        break
    match opcode[i]:
        case 0:   #mov
            print(f'({i})', end=' ')    #地址
            match (opcode[i + 1]):
                case 1:
                    print('mov flag[r[2]], r[1]')
                case 2:
                    print(f'mov r[{opcode[i + 2]}], r[{opcode[i + 3]}]')
                case 3:
                    print(f'mov r[{opcode[i + 2]}], {opcode[i + 3]}')
                case _:
                    print('mov r[0], flag[r[2]] ')
            i += 4
        case 1:     #push
            print(f'({i})', end=' ')
            match (opcode[i + 1]):
                case 1:
                    print('push r[0]')
                case 2:
                    print('push r[2]')
                case 3:
                    print('case r[3]')
                case _:
                    print('push r[0]')
            i += 2
        case 2:   #pop
            print(f'({i})', end=' ')
            match (opcode[i + 1]):
                case 1:
                    print('pop r[1]')
                case 2:
                    print('pop r[2]')
                case 3:
                    print('pop r[3]')
                case _:
                    print('pop r[0]')
            i += 2
        case 3:   #運算
           print(f'({i})', end=' ')
           match (opcode[i + 1]):
               case 0:
                   print(f'add r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
               case 1:
                   print(f'sub r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
               case 2:
                   print(f'mul r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
               case 3:
                   print(f'xor r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
               case 4:
                   print(f'shl r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
               case 5:
                   print(f'shr r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
           i += 4
        case 4:   #cmp
           print(f'({i})', end=' ')
           print('cmp r[0] r[1]')
           i += 1
        case 5:   #jmp
            print(f'({i})', end=' ')
            print(f'jmp {opcode[i + 1]}')
            i += 2
        case 6:   #jz
            print(f'({i})', end=' ')
            print(f'jz {opcode[i + 1]}')
            i += 2
        case 7:   #jnz
            print(f'({i})', end=' ')
            print(f'jnz {opcode[i + 1]}')
            i += 2

列印出來的彙編指令(我給了一些註釋)

(0) mov r[2], 0
(4) add r[2] r[3]      //r[3]就是i
(8) mov r[0], flag[r[2]]  //這裡的flag[r[2]]就是輸入的input
(12) mov r[1], r[0]   
(16) mov r[2], 50
(20) add r[2] r[3]
(24) mov r[0], flag[r[2]] //將flag記憶體處偏移50 + i的值存入r[0]
(28) add r[1] r[0]   //將input 與 偏移50+i的值相加
(32) mov r[2], 100   
(36) add r[2] r[3]   
(40) mov r[0], flag[r[2]]  //將flag記憶體處偏移100 + i的值存入r[0]
(44) xor r[1] r[0] //將input 與 偏移100+i的值異或,將值存入v[1]
(48) mov r[0], 8  //賦值8給r[0]
(52) mov r[2], r[1] 
(56) shl r[1] r[0]   
(60) shr r[2] r[0]  
(64) add r[1] r[2]  //將異或後的值左移八位右移八位相加存入r[1](也就是低八位和高八位換位)
(68) mov r[0], r[1]  //將運算結果儲存在r[0]中
(72) push r[0]   //將運算結果壓入棧中
(74) mov r[0], 1
(78) add r[3] r[0]
(82) mov r[0], r[3]
(86) mov r[1], 40   
(90) cmp r[0] r[1]   //判斷迴圈結束
(91) jz 95
(93) jmp 0
(95) mov r[3], 0
(99) pop r[1]   //將結果出棧儲存到r[1]中,所以最後一個結果先與第一個比較
(101) mov r[2], 150
(105) add r[2] r[3]
(109) mov r[0], flag[r[2]]  
(113) cmp r[0] r[1]   //將運算結果與偏移150位的資料比較
(114) jnz 136 
(116) mov r[0], 1
(120) add r[3] r[0]
(124) mov r[0], r[3]
(128) mov r[1], 40
(132) cmp r[0] r[1]  //繼續迴圈
(133) jnz 99

將輸入與input記憶體偏移50處的值相加,後與偏移100的值異或,最後與偏移150處的值逆序(出棧壓棧操作導致的)比較

編寫解密指令碼

enc = [18432, 61696, 16384, 8448, 13569, 25600, 30721, 63744, 6145, 20992, 9472, 23809, 18176, 64768, 26881, 23552, 44801, 45568, 60417, 20993
     , 20225, 6657, 20480, 34049, 52480, 8960, 63488, 3072, 52992, 15617, 17665, 33280, 53761, 10497, 54529, 1537, 41473, 56832, 42497, 51713]
enc = enc[::-1]
key2 =[155, 168, 2, 188, 172, 156, 206, 250, 2, 185, 255, 58, 116, 72, 25, 105, 232, 3, 203, 201, 255, 252, 128, 214, 141, 215, 114, 0, 167, 29, 61, 153, 136
       , 153, 191, 232, 150, 46, 93, 87]
key1 = [201, 169, 189, 139, 23, 194, 110, 248, 245, 110, 99, 99, 213, 70, 93, 22, 152, 56, 48, 115, 56, 193, 94, 237, 176,
        41, 90, 24, 64, 167, 253, 10, 30, 120, 139, 98, 219, 15, 143, 156]
flag = ''
for i in range(len(enc)):
     tmp1 = (enc[i] & 0xFF << 8) + ((enc[i] >> 8) & 0xFF)
     tmp2 = tmp1 ^ key1[i]
     tmp3 = tmp2 - key2[i]
     flag += chr(tmp3 & 0xff)
print(flag)

flag : hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}

高低位互換操作:

b = 18432
low_byte = b & 0xFF       # 低八位 (masking with 0xFF)
high_byte = (b >> 8) & 0xFF  # 高八位 (shift right and mask with 0xFF)
swapped_value = (low_byte << 8) + high_byte

[HGAME 2023 week3]cpp

全靠大佬wp成就TAT

進入醜的一b的main函式

image-20241112210625740

甚至都沒翻到哪裡對input進行加密了

那就先動調

下斷點動調

既然都不知道input都有多長,那就先輸入一個比較長的數

image-20241112210911330

一直f7步入

image-20241112211201797

進入到一個不知道幹啥的函式

跳出去繼續f7

image-20241112211259976

這次進入了一個疑似加密的函式,是一個異或函式

跳到異或部分image-20241112212129159

這裡就是把eax和ecx進行異或,eax應該就是加密值,但我不知道為什麼

使用ida指令碼列印ecx的值

from idaapi import *
print(hex(get_reg_val("ecx")),end = ',')

image-20241112213437133

0x4037a04e,0xfdda0246,0x3c6efa21,0xcf9cd9af,0x673347b9,0xdec4ee0,0x1380c4d1,0x3ab2a932,0x25d50a7,0x834a3982,0xcb6ea25f,0xa26ba4ab,0xa1c42135,0xd1063eba,0x2397fefc,0x55c7d126,0xabababab

進入比較函式

image-20241112213532565

image-20241112213553284

還有兩位在彙編介面看

image-20241112213633737

這就是一個比較函式

可以看到enc長度是40,那key應該也是40

編寫彙編指令碼

from struct import *
key = [0x4037a04e,0xfdda0246,0x3c6efa21,0xcf9cd9af,0x673347b9,0xdec4ee0,0x1380c4d1,0x3ab2a932,0x25d50a7,0x834a3982]
enc = [0x28, 0x50, 0xC1, 0x23, 0x98, 0xA1, 0x41, 0x36, 0x4C, 0x31, 0xCB, 0x52, 0x90, 0xF1, 0xAC, 0xCC, 0xF, 0x6C, 0x2A, 0x89, 0x7F, 0xDF, 0x11, 0x84, 0x7F, 0xE6, 0xA2, 0xE0, 0x59, 0xC7, 0xC5, 0x46, 0x5D, 0x29, 0x38, 0x93, 0xED, 0x15, 0x7A, 0xFF,]
key_LE = []
flag = []
for i in key:
    key_LE.extend((pack('>I', i)))
for i in range(len(enc)):
    print(chr(key_LE[i] ^ enc[i]), end='')

貌似是需要把enc進行小端序轉換的,我這邊直接把key大端序列印了,方便點

hgame{Cpp_1s_much_m0r3_dlff1cult_th4n_C}

只能說不懂的多的一b,哎哎,看wp也沒看懂

相關文章