According to source ❤️ ❤️ ❤️ ❤️
https://bksec.vn/blog/writeup-cscv-2025
Reverse - REEZ:
Date: 21/10/2025 🦫
Purpose : learn how to solve its 😍
Owner: Khoi nguyen - Nova 🐲
Step to solve 🦅
Solving by use IDA
Step 1: dùng F5 trong IDA để decompile
F5 dùng để biên dịch ngược - decompile 1 hàm sang 1 mã giả C - pseudocode
Ta thu được code pseudocode:
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rdx
__int64 v4; // r8
__m128i si128; // xmm0
const char *v6; // rcx
_BYTE v8[32]; // [rsp+0h] [rbp-58h] BYREF
char Str[16]; // [rsp+20h] [rbp-38h] BYREF
__m128i v10; // [rsp+30h] [rbp-28h]
__int64 v11; // [rsp+40h] [rbp-18h]
__int64 v12; // [rsp+48h] [rbp-10h]
v10 = 0;
*(_OWORD *)Str = 0;
v11 = 0;
sub_1400010F0("Enter flag: ", argv, envp);
sub_140001170("%32s", Str);
if ( strlen(Str) != 32 )
{
puts("No");
if ( ((unsigned __int64)v8 ^ v12) == _security_cookie )
return 0;
LABEL_5:
__debugbreak();
}
si128 = _mm_load_si128((const __m128i *)&xmmword_14001E030);
*(__m128i *)Str = _mm_xor_si128(_mm_load_si128((const __m128i *)Str), si128);
v10 = _mm_xor_si128(si128, v10);
if ( _mm_movemask_epi8(
_mm_and_si128(
_mm_cmpeq_epi8(*(__m128i *)Str, (__m128i)xmmword_140029000),
_mm_cmpeq_epi8(v10, (__m128i)xmmword_140029010))) == 0xFFFF )
v6 = (const char *)&unk_140023E7C;
else
v6 = "No";
sub_1400010F0(v6, v3, v4);
if ( ((unsigned __int64)v8 ^ v12) != _security_cookie )
goto LABEL_5;
return 0;
}
Phân tích:
Lệnh SIMD (SSE):
SIMD - Single Instruction Multiple Date : 1 lệnh CPU xử lý nhiều tác vụ khác nhau cùng lúc
ta thấy có
sub_1400010F0("Enter flag: ", argv, envp);
sub_140001170("%32s", Str);
if ( strlen(Str) != 32 )
sub_1400010F0("Enter flag: ", argv, envp); => in ra Enter flag
sub_140001170("%32s", Str); => lấy 32 kí tự và nhập vào str
if ( strlen(Str) != 32 ) => check xem có phải đúng 32 bit không
- lấy 32 byte mà char Str có 16 byte thế nên nó sẽ trản bit . Vây câu hỏi là nó sẽ tràn vào đâu?
- dựa trên mã giả mà chúng ta thu được bằng IDA thì ta có thể suy ra được vị trí mà nó sẽ tràn 👍
_BYTE v8[32]; // [rsp+0h] [rbp-58h] BYREF
char Str[16]; // [rsp+20h] [rbp-38h] BYREF
__m128i v10; // [rsp+30h] [rbp-28h]
__int64 v11; // [rsp+40h] [rbp-18h]
__int64 v12; // [rsp+48h] [rbp-10h]
Dựa trên rsp (rengister stack pointer ) và rbp (register base pointer ) thì ta sắp xếp được theo thứ tự giảm dần từ high address đến lower address:
v12
v11
v10
str
v8
dựa trên dữ liệu trên thì chúng ta có thể suy ra khi trản số thì sẽ đẩy vào v10 ( v10 có thể chứa 16 byte dựa trên --m128i nên suy ra v10 chứa 16 byte)
bỏ qua các trường hợp để check điều kiện nếu sai password thì ta thu được:
---------------------------------------1---------------------------------------
si128 = _mm_load_si128((const __m128i *)&xmmword_14001E030);
- Tương đương với trong assemply là :
movdqa xmm0, cs:xmmword_14001E030
tại xmmword_14001E030 chứa:
xmmword_14001E030 xmmword 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh
=>dùng để lấy dữ liệu từ xmmword_14001E030 và chuyển vào si123 hay xmm0
---------------------------------------2---------------------------------------
*(__m128i *)Str = _mm_xor_si128(_mm_load_si128((const __m128i *)Str), si128);
- Tương đương với trong assemply là :
movdqa xmm1, xmmword ptr [rsp+58h+Str]
pxor xmm1, xmm0
movdqa xmmword ptr [rsp+58h+Str], xmm1
=> dùng để gán lưu giá trị tại địa chỉ rsp+58h+Str có kích thước 16 byte - 128 bit vào xmm1
=> sau đó xmm1 xor với xmm 0 lưu tại xmm1
=> xmm0 là key để mã hóa flag => lưu ở xmmword_14001E030
=> có thể dùng key này để xor với flag đã mã hóa để ra flag
---------------------------------------3---------------------------------------
v10 = _mm_xor_si128(si128, v10);
- Tương đương với trong assemply là :
pxor xmm0, [rsp+58h+var_28]
movdqa [rsp+58h+var_28], xmm0
=> xor xmm0 với [rsp+58h+var_28] - v10
=> kết quả lưu tại xmm0 và tại địa chỉ rsp+58h+var_28 - v10
---------------------------------------4---------------------------------------
if ( _mm_movemask_epi8(
_mm_and_si128(
_mm_cmpeq_epi8(*(__m128i *)Str, (__m128i)xmmword_140029000),
_mm_cmpeq_epi8(v10, (__m128i)xmmword_140029010))) == 0xFFFF )
-Tương đương với trong assemply là :
pcmpeqb xmm0, cs:xmmword_140029010
pcmpeqb xmm1, cs:xmmword_140029000
pand xmm1, xmm0
pmovmskb eax, xmm1
cmp eax, 0FFFFh
jz short loc_1400010CA
-Phân tích
pcmpeqb xmm0, cs:xmmword_140029010
=> Dùng để so sánh từng byte trong xmm0 với từng byte trong xmmword_140029010
nếu bằng nhau thì sẽ ra 0xFF
nếu không bằng nhau thì sẽ ra 0x00
pcmpeqb xmm1, cs:xmmword_140029000 tương tự như trên
pand xmm1, xmm0
=> dùng để and xmm1 và xmm0
nếu khi mà 1 trong 2 là 0x00 thì sẽ and ra 0x00
nếu cả 2 cùng là 0xFF thì sẽ ra 0xFF
pmovmskb eax, xmm1
cmp eax, 0FFFFh
jz short loc_1400010CA
pmovmskb eax, xmm1 - Packed Move Byte Mask dùng để nén dữ liệu từ 128 bit của thanh ghi xmm1 thành 16 bit lower của thanh ghi eax
nó sẽ trích 1 byte cao nhất của từng byte và gắn từ bit 0 đến bit 15 của thanh ghi eax 16 byte high còn lại eax sẽ là 0
=> ta thây eax sẽ chứa 0x0000FFFF - 0x0FFFF sẽ == 0FFFFh và và sẽ nhảy tởi short loc_1400010CA và tại đó chứa yes
=> đầy là đièu kiên để check cái mình nhập vào có đúng với flag mã hóa đã có không
------------------------------------Kêt luận ------------------------------------
ta có
xmm1 chứa phần đầu input mã hóa và được check bởi cs:xmmword_140029000
=> xmmword_140029000 chứa flag đầu được mã hóa
xmm0 chứa phần sau input mã hóa và được check bởi cs:xmmword_140029010
=> xmmword_140029010 chừa flag sau được mã hóa
key
0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh
xmmword_140029000
0CBCCF5D9C3F5D9C3C2DEF5D3D8D8C5D9h
=> af_si_siht_yrros => sorry_this_is_fa
xmmword_140029010
8B8B8B8B8B8B8B8B8BCDCBC6CCF5CFC1h
=> !!!!!!!!!galf_ek => ke_flag!!!!!!!!!
flag : sorry_this_is_fake_flag!!!!!!!!!
=> đó không phải flag đúng
phần tích tĩnh thì ta thấy sub_140001210 cũng truy cập những hàm dùng xmmword_140029000 và xmmword_140029010 thì ta thấy

