目录
126_[NPUCTF2020]EzObfus-Chapter2
程序先对输入的每个字符作个处理sub_41644A()(逐字符)然后再作个逐字符的位处理,然后比对,这个符合爆破的特征
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- int j; // [esp+18h] [ebp-10h]
- int i; // [esp+1Ch] [ebp-Ch]
-
- sub_416F80();
- puts("Give Me Your Flag:\n");
- scanf("%s", &Str);
- if ( strlen(&Str) == 22 )
- {
- sub_41644A(); // str:0x426020 套函数里对(22个字符+i)^i 13-14除外
- for ( i = 1; i <= 21; ++i )
- {
- *(&Str + i) += (dword_424080[i % 6] >> 6) ^ (16 * dword_424080[(i - 1) % 6]);
- *(&Str + i) = ((int)(unsigned __int8)*(&Str + i) >> 3) | (32 * *(&Str + i));
- }
- for ( j = 0; j <= 21; ++j )
- {
- if ( *(&Str + j) != byte_424040[j] )
- goto LABEL_2;
- }
- puts("Good Job!\n");
- }
- else
- {
- LABEL_2:
- puts("Error!\n");
- }
- return 0;
- }
这里sub_41644A()极其复杂,网上有人猜出是加序号再异或序号,但不完全对。不过可以肯定每次仅对一个字符处理时不涉及其它字符,这个就可以爆破了,先用程序patch一下比对的位数,逐位复制程序
- data = list(open('attachment.exe', 'rb').read())
- for i in range(22):
- data[0x15a06] = i
- filename = f"aaa{i}.exe"
- open(filename, 'wb').write(bytes(data))
然后再逐位爆破
- flag = ''
- for i in range(1):
- filename = f".\\aaa{i}.exe"
- p = subprocess.Popen('.\\aaa0.exe', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
- print('pid', p.pid)
- print('s', p.stdout.readline())
- for j in range(0x21, 0x7f):
- tmp = ((flag+chr(j)).ljust(22,'A')+'\n').encode()
- print('tmp:', tmp)
- p.stdin.write(tmp)
- p.stdin.flush()
- out = p.stdout.readline()
- print("A:",out)
- if b'Error' not in out:
- flag += chr(j)
- print(flag)
- break
- #npuctf{WDNMD_LJ_OBFU!}
这是个apk文件,先用jd打开找到Resources/AndroidManifest.xml 看里边的内容<activity android:name="com.example.myapplication.MainActivity"> 这是起点
从这里找到flag打包方式
- public class MainActivity extends AppCompatActivity {
- ......
- protected void onCreate(Bundle savedInstanceState) {
- ......
- this.mBtnLogin.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- MainActivity mainActivity = MainActivity.this;
- if (mainActivity.check(mainActivity.mEtflag.getText().toString())) { //调用检查
- Toast.makeText(MainActivity.this, "You Are Right!flag is flag{md5(input)}", 0).show(); //flag组装方式
- } else {
- Toast.makeText(MainActivity.this, "Sorry your flag is wrong!", 0).show();
- }
- }
- });
- }
- }
我核对函数,这个函数在lib里lib\arm64-v8a\libnative-lib.so
用IDA打开找到对应函数
- bool __fastcall Java_com_example_myapplication_MainActivity_check(__int64 a1, __int64 a2, __int64 a3)
- {
- ...
- if ( sub_40040((__int64)v6) == 7 )
- {
- for ( i = 0; i <= 6; ++i )
- byte_C80E0[i] = *(_BYTE *)sub_40064((__int64)v6, i);
- v5 = sub_3ED8C((unsigned int *)byte_C80E0); //加密函数
- }
- }
- bool __fastcall sub_3ED8C(unsigned int *a1)
- {
- ...
- v5 = *a1;
- v4 = a1[1];
- v3 = 0;
- for ( i = 0; i < 0x20; ++i )
- {
- v3 += dword_C8010;
- v5 += (16 * v4 + dword_C8000) ^ (v4 + v3) ^ ((v4 >> 5) + dword_C8004);
- v4 += (16 * v5 + dword_C8008) ^ (v5 + v3) ^ ((v5 >> 5) + dword_C800C);
- }
- *a1 = v5;
- a1[1] = v4;
- return *a1 == 0xF5A98FF3 && a1[1] == 0xA21873A3;
处理完运行发现不对。原来这里也用了反调,在.init_array里有函数
.init_array:00000000000C0F98 3C EF 03 00 00 00 00 00 off_C0F98 DCQ sub_3EF3C
函数对几个key 作了变更
- __int64 sub_3EF3C()
- {......
- if ( !strcmp(s1, "0") )
- sub_3EF18();
- }
- void sub_3EF18()
- {
- dword_C8004 = 7;
- dword_C8008 = 8;
- dword_C800C = 6;
- }
根据新的key写出程序
- c8010 = 0x458BCD42
- c8000 = 9
- c8004 = 7
- c8008 = 8
- c800c = 6
- v3 = c8010 *0x20
-
- v5 = 0xF5A98FF3
- v4 = 0xA21873A3
- for i in range(0x1f,-1,-1):
- v4 -= (16 * v5 + c8008) ^ ( v5 + v3) ^ ((v5 >> 5) + c800c)
- v4 &= 0xffffffff
- v5 -= (16 * v4 + c8000) ^ ( v4 + v3) ^ ((v4 >> 5) + c8004)
- v5 &= 0xffffffff
- v3 -= c8010
- v3 &= 0xffffffff
- print(v5, v4,v3)
- print(bytes.fromhex(hex(v5)[2:])[::-1]+bytes.fromhex(hex(v4)[2:])[::-1])
- #GKcTFg0
- m = b'GKcTFg0'
-
- from hashlib import md5
- print(md5(m).digest().hex())
- #flag{77bca47fe645ca1bd1ac93733171c9c4}
打开发现是.net的程序,用dnSpy打开发现有混淆,用de4dot处理一下,再打开一个,找到main,
-
- string text = Console.ReadLine().Trim(); //读取输入的密码
- string b = Class3.smethod_0(class1_, byte_2) + '_' + Class3.smethod_3();
- if (text == b)
- {
- Console.WriteLine(Encoding.ASCII.GetString(bytes4)); //Thank you for providing the correct password.
- Console.Write(Encoding.ASCII.GetString(bytes5)); //Use the following email address to proceed to the next challenge:
- Console.WriteLine(Class3.smethod_1(text, byte_));
- return;
- }
再回到原来没处理过的程序,在比较这个位置下断点,然后执行

执行到断点后可以看到对比的数据
metaprogrammingisherd_DD9BE1704C690FB422F1509A46ABC988
运行程序输入这个密码得到flag
- C:\buuctf.reverse\129_[FlareOn2]YUSoMeta>YUSoMeta
- Warning! This program is 100% tamper-proof!
- Please enter the correct password: metaprogrammingisherd_DD9BE1704C690FB422F1509A46ABC988
- Thank you for providing the correct password.
- Use the following email address to proceed to the next challenge: Justr3adth3sourc3@flare-on.com
- #flag{Justr3adth3sourc3@flare-on.com}
ida打开后发现它处理的东西在函数String_t0_intDll里,打开dll文件(函数在dll文件中),打开主要加密逻辑
- _BOOL8 __fastcall String_to_long(__int64 a1)
- {
- void *v2; // rax
- char v3; // [rsp+30h] [rbp-138h]
- int j; // [rsp+34h] [rbp-134h]
- int v5; // [rsp+38h] [rbp-130h]
- int i; // [rsp+3Ch] [rbp-12Ch]
- _QWORD *v7; // [rsp+48h] [rbp-120h]
- __int64 v8; // [rsp+50h] [rbp-118h]
- _QWORD *v9; // [rsp+58h] [rbp-110h]
- int v10[8]; // [rsp+60h] [rbp-108h]
- char *v11; // [rsp+80h] [rbp-E8h]
- const struct std::_Container_base0 *v12; // [rsp+88h] [rbp-E0h]
- __int64 v13; // [rsp+90h] [rbp-D8h]
- __int64 v14; // [rsp+98h] [rbp-D0h]
- char *v15; // [rsp+A0h] [rbp-C8h]
- const struct std::_Container_base0 *v16; // [rsp+A8h] [rbp-C0h]
- char v17[32]; // [rsp+B0h] [rbp-B8h] BYREF
- char v18[32]; // [rsp+D0h] [rbp-98h] BYREF
- char v19[32]; // [rsp+F0h] [rbp-78h] BYREF
- char v20[32]; // [rsp+110h] [rbp-58h] BYREF
- char v21[32]; // [rsp+130h] [rbp-38h] BYREF
-
- v7 = operator new(0x128ui64);
- if ( v7 )
- {
- *v7 = 9i64;
- `eh vector constructor iterator'(v7 + 1, 0x20ui64, 9ui64, sub_180003C90, (void (__stdcall *)(void *))sub_180003AF0);
- v9 = v7 + 1;
- }
- else
- {
- v9 = 0i64;
- }
- sub_180003C90(v20);
- v11 = v18;
- v12 = sub_180003CE0((const struct std::_Container_base0 *)v18, a1);
- v8 = sub_1800033E0(v12); // base64
- if ( (unsigned __int64)unknown_libname_105(a1) >= 0xA )
- {
- sub_180003A60((__int64)v20, v8);
- v5 = 0;
- for ( i = 0; ; ++i )
- {
- v13 = v5;
- if ( v5 >= (unsigned __int64)unknown_libname_105(v20) )
- break;
- v14 = sub_180003900(v20, v17, v5, 4i64);
- sub_180003B20(&v9[4 * i], v14);
- ((void (__stdcall *)(void *))sub_180003AF0)(v17);
- sub_1800039E0(&v9[4 * i], &unk_180034570);
- v5 += 4; // 每4个一组分组
- }
- sub_180003C90(v21);
- for ( j = 0; j < 8; ++j )
- {
- sub_180003A90(v21, &v9[4 * j]);
- v15 = v19;
- v16 = sub_180003CE0((const struct std::_Container_base0 *)v19, (__int64)v21);
- v10[j] = sub_180003390(v16); // hash
- }
- v3 = 0;
- v2 = (void *)sub_180004B90(&qword_18003A200, "-------Checking-----");
- _CallMemberFunction0(v2, sub_180004F60);
- if ( v10[0] == 0x6C43B2A7
- && v10[1] == 0x7954FD91
- && v10[2] == 0xA3E9532
- && v10[3] == 0xB87B5156
- && v10[4] == 0xDA847742
- && v10[5] == 0x2395E7F3
- && v10[6] == 0xA679D954
- && v10[7] == 0xE1FAAFF7 )
- {
- v3 = 1;
- }
- sub_180003310(v9, v8);
- ((void (__stdcall *)(void *))sub_180003AF0)(v21);
- ((void (__stdcall *)(void *))sub_180003AF0)(v20);
- return v3 != 0;
- }
- else
- {
- ((void (__stdcall *)(void *))sub_180003AF0)(v20);
- return 0i64;
- }
- }
先是base64再每4个一组作个自定义的hash再比较,hash比较深
- __int64 __fastcall sub_180003390(void *a1)
- {
- unsigned int v2; // [rsp+20h] [rbp-18h]
- char v3[4]; // [rsp+24h] [rbp-14h] BYREF
-
- v2 = unknown_libname_104(v3, a1); // hash
- sub_180003AF0(a1);
- return v2;
- }
- // Microsoft VisualC 64bit universal runtime
- __int64 __fastcall unknown_libname_104(__int64 a1, __int64 a2)
- {
- return sub_180004B40(a2); // call hash
- }
- __int64 __fastcall sub_180004B40(__int64 a1)
- {
- __int64 *v1; // rax
- __int64 *v3; // [rsp+20h] [rbp-28h]
- char v4[8]; // [rsp+28h] [rbp-20h] BYREF
- char v5[24]; // [rsp+30h] [rbp-18h] BYREF
-
- v3 = (__int64 *)sub_1800059D0(a1, v4);
- v1 = (__int64 *)sub_180005A30(a1, v5);
- return sub_180005FB0(*v1, *v3); // call hash
- }
- __int64 __fastcall sub_180005FB0(__int64 a1, __int64 a2)
- {
- unsigned __int8 *v2; // rax
- __int64 v4; // [rsp+20h] [rbp-18h] BYREF
- __int64 v5; // [rsp+40h] [rbp+8h] BYREF
- __int64 v6; // [rsp+48h] [rbp+10h] BYREF
-
- v6 = a2;
- v5 = a1;
- v4 = 0i64;
- while ( (unsigned __int8)sub_180006100(&v5, &v6) )
- {
- v2 = (unsigned __int8 *)unknown_libname_107(&v5);
- sub_180006200(&v4, v2); // call hash
- std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>::operator++(&v5);
- }
- return v4;
- }
- __int64 __fastcall sub_180006200(__int64 *a1, unsigned __int8 *a2)
- {
- __int64 v2; // rax
- char v4[8]; // [rsp+20h] [rbp-18h] BYREF
-
- v2 = sub_180002E00(v4, *a2);
- return hash_180002D30(a1, v2);
-
- }
- __int64 __fastcall sub_180002D30(__int64 *a1, __int64 a2)
- {
- __int64 result; // rax
-
- *a1 ^= 0xC6A4A7935BD1E995ui64 * (((0xC6A4A7935BD1E995ui64 * a2) >> 47) ^ (0xC6A4A7935BD1E995ui64 * a2));
- *a1 *= 0xC6A4A7935BD1E995ui64;
- result = *a1 + 0xE6546B64i64;
- *a1 = result;
- return result;
- }
然后根据这个每3个字符一组进行爆破
- from itertools import product
- import string
- from base64 import b64encode
-
- c = [0x6C43B2A7,0x7954FD91,0xA3E9532,0xB87B5156,0xDA847742,0x2395E7F3,0xA679D954,0xE1FAAFF7]
-
- '''
- String_t0_intDll.dll sub_180002D30
- *a1 ^= 0xC6A4A7935BD1E995ui64 * (((0xC6A4A7935BD1E995ui64 * a2) >> 47) ^ (0xC6A4A7935BD1E995ui64 * a2));
- *a1 *= 0xC6A4A7935BD1E995ui64;
- result = *a1 + 0xE6546B64i64;
- *a1 = result;
- '''
- def hash(v):
- n = b64encode(''.join(v).encode())
- delta = 0xC6A4A7935BD1E995
- ret = 0
- for i in range(4):
- ret ^= delta * ((((delta * n[i])&0xffffffffffffffff)>>47) ^ (delta * n[i]))
- ret *= delta
- ret += 0xE6546B64
-
- return ret&0xffffffff
-
- chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_-!?'
- flag = ['']*8
- def bp():
- for v in product(chars, repeat=3): #
- m = hash(v)
- if m in c:
- flag[c.index(m)]= ''.join(v)
- print(c.index(m), v)
-
- bp()
- print(''.join(flag))
- #GXY{I_have_no_gir1_frieN
- #flag{I_have_no_gir1_frieNd}