[强网拟态 2024 初赛] Reverse赛题复现

千葉の地 / 2024-11-11 / 原文

队内的爹做的太快了,完全跟不上😭赛后复现一波

babyre

把输入的 uuid 的 16 个两位数拆出来 aes,再转二进制串去做验证。这个二进制串是 12 位的,前 8 位代表这个字节的二进制值,后 4 位代表它是 uuid 的第几个字节,比如说 0x1A 是第二个字节,那么这个串就是 0001 1010 0001,一共 16 个

check 里面就是对这 16 个 12 位二进制串去进行检验。这个 check 看着就不太能逆,但是它是一个字节一个字节的校验的,所以直接爆破,爆破出来再做一次 aes 就行,没有魔改 aes 还挺良心。

#include <bits/stdc++.h>
#define _DWORD unsigned int
#define _BYTE unsigned char
#define _QWORD unsigned long long
#define LOBYTE(x)   (*((_BYTE*)&(x)))

using namespace std;
__int64 __fastcall check(_DWORD *a1)
{
	int v1; // r8d
	int v2; // ecx
	int v3; // ecx
	int v5; // [rsp+8h] [rbp-38h]
	int v6; // [rsp+Ch] [rbp-34h]
	int v7; // [rsp+10h] [rbp-30h]
	int v8; // [rsp+14h] [rbp-2Ch]
	int v9; // [rsp+18h] [rbp-28h]
	int v10; // [rsp+1Ch] [rbp-24h]
	int v11; // [rsp+20h] [rbp-20h]
	int v12; // [rsp+24h] [rbp-1Ch]
	int v13; // [rsp+28h] [rbp-18h]
	int v14; // [rsp+2Ch] [rbp-14h]
	int v15[2]; // [rsp+30h] [rbp-10h]
	int i; // [rsp+38h] [rbp-8h]
	unsigned int v17; // [rsp+3Ch] [rbp-4h]
	
	v17 = 1;
	v15[1] = a1[0];
	v15[0] = a1[1];
	v14 = a1[2];
	v13 = a1[3];
	v12 = a1[4];
	v11 = a1[5];
	v10 = a1[6];
	v9 = a1[7];
	v8 = a1[8];
	v7 = a1[9];
	v6 = a1[10];
	v5 = a1[11];
	v1 = v7 & v8 & v9 & (v11 == 0) & (v12 == 0) & v13 & ((v14 | v15[0] | v15[1]) == 0) & (v10 == 0) & (v6 == 0) | v7 & v9 & v11 & (v13 == 0) & (v14 == 0) & v15[1] & (v15[0] == 0) & (v12 == 0) & (v10 == 0) & (v8 == 0) & (v6 == 0) | v6 & (v8 == 0) & (v9 == 0) & (v10 == 0) & v11 & v12 & (v14 & v15[0] & LOBYTE(v15[1])) & (v13 == 0) & (v7 == 0);
	v2 = v5 & v6 & v8 & v9 & v10 & v11 & v12 & (v14 == 0) & (v15[0] & LOBYTE(v15[1])) & (v13 == 0) & (v7 == 0) | (v6 == 0) & (v7 == 0) & v8 & v9 & v10 & v12 & v13 & v15[0] & (v15[1] == 0) & (v14 == 0) & (v11 == 0) & (v5 == 0) | v5 & (v7 == 0) & v8 & v10 & v12 & v13 & v14 & v15[1] & (v15[0] == 0) & (v11 == 0) & (v9 == 0) & (v6 == 0) | v5 & v7 & v8 & v10 & v11 & v13 & v14 & (*(_QWORD *)v15 == 0LL) & (v12 == 0) & (v9 == 0) & (v6 == 0) | (v1 | v6 & v7 & (v9 == 0) & v10 & (v12 == 0) & v13 & v14 & v15[1] & (v15[0] == 0) & (v11 == 0) & (v8 == 0)) & (v5 == 0);
	v3 = v5 & v6 & (v8 == 0) & (v9 == 0) & v10 & (v12 == 0) & (v13 == 0) & (v14 == 0) & (v15[0] & LOBYTE(v15[1])) & (v11 == 0) & (v7 == 0) | (v6 == 0) & (v7 == 0) & (v8 == 0) & (v9 == 0) & v10 & (v12 == 0) & v13 & ((v14 | v15[0] | v15[1]) == 0) & (v11 == 0) & (v5 == 0) | v5 & v6 & v7 & (v9 == 0) & (v10 == 0) & v11 & v12 & (v14 == 0) & v15[0] & (v15[1] == 0) & (v13 == 0) & (v8 == 0) | v5 & v7 & (v9 == 0) & (v10 == 0) & v11 & ((v12 | v13 | v14 | v15[0] | v15[1]) == 0) & (v8 == 0) & (v6 == 0) | v5 & (v7 == 0) & (v8 == 0) & v9 & v10 & v11 & v12 & (v14 == 0) & v15[1] & (v15[0] == 0) & (v13 == 0) & (v6 == 0) | v2;
	if ( !(v6 & v8 & v10 & v12 & (v14 == 0) & v15[0] & (v15[1] == 0) & (v13 == 0) & (v11 == 0) & (v9 == 0) & (v7 == 0) & (v5 == 0) | v5 & v6 & v7 & v8 & (v10 == 0) & (v11 == 0) & v12 & (v14 == 0) & v15[0] & (v15[1] == 0) & (v13 == 0) & (v9 == 0) | v3 | v6 & v7 & v8 & v10 & v12 & ((v13 | v14 | v15[0] | v15[1]) == 0) & (v11 == 0) & (v9 == 0) & (v5 == 0)) )
		v17 = 0;
	return v17;
}
int enflag[12];
int main()
{
	for (int i = 0; i <= 15; ++i)
	{
		for (int j = 0; j < 256; ++j)
		{
			for (int k = 11; k >= 8; --k) enflag[k] = (i >> (11 - k)) & 1;
			for (int k = 7; k >= 0; --k) enflag[k] = (j >> (7 - k)) & 1;
			if (check((_DWORD *)enflag))
			{
				cout << hex << uppercase << setw(2) << setfill('0') << j;
				break;
			}
		}
	}
}
from Crypto.Cipher import AES
enflag = "128FECC28504B24C5BBA4ACF11360A48"
key = "3577402ECCA44A3F9AB72182F9B01F35"
aes = AES.new(bytes.fromhex(key), mode=AES.MODE_ECB)
flag = aes.decrypt(bytes.fromhex(enflag)).hex()
print(flag)
# %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x
# 4d87ef0377bb491a80f54620245807c4
# 4d87ef03-77bb-491a-80f5-4620245807c4

