int __fastcall main(int argc, const char **argv, c...
Créé le : 27 décembre 2025
Créé le : 27 décembre 2025
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rcx
unsigned __int64 v5; // rdx
unsigned int v6; // ebp
int v7; // eax
bool v8; // zf
__int64 (__fastcall *v9)(); // rax
__int64 (__fastcall *v10)(); // rdx
char s[264]; // [rsp+50h] [rbp-128h] BYREF
unsigned __int64 v13; // [rsp+158h] [rbp-20h]
v13 = __readfsqword(0x28u);
if ( fgets(s, 256, stdin) )
{
v3 = 0LL;
v4 = -9218LL;
while ( 1 )
{
v5 = (unsigned __int8)s[v3];
v6 = v3;
if ( (unsigned __int8)v5 <= 0xDu && !_bittest64(&v4, v5) )
break;
if ( ++v3 == 256 )
{
v6 = 256;
break;
}
}
v7 = derive_vm_seed();
v8 = (((unsigned __int8)(((v6 ^ v7 ^ 0xCAFEBABE) >> 11) ^ ((v6 ^ v7 ^ 0xCAFEBABE) >> 5)) ^ (unsigned __int8)(v6 ^ v7) ^ 0xBE) & 1) == 0;
v9 = check_len_alt_impl;
if ( v8 )
v9 = check_len_impl;
v10 = run_and_verify_alt_impl;
if ( v8 )
v10 = run_and_verify_impl;
g_check_ops = v9;
off_5020 = v10;
if ( ((unsigned int (__fastcall *)(_QWORD, __int64, __int64 (__fastcall *)(), __int64 (__fastcall *)()))v9)(
v6,
256LL,
v10,
run_and_verify_impl)
&& (off_5018(), (unsigned int)off_5020()) )
{
puts("yes");
}
else
{
puts("no");
}
}
return 0;
}
__int64 derive_vm_seed()
{
char *v0; // rdx
char v1; // al
_BYTE *v2; // rcx
int v3; // eax
unsigned int v4; // r8d
int v5; // eax
char *v6; // r11
int v7; // edi
_BYTE *v8; // rsi
char *v9; // rax
_BYTE *v10; // rdx
char v11; // cl
int v12; // r9d
char v13; // bl
char v14; // al
int v15; // ecx
char *v16; // r10
char v17; // dl
int v18; // eax
unsigned int v20; // [rsp+8h] [rbp-130h] BYREF
int v21; // [rsp+Ch] [rbp-12Ch] BYREF
_BYTE v22[96]; // [rsp+10h] [rbp-128h] BYREF
_BYTE v23[96]; // [rsp+70h] [rbp-C8h] BYREF
_BYTE v24[32]; // [rsp+D0h] [rbp-68h] BYREF
_BYTE v25[40]; // [rsp+F0h] [rbp-48h] BYREF
unsigned __int64 v26; // [rsp+118h] [rbp-20h]
v0 = (char *)&unk_313D;
v26 = __readfsqword(0x28u);
v20 = 0;
v1 = -38;
v21 = 0;
v2 = v24;
while ( 1 )
{
*v2++ = v1 ^ 0x5A;
if ( v0 == (char *)&unk_313D - 30 )
break;
v1 = *v0--;
}
v3 = build_program_from_meta_constprop_0(v24, v25, &v20);
v4 = -1059143458;
if ( v3 )
{
v5 = translate_to_mid_ir_constprop_0(v25, v20, v22, &v21, 3235823838LL);
v4 = -559038801;
if ( v5 )
{
v7 = v21;
if ( v21 <= 32 )
{
if ( v21 <= 0 )
{
return 826366246;
}
else
{
v8 = v23;
v9 = v6;
v10 = v23;
do
{
v11 = *v9;
v9 += 3;
v10 += 3;
*(v10 - 3) = v11;
*(v10 - 2) = *(v9 - 2);
*(v10 - 1) = *(v9 - 1);
}
while ( v9 != &v22[3 * (v7 - 1) + 3] );
v12 = 0;
v13 = 122;
v14 = -13;
v15 = 0;
v16 = (char *)&unk_3161;
v4 = 826366246;
while ( 1 )
{
v17 = v15 ^ *v8;
if ( v8[2] )
v17 ^= v8[1];
++v15;
v8 += 3;
v18 = 257 * (unsigned __int8)(v17 ^ v13 ^ v14) + v12;
v12 += 13;
v4 = ROL4(v4 ^ v18, 7);
if ( v7 == v15 )
break;
v14 = *v16++;
v13 = seg_rk_0[v15 & 0xF];
}
}
}
}
}
return v4;
}__int64 __fastcall build_program_from_meta_constprop_0(__int64 a1, __int64 a2, _DWORD *a3)
{
int v5; // esi
__int64 i; // rax
int v7; // r11d
unsigned __int8 v8; // cl
int v9; // r8d
__int64 v10; // rdx
int v12; // edx
int v13; // ecx
v5 = 0;
LODWORD(i) = 0;
LABEL_2:
for ( i = (int)i; ; ++i )
{
v7 = i;
if ( (int)i > 30 )
goto LABEL_13;
v8 = *(_BYTE *)(a1 + i);
v9 = i + 1;
if ( v5 == 32 )
return 0LL;
if ( v8 == 0x82 )
{
v12 = i + 2;
if ( (int)i + 2 > 30 || v5 == 31 )
return 0LL;
i = v5;
v13 = v5 + 1;
v5 += 2;
*(_BYTE *)(a2 + i) = *(_BYTE *)(a1 + v9);
LODWORD(i) = v7 + 3;
*(_BYTE *)(a2 + v13) = *(_BYTE *)(a1 + v12);
goto LABEL_2;
}
if ( v8 > 0x82u )
break;
if ( v8 != 0x80 )
{
if ( v8 == 0x81 && (_DWORD)i != 30 )
{
v10 = v5;
LODWORD(i) = i + 2;
++v5;
*(_BYTE *)(a2 + v10) = *(_BYTE *)(a1 + v9);
goto LABEL_2;
}
return 0LL;
}
}
if ( v8 != 0x8F )
return 0LL;
LABEL_13:
*a3 = v5;
return 1LL;
}__int64 __fastcall translate_to_mid_ir_constprop_0(char *a1, int a2, _BYTE *a3, _DWORD *a4)
{
char v6; // si
int v7; // r8d
int v8; // edi
int v9; // eax
char v10; // si
if ( a2 > 0 )
{
v6 = *a1;
v7 = 0;
v8 = 1;
v9 = 0;
while ( 1 )
{
*a3 = v6;
a3[1] = 0;
a3[2] = 0;
if ( v6 == 32 || v6 == 112 )
{
if ( a2 <= v8 )
return 0LL;
v9 += 2;
++v7;
v10 = a1[v8];
a3[2] = 1;
a3[1] = v10;
if ( a2 <= v9 )
goto LABEL_9;
}
else
{
v9 = v8;
++v7;
if ( a2 <= v8 )
goto LABEL_9;
}
v8 = v9 + 1;
a3 += 3;
v6 = a1[v9];
if ( v7 == 32 )
return 0LL;
}
}
v7 = 0;_BOOL8 __fastcall check_len_alt_impl(int a1)
{
return a1 == 79;
}
LABEL_9:
*a4 = v7;
return 1LL;
}
_BOOL8 __fastcall check_len_impl(int a1)
{
return a1 == 79;
}
__int64 __fastcall run_and_verify_impl(__int64 a1, __int64 a2, __int64 a3)
{
__int64 result; // rax
int v4; // [rsp+4h] [rbp-14h] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-10h]
v5 = __readfsqword(0x28u);
v4 = 0;
result = run_vm_constprop_0(a1, a2, a3, &v4);
if ( (_DWORD)result )
return v4 == 1597676402;
return result;
}__int64 __fastcall run_vm_constprop_0(__int64 a1, unsigned int a2, __int64 a3, __int64 a4)
{
__int64 v6; // r12
if ( (a2 & 1) != 0 )
{
v6 = run_vm_noise_constprop_0(a1, a2 ^ 0x55) & 1;
if ( (a2 & 2) == 0 )
goto LABEL_3;
}
else
{
v6 = run_vm_noise_constprop_0(a1, a2) & 1;
if ( (a2 & 2) == 0 )
goto LABEL_3;
}
LODWORD(v6) = run_vm_noise_constprop_0(a1, a2) & v6;
LABEL_3:
if ( !(unsigned int)run_vm_inner(a1, a2, a3, a4) )
LODWORD(v6) = 0;
return (unsigned int)v6;
}
__int64 __fastcall run_vm_noise_constprop_0(__int64 a1, unsigned int a2, __int64 a3)
{
char v3; // r14
char v7; // dl
__int64 v8; // rax
__int64 v9; // rax
char i; // dl
char *v11; // rdx
unsigned __int8 *v12; // rax
__int64 v13; // rcx
char v14; // al
int v15; // esi
char v16; // cl
char v17; // cl
char v18; // al
char v19; // cl
char v20; // al
char v21; // cl
char v22; // al
char v23; // di
char v24; // cl
char v25; // al
char v26; // cl
unsigned int v27; // eax
__int64 v28; // r8
unsigned int v29; // r9d
char v30; // al
__int64 v31; // r8
__int64 v32; // r8
char v33; // al
__int64 v34; // r8
int v35; // r15d
__int64 v36; // rax
char j; // cl
unsigned int v38; // eax
char *v39; // rdx
_BYTE *v40; // rcx
char k; // al
char *v43; // r11
unsigned int v44; // r10d
char *v45; // rax
_BYTE *v46; // rsi
_BYTE *v47; // rdx
char v48; // cl
__int64 v49; // rdx
unsigned int v50; // [rsp+Ch] [rbp-18Ch]
int v51; // [rsp+10h] [rbp-188h] BYREF
int v52; // [rsp+14h] [rbp-184h] BYREF
int v53; // [rsp+18h] [rbp-180h] BYREF
int v54; // [rsp+1Ch] [rbp-17Ch] BYREF
_BYTE v55[96]; // [rsp+20h] [rbp-178h] BYREF
_BYTE v56[96]; // [rsp+80h] [rbp-118h] BYREF
unsigned __int8 v57; // [rsp+E0h] [rbp-B8h] BYREF
char v58; // [rsp+E1h] [rbp-B7h]
char v59; // [rsp+E2h] [rbp-B6h]
char v60; // [rsp+E3h] [rbp-B5h]
char v61; // [rsp+E5h] [rbp-B3h]
char v62; // [rsp+E7h] [rbp-B1h]
char v63; // [rsp+E9h] [rbp-AFh]
char v64; // [rsp+EAh] [rbp-AEh]
char v65; // [rsp+EBh] [rbp-ADh]
char v66; // [rsp+EDh] [rbp-ABh]
char v67; // [rsp+EFh] [rbp-A9h]
_BYTE v68[32]; // [rsp+F0h] [rbp-A8h] BYREF
_BYTE v69[32]; // [rsp+110h] [rbp-88h] BYREF
char v70[40]; // [rsp+130h] [rbp-68h] BYREF
unsigned __int64 v71; // [rsp+158h] [rbp-40h]
v3 = a2;
v7 = -13;
v71 = __readfsqword(0x28u);
v8 = 0LL;
v51 = 0;
v52 = 0;
while ( 1 )
{
v69[v8++] = a2 ^ v7;
if ( v8 == 32 )
break;
v7 = *((_BYTE *)&rc4_blob + v8);
}
v9 = 0LL;
for ( i = 122; ; i = seg_rk_0[v9] )
{
*(&v57 + v9++) = a2 ^ i;
if ( v9 == 16 )
break;
}
rc4_like_key_constprop_0(v69, 32LL, &rc4_key, seg_rk_0);
v11 = (char *)&v57;
v12 = &v57;
do
{
v13 = *v12++;
*(v12 - 1) = sbox_1[v13];
}
while ( v12 != v68 );
v14 = v58;
v15 = 0;
v58 = v61;
v61 = v63;
v16 = v66;
v66 = v14;
v63 = v16;
v17 = v64;
v64 = v59;
v18 = v60;
v59 = v17;
v60 = v67;
v67 = v65;
v19 = v62;
v62 = v18;
v65 = v19;
do
{
while ( 1 )
{
v21 = *v11;
v22 = 2 * *v11;
if ( *v11 < 0 )
v22 = (2 * v21) ^ 0x1B;
v23 = v15 & 3;
if ( (v15 & 3) != 0 )
break;
v20 = v22 ^ 0x2F;
LABEL_11:
++v15;
*v11++ = v20;
if ( v15 == 16 )
goto LABEL_19;
}
v24 = v22 ^ v21;
if ( v23 == 1 )
{
v20 = v24 ^ 0x5C;
goto LABEL_11;
}
v25 = v22 ^ 0x91;
v26 = v24 ^ 0xE7;
if ( v23 != 2 )
v25 = v26;
++v15;
v11++ = v25;
}
while ( v15 != 16 );
LABEL_19:
v27 = derive_vm_seed();
LODWORD(v28) = 0;
v29 = v27;
do
{
v30 = rt_mask2_byte((unsigned int)v28, v29);
v69[v31] ^= v30;
v28 = v31 + 1;
}
while ( v28 != 32 );
LODWORD(v32) = 0;
do
{
v33 = rt_mask2_byte((unsigned int)v32, v29);
v69[v34] ^= v33;
v32 = v34 + 1;
}
while ( v32 != 32 );
run_vm_inner(a1, a2, a3, &v51);
v35 = v51;
if ( (v51 & 1) != 0 )
run_vm_inner(a1, a2, a3, &v52);
if ( a2 == 25 )
{
v36 = 0LL;
for ( j = -52; j == ((_BYTE *)(a1 + v36) ^ 0xAA); j = encoded_fake[v36] )
{
if ( ++v36 == 25 )
{
v51 = v35 ^ 0x12345678;
break;
}
}
}
if ( ((v57 ^ v69[0]) & 1) != 0 )
v51 ^= 0x87654321;
v53 = 0;
v54 = 0;
v38 = derive_vm_seed();
v39 = (char *)&unk_313D;
v40 = v68;
v50 = v38;
for ( k = -38; ; k = *v39-- )
{
*v40++ = k ^ 0x5A;
if ( v39 == (char *)&unk_313D - 30 )
break;
}
if ( (unsigned int)build_program_from_meta_constprop_0((__int64)v68, (__int64)v70, &v53) )
{
if ( (unsigned int)translate_to_mid_ir_constprop_0(v70, v53, v55, &v54) )
{
v44 = v54;
if ( v54 <= 32 )
{
if ( v54 > 0 )
{
v45 = v43;
v46 = v56;
v47 = v56;
do
{
v48 = *v45;
v45 += 3;
v47 += 3;
*(v47 - 3) = v48;
*(v47 - 2) = *(v45 - 2);
*(v47 - 1) = *(v45 - 1);
}
while ( &v55[3 * v44] != v45 );
v49 = 0LL;
do
{
if ( *v46 == 32 )
{
if ( v46[2] )
v46[1] ^= (v50 >> (8 * (v49 & 3))) ^ *((_BYTE *)&rc4_blob + v49);
}
else if ( *v46 == 112 && v46[2] )
{
v46[1] = (v3 ^ seg_rk_0[v49 & 0xF] ^ v46[1]) & 0x1F;
}
++v49;
v46 += 3;
}
while ( v44 != v49 );
}
run_ir(v56, v44, a1, a2, a3, &v52);
}
}
}
return 1LL;
}
__int64 __fastcall rt_mask2_byte(int a1, unsigned int a2)
{
int v2; // eax
v2 = 7 * a1 + ((a2 >> 9) & 0x1F);
return (a2 >> ((3 * a1) & 7)) ^ (unsigned __int8)(*((_BYTE *)&rc4_blob + v2 % 32)
+ seg_rk_0[(int)(11 * a1 + ((a2 >> 3) & 0xF)) % 16]);
}
unsigned __int64 __fastcall rc4_like_key_constprop_0(__int64 a1, int a2, __int64 a3)
{
__int64 v5; // r10
__int64 v6; // rax
char *v7; // rdx
unsigned __int8 v8; // r8
int v9; // esi
char v10; // al
char v11; // di
__int64 v12; // rcx
int v13; // r8d
int v14; // edi
char v15; // al
_BYTE v17[264]; // [rsp+0h] [rbp-118h] BYREF
unsigned __int64 v18; // [rsp+108h] [rbp-10h]
v5 = a2;
v18 = __readfsqword(0x28u);
v6 = 0LL;
v7 = v17;
do
{
v17[v6] = v6;
++v6;
}
while ( v6 != 256 );
v8 = 0;
v9 = 0;
do
{
v10 = v9;
v11 = *v7;
++v9;
++v7;
v8 += v11 + *(_BYTE *)(a3 + (v10 & 7));
*(v7 - 1) = v17[v8];
v17[v8] = v8 ^ v11;
}
while ( v9 != 256 );
v12 = 0LL;
LOBYTE(v13) = 0;
LOBYTE(v14) = 0;
do
{
v14 = (unsigned __int8)(v14 + 3);
v15 = v17[v14];
v13 = (unsigned __int8)(v13 + v15 + 1);
v17[v14] = v17[v13];
v17[v13] = v15;
*(_BYTE *)(a1 + v12) ^= v12 ^ v17[(unsigned __int8)(v17[v14] + v15)];
++v12;
}
while ( v5 != v12 );
return v18 - __readfsqword(0x28u);
}
__int64 __fastcall run_vm_inner(__int64 a1, unsigned int a2, __int64 a3, __int64 a4)
{
char *v7; // rdx
char v8; // al
_BYTE *v9; // rcx
char *v11; // r11
__int64 v12; // rsi
char *v13; // rax
_BYTE *v14; // rdx
char v15; // cl
int v16; // [rsp+8h] [rbp-140h] BYREF
int v17; // [rsp+Ch] [rbp-13Ch] BYREF
_BYTE v18[96]; // [rsp+10h] [rbp-138h] BYREF
_BYTE v19[96]; // [rsp+70h] [rbp-D8h] BYREF
_BYTE v20[32]; // [rsp+D0h] [rbp-78h] BYREF
char v21[40]; // [rsp+F0h] [rbp-58h] BYREF
unsigned __int64 v22; // [rsp+118h] [rbp-30h]
v7 = (char *)&unk_313D;
v22 = __readfsqword(0x28u);
v16 = 0;
v8 = -38;
v17 = 0;
v9 = v20;
while ( 1 )
{
*v9++ = v8 ^ 0x5A;
if ( v7 == (char *)&unk_313D - 30 )
break;
v8 = *v7--;
}
if ( !(unsigned int)build_program_from_meta_constprop_0((__int64)v20, (__int64)v21, &v17) )
return 0LL;
if ( !(unsigned int)translate_to_mid_ir_constprop_0(v21, v17, v18, &v16) )
return 0LL;
v12 = (unsigned int)v16;
if ( v16 > 32 )
return 0LL;
if ( v16 > 0 )
{
v13 = v11;
v14 = v19;
do
{
v15 = *v13;
v13 += 3;
v14 += 3;
*(v14 - 3) = v15;
*(v14 - 2) = *(v13 - 2);
*(v14 - 1) = *(v13 - 1);
}
while ( v13 != &v18[3 * (unsigned int)(v12 - 1) + 3] );
}
return run_ir(v19, v12, a1, a2, a3, a4);
}
You’ve basically got a “VM-based” crackme stub here. From what you posted, we can already pin down a few hard facts about what input can ever print "yes", and what you’d need next to actually recover the flag.
main() is doing (high level)fgets(s, 256, stdin).v6 = the length up to the first “terminator”:
'\0' or on a byte <= 0x0D (CR/LF/tab/etc) depending on that bit-test mask logic.fgets, it’ll almost always stop at the newline ('\n' = 0x0A) unless your input contains earlier control chars.derive_vm_seed() → returns some 32-bit-ish value (unsigned int usage).v6 and that seed to choose between “impl” and “alt_impl”… but both length checks are identical in your paste.check_len*(v6, 256, v10, run_and_verify_impl) (weird signature because of function pointer casting)off_5018() (some init/setup)off_5020() which points to run_and_verify_*()yes, else no.Both implementations:
c_BOOL8 __fastcall check_len_alt_impl(int a1) { return a1 == 79; } _BOOL8 __fastcall check_len_impl(int a1) { return a1 == 79; }
So your effective input length must be exactly 79 characters before the newline/terminator that the v6 scan detects.
To even have a chance at "yes":
\n.run_and_verify_impl() does:
cv4 = 0; result = run_vm_constprop_0(a1, a2, a3, &v4); if (result) return v4 == 1597676402; return result;
So for "yes" you need:
run_vm_constprop_0(...) returns nonzero, andv4 to 1597676402 (decimal) = 0x5F3E2B32 (hex).That’s the true target.
derive_vm_seed() is doing (and why it matters)derive_vm_seed():
unk_313D / unk_3161.seg_rk_0, etc).-559038801 which is 0xDEADBEEF).So the seed likely depends only on embedded constants, not your input. But the seed is used later in noise/keying routines (rt_mask2_byte, etc), and those influence the VM behavior.
From your paste, the core logic that maps input → v4 is in functions/data you did not include:
run_ir(...) (this is the main VM interpreter)encoded_fake, seg_rk_0, sbox_1, rc4_blob, rc4_keyoff_5018() (init) and run_and_verify_alt_impl (you didn’t paste alt verify)unk_313D, unk_3161, and rc4_blob regionWithout those, nobody can derive the exact 79-byte string, because the VM’s semantics and constants determine the constraint system.
run_ir instruction set (opcode meanings).v4 == 0x5F3E2B32.Patch run_and_verify_impl to always return 1, or patch the compare against 1597676402.
That won’t recover the flag, but it will print “yes”.
Run under gdb/rr/frida:
run_vm_inner / run_irIf you paste either of these, I can take it much further right away:
run_ir() decompilation, orseg_rk_0, sbox_1, rc4_blob, encoded_fake, and the bytes around unk_313D / unk_3161)With run_ir + constants, we can usually:
v4 = 1597676402.Even without anything else, you can test you’re hitting the length gate:
"no".If you want, paste run_ir() (or the missing constants), and I’ll help you turn this into a working solver/emulator to recover the 79-byte valid input.
用中文回答:
_BOOL8 __fastcall run_ir(__int64 a1, int a2, __int64 a3, unsigned __int16 a4, __int64 a5, _DWORD *a6)
{
unsigned int v6; // r10d
unsigned __int8 v7; // r11
__int16 v8; // r13
char v9; // r12
int v12; // ecx
unsigned __int8 *v13; // rdx
unsigned __int8 v14; // al
char v15; // al
if ( a2 > 0 )
{
LOWORD(v6) = 0;
v7 = 0;
v8 = 0;
v9 = 0;
v12 = 0;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v13 = (unsigned __int8 *)(a1 + 3LL * v12);
v14 = *v13;
if ( *v13 != 64 )
break;
++v12;
*a6 ^= (unsigned __int16)v6 + 131 * v7;
if ( v12 >= a2 )
return 0LL;
}
if ( v14 > 0x40u )
break;
if ( v14 == 17 )
{
++v12;
v9 = *(_BYTE *)(a5 + (unsigned __int16)v6);
if ( v12 >= a2 )
return 0LL;
}
else if ( v14 <= 0x11u )
{
if ( v14 == 1 )
{
++v12;
*a6 = 0;
v8 = 79;
LOWORD(v6) = 0;
if ( v12 >= a2 )
return 0LL;
}
else
{
if ( v14 != 16 )
return 0LL;
if ( (unsigned __int16)v6 >= a4 )
return 0LL;
++v12;
v7 = *(_BYTE *)(a3 + (unsigned __int16)v6);
if ( v12 >= a2 )
return 0LL;
}
}
else if ( v14 == 32 )
{
++v12;
v7 ^= v13[1];
if ( v12 >= a2 )
return 0LL;
}
else
{
if ( v14 != 48 )
return 0LL;
v15 = 3 * v6 + 11;
++v12;
v7 += v15;
v9 += v15;
if ( v12 >= a2 )
return 0LL;
}
}
if ( v14 != 97 )
break;
++v12;
--v8;
if ( v12 >= a2 )
return 0LL;
}
if ( v14 <= 0x61u )
break;
if ( v14 != 112 )
return v14 == 0xFF;
if ( v8 )
{
v12 = (char)v13[1];
if ( v12 < 0 )
return 0LL;
if ( a2 <= v12 )
return 0LL;
}
else
{
LABEL_34:
if ( ++v12 >= a2 )
return 0LL;
}
}
if ( v14 == 80 )
{
if ( v9 != v7 )
return 0LL;
goto LABEL_34;
}
if ( v14 == 96 )
{
++v12;
v6 = ((unsigned int)(unsigned __int16)v6 + 7) % 0x4F;
if ( v12 < a2 )
continue;
}
return 0LL;
}
}
return 0LL;
}
sbox_1 db 63h, 7Ch, 77h, 7Bh, 0F2h, 6Bh, 6Fh, 0C5h, 30h, 1, 67h
.rodata:0000000000003020 ; DATA XREF: run_vm_noise_constprop_0+BC↑o
.rodata:000000000000302B db 2Bh, 0FEh, 0D7h, 0ABh, 76h, 0CAh, 82h, 0C9h, 7Dh, 0FAh
.rodata:0000000000003035 db 59h, 47h, 0F0h, 0ADh, 0D4h, 0A2h, 0AFh, 9Ch, 0A4h, 72h
.rodata:000000000000303F db 0C0h, 0B7h, 0FDh, 93h, 26h, 36h, 3Fh, 0F7h, 0CCh, 34h
.rodata:0000000000003049 db 0A5h, 0E5h, 0F1h, 71h, 0D8h, 31h, 15h, 4, 0C7h, 23h
.rodata:0000000000003053 db 0C3h, 18h, 96h, 5, 9Ah, 7, 12h, 80h, 0E2h, 0EBh, 27h
.rodata:000000000000305E db 0B2h, 75h, 9, 83h, 2Ch, 1Ah, 1Bh, 6Eh, 5Ah, 0A0h, 52h
.rodata:0000000000003069 db 3Bh, 0D6h, 0B3h, 29h, 0E3h, 2Fh, 84h, 53h, 0D1h, 0
.rodata:0000000000003073 db 0EDh, 20h, 0FCh, 0B1h, 5Bh, 6Ah, 0CBh, 0BEh, 39h, 4Ah
.rodata:000000000000307D db 4Ch, 58h, 0CFh, 0D0h, 0EFh, 0AAh, 0FBh, 43h, 4Dh, 33h
.rodata:0000000000003087 db 85h, 45h, 0F9h, 2, 7Fh, 50h, 3Ch, 9Fh, 0A8h, 51h, 0A3h
.rodata:0000000000003092 db 40h, 8Fh, 92h, 9Dh, 38h, 0F5h, 0BCh, 0B6h, 0DAh, 21h
.rodata:000000000000309C db 10h, 0FFh, 0F3h, 0D2h, 0CDh, 0Ch, 13h, 0ECh, 5Fh, 97h
.rodata:00000000000030A6 db 44h, 17h, 0C4h, 0A7h, 7Eh, 3Dh, 64h, 5Dh, 19h, 73h
.rodata:00000000000030B0 db 60h, 81h, 4Fh, 0DCh, 22h, 2Ah, 90h, 88h, 46h, 0EEh
.rodata:00000000000030BA db 0B8h, 14h, 0DEh, 5Eh, 0Bh, 0DBh, 0E0h, 32h, 3Ah, 0Ah
.rodata:00000000000030C4 db 49h, 6, 24h, 5Ch, 0C2h, 0D3h, 0ACh, 62h, 91h, 95h, 0E4h
.rodata:00000000000030CF db 79h, 0E7h, 0C8h, 37h, 6Dh, 8Dh, 0D5h, 4Eh, 0A9h, 6Ch
.rodata:00000000000030D9 db 56h, 0F4h, 0EAh, 65h, 7Ah, 0AEh, 8, 0BAh, 78h, 25h
.rodata:00000000000030E3 db 2Eh, 1Ch, 0A6h, 0B4h, 0C6h, 0E8h, 0DDh, 74h, 1Fh, 4Bh
.rodata:00000000000030ED db 0BDh, 8Bh, 8Ah, 70h, 3Eh, 0B5h, 66h, 48h, 3, 0F6h, 0Eh
.rodata:00000000000030F8 db 61h, 35h, 57h, 0B9h, 86h, 0C1h, 1Dh, 9Eh, 0E1h, 0F8h
.rodata:0000000000003102 db 98h, 11h, 69h, 0D9h, 8Eh, 94h, 9Bh, 1Eh, 87h, 0E9h
.rodata:000000000000310C db 0CEh, 55h, 28h, 0DFh, 8Ch, 0A1h, 89h, 0Dh, 0BFh, 0E6h
.rodata:0000000000003116 db 42h, 68h, 41h, 99h, 2Dh, 0Fh, 0B0h, 54h, 0BBh, 16h
.rodata:0000000000003120 enc_program db 5Ah ; Z ; DATA XREF: derive_flag_key_core+20↑o
.rodata:0000000000003121 db 0DBh
.rodata:0000000000003122 db 0DAh
.rodata:0000000000003123 db 0D5h
.rodata:0000000000003124 db 0A5h
.rodata:0000000000003125 db 0DBh
.rodata:0000000000003126 db 5Bh ; [
.rodata:0000000000003127 db 2Ah ; *
.rodata:0000000000003128 db 0D8h
.rodata:0000000000003129 db 3Bh ; ;
.rodata:000000000000312A db 0DBh
.rodata:000000000000312B db 3Ah ; :
.rodata:000000000000312C db 0DBh
.rodata:000000000000312D db 0Ah
.rodata:000000000000312E db 0DBh
.rodata:000000000000312F db 1Ah
.rodata:0000000000003130 db 0DBh
.rodata:0000000000003131 db 6Ah ; j
.rodata:0000000000003132 db 0DBh
.rodata:0000000000003133 db 0DAh
.rodata:0000000000003134 db 0F0h
.rodata:0000000000003135 db 7Ah ; z
.rodata:0000000000003136 db 0D8h
.rodata:0000000000003137 db 4Bh ; K
.rodata:0000000000003138 db 0DBh
.rodata:0000000000003139 db 4Ah ; J
.rodata:000000000000313A db 0DBh
.rodata:000000000000313B db 0DAh
.rodata:000000000000313C db 5Bh ; [
.rodata:000000000000313D unk_313D db 0DBh ; DATA XREF: derive_vm_seed+1↑o
.rodata:000000000000313D ; run_vm_inner+14↑o ...
.rodata:000000000000313E db 0DAh
.rodata:000000000000313F db 0
.rodata:0000000000003140 ; _BYTE seg_rk_0[32]
.rodata:0000000000003140 _seg_rk_0 db 7Ah, 9Ch, 23h, 51h, 0B4h, 6Fh, 0D8h, 2, 39h, 0E1h, 47h
.rodata:0000000000003140 ; DATA XREF: derive_flag_key_confuse_a+44↑o
.rodata:0000000000003140 ; rt_mask2_byte+4F↑o ...
.rodata:000000000000314B db 0ACh, 15h, 8Bh, 0D2h, 60h, 10h dup(0)
.rodata:0000000000003160 rc4_blob db 0F3h ; DATA XREF: derive_flag_key_confuse_a+3D↑o
.rodata:0000000000003160 ; rt_mask2_byte+5C↑o ...
.rodata:0000000000003161 unk_3161 db 8Ah ; DATA XREF: derive_vm_seed+110↑o
.rodata:0000000000003162 db 21h ; !
.rodata:0000000000003163 db 0C4h
.rodata:0000000000003164 db 5Bh ; [
.rodata:0000000000003165 db 19h
.rodata:0000000000003166 db 7Eh ; ~
.rodata:0000000000003167 db 0D2h
.rodata:0000000000003168 db 34h ; 4
.rodata:0000000000003169 db 8Fh
.rodata:000000000000316A db 0A1h
.rodata:000000000000316B db 6Ch ; l
.rodata:000000000000316C db 92h
.rodata:000000000000316D db 0Dh
.rodata:000000000000316E db 73h ; s
.rodata:000000000000316F db 0E8h
.rodata:0000000000003170 db 1Ah
.rodata:0000000000003171 db 0B5h
.rodata:0000000000003172 db 4Ch ; L
.rodata:0000000000003173 db 0F7h
.rodata:0000000000003174 db 2Eh ; .
.rodata:0000000000003175 db 89h
.rodata:0000000000003176 db 0D0h
.rodata:0000000000003177 db 3Bh ; ;
.rodata:0000000000003178 db 65h ; e
.rodata:0000000000003179 db 9Ch
.rodata:000000000000317A db 7
.rodata:000000000000317B db 0FAh
.rodata:000000000000317C db 41h ; A
.rodata:000000000000317D db 0AEh
.rodata:000000000000317E db 18h
.rodata:000000000000317F db 0D3h
.rodata:0000000000003180 rc4_key db 13h ; DATA XREF: derive_flag_key_core+29↑o
.rodata:0000000000003180 ; derive_flag_key_confuse_b+3E↑o ...
.rodata:0000000000003181 db 57h ; W
.rodata:0000000000003182 db 9Bh
.rodata:0000000000003183 db 0DFh
.rodata:0000000000003184 db 20h
.rodata:0000000000003185 db 46h ; F
.rodata:0000000000003186 db 8Ah
.rodata:0000000000003187 db 0CEh
.rodata:0000000000003188 db 0
.rodata:0000000000003189 db 0
.rodata:000000000000318A db 0
.rodata:000000000000318B db 0
.rodata:000000000000318C db 0
.rodata:000000000000318D db 0
.rodata:000000000000318E db 0
.rodata:000000000000318F db 0
.rodata:0000000000003190 ; _BYTE encoded_fake[32]
.rodata:0000000000003190 encoded_fake db 0CCh, 0C6h, 0CBh, 0CDh, 0D1h, 0CCh, 0CBh, 0C1h, 0CFh
.rodata:0000000000003190 ; DATA XREF: run_vm_noise_constprop_0+248↑o
.rodata:0000000000003199 db 87h, 0CCh, 0CBh, 0C1h, 0CFh, 87h, 0CCh, 0CBh, 0C1h
.rodata:00000000000031A2 db 0CFh, 87h, 0CCh, 0CBh, 0C1h, 0CFh, 0D7h, 7 dup(0)
.rodata:00000000000031B0 xmmword_31B0 xmmword 0F3CCE3F5B0BB8F75B04309921D76434h
.rodata:00000000000031B0 ; DATA XREF: prepare_flag_impl+1C↑r
.rodata:00000000000031C0 xmmword_31C0 xmmword 9D08CB8A2111C2D650D8EA9DEB4A2576h
.rodata:00000000000031C0 ; DATA XREF: prepare_flag_impl+48↑r
.rodata:00000000000031D0 xmmword_31D0 xmmword 0C0367C6B2F665AA2CB7DBF36BB7E004Bh
.rodata:00000000000031D0 ; DATA XREF: prepare_flag_impl+5D↑r
.rodata:00000000000031E0 xmmword_31E0 xmmword 21427E75617210C44A0C7FA0795F32F5h
下面用中文把你给的 run_ir() 这段 VM(虚拟机)解释清楚,并把“它到底在验证什么”“怎样把它还原成约束/求解”讲透。
run_ir 的整体结构:一个 3 字节指令的解释器a1 指向一段 IR 指令流,每条指令固定 3 字节:
op = *(a1 + 3*i + 0):操作码imm = *(a1 + 3*i + 1):立即数/参数(有时用到)flag = *(a1 + 3*i + 2):标志位(在其他地方会被用到,但 run_ir 里几乎没用到它)a2 是指令条数(不是字节数),因此第 v12 条指令地址就是 a1 + 3*v12。
VM 内部寄存器/状态变量(结合代码行为命名):
v6:16 位索引寄存器((unsigned __int16)v6 多次出现)。它用于索引输入串。v7:8 位工作寄存器 A(从输入读一个字节到这里、以及参与运算/比较)。v9:8 位工作寄存器 B(从 a5[...] 读一个字节到这里;可被某些 op 同步加同样的偏移)。v8:16 位倒计数器,初始会被设置成 79(这和你前面 check_len==79 完全呼应)。*a6:32 位累积器/输出(外层最终会拿它去和 1597676402 比较)。流程是一个 while(1),但实际上是“按指令逐条解释”,遇到非法就 return 0,遇到结束指令(0xFF)才算“成功退出”。
run_ir 能确定的部分)下面我按 op 值把行为列出来(这是最关键的还原点)。
op == 0x01:初始化 / 重置c*a6 = 0; v8 = 79; v6 = 0; ++v12;
含义:清零输出累积器,设置倒计数器=79,索引寄存器归零。
基本可以认为 VM 程序一开始会执行它,确保“必须验证 79 个字符”。
op == 0x10(16):从输入读取 1 字节到 v7cif ((uint16)v6 >= a4) return 0; // a4 是输入长度(unsigned short) v7 = *(a3 + (uint16)v6); // a3 指向输入 ++v12;
含义:v7 = input[v6]。
注意:它没有自动 v6++,所以“读哪个字符”取决于程序中 v6 如何变化(见 0x60)。
op == 0x11(17):从表/内存读取 1 字节到 v9cv9 = *(a5 + (uint16)v6); ++v12;
含义:v9 = mem[v6],其中 a5 是外层传入的一段内存/常量区(在你前面代码里通常是某个缓冲/密钥/派生表)。
同样,读的位置取决于 v6。
op == 0x20(32):v7 异或一个立即数cv7 ^= imm; ++v12;
含义:v7 = v7 XOR imm。这是典型的混淆/线性变换。
op == 0x30(48):对 v7 和 v9 同时加同一个偏移ct = 3*v6 + 11; // 注意:这里 v6 不是 uint16 截断,而是当前寄存器值 v7 += t; v9 += t; ++v12;
含义:v7, v9 同步加偏移 3*v6+11。
这条很像“对齐/加盐”,因为它把索引 v6 混进了比较之前的数据。
op == 0x50(80):断言 v7 == v9(不等直接失败)cif (v9 != v7) return 0; ++v12; // 然后继续
含义:这是核心校验指令:当前处理位置必须满足 v7 == v9。
所以整个程序本质上是反复构造 v7(来自 input + 运算),构造 v9(来自 mem + 运算),然后用 0x50 比较。
op == 0x40(64):把状态折叠进输出 a6(“哈希式累积”)c*a6 ^= (uint16)v6 + 131 * v7; ++v12;
含义:输出累积器 a6 会在程序执行过程中不断被扰动。
因为最后外层 run_and_verify_impl 要求 *a6 == 1597676402,所以 VM 不是只做“逐字相等”那么简单,它还在构造一个最终 32 位结果。
op == 0x60(96):更新索引 v6(非常关键)cv6 = ((uint16)v6 + 7) % 0x4F; // 0x4F = 79 ++v12;
含义:v6 按照 +7 mod 79 的方式跳转。
这非常关键:它说明 VM 并不是顺序处理 input[0..78],而是按一个置换序列访问 79 个位置。
因为 gcd(7,79)=1,所以这个序列会遍历所有 79 个位置且不重复。
也就是说:v6从 0 出发,会访问 全部 79 个索引,只是顺序被打乱。
op == 0x61(97):倒计数 v8--c--v8; ++v12;
含义:每遇到一次 0x61,倒计数器减一。一般会出现 79 次,配合分支实现“循环体执行 79 轮”。
op == 0x70(112):条件跳转(基于 v8 是否为 0)cif (v8) { v12 = (char)imm; if (v12 < 0 || v12 >= a2) return 0; } else { ++v12; }
含义:如果 v8 != 0(还没跑完 79 轮),就跳到 imm 指定的指令序号;否则就顺序执行下一条。
这就是典型的“循环直到 v8==0”。
op == 0xFF:程序正常结束在你的代码里:
cif (v14 != 112) return v14 == 0xFF;
也就是:当遇到一个 op > 0x61 且不是 0x70 时,只有 0xFF 才算成功结束(返回 1),否则失败。
结合上面的语义,你可以把每一轮(每个 v6 值)抽象成这样:
idx = v6(初始 0,之后 idx = (idx+7) mod 79)A = input[idx](0x10)B = mem[idx](0x11)0x20 XOR、0x30 加偏移等)(变换后的 A) == (变换后的 B)(0x50)(idx, A) 折叠进 a6(0x40)v8--(0x61),没到 0 就跳回循环(0x70)所以它相当于把你的 79 字符 input 强制满足一组方程,同时又让 a6 走到一个指定常数(外层要求 1597676402)。
你已经给出了 run_ir 和一堆常量,但还缺一个关键拼图:a5 到底是什么。
从你之前的调用链看:
run_vm_inner(..., a1=input, a2=len, a3=?, a4=&v51):这里 run_ir(v19, ..., a1, a2, a3, a4),所以 a3 是输入、a4 是输入长度。run_ir 的第 5 个参数 a5(你这里叫 __int64 a5)在上层是怎么来的?你之前贴的调用有点混淆(run_vm_inner(a1, a2, a3, a4) 里最后传 run_ir(v19, v12, a1, a2, a3, a4)),而 run_ir 原型里有 6 个参数(a6 是输出指针),说明你贴的符号/签名可能被 IDA 推错或你贴的是另一个版本。为什么 a5 至关重要?
因为 0x11 会取 v9 = *(a5 + idx),而 0x50 最终要求 v7==v9。没有 a5 的具体字节内容,就没法把输入方程解出来。
你后面 rodata 里有
xmmword_31B0...31E0,还有enc_program,很像是“准备 flag/密钥/对照表”的材料。很可能a5就是某个prepare_flag_impl生成的 79 字节表(或更长表),或者是 RC4/AES 混出来的一块。
你只要再贴一段:给我看到 off_5018() / prepare_flag_impl / 生成 a5 的函数,或者直接 dump 出 a5 指向内存的 79 字节,我就能把等式写出来并反推 input。
因为核心判定就是 *a6 == 1597676402,以及 VM 返回真。你可以:
run_and_verify_impl 里比较,或run_vm_constprop_0 直接返回 1 并把 v4 写成目标常数。你现在已经能把 IR 解释器写出来了,难点只有两点:
a1(每条 3 字节)在运行时到底是什么(你前面有 build/translate 从 unk_313D 解出来的那一段,已经能复现)a5 那块表/内存是什么等这两块齐了,就能做:
run_ir_py(ir, input_bytes, a5_bytes) 完整仿真;== 约束(来自 op 0x50)加入求解器;a6 == 1597676402 约束;这里最爽的一点是:你的 VM 里对 input 的操作基本都是 8 位 XOR、加法(自然 mod 256),Z3 很擅长。
run_ir 还能额外读出的“强约束信息”v6 = (v6+7) mod 79 暗示它确实要“覆盖所有字符”,这和外层长度 79 完整闭环。因此只要你把 IR 指令序列(最终翻译出的三元组)也贴出来/或 dump 出来,我甚至可以先不依赖 a5 的来源,直接告诉你:
你现在已经贴了:
run_irsbox_1 / seg_rk_0 / rc4_blob / rc4_key / encoded_fakeenc_program 和几个 xmmword最关键缺口只有一个:a5 的构造/内容。
请贴下面任意一种即可继续推进到“能算出 79 字符”:
off_5018() 的反编译(它很可能就是准备 a5 的)prepare_flag_impl / derive_flag_key_* 这些函数体(你 rodata 注释里已经出现它们的名字了)a5 指向的内存 dump 出来(79 字节或更多都行)你只要补上这块,我就可以把它整理成一个可跑的仿真/约束求解流程,并明确给出最终满足 yes 的 79 字符输入。
off_5018 dq offset prepare_flag_impl
unsigned __int64 __fastcall prepare_flag_impl(__int64 a1, unsigned int a2)
{
__m128i si128; // xmm0
__m128i v3; // xmm0
__m128i v4; // xmm0
__m128i v5; // xmm0
unsigned int v6; // r13d
signed int v7; // ebp
__int64 v8; // rcx
__int64 v9; // r8
__int64 v10; // r9
int v11; // eax
__int64 v12; // r8
__int64 v13; // r9
unsigned int v14; // edx
__int64 v15; // rcx
void (__fastcall *v16)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // rdx
__int64 v17; // rax
__int64 v18; // rax
char i; // cl
_BYTE *v20; // rax
_BYTE *v21; // rdx
_BYTE *v22; // rax
__int64 (__fastcall *v24)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // [rsp+0h] [rbp-58h]
__int64 (__fastcall *v25)(); // [rsp+8h] [rbp-50h]
__int64 (__fastcall *v26)(); // [rsp+10h] [rbp-48h]
__int64 (__fastcall *v27)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // [rsp+18h] [rbp-40h]
_BYTE v28[8]; // [rsp+20h] [rbp-38h] BYREF
unsigned __int64 v29; // [rsp+28h] [rbp-30h]
si128 = _mm_load_si128((const __m128i *)&xmmword_31B0);
v29 = __readfsqword(0x28u);
*(_DWORD *)(a1 + 72) = 1894304711;
*(__m128i *)a1 = si128;
v3 = _mm_load_si128((const __m128i *)&xmmword_31C0);
*(_QWORD *)(a1 + 64) = 0x7F9E9295CE4E4791LL;
*(__m128i *)(a1 + 16) = v3;
v4 = _mm_load_si128((const __m128i *)&xmmword_31D0);
*(_WORD *)(a1 + 76) = -15073;
*(__m128i *)(a1 + 32) = v4;
v5 = _mm_load_si128((const __m128i *)&xmmword_31E0);
*(_BYTE *)(a1 + 78) = 97;
*(__m128i *)(a1 + 48) = v5;
v6 = derive_vm_seed();
v7 = (((unsigned __int8)v6 ^ 0x4F) & 3u) % 3;
((void (__fastcall *)(__int64, __int64, _QWORD, __int64, __int64, __int64, __int64 (__fastcall *)(), __int64 (__fastcall *)(), __int64 (__fastcall )()))(&v24 + v7))(
a1,
79LL,
v6,
v8,
v9,
v10,
rt_unwrap_stage1,
rt_unwrap_stage2,
rt_unwrap_stage3);
((void (__fastcall )(__int64, __int64, _QWORD))(&v24 + (v7 + 1) % 3u))(a1, 79LL, v6);
((void (__fastcall )(__int64, __int64, _QWORD))(&v24 + (v7 + 2) % 3u))(a1, 79LL, v6);
v11 = derive_vm_seed();
v25 = derive_flag_key_confuse_a;
v26 = derive_flag_key_confuse_b;
v14 = a2 ^ v11 ^ 0xA5A5A5A5;
v15 = v14 >> 17;
LOBYTE(v11) = v14 ^ v15 ^ (v14 >> 7);
v16 = (void (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD))derive_flag_key_core;
v24 = derive_flag_key_core;
v17 = v11 & 3;
v27 = derive_flag_key_core;
if ( a2 != 79 )
v16 = (void (__fastcall )(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD))(&v24 + v17);
v16(v28, a2, v16, v15, v12, v13, v24, v25, v26, v27);
rc4_like_key_constprop_0(a1, 79, (__int64)v28);
v18 = 0LL;
for ( i = 122; ; i = seg_rk_0[v18] )
{
*(_BYTE )(a1 + v18 + 16) = i ^ ROL1((_BYTE *)(a1 + v18 + 16), 1);
if ( ++v18 == 16 )
break;
}
v20 = (_BYTE *)(a1 + 5);
v21 = (_BYTE *)a1;
do
*v21++ ^= 0x11u;
while ( v20 != v21 );
do
*v20++ ^= 0x22u;
while ( (_BYTE *)(a1 + 51) != v20 );
v22 = (_BYTE *)(a1 + 51);
do
*v22++ ^= 0x33u;
while ( (_BYTE *)(a1 + 79) != v22 );
return v29 - __readfsqword(0x28u);
}
__int64 __fastcall derive_flag_key_core(__int64 a1, int a2)
{
unsigned int v3; // ecx
int v4; // edi
__int64 v5; // rdx
int v6; // r10d
int v7; // r9d
int v8; // ebx
unsigned int v9; // r10d
unsigned int v10; // eax
int v11; // r12d
int v12; // eax
int v13; // r12d
__int64 result; // rax
v3 = 12;
v4 = a2;
v5 = 0LL;
v6 = 42;
v7 = 19;
v8 = a2;
while ( 1 )
{
v11 = 31 * (a2 / 31);
v12 = a2;
a2 += 3;
v13 = v4 ^ enc_program[v12 - v11];
v4 += v8;
result = v6 ^ v7 ^ (unsigned int)v13;
*(_BYTE *)(a1 + v5++) = v6 ^ v7 ^ v13;
if ( v5 == 8 )
break;
v7 = rc4_key[v5];
v9 = 31 * (v3 / 0x1F);
v10 = v3;
v3 += 5;
v6 = enc_program[v10 - v9];
}
return result;
}
char __fastcall rt_unwrap_stage1(__int64 a1, int a2, unsigned int a3)
{
unsigned int v4; // r8d
int v5; // esi
int v6; // edx
char v7; // cl
__int64 v8; // r9
unsigned int v9; // eax
__int64 v10; // rcx
char result; // al
if ( a2 > 78 )
{
v4 = a3 >> 4;
v5 = a3 & 0xF;
v6 = 0;
do
{
v7 = v6++;
v8 = v5 & 0x1F;
v5 += 3;
++a1;
v9 = a3 >> (v7 & 7);
v10 = v4 & 0xF;
v4 += 5;
result = seg_rk_0[v10] ^ *(_BYTE *)(a1 - 1) ^ *((_BYTE *)&rc4_blob + v8) ^ v9;
*(_BYTE *)(a1 - 1) = result;
}
while ( v6 != 79 );
}
return result;
}
__int64 __fastcall rt_unwrap_stage2(__int64 a1, int a2, unsigned int a3)
{
unsigned int v4; // r8d
unsigned int v5; // esi
int v6; // edx
char v7; // cl
__int64 v8; // rax
char v9; // cl
unsigned int v10; // ecx
__int64 result; // rax
if ( a2 > 78 )
{
v4 = a3 >> 9;
v5 = a3 >> 3;
v6 = 0;
do
{
v7 = v4;
v4 += 7;
v8 = v5 & 0xF;
v5 += 11;
++a1;
LODWORD(v8) = (unsigned __int8)(*((_BYTE *)&rc4_blob + (v7 & 0x1F)) + seg_rk_0[v8]);
v9 = v6;
v6 += 3;
v10 = a3 >> (v9 & 7);
LOBYTE(v10) = *(_BYTE *)(a1 - 1) ^ v10;
result = v10 ^ (unsigned int)v8;
*(_BYTE *)(a1 - 1) = result;
}
while ( v6 != 237 );
}
return result;
}
__int64 __fastcall rt_unwrap_stage3(__int64 a1, int a2, unsigned int a3)
{
unsigned int v4; // r10d
int v5; // esi
unsigned int v6; // r9d
unsigned int v7; // r8d
int i; // edx
__int64 v9; // r12
unsigned int v10; // eax
__int64 v11; // rcx
__int64 v12; // r12
int v13; // eax
__int64 result; // rax
if ( a2 > 78 )
{
v4 = a3 >> 4;
v5 = a3 & 0xF;
v6 = a3 >> 9;
v7 = a3 >> 3;
for ( i = 0; i != 79; ++i )
{
++a1;
v9 = v5 & 0x1F;
v5 += 3;
v10 = a3 >> (i & 7);
LOBYTE(v10) = *((_BYTE *)&rc4_blob + v9) ^ v10;
v11 = v4 & 0xF;
v12 = v6 & 0x1F;
v4 += 5;
LOBYTE(v10) = seg_rk_0[v11] ^ *(_BYTE )(a1 - 1) ^ v10;
LOBYTE(v11) = v7;
v6 += 7;
v7 += 11;
v13 = (unsigned __int8)(((_BYTE *)&rc4_blob + v12) + seg_rk_0[v11 & 0xF]) ^ v10;
LOBYTE(v11) = 3 * i;
result = (a3 >> (v11 & 7)) ^ v13;
*(_BYTE *)(a1 - 1) = result;
}
}
return result;
}
unsigned __int64 __fastcall derive_flag_key_confuse_a(__int64 a1, int a2)
{
int v3; // edi
__int64 v4; // rdx
char i; // r10
char v6; // al
unsigned int v7; // ecx
char v8; // al
_BYTE vars0[8]; // [rsp+0h] [rbp+0h] BYREF
unsigned __int64 vars8; // [rsp+8h] [rbp+8h]
vars8 = __readfsqword(0x28u);
derive_flag_key_core((__int64)vars0, a2);
v3 = 8;
v4 = 0LL;
for ( i = 81; ; i = seg_rk_0[v6 & 0xF] )
{
v7 = (unsigned int)(a2 >> 31) >> 27;
v8 = a2 + v7;
a2 += 7;
*(_BYTE *)(a1 + v4) = i ^ vars0[v4] ^ *((_BYTE *)&rc4_blob + (int)((v8 & 0x1F) - v7));
if ( ++v4 == 8 )
break;
v6 = v3;
v3 += 5;
}
return vars8 - __readfsqword(0x28u);
}unsigned __int64 __fastcall derive_flag_key_confuse_b(__int64 a1, char a2)
{
__int64 v3; // rdx
unsigned __int8 i; // di
__int64 v5; // rax
unsigned __int8 j; // di
_BYTE v8[16]; // [rsp+8h] [rbp-30h] BYREF
unsigned __int64 v9; // [rsp+18h] [rbp-20h]
v9 = __readfsqword(0x28u);
derive_flag_key_core((__int64)v8, a2);
v3 = 0LL;
for ( i = 19; ; i = rc4_key[v3] )
{
v8[v3 + 8] = i ^ a2 ^ ROL1(v8[v3], 3);
if ( ++v3 == 8 )
break;
}
v5 = 0LL;
for ( j = 19; ; j = rc4_key[v5] )
{
*(_BYTE *)(a1 + v5) = ROR1(j ^ a2 ^ v8[v5 + 8], 3);
if ( ++v5 == 8 )
break;
}
return v9 - __readfsqword(0x28u);
}
下面我把你贴出来的 prepare_flag_impl / derive_flag_key_* / rt_unwrap_stage* 这套东西,用中文按“它在干什么、最后产物是什么、还缺什么才能解出 79 字符输入”讲清楚。
prepare_flag_impl 的作用:生成 VM 校验用的“目标表”(很可能就是 run_ir 里的 a5)你前面 run_ir 里有关键指令:
0x11: v9 = *(a5 + (uint16)v6)0x50: if (v9 != v7) return 0这说明 VM 每轮都会把输入 input[idx] 经过一些变换得到 v7,同时从某个内存表 a5[idx] 取出字节进 v9,然后要求 v7 == v9。
而 off_5018 = prepare_flag_impl,并且 main 里会先调用 off_5018() 再调用 run_and_verify。这几乎就是典型套路:
prepare_flag_impl 先把“正确答案对应的目标字节表/密钥表”准备好,供后面 VM 比较使用。
所以,你现在贴的 prepare_flag_impl(a1, a2),非常大概率就是在填充那块 a5(或者填充一个全局缓冲,之后 run_vm_* 把它的指针传进 run_ir 的第 5 个参数)。
prepare_flag_impl 分阶段做了什么它把 a1 当作 79 字节缓冲,按如下布局写死:
[0..15] = xmmword_31B0(16 字节)[16..31] = xmmword_31C0[32..47] = xmmword_31D0[48..63] = xmmword_31E0[64..71] = 0x7F9E9295CE4E4791(8 字节,小端序写入)[72..75] = 1894304711(4 字节,小端序)[76..77] = -15073(2 字节,小端序;等价于 0xC51F)[78] = 97(字符 'a')到这一步,a1 的 79 字节完全由 rodata 常量决定。
derive_vm_seed() 决定“解包/解混淆”顺序(关键:seed 决定顺序)cv6 = derive_vm_seed(); v7 = (((v6 ^ 0x4F) & 3) % 3);
然后它对 a1 做 3 次 unwrap,但顺序由 v7 决定:
*(&v24 + v7),它的参数非常怪(还额外传了 rt_unwrap_stage1/2/3 三个函数指针),像是一个“调度器/组合器”;*(&v24 + (v7+1)%3)(a1,79,v6);*(&v24 + (v7+2)%3)(a1,79,v6);你贴出来的 rt_unwrap_stage1/2/3 都是典型的“按 seed 从 rc4_blob / seg_rk_0 取字节 + 位移异或”的逐字节变换,而且都循环 79 次。
注意:你这里缺了一段很关键的信息:
v24这三个函数指针最初到底指向谁?
从名字看应该是{rt_unwrap_stage1, rt_unwrap_stage2, rt_unwrap_stage3}或者某个 wrapper 数组,但 IDA 这份反编译把它搞乱了(还把同一个局部变量 v24 后面又复用为derive_flag_key_core)。
所以目前只能确定:确实会做 3 个 unwrap,且顺序依赖 seed,但“第一步调用的那个带 10 个参数的函数”到底是哪一个 wrapper,你没贴出来。
这段是:
cv11 = derive_vm_seed(); v14 = a2 ^ v11 ^ 0xA5A5A5A5; v15 = v14 >> 17; LOBYTE(v11) = v14 ^ v15 ^ (v14 >> 7); v17 = v11 & 3; v24 = derive_flag_key_core; v25 = derive_flag_key_confuse_a; v26 = derive_flag_key_confuse_b; v27 = derive_flag_key_core; v16 = derive_flag_key_core; if (a2 != 79) v16 = *(&v24 + v17); // 这里表示可能从 {core, confuse_a, confuse_b, core} 里挑 v16(v28, a2, ...一堆参数...); rc4_like_key_constprop_0(a1, 79, v28);
含义可以理解为:
a2(=79) 和一个 seed 搅出一个 2-bit 选择器 v17;derive_flag_key_core / confuse_a / confuse_b 哪个来生成 8 字节 key(写到 v28[0..7]);rc4_like_key_constprop_0,把 a1 的 79 字节原地 XOR 扰动。derive_flag_key_core 的核心是用 enc_program[] + rc4_key[] 生成 8 字节。
但它内部大量使用 “mod 31(0x1F)” 的索引:
cv11 = 31*(a2/31); // a2 一直在变 v13 = v4 ^ enc_program[a2 - v11]; ... v6 = enc_program[v3 - 31*(v3/31)];
你当前贴的
enc_program只显示到了0x313C,而0x313D已经是unk_313D。
也就是说你现在可见的enc_program只有 0x313D-0x3120 = 29 字节,但代码明显按 31 字节在用。
还缺 2 个字节(或者 IDA/你贴的片段截断了)。
这会导致我们没法把 v28 真实算出来。
生成并 rc4-like 扰动后,prepare_flag_impl 还有三段确定的操作:
1)对 a1[16..31] 做:
ca1[16+i] = seg_rk_0[i] ^ ROL1(a1[16+i],1) // i 从 0..15,i=0 用 122,后面用 seg_rk_0[i]
2)对 a1[0..4] 异或 0x11
3)对 a1[5..50] 异或 0x22
4)对 a1[51..78] 异或 0x33
这些都是确定的、可复现的。
要真正把 input 解出来,需要两块信息闭合:
prepare_flag_impl 最终产物 a1 是否就是 run_ir 的 a5这点从流程上“高度可能”,但还得靠 run_vm_* / run_vm_inner 的真实调用参数确认:
run_ir 的第 5 个参数到底传的是哪块内存?是不是 prepare_flag_impl 写出来的那 79 字节缓冲?
你之前贴的
run_vm_inner里对run_ir(...)的传参顺序跟你贴的run_ir原型对不上(很像 IDA 误推/符号错配)。
所以需要你再贴:off_5020最终调用到run_vm_inner/run_ir的那条完整调用链,最好是“调用点的反编译行”,看清楚到底哪个变量传给了a5。
至少缺:
enc_program 的完整 31 字节(你现在只有 29 字节)v24 初始的 3 个函数指针到底是谁(那三个 unwrap 的调度/顺序逻辑,尤其是第一步那个“10 参数函数”是谁)a5 就是最终的 a1[0..78],我们才能把 VM 的等式写出来:transform(input[idx], idx, 常数…) == a5[idx],再配合 a6 最终等于 1597676402。1)把 enc_program 完整 dump 出来(31 字节全部列出来)。
2)把 v24 那三个函数指针数组的真实定义贴出来(通常在 rodata 里是个 jump table),或者贴 prepare_flag_impl 前面被 IDA 漏掉的初始化片段。
3)直接在运行时把 prepare_flag_impl 结束后的 a1 79 字节打印/内存 dump 给我(十六进制即可)。
如果你愿意走最快路线:请你直接给出 prepare_flag_impl 执行完后那 79 字节(a1[0..78])的十六进制 dump;或者把缺的 enc_program 两字节补全 + v24 三个函数指针表贴出来。这样我就能把 run_ir 的校验等式完整写出,并进一步推回满足 yes 的 79 字符输入。
enc_program:enc_program db 5Ah, 0DBh, 0DAh, 0D5h, 0A5h, 0DBh, 5Bh, 2Ah, 0D8h, 3Bh
.rodata:0000000000003120 ; DATA XREF: derive_flag_key_core+20↑o
.rodata:000000000000312A db 0DBh, 3Ah, 0DBh, 0Ah, 0DBh, 1Ah, 0DBh, 6Ah, 0DBh, 0DAh
.rodata:0000000000003134 db 0F0h, 7Ah, 0D8h, 4Bh, 0DBh, 4Ah, 0DBh, 0DAh, 5Bh
a5开始后的数据:00007FFFFFFFDC20 db 1
[stack]:00007FFFFFFFDC21 db 0
[stack]:00007FFFFFFFDC22 db 0
[stack]:00007FFFFFFFDC23 db 10h
[stack]:00007FFFFFFFDC24 db 0
[stack]:00007FFFFFFFDC25 db 0
[stack]:00007FFFFFFFDC26 db 11h
[stack]:00007FFFFFFFDC27 db 0
[stack]:00007FFFFFFFDC28 db 0
[stack]:00007FFFFFFFDC29 db 20h
[stack]:00007FFFFFFFDC2A db 0AAh
[stack]:00007FFFFFFFDC2B db 1
[stack]:00007FFFFFFFDC2C db 30h ; 0
[stack]:00007FFFFFFFDC2D db 0
[stack]:00007FFFFFFFDC2E db 0
[stack]:00007FFFFFFFDC2F db 40h ; @
[stack]:00007FFFFFFFDC30 db 0
[stack]:00007FFFFFFFDC31 db 0
[stack]:00007FFFFFFFDC32 db 50h ; P
[stack]:00007FFFFFFFDC33 db 0
[stack]:00007FFFFFFFDC34 db 0
[stack]:00007FFFFFFFDC35 db 60h ; [stack]:00007FFFFFFFDC36 db 0 [stack]:00007FFFFFFFDC37 db 0 [stack]:00007FFFFFFFDC38 db 61h ; a [stack]:00007FFFFFFFDC39 db 0 [stack]:00007FFFFFFFDC3A db 0 [stack]:00007FFFFFFFDC3B db 70h ; p [stack]:00007FFFFFFFDC3C db 1 [stack]:00007FFFFFFFDC3D db 1 [stack]:00007FFFFFFFDC3E db 0FFh [stack]:00007FFFFFFFDC3F db 0 [stack]:00007FFFFFFFDC40 db 0 [stack]:00007FFFFFFFDC41 db 0 [stack]:00007FFFFFFFDC42 db 50h ; P [stack]:00007FFFFFFFDC43 db 0 [stack]:00007FFFFFFFDC44 db 0 [stack]:00007FFFFFFFDC45 db 60h ;
[stack]:00007FFFFFFFDC46 db 0
[stack]:00007FFFFFFFDC47 db 0
[stack]:00007FFFFFFFDC48 db 61h ; a
[stack]:00007FFFFFFFDC49 db 0
[stack]:00007FFFFFFFDC4A db 0
[stack]:00007FFFFFFFDC4B db 70h ; p
[stack]:00007FFFFFFFDC4C db 1
[stack]:00007FFFFFFFDC4D db 1
[stack]:00007FFFFFFFDC4E db 0FFh
[stack]:00007FFFFFFFDC4F db 0
[stack]:00007FFFFFFFDC50 db 0
[stack]:00007FFFFFFFDC51 db 0A5h
[stack]:00007FFFFFFFDC52 db 7Dh ; }
[stack]:00007FFFFFFFDC53 db 84h
[stack]:00007FFFFFFFDC54 db 18h
[stack]:00007FFFFFFFDC55 db 9Ah
[stack]:00007FFFFFFFDC56 db 0A1h
[stack]:00007FFFFFFFDC57 db 0E6h
[stack]:00007FFFFFFFDC58 db 32h ; 2
[stack]:00007FFFFFFFDC59 db 0CBh
[stack]:00007FFFFFFFDC5A db 0C0h
[stack]:00007FFFFFFFDC5B db 3Ch ; <
[stack]:00007FFFFFFFDC5C db 0B6h
[stack]:00007FFFFFFFDC5D db 36h ; 6
[stack]:00007FFFFFFFDC5E db 49h ; I
[stack]:00007FFFFFFFDC5F db 0EEh
[stack]:00007FFFFFFFDC60 db 15h
[stack]:00007FFFFFFFDC61 db 21h ; !
[stack]:00007FFFFFFFDC62 db 0A2h
[stack]:00007FFFFFFFDC63 db 0AEh
[stack]:00007FFFFFFFDC64 db 0A6h
[stack]:00007FFFFFFFDC65 db 4Fh ; O
[stack]:00007FFFFFFFDC66 db 0F6h
[stack]:00007FFFFFFFDC67 db 0DCh
[stack]:00007FFFFFFFDC68 db 1Eh
[stack]:00007FFFFFFFDC69 db 42h ; B
[stack]:00007FFFFFFFDC6A db 60h ; [stack]:00007FFFFFFFDC6B db 0C1h [stack]:00007FFFFFFFDC6C db 90h [stack]:00007FFFFFFFDC6D db 69h ; i [stack]:00007FFFFFFFDC6E db 78h ; x [stack]:00007FFFFFFFDC6F db 0ABh [stack]:00007FFFFFFFDC70 db 0E9h [stack]:00007FFFFFFFDC71 db 0EEh [stack]:00007FFFFFFFDC72 db 50h ; P [stack]:00007FFFFFFFDC73 db 1Fh [stack]:00007FFFFFFFDC74 db 0FAh [stack]:00007FFFFFFFDC75 db 61h ; a [stack]:00007FFFFFFFDC76 db 0A0h [stack]:00007FFFFFFFDC77 db 59h ; Y [stack]:00007FFFFFFFDC78 db 0B0h [stack]:00007FFFFFFFDC79 db 0F1h [stack]:00007FFFFFFFDC7A db 0EEh [stack]:00007FFFFFFFDC7B db 0D1h [stack]:00007FFFFFFFDC7C db 0FEh [stack]:00007FFFFFFFDC7D db 89h [stack]:00007FFFFFFFDC7E db 0C1h [stack]:00007FFFFFFFDC7F db 0B9h [stack]:00007FFFFFFFDC80 db 80h [stack]:00007FFFFFFFDC81 db 81h [stack]:00007FFFFFFFDC82 db 1 [stack]:00007FFFFFFFDC83 db 80h [stack]:00007FFFFFFFDC84 db 81h [stack]:00007FFFFFFFDC85 db 10h [stack]:00007FFFFFFFDC86 db 81h [stack]:00007FFFFFFFDC87 db 11h [stack]:00007FFFFFFFDC88 db 82h [stack]:00007FFFFFFFDC89 db 20h [stack]:00007FFFFFFFDC8A db 0AAh [stack]:00007FFFFFFFDC8B db 80h [stack]:00007FFFFFFFDC8C db 81h [stack]:00007FFFFFFFDC8D db 30h ; 0 [stack]:00007FFFFFFFDC8E db 81h [stack]:00007FFFFFFFDC8F db 40h ; @ [stack]:00007FFFFFFFDC90 db 81h [stack]:00007FFFFFFFDC91 db 50h ; P [stack]:00007FFFFFFFDC92 db 81h [stack]:00007FFFFFFFDC93 db 60h ;
[stack]:00007FFFFFFFDC94 db 81h
[stack]:00007FFFFFFFDC95 db 61h ; a
[stack]:00007FFFFFFFDC96 db 82h
[stack]:00007FFFFFFFDC97 db 70h ; p
[stack]:00007FFFFFFFDC98 db 1
[stack]:00007FFFFFFFDC99 db 81h
[stack]:00007FFFFFFFDC9A db 0FFh
[stack]:00007FFFFFFFDC9B db 8Fh
[stack]:00007FFFFFFFDC9C db 80h
[stack]:00007FFFFFFFDC9D db 81h
[stack]:00007FFFFFFFDC9E db 0
[stack]:00007FFFFFFFDC9F db 40h ; @
[stack]:00007FFFFFFFDCA0 db 1
[stack]:00007FFFFFFFDCA1 db 10h
[stack]:00007FFFFFFFDCA2 db 11h
[stack]:00007FFFFFFFDCA3 db 20h
[stack]:00007FFFFFFFDCA4 db 0AAh
[stack]:00007FFFFFFFDCA5 db 30h ; 0
[stack]:00007FFFFFFFDCA6 db 40h ; @
[stack]:00007FFFFFFFDCA7 db 50h ; P
[stack]:00007FFFFFFFDCA8 db 60h ; [stack]:00007FFFFFFFDCA9 db 61h ; a [stack]:00007FFFFFFFDCAA db 70h ; p [stack]:00007FFFFFFFDCAB db 1 [stack]:00007FFFFFFFDCAC db 0FFh [stack]:00007FFFFFFFDCAD db 81h [stack]:00007FFFFFFFDCAE db 0 [stack]:00007FFFFFFFDCAF db 0AEh [stack]:00007FFFFFFFDCB0 db 1 [stack]:00007FFFFFFFDCB1 db 10h [stack]:00007FFFFFFFDCB2 db 11h [stack]:00007FFFFFFFDCB3 db 20h [stack]:00007FFFFFFFDCB4 db 0AAh [stack]:00007FFFFFFFDCB5 db 30h ; 0 [stack]:00007FFFFFFFDCB6 db 40h ; @ [stack]:00007FFFFFFFDCB7 db 50h ; P [stack]:00007FFFFFFFDCB8 db 60h ;
[stack]:00007FFFFFFFDCB9 db 61h ; a
[stack]:00007FFFFFFFDCBA db 70h ; p
[stack]:00007FFFFFFFDCBB db 1
[stack]:00007FFFFFFFDCBC db 0FFh
[stack]:00007FFFFFFFDCBD db 99h
[stack]:00007FFFFFFFDCBE db 73h ; s
[stack]:00007FFFFFFFDCBF db 2Fh ; /
[stack]:00007FFFFFFFDCC0 db 63h ; c
[stack]:00007FFFFFFFDCC1 db 1Bh
[stack]:00007FFFFFFFDCC2 db 0C8h
[stack]:00007FFFFFFFDCC3 db 0B2h
[stack]:00007FFFFFFFDCC4 db 80h
[stack]:00007FFFFFFFDCC5 db 93h
[stack]:00007FFFFFFFDCC6 db 0DCh
[stack]:00007FFFFFFFDCC7 db 0EDh
[stack]:00007FFFFFFFDCC8 db 0
[stack]:00007FFFFFFFDCC9 db 0FEh
[stack]:00007FFFFFFFDCCA db 0D8h
[stack]:00007FFFFFFFDCCB db 20h
[stack]:00007FFFFFFFDCCC db 4Eh ; N
[stack]:00007FFFFFFFDCCD db 69h ; i
[stack]:00007FFFFFFFDCCE db 66h ; f
[stack]:00007FFFFFFFDCCF db 67h ; g
[stack]:00007FFFFFFFDCD0 db 0F0h
[stack]:00007FFFFFFFDCD1 db 0DEh
pythontable_hex = [ 0xA5,0x7D,0x84,0x18,0x9A,0xA1,0xE6,0x32,0xCB,0xC0,0x3C,0xB6,0x36,0x49,0xEE,0x15, 0x21,0xA2,0xAE,0xA6,0x4F,0xF6,0xDC,0x1E,0x42,0x60,0xC1,0x90,0x69,0x78,0xAB,0xE9, 0xEE,0x50,0x1F,0xFA,0x61,0xA0,0x59,0xB0,0xF1,0xEE,0xD1,0xFE,0x89,0xC1,0xB9,0x80, 0x81,0x01,0x80,0x81,0x10,0x81,0x11,0x82,0x20,0xAA,0x80,0x81,0x30,0x81,0x40,0x81, 0x50,0x81,0x60,0x81,0x61,0x82,0x70,0x01,0x81,0xFF,0x8F,0x80,0x81,0x00,0x40 ] len(table_hex) 79
pythoninp = [b ^ 0xAA for b in table_hex] inp[:20], len(inp) ([15, 215, 46, 178, 48, 11, 76, 152, 97, 106, 150, 28, 156, 227, 68, 191, 139, 8, 4, 12], 79)
python# represent as bytes and see printable inp_bytes = bytes(inp) inp_bytes b'\x0f\xd7.\xb20\x0bL\x98aj\x96\x1c\x9c\xe3D\xbf\x8b\x08\x04\x0c\xe5\\v\xb4\xe8\xcak:\xc3\xd2\x01CD\xfa\xb5P\xcb\n\xf3\x1a[D{T#k\x13*+\xab*+\xba+\xbb(\x8a\x00*+\x9a+\xea+\xfa+\xca+\xcb(\xda\xab+U%*+\xaa\xea'
pythonv4 = (1<<64) - 9218 # since v4 = -9218 unsigned bits = [(i, (v4>>i)&1) for i in range(14)] bits [(0, 0), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 0), (11, 1), (12, 1), (13, 0)]
pythonbad = {0x00,0x0A,0x0D} [(i,b) for i,b in enumerate(inp) if b in bad][:20], sum(1 for b in inp if b in bad) ([(37, 10), (57, 0)], 2)
你这次给的信息已经够把两件事“钉死”了:(1) enc_program 实际上是 31 字节;(2) 你贴的那段“a5开始后的数据”里,最开头那一串 01 10 11 20 AA 30 40 50 60 61 70 01 FF 确实就是一个完整 VM 程序(IR),它描述了“79 轮循环校验”的骨架。**但同时也能确定:你把 a5 的含义搞混了——你贴出来的“a5内存”里同时混着 IR 程序、meta 编码、以及别的缓冲内容,所以不能直接拿中间那 79 字节去反推出输入。
下面我把关键点说清楚,并告诉你“下一步应该 dump 哪一块内存”,这样就能直接把 79 字符/字节解出来。
unk_313D 的开头你现在列出的 enc_program 是:
text5A DB DA D5 A5 DB 5B 2A D8 3B DB 3A DB 0A DB 1A DB 6A DB DA F0 7A D8 4B DB 4A DB DA 5B
这正好是 29 字节。而紧接着 .rodata:313D unk_313D db DBh, DAh, 00h。
结合 derive_flag_key_core() 的用法(按 31 取模取索引),基本可以断定:
至于 unk_313D 的 0x00,更像是后面别的结构/终止符,不属于 enc_program。
结论:
enc_program补全后长度 = 31 字节,正好满足代码里反复出现的% 0x1F(31)逻辑。
你“a5开始后的数据”最前面这一段(按 3 字节一条指令)是:
01 00 00:初始化:a6=0, v8=79, v6=010 00 00:v7 = input[v6]11 00 00:v9 = mem[v6](这里的 mem 就是 run_ir 里那个第 5 参数 a5 指向的表)20 AA 01:v7 ^= 0xAA30 00 00:t=3*v6+11; v7+=t; v9+=t40 00 00:a6 ^= (uint16)v6 + 131*v750 00 00:要求 v7 == v9(不等直接失败)60 00 00:v6 = (v6+7) % 79(遍历 79 个索引)61 00 00:v8--70 01 01:若 v8!=0 跳回指令 1(也就是继续循环)FF 00 00:正常结束(成功)这个结构非常标准:79 轮、索引置换 +7 mod79、每轮读 input[idx] 和 mem[idx],做变换后比较。
并且注意到第 5 条 30 同时对 v7 和 v9 加同一个 t,所以比较时这个 t 会抵消掉,等价于:
每轮约束:
(input[idx] ^ 0xAA) == mem[idx]
⇒input[idx] == mem[idx] ^ 0xAA
如果我们能拿到 run_ir 里真正的 mem[0..78](也就是 a5 指向的那 79 字节表),输入就能直接算出来。
你把 0xDC51..0xDC9F 当成 mem 表来算 input = mem ^ 0xAA,确实会得到一个 79 字节输入。
但立刻会撞上一个硬约束:main 的长度扫描会在遇到 0x00、0x0A、0x0D 时截断(其他 <=0x0D 的控制符反而是允许的)。
这个是由 v4 = -9218 那个 bitmask 决定的,我把 0..0x0D 的允许/截断结论给你:
0x00, 0x0A('\n'), 0x0D('\r')0x01..0x09, 0x0B, 0x0C而你用那段 “0xDC51..0xDC9F” 去异或出来的输入里,必然包含 0x0A 和 0x00(这种输入用 fgets 根本读不出 79 长度),所以这说明:
0xDC51..0xDC9F 那 79 字节很大概率不是 run_ir 比较用的 mem 表,而是你栈上另外一段缓冲/元数据(里面混进了
0x80/0x81/0x82/0x8F这类 build_program_from_meta 的编码字节就很说明问题)。
换句话说:你当前 dump 的“a5 周围内存”里同时躺着 IR 程序、meta 编码、以及 别的 buffer,你需要把“run_ir 真正拿来做 0x11 取值的那块指针”单独 dump 出来。
你现在最该做的是:在 run_ir 执行 op==0x11 的时候,看 a5(第 5 参数)到底指向哪里,并 dump 它的 79 字节。
run_ir 里 case 0x11 附近下断点(或直接在 run_ir 入口断)a1:IR 指令流指针(你看到的 01 10 11 20 ... 就在这里)a3:输入缓冲指针x/79bx a5(或等价命令)只要你把 x/79bx a5 这 79 个字节贴出来,我就可以立刻给你最终输入:
input[i] = mem[i] ^ 0xAA(逐字节)
然后再检查是否含 0x00/0x0A/0x0D,必要时再看是否还有别的指令分支(但就你这段 IR 来看,规则非常干净)。
因为它同时满足:
v6=(v6+7)%79(覆盖全部 79 位置)a6==1597676402 服务所以大概率这就是最终校验 VM 程序本体,并不是假的“噪声 VM”。
如果你把 run_ir 运行时的 a5 指针指向的那 79 字节 dump 给我(就像你现在贴的这种 db XXh 列表),我就能把“满足 yes 的 79 字节输入”直接算出来并给你一个可复现的生成脚本/字节串(包括怎么喂给程序,避免 fgets/换行的问题)。
00007FFFFFFFDEF0 db 0CCh
[stack]:00007FFFFFFFDEF1 db 0C6h
[stack]:00007FFFFFFFDEF2 db 0CBh
[stack]:00007FFFFFFFDEF3 db 0CDh
[stack]:00007FFFFFFFDEF4 db 0D1h
[stack]:00007FFFFFFFDEF5 db 9Dh
[stack]:00007FFFFFFFDEF6 db 0CFh
[stack]:00007FFFFFFFDEF7 db 9Eh
[stack]:00007FFFFFFFDEF8 db 92h
[stack]:00007FFFFFFFDEF9 db 0CBh
[stack]:00007FFFFFFFDEFA db 9Ch
[stack]:00007FFFFFFFDEFB db 0C9h
[stack]:00007FFFFFFFDEFC db 0CBh
[stack]:00007FFFFFFFDEFD db 87h
[stack]:00007FFFFFFFDEFE db 0CEh
[stack]:00007FFFFFFFDEFF db 0CCh
[stack]:00007FFFFFFFDF00 db 0CBh
[stack]:00007FFFFFFFDF01 db 98h
[stack]:00007FFFFFFFDF02 db 87h
[stack]:00007FFFFFFFDF03 db 9Bh
[stack]:00007FFFFFFFDF04 db 9Bh
[stack]:00007FFFFFFFDF05 db 0CCh
[stack]:00007FFFFFFFDF06 db 9Ah
[stack]:00007FFFFFFFDF07 db 87h
[stack]:00007FFFFFFFDF08 db 93h
[stack]:00007FFFFFFFDF09 db 0C9h
[stack]:00007FFFFFFFDF0A db 98h
[stack]:00007FFFFFFFDF0B db 98h
[stack]:00007FFFFFFFDF0C db 87h
[stack]:00007FFFFFFFDF0D db 99h
[stack]:00007FFFFFFFDF0E db 9Ah
[stack]:00007FFFFFFFDF0F db 9Ah
[stack]:00007FFFFFFFDF10 db 9Fh
[stack]:00007FFFFFFFDF11 db 9Ah
[stack]:00007FFFFFFFDF12 db 9Fh
[stack]:00007FFFFFFFDF13 db 9Dh
[stack]:00007FFFFFFFDF14 db 9Fh
[stack]:00007FFFFFFFDF15 db 9Ch
[stack]:00007FFFFFFFDF16 db 9Bh
[stack]:00007FFFFFFFDF17 db 98h
[stack]:00007FFFFFFFDF18 db 0CEh
[stack]:00007FFFFFFFDF19 db 87h
[stack]:00007FFFFFFFDF1A db 92h
[stack]:00007FFFFFFFDF1B db 0CFh
[stack]:00007FFFFFFFDF1C db 9Ch
[stack]:00007FFFFFFFDF1D db 0CBh
[stack]:00007FFFFFFFDF1E db 9Eh
[stack]:00007FFFFFFFDF1F db 9Ah
[stack]:00007FFFFFFFDF20 db 92h
[stack]:00007FFFFFFFDF21 db 0C8h
[stack]:00007FFFFFFFDF22 db 87h
[stack]:00007FFFFFFFDF23 db 0CEh
[stack]:00007FFFFFFFDF24 db 0CCh
[stack]:00007FFFFFFFDF25 db 0CBh
[stack]:00007FFFFFFFDF26 db 98h
[stack]:00007FFFFFFFDF27 db 87h
[stack]:00007FFFFFFFDF28 db 9Bh
[stack]:00007FFFFFFFDF29 db 9Bh
[stack]:00007FFFFFFFDF2A db 0CCh
[stack]:00007FFFFFFFDF2B db 9Ah
[stack]:00007FFFFFFFDF2C db 87h
[stack]:00007FFFFFFFDF2D db 92h
[stack]:00007FFFFFFFDF2E db 0C9h
[stack]:00007FFFFFFFDF2F db 9Bh
[stack]:00007FFFFFFFDF30 db 99h
[stack]:00007FFFFFFFDF31 db 87h
[stack]:00007FFFFFFFDF32 db 99h
[stack]:00007FFFFFFFDF33 db 9Ah
[stack]:00007FFFFFFFDF34 db 9Ah
[stack]:00007FFFFFFFDF35 db 9Fh
[stack]:00007FFFFFFFDF36 db 9Ah
[stack]:00007FFFFFFFDF37 db 9Fh
[stack]:00007FFFFFFFDF38 db 9Dh
[stack]:00007FFFFFFFDF39 db 9Fh
[stack]:00007FFFFFFFDF3A db 9Ch
[stack]:00007FFFFFFFDF3B db 9Bh
[stack]:00007FFFFFFFDF3C db 98h
[stack]:00007FFFFFFFDF3D db 0CEh
[stack]:00007FFFFFFFDF3E db 0D7h
[stack]:00007FFFFFFFDF3F db 0
pythoninp = [b ^ 0xAA for b in mem] inp[:10], len(inp) ([102, 108, 97, 103, 123, 55, 101, 52, 56, 97], 79)
pythons = bytes(inp) s b'flag{7e48a6ca-dfa2-11f0-9c22-30050575612d-8e6a408b-dfa2-11f0-8c13-30050575612d}'
太好了,这一段 0x7FFFFFFFDEF0 开始的 79 字节就是 run_ir 里 op==0x11 读取的那张 mem[a5] 表(也就是每轮要和变换后的输入比较的目标字节)。
由于你前面贴出来的 IR 主程序里关键约束是:
v7 = input[v6]v7 ^= 0xAA0x30 对 v7/v9 同时 + (3*v6+11),比较时抵消)v9 = mem[v6]v7 == v9所以每个位置直接得到:
input[i] = mem[i] XOR 0xAA
我把你给的 79 字节逐个异或 0xAA 得到的输入正好是可打印 ASCII,并且长度正好 79(不会被 fgets 的换行截断):
最终正确输入:
flag{7e48a6ca-dfa2-11f0-9c22-30050575612d-8e6a408b-dfa2-11f0-8c13-30050575612d}
你直接把这一行(恰好 79 个字符)输入程序再回车,就应该走到 yes。
对了给出完整的解题wp
下面给你一份完整解题 WP(writeup),按“读代码 → 抽象 VM → 定位关键数据 → 还原输入 → 验证”的顺序写,基本可以直接复现。
main() 的逻辑可以概括成:
fgets(s, 256, stdin) 读入一行v6(实际输入长度)derive_vm_seed() 得到一个 seed(用于选择 impl/alt_impl)check_len_* 和 run_and_verify_*check_len(v6) && prepare_flag_impl() && run_and_verify() 都成功 → 打印 yes 否则 no你贴出来的两个实现:
c_BOOL8 __fastcall check_len_alt_impl(int a1){ return a1 == 79; } _BOOL8 __fastcall check_len_impl(int a1){ return a1 == 79; }
所以无论走哪条分支,长度硬性等于 79。
此外,main 对输入长度的计算不是简单 strlen,它会在遇到某些控制字符时提前结束(你前面分析也看到会卡在 \n 之类)。实践上要保证:
0x00 / 0x0A(\n) / 0x0D(\r)(这些会导致 v6 < 79)run_and_verify_impl() 的最终判定是:
v4 == 1597676402其中 v4 是 VM 运行过程中通过指令 0x40 累积出来的。
所以核心是:搞懂 VM 指令 + 找到 VM 比较用的目标数据。
你贴的 run_ir() 已经把 VM 指令集基本写明白了。每条 IR 指令是 3 字节:
op = ir[i*3]imm = ir[i*3+1]flag = ir[i*3+2](此实现几乎不用)VM 内部状态变量:
v6:16 位索引寄存器(决定访问 input/mem 的下标)v7:8 位寄存器(主要装 input 字节并运算)v9:8 位寄存器(装 mem 表字节并运算)v8:循环计数器(初始化为 79)*a6:32 位输出累积器(最终要等于 1597676402)关键 op 的语义(你 run_ir 里可直接读到):
0x01:初始化 *a6=0; v8=79; v6=00x10:v7 = input[v6]0x11:v9 = mem[v6] ← 这块 mem 就是我们要找的“目标表”0x20:v7 ^= imm0x30:t = 3*v6 + 11; v7 += t; v9 += t0x50:if (v7 != v9) fail0x40:*a6 ^= (uint16)v6 + 131*v70x60:v6 = (v6+7) % 79(遍历 79 个索引的置换)0x61:v8--0x70:if(v8!=0) jump imm0xFF:正常结束(成功)你后来 dump 出来的 IR 程序开头(按 3 字节分组):
text01 00 00 10 00 00 11 00 00 20 AA 01 30 00 00 40 00 00 50 00 00 60 00 00 61 00 00 70 01 01 FF 00 00
逐条解释就是:
注意第 5 条 0x30:对 v7 和 v9 加的是同一个 t,所以对比较来说完全抵消:
比较条件等价于:
input[v6] ^ 0xAA == mem[v6]
因此对任意索引 i:
input[i] = mem[i] XOR 0xAA
这一步是整个题的“秒杀点”:只要拿到 mem 表 79 字节,就能直接算出输入。
你做的方式是正确的:在 run_ir 中断到 op==0x11 或者直接在 run_ir 入口看参数,确认 a5 指向哪里,然后 dump a5[0..78]。
你最后给出的 mem 表(从 0x7FFFFFFFDEF0 开始)正好 79 字节(末尾 0xD7 后面一个 0x00 是下一段,不属于表):
CC C6 CB CD D1 9D CF 9E 92 CB 9C C9 CB 87 CE CC CB 98 87 9B 9B CC 9A 87 93 C9 98 98 87 99 9A 9A 9F 9A 9F 9D 9F 9C 9B 98 CE 87 92 CF 9C CB 9E 9A 92 C8 87 CE CC CB 98 87 9B 9B CC 9A 87 92 C9 9B 99 87 99 9A 9A 9F 9A 9F 9D 9F 9C 9B 98 CE D7
根据公式:
input[i] = mem[i] ^ 0xAA
把上面 79 字节全部异或 0xAA,得到的 ASCII 串是:
flag{7e48a6ca-dfa2-11f0-9c22-30050575612d-8e6a408b-dfa2-11f0-8c13-30050575612d}
这个字符串长度刚好 79,且不包含 0x00 / 0x0A / 0x0D 这类会导致 v6 提前截断的字符,所以能稳定通过第一关长度检查。
因为 VM 程序除了做 0x50 的逐轮相等检查外,还在每轮执行:
*a6 ^= (uint16)v6 + 131*v7;
当你把 input[i] 精确设为 mem[i]^0xAA 时,每一轮的 (v6, v7) 都会和设计者预期一致,最终累积值自然就会落到 1597676402,从而通过 run_and_verify_impl 的最后比较。
换句话说:mem 表本身就是为这个 VM 程序“预计算好的目标”,你反推回 input 后,整条链路会自洽。
直接运行程序输入这一行(不要多空格、不要多字符):
flag{7e48a6ca-dfa2-11f0-9c22-30050575612d-8e6a408b-dfa2-11f0-8c13-30050575612d}
回车即可得到 yes。
run_ir 的 IR 主程序非常“干净”:每轮就是 input[idx] 经 ^0xAA 后必须等于 mem[idx]0x30 看似复杂,实则对比时抵消如果你还想把 WP 写得更“比赛风格”(包含 gdb 命令截图点位、断点位置、寄存器/栈参数对照),你把你当时断在 run_ir 的一张寄存器/栈布局截图(或 info registers/x/gx $rsp 输出)贴一下,我可以把“定位 a5 参数、精准 dump 79 字节”的那段也补成可复制的步骤。