movaps xmm0, cs:xmmword_14001E010
movaps cs:xmmword_140029010, xmm0
movaps xmm0, cs:xmmword_14001E000
movaps cs:xmmword_140029000, xmm0
=> Thì ta thấy xmmword_140029010 được mov giá trị tại xmmword_14001E010 và xmmword_140029000 được mov giá trị tại xmmword_14001E000
xmmword_14001E000 xmmword 939FCF9C9B9998C99DC8C9989ECFCB9Ah
=> Nữa flag đầu
xmmword_14001E010 xmmword 9F9D9D9DCB989A9B999A98CF9DCFCFCFh
=> Nữa flag sau
flag : 0ae42cb7c2316e59eee7e203102a7775
Code xor:
import binascii
key = binascii.unhexlify("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
part1 = binascii.unhexlify("939FCF9C9B9998C99DC8C9989ECFCB9A")
part2 = binascii.unhexlify("9F9D9D9DCB989A9B999A98CF9DCFCFCF")
def decrypt(part , key):
de = bytearray()
for i in range(len(part)):
de.append(part[i] ^ key[i])
return de
depart1 = decrypt(part1, key)
depart2 = decrypt(part2, key)
print(f" part 1 : {depart1}")
print(f" part 2 : {depart2}")
print("---------------------------------------------------")
flag1 = depart1[::-1].decode()
flag2 = depart2[::-1].decode()
print(f" part 1 : {flag1}")
print(f" part 2 : {flag2}")
flag = flag1 + flag2
print("---------------------------------------------------")
print(f" flag : {flag}")