目录

  1. 1. 前言
  2. 2. 逆向工程
    1. 2.1. 偷天换日
  3. 3. 数据加密
    1. 3.1. Ezenc

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

2025数字中国移动互联网(APP)安全积分争夺赛

2025/3/29 CTF线上赛 RC4
  |     |   总文章阅读量:

前言

全是 mobile 逆向

当代审做了((


逆向工程

偷天换日

拖 apk 进 jadx

image-20250329200152360

加载了 goodluck1 库

image-20250329200308508

找到 check 的位置,发现 check 方法也是从 lib 里加载的

image-20250329200353993

反编译 lib/arm64-v8a/libgoodluck1.so

Java_com_ctf_goodluck1_Check_check 函数

image-20250329200709419

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 段里

image-20250329201256531

把 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 字节码

image-20250329201528670

让 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 里调用):

image-20250329202029782

跟进 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 反编译

image-20250329202432130

base58 解码得到 flag:flag{j#n$j@m^,*1}


数据加密

Ezenc

apk 里面 check 的核心逻辑是 validinput 方法,同样是加载 so 得到

直接分析 libezenc.so

Java_com_n_ezenc_MainActivity_validateInput:

image-20250329203311188

这就是核心的加密逻辑,丢给 ds 分析出是 DFT。取出这里的 dbl_650 和 unk_E50 逆一下就好了,double 精度这里为 6 位,小端序

image-20250329203444831

image-20250329203526943

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!}