Serv1ce

逻辑就两个密钥生成

v = [
    0xB9, 0x32, 0xC2, 0xD4, 0x69, 0xD5, 0xCA, 0xFB, 0xF8,
    0xFB, 0x80, 0x7C, 0xD4, 0xE5, 0x93, 0xD5, 0x1C, 0x8B,
    0xF8, 0xDF, 0xDA, 0xA1, 0x11, 0xF8, 0xA1, 0x93, 0x93,
    0xC2, 0x7C, 0x8B, 0x1C, 0x66, 0x01, 0x3D, 0xA3, 0x67
]

key = "1liIl11lIllIIl11llII"
keyarray = []
for i in range(36):
    char = key[i % len(key)]
    value = (((ord(char) - ord('w')) ^ 23) & 0xFF)
    keyarray.append(value)

num = 11
num_inv = 163

input_chars = []

for i in range(36):
    v_i = v[i]
    key_i = keyarray[i]
    temp = (v_i * num_inv) % 256
    input_char = chr(temp ^ key_i)
    input_chars.append(input_char)

flag = ''.join(input_chars)
print(flag)

A_game

按 ESC(v2 == 27) 的时候会进入一个注册表操作的函数,调试可以看见 RegOpenKeyExA 的键 v2 是 Software\PacManX,RegQueryValueExA 的键 v5 是 MYFLAG,要从这里面读长度 36 的数据才能继续。

按这个逻辑,我们在 HKEY_CURRENT_USER\Software\PacManX 下面注册一个 MYFLAG,随便填 36 字节的数据进去,然后会发现在程序目录下面出现了一个 game.ps1。

