# PTITCTF (Revese Rust Challenges)
## Phân tích tổng quát:
- Đầu tiên mình sẽ xác định loại file này là gì.

- Nó chính là file `PE(Windown)` được compiler bằng ngôn ngữ Rust.
- Tiếp theo mình sẽ tiến hành phân tích tĩnh trên IDA.
```rust=
int __fastcall main(int argc, const char **argv, const char **envp)
{
return sub_140002480(sub_1400030E0, argc, argv, 0LL);
}
```
- Nhìn tổng quan thì hàm main() chả có gì ngoài việc return về thằng ```sub_140002480(sub_1400030E0, argc, argv, 0LL)```.
- Ta hiểu qua rằng nó sẽ run kèm theo đối số.
- VD: `./chall.exe <flag>`
- Mình sẽ xem thằng `sub_140002480()` này làm gì.
```rust=
// Hidden C++ exception states: #wind=1 #try_helpers=1
__int64 __fastcall sub_140002480(__int64 a1, __int64 a2, __int64 a3, char a4)
{
_QWORD v5[4]; // [rsp+38h] [rbp-30h] BYREF
char v6; // [rsp+5Fh] [rbp-9h]
v5[1] = a1;
v5[2] = a2;
v5[3] = a3;
v6 = a4;
v5[0] = a1;
return sub_140007C60(v5, &unk_1400207B0);
}
```
```rust=
// Hidden C++ exception states: #wind=1 #try_helpers=1
__int64 __fastcall sub_140007C60(__int64 a1, __int64 a2)
{
__int64 v4; // rax
__int64 v6; // rdx
__int64 v7; // rsi
__int64 v8; // rdx
__int64 v9; // rdx
ULONG StackSizeInBytes[2]; // [rsp+20h] [rbp-60h] BYREF
__int64 v11; // [rsp+50h] [rbp-30h] BYREF
__int64 v12; // [rsp+60h] [rbp-20h]
__int64 v13; // [rsp+68h] [rbp-18h]
__int64 v14; // [rsp+78h] [rbp-8h]
__int64 v15; // [rsp+80h] [rbp+0h] BYREF
__int64 v16; // [rsp+90h] [rbp+10h] BYREF
__int64 v17; // [rsp+98h] [rbp+18h] BYREF
v14 = -2LL;
AddVectoredExceptionHandler(0, Handler);
StackSizeInBytes[0] = 20480;
SetThreadStackGuarantee(StackSizeInBytes);
GetCurrentThread();
off_14002C028();
try
{
*(_QWORD *)StackSizeInBytes = 0LL;
v4 = sub_140008210(StackSizeInBytes);
}
catch ( __int64 v17 )
{
sub_14001E550(v17);
goto LABEL_9;
}
catch ( ... )
{
sub_14001E550(0LL);
goto LABEL_9;
}
try
{
sub_140008020(v4);
v7 = (*(int (__fastcall **)(__int64))(a2 + 40))(a1);
}
catch ( __int64 v16 )
{
v12 = sub_14001E550(v16);
v13 = v8;
goto LABEL_5;
}
catch ( ... )
{
v12 = sub_14001E550(0LL);
v13 = v9;
LABEL_5:
if ( *(_QWORD *)v13 )
(*(void (__fastcall **)(__int64))v13)(v12);
v6 = *(_QWORD *)(v13 + 8);
v7 = 101LL;
if ( v6 )
sub_140005FE0(v12, v6, *(_QWORD *)(v13 + 16));
}
try
{
if ( dword_14002C144 != 4 )
{
LOBYTE(v11) = 1;
*(_QWORD *)StackSizeInBytes = &v11;
sub_14001E890(StackSizeInBytes);
}
}
catch ( __int64 v15 )
{
sub_14001E550(v15);
goto LABEL_9;
}
catch ( ... )
{
sub_14001E550(0LL);
LABEL_9:
sub_140006730();
}
return v7;
}
```
- Càng vào sâu thì chả có manh mỗi gì nên mình sẽ quay lại thằng ```sub_1400030E0()```.
```rust=
// Hidden C++ exception states: #wind=2
__int64 sub_1400030E0()
{
__int64 v0; // rax
__int64 v1; // rdx
__int64 v2; // rdx
__int64 v3; // rax
__int64 v4; // rdx
__int64 v5; // rax
__int64 v6; // rdx
int *v7; // rdx
int v9; // [rsp+2Ch] [rbp-54h]
unsigned __int64 v10; // [rsp+98h] [rbp+18h]
__int64 v11; // [rsp+A8h] [rbp+28h]
_QWORD v12[2]; // [rsp+D0h] [rbp+50h] BYREF
_BYTE v13[48]; // [rsp+E0h] [rbp+60h] BYREF
_OWORD v14[2]; // [rsp+110h] [rbp+90h] BYREF
_BYTE v15[24]; // [rsp+138h] [rbp+B8h] BYREF
_BYTE v16[32]; // [rsp+150h] [rbp+D0h] BYREF
_BYTE v17[48]; // [rsp+170h] [rbp+F0h] BYREF
_OWORD v18[2]; // [rsp+1A0h] [rbp+120h] BYREF
_BYTE v19[48]; // [rsp+1C8h] [rbp+148h] BYREF
_BYTE v20[24]; // [rsp+1F8h] [rbp+178h] BYREF
_QWORD v21[2]; // [rsp+210h] [rbp+190h] BYREF
_BYTE *v22; // [rsp+220h] [rbp+1A0h]
char v23; // [rsp+22Fh] [rbp+1AFh] BYREF
_DWORD v24[34]; // [rsp+230h] [rbp+1B0h]
_QWORD v25[3]; // [rsp+2B8h] [rbp+238h] BYREF
_BYTE v26[24]; // [rsp+2D0h] [rbp+250h] BYREF
_QWORD v27[3]; // [rsp+2E8h] [rbp+268h] BYREF
unsigned __int64 v28; // [rsp+300h] [rbp+280h]
int *v29; // [rsp+308h] [rbp+288h]
_BYTE v30[48]; // [rsp+310h] [rbp+290h] BYREF
_BYTE v31[48]; // [rsp+340h] [rbp+2C0h] BYREF
__int64 v32; // [rsp+370h] [rbp+2F0h]
unsigned __int64 v33; // [rsp+378h] [rbp+2F8h]
int v34; // [rsp+384h] [rbp+304h]
_BYTE *v35; // [rsp+388h] [rbp+308h]
int v36; // [rsp+394h] [rbp+314h]
__int128 v37; // [rsp+398h] [rbp+318h]
__int64 v38; // [rsp+3A8h] [rbp+328h]
__int64 (__fastcall *v39)(); // [rsp+3B0h] [rbp+330h]
__int64 v40; // [rsp+3B8h] [rbp+338h]
__int128 v41; // [rsp+3C0h] [rbp+340h]
_QWORD *v42; // [rsp+3D0h] [rbp+350h]
__int64 (__fastcall *v43)(); // [rsp+3D8h] [rbp+358h]
_QWORD *v44; // [rsp+3E0h] [rbp+360h]
__int64 v45; // [rsp+3E8h] [rbp+368h]
v45 = -2LL;
v12[0] = asc_14002092E;
v12[1] = 3598LL;
v42 = v12;
v43 = sub_140003AA0;
v44 = v12;
*&v41 = v12;
*(&v41 + 1) = sub_140003AA0;
v14[1] = v41;
v14[0] = v41;
sub_140003AD0(v13, &unk_140021740, v14);
sub_14000A340(v13);
sub_140008970();
sub_140005F60(v15, v16);
if ( sub_140005670(v15) != 2 )
{
v38 = sub_140005800(v15, 0LL, &off_140021818);
v39 = sub_140004FE0;
v40 = v38;
*&v37 = v38;
*(&v37 + 1) = sub_140004FE0;
v18[1] = v37;
v18[0] = v37;
sub_140003AD0(v17, &off_1400217F8, v18);
sub_14000A450(v17);
sub_14000C820(1LL);
}
v11 = sub_140005800(v15, 1LL, &off_140021770);
v32 = v11;
v10 = sub_140004FA0();
if ( v10 < 4 )
sub_14001FA30(&off_140021788);
if ( v10 != 34 )
{
sub_140003B20(v19, &off_1400217D8);
sub_14000A450(v19);
sub_14000C820(1LL);
}
sub_140005440(v20);
v0 = sub_140004FC0(v11);
v21[0] = sub_140005D40(v0, v1);
v21[1] = v2;
while ( 1 )
{
v22 = sub_140005E60(v21);
if ( !v22 )
break;
v35 = v22;
v23 = *v22;
v36 = sub_140003710(&v23, 1LL);
sub_140005680(v20);
}
v24[0] = -1184252295;
v24[1] = -1107002784;
v24[2] = -587065671;
v24[3] = -1107002784;
v24[4] = 1037565863;
v24[5] = -1107002784;
v24[6] = 1304234792;
v24[7] = 366298937;
v24[8] = 1255198513;
v24[9] = 1842515611;
v24[10] = 30677878;
v24[11] = -2082672713;
v24[12] = 2013832146;
v24[13] = 701932520;
v24[14] = -186917087;
v24[15] = 1993550816;
v24[16] = 701932520;
v24[17] = -630205792;
v24[18] = -69523947;
v24[19] = 701932520;
v24[20] = 1304234792;
v24[21] = 1812594589;
v24[22] = -2082672713;
v24[23] = 1842515611;
v24[24] = 2013832146;
v24[25] = -1730327860;
v24[26] = 453955339;
v24[27] = 701932520;
v24[28] = 1466425173;
v24[29] = -227710402;
v24[30] = -2068763730;
v24[31] = 1790921346;
v24[32] = -1637089325;
v24[33] = -55123444;
v3 = sub_1400057A0(v20);
v5 = sub_140005910(v3, v4);
sub_140005CB0(v26, v5, v6);
sub_140001FC0(v25, v26);
v27[0] = v25[0];
v27[1] = v25[1];
v27[2] = v25[2];
while ( 1 )
{
v28 = sub_140001ED0(v27);
v29 = v7;
if ( !v7 )
break;
v33 = v28;
v9 = *v29;
v34 = *v29;
if ( v28 >= 0x22 )
sub_14001F590(v28, 34LL, &off_1400217B8);
if ( v9 != v24[v28] )
{
sub_140003B20(v30, &off_1400217D8);
sub_14000A340(v30);
sub_14000C820(1LL);
}
}
sub_140003B20(v31, &off_1400217A8);
sub_14000A340(v31);
sub_140002A10(v20);
return sub_140002C90(v15);
}
```
- Đây rồi có vẻ đây là hàm xử lý chính vì mình có thấy chuỗi data target sau khi encode.
- Nhìn sơ qua thì flag nhập vào nó sẽ xử lý bằng cách nào đó rồi return về thằng `v9` rồi nó sẽ đi so sánh với thằng data target.
- Nhưng `v9` nó có liên quan đến `v7` nhưng mình không thấy nó `v7` được tham chiếu từ đâu.
- Mình nghĩ rằng nó sẽ có một hàm xử lý riêng trước đó.
- Thì ra nó ở đây.
```rust=
if ( v10 != 34 )
{
sub_140003B20(v19, &off_1400217D8);
sub_14000A450(v19);
sub_14000C820(1LL);
}
sub_140005440(v20);
v0 = sub_140004FC0(v11);
v21[0] = sub_140005D40(v0, v1);
v21[1] = v2;
while ( 1 )
{
v22 = sub_140005E60(v21);
if ( !v22 )
break;
v35 = v22;
v23 = *v22;
v36 = sub_140003710(&v23, 1LL);
sub_140005680(v20);
}
```
- Ở đây thằng `v10` là len của flag sẽ là `34`.
- Tiếp tục thằng `while` sẽ xử lý mã hóa flag, tại thằng ```v36 = sub_140003710(&v23, 1LL);```.
- Mình sẽ xem thử nó làm gì.
```rust=
__int64 __fastcall sub_140003710(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
unsigned __int64 v3; // rdx
__int64 v4; // rdx
int v5; // edx
int v7; // edx
unsigned __int64 v8; // [rsp+38h] [rbp-490h]
_DWORD v11[256]; // [rsp+50h] [rbp-478h] BYREF
_QWORD v12[2]; // [rsp+450h] [rbp-78h] BYREF
__int64 v13; // [rsp+460h] [rbp-68h]
unsigned __int64 v14; // [rsp+468h] [rbp-60h]
unsigned int v15; // [rsp+470h] [rbp-58h]
_DWORD v16[2]; // [rsp+474h] [rbp-54h] BYREF
int v17; // [rsp+47Ch] [rbp-4Ch]
int v18; // [rsp+480h] [rbp-48h]
unsigned int v19; // [rsp+484h] [rbp-44h]
_QWORD v20[2]; // [rsp+488h] [rbp-40h] BYREF
char *v21; // [rsp+498h] [rbp-30h]
__int64 v22; // [rsp+4A0h] [rbp-28h]
__int64 v23; // [rsp+4A8h] [rbp-20h]
char v24; // [rsp+4B7h] [rbp-11h]
__int64 v25; // [rsp+4B8h] [rbp-10h]
unsigned __int64 v26; // [rsp+4C0h] [rbp-8h]
v22 = a1;
v23 = a2;
memset(v11, 0, sizeof(v11));
v12[0] = sub_140002550(0LL, 256LL);
v12[1] = v2;
while ( 1 )
{
v13 = sub_140002500(v12);
v14 = v3;
if ( !v13 )
break;
v8 = v14;
v26 = v14;
v15 = v14;
v16[0] = sub_140002540(0LL, 8LL);
v16[1] = v5;
while ( 1 )
{
v17 = sub_140002520(v16);
v18 = v7;
if ( !v17 )
break;
if ( (v15 & 1) != 0 )
v15 = (v15 >> 1) ^ 0xEDB88320;
else
v15 >>= 1;
}
if ( v8 >= 0x100 )
sub_14001F590(v8, 256LL, &off_140021848);
v11[v8] = v15;
}
v19 = -1;
v20[0] = sub_140005D40(a1, a2);
v20[1] = v4;
while ( 1 )
{
v21 = sub_140005E60(v20);
if ( !v21 )
break;
v24 = *v21;
v25 = (v24 ^ v19);
v19 = v11[v25] ^ (v19 >> 8);
}
return ~v19;
}
```
- Thuật toán cũng khá lạ nên mình sẽ tìm hiểu.
- Đây là đường link github của một người lạ nói về thuật toán này
https://gist.github.com/timepp/1f678e200d9e0f2a043a9ec6b3690635
- Nó là thuật toán `CRC 32` hiểu đơn giản là nó sẽ tạo một bảng chữ cái tưng ứng với từng 8bit hex cho mỗi ký tự theo cách của nó.
- Vậy thằng này khi chuyển về hex sẽ tương ứng với từng ký tự flag theo thứ tự
```rust=
v24[0] = -1184252295;
v24[1] = -1107002784;
v24[2] = -587065671;
v24[3] = -1107002784;
v24[4] = 1037565863;
v24[5] = -1107002784;
v24[6] = 1304234792;
v24[7] = 366298937;
v24[8] = 1255198513;
v24[9] = 1842515611;
v24[10] = 30677878;
v24[11] = -2082672713;
v24[12] = 2013832146;
v24[13] = 701932520;
v24[14] = -186917087;
v24[15] = 1993550816;
v24[16] = 701932520;
v24[17] = -630205792;
v24[18] = -69523947;
v24[19] = 701932520;
v24[20] = 1304234792;
v24[21] = 1812594589;
v24[22] = -2082672713;
v24[23] = 1842515611;
v24[24] = 2013832146;
v24[25] = -1730327860;
v24[26] = 453955339;
v24[27] = 701932520;
v24[28] = 1466425173;
v24[29] = -227710402;
v24[30] = -2068763730;
v24[31] = 1790921346;
v24[32] = -1637089325;
v24[33] = -55123444;
```
## Solution
- Và cách làm của mình sẽ viết script python tạo ra một bảng chữ các bằng 8bits hex tương ứng sau đó mapping thằng target này vào và lấy flag.
> Script solve
```python=
def crc32(data: bytes) -> int:
table = []
for i in range(256):
crc = i
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ 0xEDB88320
else:
crc >>= 1
table.append(crc)
crc = 0xFFFFFFFF
for byte in data:
crc = table[(byte ^ crc) & 0xFF] ^ (crc >> 8)
return ~crc & 0xFFFFFFFF
target = [
0xB969BE79, 0xBE047A60, 0xDD0216B9, 0xBE047A60, 0x3DD7FFA7, 0xBE047A60,
0x4DBD0B28, 0x15D54739, 0x4AD0CF31, 0x6DD28E9B, 0x01D41B76, 0x83DCEFB7,
0x7808A3D2, 0x29D6A3E8, 0xF4DBDF21, 0x76D32BE0, 0x29D6A3E8, 0xDA6FD2A0,
0xFBDB2615, 0x29D6A3E8, 0x4DBD0B28, 0x6C09FF9D, 0x83DCEFB7, 0x6DD28E9B,
0x7808A3D2, 0x98DD4ACC, 0x1B0ECF0B, 0x29D6A3E8, 0x5767DF55, 0xF26D6A3E,
0x84B12BAE, 0x6ABF4A82, 0x9E6BFFD3, 0xFCB6E20C
]
charset = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_"
result = b""
for t in target:
found = False
for c in charset:
if crc32(bytes([c])) == t:
result += bytes([c])
found = True
break
if not found:
result += b"?"
print("Flag:", result.decode(errors="replace"))
```
> Flag: PTITCTF{B3g1n_0f_My_Fr13nds_Ru57?}