前言
全是 mobile 逆向
当代审做了((
逆向工程
偷天换日
拖 apk 进 jadx
加载了 goodluck1 库
找到 check 的位置,发现 check 方法也是从 lib 里加载的
反编译 lib/arm64-v8a/libgoodluck1.so
Java_com_ctf_goodluck1_Check_check 函数
sub_E5D4 函数
__int64 __fastcall sub_E5D4(_JNIEnv *a1, __int64 a2)
{
__int64 v2; // x10
__int64 v3; // x10
__int64 v4; // x10
__int64 v5; // x17
__int64 v6; // x17
__int64 v7; // x17
__int64 v8; // x17
int v10; // [xsp+Ch] [xbp-84h]
int v11; // [xsp+10h] [xbp-80h]
int v12; // [xsp+14h] [xbp-7Ch]
__int64 v13; // [xsp+38h] [xbp-58h]
int i; // [xsp+44h] [xbp-4Ch]
unsigned int v15; // [xsp+48h] [xbp-48h]
int v16; // [xsp+58h] [xbp-38h]
int v17; // [xsp+58h] [xbp-38h]
int v18; // [xsp+5Ch] [xbp-34h]
char *v19; // [xsp+60h] [xbp-30h]
__int64 v20; // [xsp+68h] [xbp-28h]
int ArrayLength; // [xsp+74h] [xbp-1Ch]
__int64 ByteArrayElements; // [xsp+78h] [xbp-18h]
ByteArrayElements = _JNIEnv::GetByteArrayElements(a1, a2, 0LL);
ArrayLength = _JNIEnv::GetArrayLength(a1, a2);
v20 = 4 * ((ArrayLength + 2) / 3);
v19 = (char *)operator new[](v20 + 1);
v18 = 0;
v16 = 0;
while ( v18 < ArrayLength )
{
v2 = v18++;
v12 = *(unsigned __int8 *)(ByteArrayElements + v2);
if ( v18 >= ArrayLength )
{
v11 = 0;
}
else
{
v3 = v18++;
v11 = *(unsigned __int8 *)(ByteArrayElements + v3);
}
if ( v18 >= ArrayLength )
{
v10 = 0;
}
else
{
v4 = v18++;
v10 = *(unsigned __int8 *)(ByteArrayElements + v4);
}
v15 = (v12 << 16) | (v11 << 8) | v10;
v5 = v16;
v17 = v16 + 1;
v19[v5] = aAbcdefghijklmn[(v15 >> 18) & 0x3F];
v6 = v17++;
v19[v6] = aAbcdefghijklmn[(v15 >> 12) & 0x3F];
v7 = v17++;
v19[v7] = aAbcdefghijklmn[(v15 >> 6) & 0x3F];
v8 = v17;
v16 = v17 + 1;
v19[v8] = aAbcdefghijklmn[v10 & 0x3F];
}
for ( i = 0; i < (3 - ArrayLength % 3) % 3; ++i )
v19[v20 - 1 - i] = 61;
v19[v20] = 0;
v13 = _JNIEnv::NewStringUTF(a1, v19);
_JNIEnv::ReleaseByteArrayElements(a1, a2, ByteArrayElements, 2LL);
if ( v19 )
operator delete[](v19);
return v13;
}
aAbcdefghijklmn DCB "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",0
是个 base64,不过没什么关系
重点看最底下 strcmp
的 byte_36040,按 x 查看交叉引用,有 datadiv_decode15146375311672927909
函数
_BYTE *datadiv_decode15146375311672927909()
{
_BYTE *result; // x0
unsigned int v52; // [xsp+Ch] [xbp-2C4h]
unsigned int v53; // [xsp+10h] [xbp-2C0h]
unsigned int v54; // [xsp+14h] [xbp-2BCh]
unsigned int v55; // [xsp+18h] [xbp-2B8h]
unsigned int v56; // [xsp+1Ch] [xbp-2B4h]
unsigned int v57; // [xsp+20h] [xbp-2B0h]
unsigned int v58; // [xsp+24h] [xbp-2ACh]
unsigned int v59; // [xsp+28h] [xbp-2A8h]
unsigned int v60; // [xsp+2Ch] [xbp-2A4h]
unsigned int v61; // [xsp+30h] [xbp-2A0h]
unsigned int v62; // [xsp+34h] [xbp-29Ch]
unsigned int v63; // [xsp+38h] [xbp-298h]
unsigned int v64; // [xsp+3Ch] [xbp-294h]
unsigned int v65; // [xsp+40h] [xbp-290h]
unsigned int v66; // [xsp+44h] [xbp-28Ch]
unsigned int v67; // [xsp+48h] [xbp-288h]
unsigned int v68; // [xsp+4Ch] [xbp-284h]
unsigned int v69; // [xsp+50h] [xbp-280h]
unsigned int v70; // [xsp+54h] [xbp-27Ch]
unsigned int v71; // [xsp+58h] [xbp-278h]
unsigned int v72; // [xsp+5Ch] [xbp-274h]
unsigned int v73; // [xsp+60h] [xbp-270h]
unsigned int v74; // [xsp+64h] [xbp-26Ch]
unsigned int v75; // [xsp+68h] [xbp-268h]
unsigned int v76; // [xsp+6Ch] [xbp-264h]
unsigned int v77; // [xsp+70h] [xbp-260h]
unsigned int v78; // [xsp+74h] [xbp-25Ch]
unsigned int v79; // [xsp+78h] [xbp-258h]
unsigned int v80; // [xsp+7Ch] [xbp-254h]
unsigned int v81; // [xsp+80h] [xbp-250h]
unsigned int v82; // [xsp+84h] [xbp-24Ch]
unsigned int v83; // [xsp+88h] [xbp-248h]
unsigned int v84; // [xsp+8Ch] [xbp-244h]
unsigned int v85; // [xsp+90h] [xbp-240h]
unsigned int v86; // [xsp+94h] [xbp-23Ch]
unsigned int v87; // [xsp+98h] [xbp-238h]
unsigned int v88; // [xsp+9Ch] [xbp-234h]
unsigned int v89; // [xsp+A0h] [xbp-230h]
unsigned int v90; // [xsp+A4h] [xbp-22Ch]
unsigned int v91; // [xsp+A8h] [xbp-228h]
signed int v92; // [xsp+ACh] [xbp-224h]
unsigned int v93; // [xsp+B0h] [xbp-220h]
unsigned int v94; // [xsp+B4h] [xbp-21Ch]
unsigned int v95; // [xsp+B8h] [xbp-218h]
unsigned int v96; // [xsp+BCh] [xbp-214h]
unsigned int v97; // [xsp+C0h] [xbp-210h]
unsigned int v98; // [xsp+C4h] [xbp-20Ch]
unsigned int v99; // [xsp+C8h] [xbp-208h]
unsigned int v100; // [xsp+CCh] [xbp-204h]
unsigned int v101; // [xsp+D0h] [xbp-200h]
unsigned int v102; // [xsp+D4h] [xbp-1FCh]
result = byte_36118;
v102 = 0;
do
asc_36010[v102] ^= 0x67u;
while ( v102++ < 0x10 );
v101 = 0;
do
byte_36024[v101] ^= 0x86u;
while ( v101++ < 8 );
v100 = 0;
do
byte_36030[v100] ^= 0x83u;
while ( v100++ < 4 );
v99 = 0;
do
byte_36040[v99] ^= 0xC5u;
while ( v99++ < 0x19 );
v98 = 0;
do
byte_36060[v98] ^= 0x71u;
while ( v98++ < 0x1A );
v97 = 0;
do
byte_36080[v97] ^= 0xBCu;
while ( v97++ < 0x16 );
v96 = 0;
do
byte_360A0[v96] ^= 0xC9u;
while ( v96++ < 0x1C );
v95 = 0;
do
byte_360C0[v95] ^= 0xC7u;
while ( v95++ < 0x13 );
v94 = 0;
do
byte_360E0[v94] ^= 0xE1u;
while ( v94++ < 0x19 );
v93 = 0;
do
byte_36100[v93] ^= 0xC6u;
while ( v93++ < 0x17 );
v92 = 0;
do
byte_36118[v92] = ~byte_36118[v92];
while ( (unsigned int)v92++ < 9 );
v91 = 0;
do
byte_36130[v91] ^= 0x66u;
while ( v91++ < 0x24 );
v90 = 0;
do
aSyorty[v90] ^= 0x3Du;
while ( v90++ < 0x20 );
v89 = 0;
do
byte_36184[v89] ^= 0x7Cu;
while ( v89++ < 6 );
v88 = 0;
do
byte_36190[v88] ^= 0xFDu;
while ( v88++ < 0x3D );
v87 = 0;
do
byte_361D0[v87] ^= 0xEAu;
while ( v87++ < 6 );
v86 = 0;
do
aClfpmkfAmlvglv[v86] ^= 2u;
while ( v86++ < 0x27 );
v85 = 0;
do
byte_36208[v85] ^= 0xCEu;
while ( v85++ < 9 );
v84 = 0;
do
byte_36214[v84] ^= 0xFAu;
while ( v84++ < 3 );
v83 = 0;
do
byte_36218[v83] ^= 0xE3u;
while ( v83++ < 4 );
v82 = 0;
do
byte_36220[v82] ^= 0xDEu;
while ( v82++ < 0x29 );
v81 = 0;
do
byte_36250[v81] ^= 0xBEu;
while ( v81++ < 0x13 );
v80 = 0;
do
byte_36264[v80] ^= 0x91u;
while ( v80++ < 4 );
v79 = 0;
do
byte_3626C[v79] ^= 0x7Fu;
while ( v79++ < 7 );
v78 = 0;
do
aJbbijbbiaxnfax[v78] ^= 0xDu;
while ( v78++ < 0x10 );
v77 = 0;
do
a8AA[v77] ^= 0x4Eu;
while ( v77++ < 0x13 );
v76 = 0;
do
a4[v76] ^= 0x40u;
while ( v76++ < 8 );
v75 = 0;
do
byte_362C0[v75] ^= 4u;
while ( v75++ < 0x18 );
v74 = 0;
do
byte_362DC[v74] ^= 0x33u;
while ( v74++ < 3 );
v73 = 0;
do
byte_362E0[v73] ^= 0xF9u;
while ( v73++ < 0x19 );
v72 = 0;
do
aNqmwjwqp[v72] ^= 0x3Eu;
while ( v72++ < 8 );
v71 = 0;
do
byte_36310[v71] ^= 0xD4u;
while ( v71++ < 0x14 );
v70 = 0;
do
byte_36330[v70] ^= 0xA3u;
while ( v70++ < 0x1E );
v69 = 0;
do
byte_36350[v69] ^= 0x8Cu;
while ( v69++ < 0xF );
v68 = 0;
do
aHjLcnCNkj[v68] ^= 0x2Fu;
while ( v68++ < 0xE );
v67 = 0;
do
byte_36370[v67] ^= 0x81u;
while ( v67++ < 0x19 );
v66 = 0;
do
a1H443H[v66] ^= 0x47u;
while ( v66++ < 0x1D );
v65 = 0;
do
byte_363B0[v65] ^= 0xA7u;
while ( v65++ < 8 );
v64 = 0;
do
byte_363C0[v64] ^= 0xC8u;
while ( v64++ < 0x1B );
v63 = 0;
do
a6O3934O[v63] ^= 0x40u;
while ( v63++ < 0x19 );
v62 = 0;
do
byte_363FC[v62] ^= 0xCu;
while ( v62++ < 0xB );
v61 = 0;
do
byte_36410[v61] ^= 0xCCu;
while ( v61++ < 0x24 );
v60 = 0;
do
byte_36440[v60] ^= 0xB4u;
while ( v60++ < 0x24 );
v59 = 0;
do
byte_36468[v59] ^= 0x29u;
while ( v59++ < 6 );
v58 = 0;
do
byte_36470[v58] ^= 0x71u;
while ( v58++ < 0x2F );
v57 = 0;
do
aUbuAXUffumxG[v57] ^= 0x14u;
while ( v57++ < 0x13 );
v56 = 0;
do
byte_364B4[v56] ^= 0xC5u;
while ( v56++ < 3 );
v55 = 0;
do
byte_364C0[v55] ^= 0xA0u;
while ( v55++ < 0x15 );
v54 = 0;
do
byte_364D8[v54] ^= 0x31u;
while ( v54++ < 7 );
v53 = 0;
do
byte_364E0[v53] ^= 0xFBu;
while ( v53++ < 0x15 );
v52 = 0;
do
byte_364F8[v52] ^= 0xC3u;
while ( v52++ < 3 );
return result;
}
是一大坨异或和一个取反,每组 byte 都在 data 段里
把 byte 全部提取出来并且做异或or取反
def decrypt_data(data, operation, xor_value=None, count=None):
if count is not None and count <= len(data):
data = data[:count]
if operation == 'xor' and xor_value is not None:
return bytes([b ^ xor_value for b in data])
elif operation == 'not':
return bytes([(~b) & 0xFF for b in data])
return None
encrypted_data = {
'byte_36024': bytes([0xE1, 0xE3, 0xF2, 0xC4, 0xFF, 0xF2, 0xE3, 0xF5, 0x86, 0,0,0]),
'byte_36030': bytes([0xAB,0xAA,0xD8,0xC1,0x83] + [0]*11),
'byte_36040': bytes([0xA8,0xBD,0xAD,0x9F,0xF6,0xB1,0xBF,0x8C,0x90,0x87,0xAB,0x9F,0x96,0x94,0xA9,0xA4,0x8D,0x8F,0xB0,0x8F,0x81,0x87,0xFC,0xE4,0xE4,0xC5] + [0]*6),
'byte_36060': bytes([0x10,0x1F,0x15,3,0x1E,0x18,0x15,0x5E,0x10,1,1,0x5E,0x30,0x12,5,0x18,7,0x18,5,8,0x25,0x19,3,0x14,0x10,0x15,0x71] + [0]*5),
'byte_36080': bytes([0xCF,0xFF,0xC9,0xCE,0xCE,0xD9,0xD2,0xC8,0xFD,0xDF,0xC8,0xD5,0xCA,0xD5,0xC8,0xC5,0xE8,0xD4,0xCE,0xD9,0xDD,0xD8,0xBC] + [0]*9),
'byte_360A0': bytes([0x85,0xA8,0xA7,0xAD,0xBB,0xA6,0xA0,0xAD,0xE6,0xA8,0xB9,0xB9,0xE6,0x88,0xAA,0xBD,0xA0,0xBF,0xA0,0xBD,0xB0,0x9D,0xA1,0xBB,0xAC,0xA8,0xAD,0xF2,0xC9] + [0]*3),
'byte_360C0': bytes([0xAA,0x8E,0xA9,0xAE,0xB3,0xAE,0xA6,0xAB,0x86,0xB7,0xB7,0xAB,0xAE,0xA4,0xA6,0xB3,0xAE,0xA8,0xA9,0xC7] + [0]*12),
'byte_360E0': bytes([0xAD,0x80,0x8F,0x85,0x93,0x8E,0x88,0x85,0xCE,0x80,0x91,0x91,0xCE,0xA0,0x91,0x91,0x8D,0x88,0x82,0x80,0x95,0x88,0x8E,0x8F,0xDA,0xE1] + [0]*6),
'byte_36100': bytes([0xA7,0xA8,0xA2,0xB4,0xA9,0xAF,0xA2,0xE9,0xA7,0xB6,0xB6,0xE9,0x87,0xB6,0xB6,0xAA,0xAF,0xA5,0xA7,0xB2,0xAF,0xA9,0xA8,0xC6]),
'byte_36118': bytes([0x98,0x9A,0x8B,0xBE,0x8C,0x8C,0x9A,0x8B,0x8C,0xFF] + [0]*14),
'byte_36130': bytes([0x4E,0x4F,0x2A,7,8,2,0x14,9,0xF,2,0x49,5,9,8,0x12,3,8,0x12,0x49,0x14,3,0x15,0x49,0x27,0x15,0x15,3,0x12,0x2B,7,8,7,1,3,0x14,0x5D,0x66] + [0]*11),
'aSyorty': bytes([0x5C,0x53,0x59,0x4F,0x52,0x54,0x59]),
'byte_36184': bytes([0x13,0x0C,0x19,0x12,0x3A,0x18,0x7C] + [0]*5),
'byte_36190': bytes([0xD5,0xB1,0x97,0x9C,0x8B,0x9C,0xD2,0x91,0x9C,0x93,0x9A,0xD2,0xAE,0x89,0x8F,0x94,0x93,0x9A,0xC6,0xD4,0xB1,0x9C,0x93,0x99,0x8F,0x92,0x94,0x99,0xD2,0x9E,0x92,0x93,0x89,0x98,0x93,0x89,0xD2,0x8F,0x98,0x8E,0xD2,0xBC,0x8E,0x8E,0x98,0x89,0xBB,0x94,0x91,0x98,0xB9,0x98,0x8E,0x9E,0x8F,0x94,0x8D,0x89,0x92,0x8F,0xC6,0xFD] + [0]*2),
'byte_361D0': bytes([0x89,0x89,0xC4,0x8E,0x8B,0x9E,0xEA] + [0]*9),
'aClfpmkfAmlvglv': bytes(b"clfpmkf-amlvglv-pgq-CqqgvDkngFgqapkrvmp") + bytes([0x02]),
'byte_36208': bytes([0xA9,0xAB,0xBA,0x82,0xAB,0xA0,0xA9,0xBA,0xA6,0xCE,0,0]),
'byte_36214': bytes([0xD2,0xD3,0xB0,0xFA]),
'byte_36218': bytes([0x8C,0x93,0x86,0x8D,0xE3] + [0]*3),
'byte_36220': bytes([0xF6,0x92,0xB4,0xBF,0xA8,0xBF,0xF1,0xB2,0xBF,0xB0,0xB9,0xF1,0x8D,0xAA,0xAC,0xB7,0xB0,0xB9,0xE5,0xF7,0x92,0xB4,0xBF,0xA8,0xBF,0xF1,0xB7,0xB1,0xF1,0x97,0xB0,0xAE,0xAB,0xAA,0x8D,0xAA,0xAC,0xBB,0xBF,0xB3,0xE5,0xDE] + [0]*6),
'byte_36250': bytes([0xD4,0xDF,0xC8,0xDF,0x91,0xD7,0xD1,0x91,0xF7,0xD0,0xCE,0xCB,0xCA,0xED,0xCA,0xCC,0xDB,0xDF,0xD3,0xBE]),
'byte_36264': bytes([0xE3,0xF4,0xF0,0xF5,0x91] + [0]*3),
'byte_3626C': bytes([0x57,0x24,0x3D,0x36,0x36,0x56,0x36,0x7F] + [0]*12),
'aJbbijbbiaxnfax': bytes(b"jbbijbbiaxnfaxnf") + bytes([0x0D,0x00]),
'a8AA': bytes([0x24,0x2F,0x38,0x2F,0x61,0x20,0x27,0x21,0x61,0x0c,0x37,0x3a,0x2b,0x0c,0x3b,0x28,0x28,0x2b,0x3c,0x4e]),
'byte_362C0': bytes([0x2C,0x4D,0x2D,0x48,0x6E,0x65,0x72,0x65,0x2B,0x6A,0x6D,0x6B,0x2B,0x46,0x7D,0x70,0x61,0x46,0x71,0x62,0x62,0x61,0x76,0x3F,0x04] + [0]*4),
'byte_362DC': bytes([0x43,0x46,0x47,0x33]),
'byte_362E0': bytes([0xD1,0xA2,0xBB,0xD0,0xB5,0x93,0x98,0x8F,0x98,0xD6,0x97,0x90,0x96,0xD6,0xBB,0x80,0x8D,0x9C,0xBB,0x8C,0x9F,0x9F,0x9C,0x8B,0xC2,0xF9] + [0]*5),
'aNqmwjwqp': bytes(b"NQMWJWQP>") + bytes([0x00]),
'byte_36310': bytes([0xFC,0x9D,0xFD,0x98,0xBE,0xB5,0xA2,0xB5,0xFB,0xBA,0xBD,0xBB,0xFB,0x96,0xA1,0xB2,0xB2,0xB1,0xA6,0xEF,0xD4] + [0]*11),
'byte_36330': bytes([0xC0,0xCC,0xCE,0x8C,0xC0,0xD7,0xC5,0x8C,0xC4,0xCC,0xCC,0xC7,0xCF,0xD6,0xC0,0xC8,0x92,0x8C,0xEE,0xC2,0xCA,0xCD,0xE2,0xC0,0xD7,0xCA,0xD5,0xCA,0xD7,0xDA,0xA3,0x00]),
'byte_36350': bytes([0xE6,0xED,0xFA,0xED,0xA3,0xE0,0xED,0xE2,0xEB,0xA3,0xCF,0xE0,0xED,0xFF,0xFF,0x8C]),
'aHjLcnCNkj': bytes(b"HJ[lCN\\\\c@NKJ]/") + bytes([0x00]),
'byte_36370': bytes([0xA9, 0xA8, 0xCD, 0xEB, 0xE0, 0xF7, 0xE0, 0xAE, 0xED, 0xE0, 0xEF, 0xE6, 0xAE, 0xC2, 0xED, 0xE0, 0xF2,0xF2, 0xCD, 0xEE, 0xE0, 0xE5, 0xE4, 0xF3, 0xBA, 0x81] + [0]*6),
'a1H443H': bytes([0x23,0x26,0x2B,0x31,0x2E,0x2C,0x68,0x34,0x3E,0x34,0x33,0x22,0x2A,0x68,0x17,0x26,0x33,0x2f,0x04,0x2b,0x26,0x34,0x34,0x0b,0x28,0x26,0x23,0x22,0x35,0x47,0,0]),
'byte_363B0': bytes([0xD7,0xC6,0xD3,0xCF,0xEB,0xCE,0xD4,0xD3,0xA7] + [0]*7),
'byte_363C0': bytes([0x84,0xAC,0xA9,0xA4,0xBE,0xA1,0xA3,0xE7,0xBB,0xB1,0xBB,0xBC,0xAD,0xA5,0xE7,0x8C,0xAD,0xB0,0x98,0xA9,0xBC,0xA0,0x84,0xA1,0xBB,0xBC,0xF3,0xC8] + [0]*4),
'a6O3934O': bytes([0x24,0x21,0x2C,0x36,0x29,0x2B,0x6F,0x33,0x39,0x33,0x34,0x25,0x2D,0x6F,0x04,0x25,0x38,0x10,0x21,0x34,0x28,0x0c,0x29,0x33,0x34,0x40,0,0]),
'byte_363FC': bytes([0x68,0x69,0x74,0x49,0x60,0x69,0x61,0x69,0x62,0x78,0x7F,0x0C] + [0]*8),
'byte_36410': bytes([0x97,0x80,0xA8,0xAD,0xA0,0xBA,0xA5,0xA7,0xE3,0xBF,0xB5,0xBF,0xB8,0xA9,0xA1,0xE3,0x88,0xA9,0xB4,0x9C,0xAD,0xB8,0xA4,0x80,0xA5,0xBF,0xB8,0xE8,0x89,0xA0,0xA9,0xA1,0xA9,0xA2,0xB8,0xF7,0xCC] + [0]*11),
'byte_36440': bytes([0xD0,0xD5,0xD8,0xC2,0xDD,0xDF,0x9B,0xC7,0xCD,0xC7,0xC0,0xD1,0xD9,0x9B,0xFD,0xDA,0xF9,0xD1,0xD9,0xDB,0xC6,0xCD,0xF0,0xD1,0xCC,0xF7,0xD8,0xD5,0xC7,0xC7,0xF8,0xDB,0xD5,0xD0,0xD1,0xC6,0xB4] + [0]*3),
'byte_36468': bytes([0x15,0x40,0x47,0x40,0x5D,0x17,0x29,0x00]),
'byte_36470': bytes([0x59,0x3D,0x1B,0x10,7,0x10,0x5E,0x1F,0x18,0x1E,0x5E,0x33,8,5,0x14,0x33,4,0x17,0x17,0x14,3,0x4A,0x3D,0x1B,0x10,7,0x10,0x5E,0x1D,0x10,0x1F,0x16,0x5E,0x32,0x1D,0x10,2,2,0x3D,0x1E,0x10,0x15,0x14,3,0x4A,0x58,0x27,0x71]),
'aUbuAXUffumxG': bytes(b"~ubu;a`}x;UffumX}g`") + bytes([0x14]),
'byte_364B4': bytes([0xA4,0xA1,0xA1,0xC5] + [0]*8),
'byte_364C0': bytes([0x88,0xEC,0xCA,0xC1,0xD6,0xC1,0x8F,0xCC,0xC1,0xCE,0xC7,0x8F,0xEF,0xC2,0xCA,0xC5,0xC3,0xD4,0x9B,0x89,0xFA,0xA0] + [0]*2),
'byte_364D8': bytes([0x45,0x5E,0x70,0x43,0x43,0x50,0x48,0x31]),
'byte_364E0': bytes([0xD3,0xD2,0xA0,0xB7,0x91,0x9A,0x8D,0x9A,0xD4,0x97,0x9A,0x95,0x9C,0xD4,0xB4,0x99,0x91,0x9E,0x98,0x8F,0xC0,0xFB] + [0]*2),
'byte_364F8': bytes([0xEB,0xEA,0x95,0xC3] + [0]*4)
}
# 完整解密配置(与C代码完全对应)
decryption_config = [
# 原始配置
('byte_36024', 'xor', 0x86, 8+1),
('byte_36030', 'xor', 0x83, 4+1),
('byte_36040', 'xor', 0xC5, 0x19+1),
('byte_36060', 'xor', 0x71, 0x1A+1),
('byte_36080', 'xor', 0xBC, 0x16+1),
('byte_360A0', 'xor', 0xC9, 0x1C+1),
('byte_360C0', 'xor', 0xC7, 0x13+1),
('byte_360E0', 'xor', 0xE1, 0x19+1),
('byte_36100', 'xor', 0xC6, 0x17+1),
('byte_36118', 'not', None, 9+1),
('byte_36130', 'xor', 0x66, 0x24+1),
('aSyorty', 'xor', 0x3D, 0x20+1),
# 新增配置
('byte_36184', 'xor', 0x7C, 6+1),
('byte_36190', 'xor', 0xFD, 0x3D+1),
('byte_361D0', 'xor', 0xEA, 6+1),
('aClfpmkfAmlvglv', 'xor', 0x02, 0x27+1),
('byte_36208', 'xor', 0xCE, 9+1),
('byte_36214', 'xor', 0xFA, 3+1),
('byte_36218', 'xor', 0xE3, 4+1),
('byte_36220', 'xor', 0xDE, 0x29+1),
('byte_36250', 'xor', 0xBE, 0x13+1),
('byte_36264', 'xor', 0x91, 4+1),
('byte_3626C', 'xor', 0x7F, 7+1),
('aJbbijbbiaxnfax', 'xor', 0x0D, 0x10+1),
('a8AA', 'xor', 0x4E, 0x13+1),
('byte_362C0', 'xor', 0x04, 0x18+1),
('byte_362DC', 'xor', 0x33, 3+1),
('byte_362E0', 'xor', 0xF9, 0x19+1),
('aNqmwjwqp', 'xor', 0x3E, 8+1),
('byte_36310', 'xor', 0xD4, 0x14+1),
('byte_36330', 'xor', 0xA3, 0x1E+1),
('byte_36350', 'xor', 0x8C, 0xF+1),
('aHjLcnCNkj', 'xor', 0x2F, 0xE+1),
('byte_36370', 'xor', 0x81, 0x19+1),
('a1H443H', 'xor', 0x47, 0x1D+1),
('byte_363B0', 'xor', 0xA7, 8+1),
('byte_363C0', 'xor', 0xC8, 0x1B+1),
('a6O3934O', 'xor', 0x40, 0x19+1),
('byte_363FC', 'xor', 0x0C, 0xB+1),
('byte_36410', 'xor', 0xCC, 0x24+1),
('byte_36440', 'xor', 0xB4, 0x24+1),
('byte_36468', 'xor', 0x29, 6+1),
('byte_36470', 'xor', 0x71, 0x2F+1),
('aUbuAXUffumxG', 'xor', 0x14, 0x13+1),
('byte_364B4', 'xor', 0xC5, 3+1),
('byte_364C0', 'xor', 0xA0, 0x15+1),
('byte_364D8', 'xor', 0x31, 7+1),
('byte_364E0', 'xor', 0xFB, 0x15+1),
('byte_364F8', 'xor', 0xC3, 3+1)
]
if __name__ == '__main__':
results = {}
for config in decryption_config:
key = config[0]
if data := encrypted_data.get(key):
decoded = decrypt_data(data, *config[1:])
if decoded:
try:
clean = decoded.strip(b'\x00').decode('utf-8')
results[key] = f"STRING: {clean}"
except:
results[key] = f"HEX: {decoded.hex()}"
print("{:<20} {:<60}".format("Data Name", "Decrypted Result"))
print("="*85)
for k, v in sorted(results.items()):
print("{:<20} {:<60}".format(k, v))
得到一堆 java 字节码
让 ds 理了个大概的逻辑:
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class MainActivity extends Activity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
// 1. 从Assets读取加密Dex
AssetManager assets = getAssets();
AssetFileDescriptor fd = assets.openFd("cc.dat");
InputStream in = fd.createInputStream();
byte[] encryptedDex = new byte[(int) fd.getLength()];
in.read(encryptedDex);
// 2. 异或解密Dex
byte[] decryptedDex = xorDecrypt(encryptedDex, "goodgoodluckluck");
// 3. 创建内存Dex加载器
ByteBuffer dexBuffer = ByteBuffer.wrap(decryptedDex);
ClassLoader parentLoader = getClass().getClassLoader();
Class<?> inMemoryLoaderClass = Class.forName("dalvik.system.InMemoryDexClassLoader");
Object dexLoader = inMemoryLoaderClass.getConstructor(ByteBuffer.class, ClassLoader.class)
.newInstance(dexBuffer, parentLoader);
// 4. 反射注入Dex到类路径
Object pathList = getField(parentLoader, "pathList");
Object[] oldElements = (Object[]) getField(pathList, "dexElements");
Method addMethod = pathList.getClass().getMethod("add", ByteBuffer.class);
Object newElement = addMethod.invoke(pathList, dexBuffer);
Object[] newElements = Arrays.copyOf(oldElements, oldElements.length + 1);
newElements[oldElements.length] = newElement;
setField(pathList, "dexElements", newElements);
// 5. 加载并执行隐藏类
Class<?> hiddenClass = ((ClassLoader) dexLoader).loadClass("com.ctf.goodluck1.MainActivity");
Method checkMethod = hiddenClass.getMethod("check", Context.class);
String flag = (String) checkMethod.invoke(null, this);
Log.d("FLAG", "Flag: " + flag);
} catch (Exception e) {
e.printStackTrace();
}
}
private byte[] xorDecrypt(byte[] data, String key) {
byte[] keyBytes = key.getBytes();
byte[] result = new byte[data.length];
for (int i = 0; i < data.length; i++) {
result[i] = (byte) (data[i] ^ keyBytes[i % keyBytes.length]);
}
return result;
}
private Object getField(Object obj, String fieldName) throws Exception {
Field f = obj.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
return f.get(obj);
}
private void setField(Object obj, String fieldName, Object value) throws Exception {
Field f = obj.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(obj, value);
}
}
总之就是从 assets/cc.dat 中读取加密的dex,密钥为 goodgoodluckluck
然后找一下加密 dex 的算法,这里找一下密钥 aJbbijbbiaxnfax 在哪被调用的即可,在 sub_DD38 中(顺带一提往上走就是 JNI_Onload 里调用):
跟进 sub_DAD8
__int64 __fastcall sub_DAD8(__int64 a1, unsigned __int64 a2, __int64 a3, __int64 a4)
{
__int64 result; // x0
unsigned int v5; // w8
int v6; // w8
int v7; // w9
char v8; // [xsp+2Ch] [xbp-154h]
unsigned __int64 i; // [xsp+30h] [xbp-150h]
unsigned int v10; // [xsp+40h] [xbp-140h]
unsigned int v11; // [xsp+44h] [xbp-13Ch]
char v14[264]; // [xsp+68h] [xbp-118h] BYREF
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
result = sub_D918(v14, a3, a4);
v11 = 0;
v10 = 0;
for ( i = 0LL; i < a2; ++i )
{
if ( (int)(v11 + 1) >= 0 )
v5 = v11 + 1;
else
v5 = v11 + 256;
v11 = v11 + 1 - (v5 & 0xFFFFFF00);
v6 = v10 + (unsigned __int8)v14[v11];
v7 = v6 + 255;
if ( v6 >= 0 )
v7 = v10 + (unsigned __int8)v14[v11];
v10 = v6 - (v7 & 0xFFFFFF00);
v8 = v14[v11];
v14[v11] = v14[v10];
v14[v10] = v8;
*(_BYTE *)(a1 + i) ^= v14[(unsigned __int8)(v14[v11] + v14[v10])];
}
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return result;
}
经典 rc4,于是解密脚本出来了,重新生成未加密的 apk
import sys
import zipfile
from io import BytesIO
def rc4_decrypt(data: bytes, key: bytes) -> bytes:
"""RC4解密算法"""
# 初始化S盒
S = list(range(256))
j = 0
# KSA阶段
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# PRGA阶段
i = j = 0
decrypted = bytearray()
for byte in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
decrypted.append(byte ^ k)
return bytes(decrypted)
def build_apk(decrypted_dex: bytes, output_path: str):
"""构建最小化APK"""
# 基础AndroidManifest.xml (默认包名)
manifest = b'''<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ctf.goodluck1">
<application android:label="CTF App">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>'''
with zipfile.ZipFile(output_path, 'w') as apk:
# 添加解密后的Dex
apk.writestr('classes.dex', decrypted_dex)
# 添加AndroidManifest.xml(需编译为二进制格式)
from androguard.core.bytecodes.axml import AXMLPrinter
from xml.dom import minidom
# 将XML转换为二进制格式
axml = AXMLPrinter(manifest).get_buff()
apk.writestr('AndroidManifest.xml', axml)
# 添加空资源文件(避免安装失败)
apk.writestr('resources.arsc', b'')
apk.writestr('res/empty.txt', b'') # 创建空目录
def main():
if len(sys.argv) != 4:
print("Usage: python decrypt_rc4.py <encrypted_cc.dat> <key> <output.apk>")
sys.exit(1)
input_file = sys.argv[1]
key = sys.argv[2]
output_apk = sys.argv[3]
# 读取加密数据
with open(input_file, 'rb') as f:
encrypted_data = f.read()
# RC4解密
decrypted_dex = rc4_decrypt(encrypted_data, key.encode())
# 验证Dex文件头
if decrypted_dex[:4] != b'dex\n':
print("[!] 解密失败:无效的Dex文件头")
sys.exit(2)
# 构建APK
build_apk(decrypted_dex, output_apk)
print(f"[+] APK生成成功:{output_apk}")
if __name__ == "__main__":
main()
将新生成的 apk 拖进 jadx 反编译
base58 解码得到 flag:flag{j#n$j@m^,*1}
数据加密
Ezenc
apk 里面 check 的核心逻辑是 validinput 方法,同样是加载 so 得到
直接分析 libezenc.so
Java_com_n_ezenc_MainActivity_validateInput:
这就是核心的加密逻辑,丢给 ds 分析出是 DFT。取出这里的 dbl_650 和 unk_E50 逆一下就好了,double 精度这里为 6 位,小端序
import struct
hex_data = [
'0xea', '0x5b', '0xe6', '0x74', '0x59', '0x5a', '0x59', '0xc0',
'0xf4', '0xdf', '0x83', '0xd7', '0x2e', '0x5d', '0x13', '0x40',
'0x68', '0xcc', '0x24', '0xea', '0x5', '0x79', '0x40', '0xc0',
'0x40', '0xbd', '0x19', '0x35', '0x5f', '0x65', '0xf1', '0x3f',
'0xdb', '0xdf', '0xd9', '0x1e', '0xbd', '0x35', '0x4e', '0xc0',
'0x57', '0x5e', '0xf2', '0x3f', '0xf9', '0x8b', '0x38', '0xc0',
'0x4a', '0x98', '0x69', '0xfb', '0x57', '0x21', '0x63', '0x40',·
'0x54', '0x8c', '0xf3', '0x37', '0xa1', '0x1c', '0x4b', '0x40',
'0x35', '0xb3', '0x96', '0x2', '0xd2', '0x1e', '0x7', '0x40',
'0xff', '0x3e', '0xe3', '0xc2', '0x1', '0x48', '0x64', '0x40',
'0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x80',
'0xff', '0x3e', '0xe3', '0xc2', '0x1', '0x48', '0x64', '0xc0',
'0x35', '0xb3', '0x96', '0x2', '0xd2', '0x1e', '0x7', '0xc0',
'0x54', '0x8c', '0xf3', '0x37', '0xa1', '0x1c', '0x4b', '0xc0',
'0x4a', '0x98', '0x69', '0xfb', '0x57', '0x21', '0x63', '0xc0',
'0x57', '0x5e', '0xf2', '0x3f', '0xf9', '0x8b', '0x38', '0x40',
'0xdb', '0xdf', '0xd9', '0x1e', '0xbd', '0x35', '0x4e', '0x40',
'0x40', '0xbd', '0x19', '0x35', '0x5f', '0x65', '0xf1', '0xbf',
'0x68', '0xcc', '0x24', '0xea', '0x5', '0x79', '0x40', '0x40',
'0xf4', '0xdf', '0x83', '0xd7', '0x2e', '0x5d', '0x13', '0xc0',
'0xea', '0x5b', '0xe6', '0x74', '0x59', '0x5a', '0x59', '0x40'
]
# 转换函数(处理带0x前缀的十六进制字符串)
def hex_to_double(hex_list):
results = []
for i in range(0, len(hex_list), 8):
byte_group = hex_list[i:i+8]
byte_str = bytes([int(x, 16) for x in byte_group])
value = struct.unpack('<d', byte_str)[0] # 小端序
results.append(value)
return results
# 执行转换
dbl_650 = hex_to_double(hex_data)
# 打印验证
output = []
for i, val in enumerate(dbl_650):
output.append(eval(f"{val:20.6f}"))
print(output)
imag_part 开头填个 0,编写exp:
import numpy as np
# 获取到的预设值
real_part = [2019.0, 136.471471, -8.025564, -102.391288, 33.455527, 184.705093, 29.993235, 82.585939, -75.967191,1.628784, -125.456008, -89.0, -125.456008, 1.628784, -75.967191, 82.585939, 29.993235, 184.705093,33.455527, -102.391288, -8.025564, 136.471471] # dbl_650数组的22个值
imag_part = [0,-101.41171, 4.840999, -32.945493, 1.087249, -60.419834, -24.546772, 153.04199, 54.22367, 2.890049, 162.250215, -0.0, -162.250215, -2.890049, -54.22367, -153.04199, 24.546772, 60.419834, -1.087249, 32.945493, -4.840999, 101.41171]
# 组合成复数
freq_domain = np.array(real_part) + 1j * np.array(imag_part)
# 执行逆DFT
time_domain = np.fft.ifft(freq_domain)
# 取实部并转换为ASCII字符
flag = ''.join([chr(int(round(x.real))) for x in time_domain])
print(flag)
得到 flag:flag{th3_df7_S0_3asy!}