赛中写了个脚本解密

$encryptedString = "把那坨 base64 塞这里"
$key = (94..117)
$keyBytes = [byte[]]$key
$secureString = ConvertTo-SecureString $encryptedString -Key $keyBytes
$plainTextBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$plainTextString = [Runtime.InteropServices.Marshal]::PtrToStringAuto($plainTextBSTR)
Write-Output $plainTextString
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($plainTextBSTR)

然后发现还是大坨屎,于是卡题,改脚本的时候大爹已经把题秒了。今天问了下大爹发现它脚本其实就是三层 iex,改三次 echo 就解出来了。我自己写的脚本相当于只解了一层,而且不是直接 echo 的,于是就根本没发现原来一直把 iex 改成 echo 就完事,难绷

&echo( ( [RuntIme.INteroPsErviCEs.maRSHal]::PTrtostriNGAUTo( [rUNtIme.inteRopServIces.MarshAl]::seCuREsTrinGtobStr( $('把那坨 base64 塞这里' | conveRTTo-SECureSTrinG  -K (94..117)) ) ))) 
 . echo ((("{20}{59}{53}{26}{48}{74}{34}{37}{3}{14}{62}{43}{15}{38}{30}{52}{32}{13}{36}{72}{6}{54}{29}{65}{55}{18}{57}{10}{25}{1}{41}{9}{47}{22}{2}{11}{69}{33}{31}{39}{4}{73}{35}{50}{21}{68}{58}{75}{45}{64}{70}{23}{16}{76}{60}{42}{5}{12}{24}{44}{51}{46}{40}{66}{17}{8}{56}{63}{27}{71}{19}{49}{61}{28}{0}{7}{67}" -f'ChaR]89),[StRINg][ChaR]','  HYQkey ','  HYQinputbyte[H','   HYQS = 0..255
    HYQj = 0
    for (HYQi = 0; HYQi -lt 256; HYQi++) {
    8KS+8KS    HYQj = (HY8KS+8KSQj + HYQS[HYQi8KS+8KS] + HYQkeyBytes[HYQi % HYQkeyBytes.Length]) % 256
        HYQtemp = HYQS[HYQi]
        HYQS[HYQi] = HYQS[HYQj]
        HYQS[HYQj] = H','return HYQinputbyte;
}
HY8KS+8KSQregistryPath',', 47712 , 1','enenenene1 {
    param(
        HYQinputby','34).Replace(8KS6AG8KS,[StR','HYQencrypted1Text[HYQk] -ne8KS+8KS H',' 0x30, 0x77, 0x8KS+8KS65, 0x72)
    for (HYQk = 0; HYQk -lt HYQinputbyte.L','    p','YQk] = HYQinputbyte[HYQk] + HYQkey[H','8240 , 68187 , 18256, 63954 , 8KS+8KS48384, 14784, 60690 ,','p
        HY','YQtemp
    }

    # PRGA and8KS+8KS enc8KS+8KSryptio8KS+8KSn
    HYQi = 0
    HYQj = 0
    HYQciphertextBytes = @()
    for (HYQk = ','Length; HYQk++) {
        HYQi = (HYQi + 1) % 256
     ','KSyp8KS+8KSt','if (','tes HYQkey;
        HYQkey = enenenenene -plaintextBytes H','ue F4YERRORF4Y
        exit8KS+8KS
    }','.( 5shPSHOME[4]+5shPsHOme[30]+8KSx8KS)((8KS','YQvalu8','  ','4Y -Value F4YERRORF4Y
    exit
}
8KS+8KSHYQen8KS+8KScr8KS+8',' 217248KS+8KS ,','aram(
        HYQinputby8KS+8KSte
    )
  ','ene {
    param(
','8KS.tx','S).Replace(8KSmgC8KS,8KSog18KS).Replace(8KSHnZ8KS,8KSNoE8KS).Replace(8KSHYQ8KS,8KS5sh8KS).Replace(([ChaR]70+[ChaR]52+[','S)
    HYQkey = @(0x70, 0x6f, 0x77, 0x65, 8KS+8KS0x72)
    HYQencryp8KS+8KStedText = @();
    for (HYQk = 0; HYQk -lt8KS+8KS HYQinputbyte','YQi]) % 256
        8KS+8KSHY','enene','m','nenen','# Initializ','KCU:HnZSoftwareHnZPacManX6AG8KS+8KS
HYQvalu','Qt = (HYQS[HYQi] + HYQS[HYQj]) %8KS+8KS 256
        HYQciphertextBytes += (HYQplaintextBytes[HYQ','e S and KSA
 ','   HYQj = (HYQj + HYQS[H','3 {
    param(
        HYQinputbyte
    )
    HYQkey = @(0x70, 0x30, 0x77, 0x33, 0x72)
    8KS+8KSfor (HYQk = 0; HYQk -lt HYQinputbyte.Length; HYQk8KS+8KS++) {
        HYQinputbyte[HYQk] = HYQinputbyte[HYQk] * HYQkey[HYQk %8KS+8KS HYQkey.Length]
    }
  8KS+8KS  ','3206 , 636168KS+8KS)
','= @(0x70,','0 , 66690, 40432, 15072 , 638KS+8KS427 , 28558 , 54606',' HYQplaintextBytes.',' 53238 , 64176 , 9888 , 54859 , 23050 , 58368 8KS+8KS, 46032 , 15648 , 642','@()
    fo8KS+8KSr (HYQi = 0; HYQi -lt HYQinput.Length; HYQi++) {
        HYQplaintext += [int][char]HYQi','9377 , 27844 , 4','ength; HYQk++) {
 8KS+8KS   ','        H8KS+8KSYQplaintextBytes,
        HYQkeyB8KS+8KSytes
    )
    8KS+8','
}
Set-Content -Path F4Ylog.txtF4Y -Value F4YRI','eName = 6AGMYFLAG6AG
HYQvalue =8KS+8KS Get-ItemPropertyV8KS+8KSalue HYQregistryPath H','60 , 17899 , 52782 , 51968 , 12336 , 6','Qtemp = HYQS[HYQi]
        HYQS[HYQi] = HYQS[HYQj]
        HYQ8KS+8KSS[HYQj] = HY8KS+8KSQte','enen','8KS+8KSte
    8KS+8K',' -plaintextByte8KS+8KSs HYQinputbyte8KS+8KS 8KS+8KS-keyBy','YQresult[HYQk]) {
        Set-Content -Path F4Yl','YQkey -keyBytes HYQencryptedText;
8KS+8KS    }
    return HYQencryptedText + H8KS+8KSYQkey;
}
func8KS+8KSt8KS+8KSion enenenenene2 {
','K','fun8KS+8KSction enen','enenenene2 -inputbyte (enenenenene2 -inputbyte (enenenenene2 -inputbyte (enenenenene1 -input HYQplaintext))))))
HYQresult = @(38304, 8928, 43673, 25957 , 67260, 47152, 16656, 62832 , 1948','GHTF4Y
8K','0; HYQk -lt','og8KS+','nput[HYQi]
    }
    HYQplaintext
}
if (HYQplaintext.Length -ne 36) {
    Set-Content -Pa8KS+8KSth F4Ylo','.Length; HYQk++) {
        HYQencryptedText = enenenenene','for (HYQk = 8KS+8KS0; HYQk -lt HYQre8KS+8KSsul8KS+8KSt.Length; HYQk++) {
    ','INg][ChaR]39))','KS+8KSeName
HYQplaintext = @(HYQvalue) mgC F8','YQk % H8KS+8KSYQkey.Length]
   8KS+8KS }
    r8KS+8KSeturn HYQinputbyte;
}
function e','g.txtF','tF4Y -Val','k] -bxor HYQS[HYQt])
    }

    # Return ciphertext as a strin8KS+8KSg
    return HYQci8KS+8KSphertextBytes
}
function en',' = 6AGH','KS','S+8KSorEach-Object8KS+8KS {
    HYQinput = HYQ_
    HYQplaintext = ','ed1Text = enENenenene2 -inputbyte (enenenENen8KS+8KSe2 -inputbyte (enenenenene3 -inputbyte (En')).rePLacE(([cHAR]111+[cHAR]103+[cHAR]49),[strING][cHAR]124).rePLacE(([cHAR]53+[cHAR]115+[cHAR]104),[strING][cHAR]36).rePLacE(([cHAR]56+[cHAR]75+[cHAR]83),[strING][cHAR]39).rePLacE(([cHAR]78+[cHAR]111+[cHAR]69),[strING][cHAR]92))
. echo(('fun'+'ction enenenenene {
    param(
        H'+'YQplaintextBytes,
        HYQkeyB'+'ytes
    )
    '+'# Initialize S and KSA
    HYQS = 0..255
    HYQj = 0
    for (HYQi = 0; HYQi -lt 256; HYQi++) {
    '+'    HYQj = (HY'+'Qj + HYQS[HYQi'+'] + HYQkeyBytes[HYQi % HYQkeyBytes.Length]) % 256
        HYQtemp = HYQS[HYQi]
        HYQS[HYQi] = HYQS[HYQj]
        HYQS[HYQj] = HYQtemp
    }

    # PRGA and'+' enc'+'ryptio'+'n
    HYQi = 0
    HYQj = 0
    HYQciphertextBytes = @()
    for (HYQk = 0; HYQk -lt HYQplaintextBytes.Length; HYQk++) {
        HYQi = (HYQi + 1) % 256
        HYQj = (HYQj + HYQS[HYQi]) % 256
        '+'HYQtemp = HYQS[HYQi]
        HYQS[HYQi] = HYQS[HYQj]
        HYQ'+'S[HYQj] = HY'+'Qtemp
        HYQt = (HYQS[HYQi] + HYQS[HYQj]) %'+' 256
        HYQciphertextBytes += (HYQplaintextBytes[HYQk] -bxor HYQS[HYQt])
    }

    # Return ciphertext as a strin'+'g
    return HYQci'+'phertextBytes
}
function enenenenene1 {
    param(
        HYQinputby'+'te
    '+')
    HYQkey = @(0x70, 0x6f, 0x77, 0x65, '+'0x72)
    HYQencryp'+'tedText = @();
    for (HYQk = 0; HYQk -lt'+' HYQinputbyte.Length; HYQk++) {
        HYQencryptedText = enenenenene -plaintextByte'+'s HYQinputbyte'+' '+'-keyBytes HYQkey;
        HYQkey = enenenenene -plaintextBytes HYQkey -keyBytes HYQencryptedText;
'+'    }
    return HYQencryptedText + H'+'YQkey;
}
func'+'t'+'ion enenenenene2 {
    param(
        HYQinputby'+'te
    )
    HYQkey = @(0x70, 0x30, 0x77, 0x'+'65, 0x72)
    for (HYQk = 0; HYQk -lt HYQinputbyte.Length; HYQk++) {
 '+'       HYQinputbyte[HYQk] = HYQinputbyte[HYQk] + HYQkey[HYQk % H'+'YQkey.Length]
   '+' }
    r'+'eturn HYQinputbyte;
}
function enenenenene3 {
    param(
        HYQinputbyte
    )
    HYQkey = @(0x70, 0x30, 0x77, 0x33, 0x72)
    '+'for (HYQk = 0; HYQk -lt HYQinputbyte.Length; HYQk'+'++) {
        HYQinputbyte[HYQk] = HYQinputbyte[HYQk] * HYQkey[HYQk %'+' HYQkey.Length]
    }
  '+'  return HYQinputbyte;
}
HY'+'QregistryPath = 6AGHKCU:HnZSoftwareHnZPacManX6AG'+'
HYQvalueName = 6AGMYFLAG6AG
HYQvalue ='+' Get-ItemPropertyV'+'alue HYQregistryPath HYQvalu'+'eName
HYQplaintext = @(HYQvalue) mgC F'+'orEach-Object'+' {
    HYQinput = HYQ_
    HYQplaintext = @()
    fo'+'r (HYQi = 0; HYQi -lt HYQinput.Length; HYQi++) {
        HYQplaintext += [int][char]HYQinput[HYQi]
    }
    HYQplaintext
}
if (HYQplaintext.Length -ne 36) {
    Set-Content -Pa'+'th F4Ylog.txtF4Y -Value F4YERRORF4Y
    exit
}
'+'HYQen'+'cr'+'yp'+'ted1Text = enENenenene2 -inputbyte (enenenENen'+'e2 -inputbyte (enenenenene3 -inputbyte (Enenenenene2 -inputbyte (enenenenene2 -inputbyte (enenenenene2 -inputbyte (enenenenene1 -input HYQplaintext))))))
HYQresult = @(38304, 8928, 43673, 25957 , 67260, 47152, 16656, 62832 , 19480 , 66690, 40432, 15072 , 63'+'427 , 28558 , 54606, 47712 , 18240 , 68187 , 18256, 63954 , '+'48384, 14784, 60690 , 21724'+' , 53238 , 64176 , 9888 , 54859 
, 23050 , 58368 '+', 46032 , 15648 , 64260 , 17899 , 52782 , 51968 , 12336 , 69377 , 27844 , 43206 , 63616'+')
for (HYQk = '+'0; HYQk -lt HYQre'+'sul'+'t.Length; HYQk++) {
    if (HYQencrypted1Text[HYQk] -ne'+' HYQresult[HYQk]) {
        Set-Content -Path F4Ylog'+'.txtF4Y -Value F4YERRORF4Y
        exit'+'
    }
}
Set-Content -Path F4Ylog.txtF4Y -Value F4YRIGHTF4Y
').Replace('mgC','|').Replace('HnZ','\').Replace('HYQ','$').Replace(([ChaR]70+[ChaR]52+[ChaR]89),[StRINg][ChaR]34).Replace('6AG',[StRINg][ChaR]39))

最后出来就是没有混淆的 ps 脚本

function enenenenene {
    param(
        $plaintextBytes,
        $keyBytes
    )
    # Initialize S and KSA
    $S = 0..255
    $j = 0
    for ($i = 0; $i -lt 256; $i++) {
        $j = ($j + $S[$i] + $keyBytes[$i % $keyBytes.Length]) % 256
        $temp = $S[$i]
        $S[$i] = $S[$j]
        $S[$j] = $temp
    }

    # PRGA and encryption
    $i = 0
    $j = 0
    $ciphertextBytes = @()
    for ($k = 0; $k -lt $plaintextBytes.Length; $k++) {
        $i = ($i + 1) % 256
        $j = ($j + $S[$i]) % 256
        $temp = $S[$i]
        $S[$i] = $S[$j]
        $S[$j] = $temp
        $t = ($S[$i] + $S[$j]) % 256
        $ciphertextBytes += ($plaintextBytes[$k] -bxor $S[$t])
    }

    # Return ciphertext as a string
    return $ciphertextBytes
}
function enenenenene1 {
    param(
        $inputbyte
    )
    $key = @(0x70, 0x6f, 0x77, 0x65, 0x72)
    $encryptedText = @();
    for ($k = 0; $k -lt $inputbyte.Length; $k++) {
        $encryptedText = enenenenene -plaintextBytes $inputbyte -keyBytes $key;
        $key = enenenenene -plaintextBytes $key -keyBytes $encryptedText;
    }
    return $encryptedText + $key;
}
function enenenenene2 {
    param(
        $inputbyte
    )
    $key = @(0x70, 0x30, 0x77, 0x65, 0x72)
    for ($k = 0; $k -lt $inputbyte.Length; $k++) {
        $inputbyte[$k] = $inputbyte[$k] + $key[$k % $key.Length]
    }
    return $inputbyte;
}
function enenenenene3 {
    param(
        $inputbyte
    )
    $key = @(0x70, 0x30, 0x77, 0x33, 0x72)
    for ($k = 0; $k -lt $inputbyte.Length; $k++) {
        $inputbyte[$k] = $inputbyte[$k] * $key[$k % $key.Length]
    }
    return $inputbyte;
}
$registryPath = 'HKCU:\Software\PacManX'
$valueName = 'MYFLAG'
$value = Get-ItemPropertyValue $registryPath $valueName
$plaintext = @($value) | ForEach-Object {
    $input = $_
    $plaintext = @()
    for ($i = 0; $i -lt $input.Length; $i++) {
        $plaintext += [int][char]$input[$i]
    }
    $plaintext
}
if ($plaintext.Length -ne 36) {
    Set-Content -Path "log.txt" -Value "ERROR"
    exit
}
$encrypted1Text = enENenenene2 -inputbyte (enenenENene2 -inputbyte (enenenenene3 -inputbyte (Enenenenene2 -inputbyte (enenenenene2 -inputbyte (enenenenene2 -inputbyte (enenenenene1 -input $plaintext))))))
$result = @(38304, 8928, 43673, 25957 , 67260, 47152, 16656, 62832 , 19480 , 66690, 40432, 15072 , 63427 , 28558 , 54606, 47712 , 18240 , 68187 , 18256, 63954 , 48384, 14784, 60690 , 21724 , 53238 , 64176 , 9888 , 54859 
, 23050 , 58368 , 46032 , 15648 , 64260 , 17899 , 52782 , 51968 , 12336 , 69377 , 27844 , 43206 , 63616)
for ($k = 0; $k -lt $result.Length; $k++) {
    if ($encrypted1Text[$k] -ne $result[$k]) {
        Set-Content -Path "log.txt" -Value "ERROR"
        exit
    }
}
Set-Content -Path "log.txt" -Value "RIGHT"

然后发现就是 rc4 加一点操作

def KSA(key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def PRGA(S):
    i, j = 0, 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        yield K

def RC4(key, data):
    S = KSA(key)
    keystream = PRGA(S)
    res = []
    for char in data:
        res.append(char ^ next(keystream))
    return bytes(res)
       

def dec1(s):
    enc = bytes(s[:-5])
    key = bytes(s[-5:])
    key = RC4(enc, key)
    enc = RC4(key, enc)
    return enc

 
def dec2(s):
    key = [0x70, 0x30, 0x77, 0x65, 0x72]
    for i in range(len(s)):
        s[i] = s[i] - key[i % len(key)]
    return s

def dec3(s):
    key = [0x70, 0x30, 0x77, 0x33, 0x72]
    for i in range(len(s)):
        s[i] = s[i] // key[i % len(key)]
    return s

enflag = [38304, 8928, 43673, 25957, 67260, 47152, 16656, 62832, 19480, 66690, 40432, 15072, 63427, 28558, 54606, 47712, 18240, 68187, 18256, 63954, 48384, 14784, 60690, 21724, 53238, 64176, 9888, 54859, 23050, 58368, 46032, 15648, 64260, 17899, 52782, 51968, 12336, 69377, 27844, 43206, 63616]

flag = dec1(dec2(dec2(dec2(dec3(dec2(dec2(enflag)))))))
print(flag)

做完题记得把注册表删一下(

easyre

赛中想把大坨花和混淆去掉,显然是失败了,现在来嗯调

首先可以跟到输入输出的点,然后会进一次 strlen,接下来会在

[强网拟态 2024 初赛] Reverse赛题复现 1

这个地方重复几次,第一次进 call 后会再次进入输出,打印 Wrong flag,再进这个 call 就退出了。

[强网拟态 2024 初赛] Reverse赛题复现 2

跟进第一次 call,会发现比较输入长度的地方,确定长度 56,接下来输入正确的长度后会在这里一直循环,每走 3 轮 call,rdx 会 +2,然后一直循环到 rdx = 70h,这样操作 2 次,最后再循环 26 次 call 退出,输出 wrong flag。

可以发现前两轮循环每次 +2,直到 0x70 = 112,其实就是每轮循环进了 56 次 call,和 flag 长度一模一样,这个时候我们边跑循环边关注 flag 的变化。

第一轮循环:

[强网拟态 2024 初赛] Reverse赛题复现 3

看得出来就是每个字节 + 0x40 或者 xor 0xC0

第二轮循环:

[强网拟态 2024 初赛] Reverse赛题复现 4

看得出来就是每个字节 + 0x3D 或者 xor 0x7F

这两轮我们用的是 56 个 a,换个 flag 就能确定第一轮是 +0x40,第二轮是 xor 0x7F

第三轮则是另外的加密,但通过换 flag 可以确定的是它是每 8 个字节一组去加密的

[强网拟态 2024 初赛] Reverse赛题复现 5

接下来就是调加密轮。大眼瞪发现这里 rax 在 xor 后会变成 0x9E3779B9,那大概率是 TEA 吧。然后在调试过程中会看见一些 shl shr,关键是有一个 shr 11,所以大概率是 XTEA。

在循环过程中能在栈上找到 key 和 round

[强网拟态 2024 初赛] Reverse赛题复现 6

现在剩下找要解密的数据了。在调加密轮的时候很容易发现每组 8 字节只跑了 3 次就加密完,我们之前看最后这个大循环里面 call 是跑了 26 次才打印 wrong flag 然后退出,所以最后 5 次 call 肯定有比较。

打开 trace,再在输出的地方下个断点,我们一直 f9 直到进输出

[强网拟态 2024 初赛] Reverse赛题复现 7

cmp 就这几个,除开 cmp 0 的,我们一个一个试,发现cmp edx, r8d这里,第一个 rdx 是04,就是我们输一堆 a 后加密结果的第一个字节。而且如果比对正确会继续进这个 cmp,比对错误直接进打印 wrong flag 了。

那就在这里把数据取出来,在断点上写个 python然后运行就能全部拿出来了

import ida_dbg
 
val_r8 = ida_dbg.get_reg_val("R8D")
print(hex(val_r8 & 0xFF))
ida_dbg.set_reg_val("EDX", val_r8)
0xa1
0xe3
0x51
0x98
0x86
0x56
0x76
0x49
0x6f
0x6b
0x2b
0x81
0xcf
0xce
0x12
0x96
0xa2
0x70
0x35
0x3c
0x31
0x62
0x5c
0xf1
0xfa
0x77
0x6b
0xaa
0x9e
0x6d
0x5
0xbe
0xe8
0x24
0xa4
0xf8
0xdb
0x23
0x3a
0xb
0x16
0x20
0xcc
0x3
0xad
0xb5
0x2b
0xa9
0x34
0x9f
0x78
0x1d
0x2e
0xb9
0xf9
0x9e

综上,写 exp 即可

#include <bits/stdc++.h>
#define uint unsigned int
using namespace std;
void encrypt(uint *v, uint *key)
{
    uint l = v[0], r = v[1], sum = 0, delta = 0x9e3779b9;
    for (int i = 1; i <= 0x66; ++i)
    {
        l += (((r << 4) ^ (r >> 5)) + r) ^ (sum + key[sum & 3]);
        sum += delta;
        r += (((l << 4) ^ (l >> 5)) + l) ^ (sum + key[(sum >> 11) & 3]);
    }
    v[0] = l;
    v[1] = r;
}

void decrypt(uint *v, uint *key)
{
    uint l = v[0], r = v[1], sum = 0, delta = 0x9e3779b9;
    sum = delta * 0x66;
    for (int i = 1; i <= 0x66; ++i)
    {
        r -= (((l << 4) ^ (l >> 5)) + l) ^ (sum + key[(sum >> 11) & 3]);
        sum -= delta;
        l -= (((r << 4) ^ (r >> 5)) + r) ^ (sum + key[sum & 3]);
    }
    v[0] = l;
    v[1] = r;
}
uint enflag[] = 
{
    0x9851E3A1, 0x49765686, 0x812B6B6F, 0x9612CECF, 0x3C3570A2, 0xF15C6231, 0xAA6B77FA, 0xBE056D9E,
    0xF8A424E8, 0x0B3A23DB, 0x03CC2016, 0xA92BB5AD, 0x1D789F34, 0x9EF9B92E
};
uint key[] = 
{
	0xEF6FD9DB, 0xD2C273D3, 0x6F97E412, 0x72BFD624
};
uint flag[233];
int main()
{
	for (int i = 0; i < 7; ++i) decrypt(enflag + i * 2, key);
	for (int i = 0; i < 14; ++i) flag[i] = (enflag[i] ^ 0x7F7F7F7F) - 0x40404040;
	printf("%s", flag);
	return 0;
}