jeb反编译
java层没东西,核心代码在native层。
ida分析so
静态注册的函数,直接可以找到check函数
jstring __fastcall Java_com_example_myapplication_MainActivity_check(JNIEnv *a1, int a2, int a3)
{char *v4; // r0int v5; // r4_BYTE *v6; // r2int v7; // r2unsigned int i; // r6unsigned int v9; // r2int v10; // r0_BYTE *v11; // r1jstring v12; // r4_BYTE *v14; // r1int v15; // r5jstring (*NewStringUTF)(JNIEnv *, const char *); // r2int v17; // r0unsigned __int8 v18[12]; // [sp+0h] [bp-A0h] BYREFunsigned __int8 v19; // [sp+Ch] [bp-94h] BYREF_BYTE v20[7]; // [sp+Dh] [bp-93h] BYREF_BYTE *v21; // [sp+14h] [bp-8Ch]char v22[12]; // [sp+18h] [bp-88h] BYREFchar v23[12]; // [sp+24h] [bp-7Ch] BYREFchar v24[48]; // [sp+30h] [bp-70h] BYREFchar v25[12]; // [sp+60h] [bp-40h] BYREFchar v26[12]; // [sp+6Ch] [bp-34h] BYREFunsigned __int8 v27; // [sp+78h] [bp-28h] BYREF_BYTE v28[7]; // [sp+79h] [bp-27h] BYREF_BYTE *v29; // [sp+80h] [bp-20h]v4 = (*a1)->GetStringUTFChars(a1, a3, 0);std::string::basic_string<decltype(nullptr)>(&v27, v4);sub_967E(v24, &v27, 0, 5); // 输入字符串,下标0,长度5,即前5位v5 = sub_9670(v24, "flag{"); // 判断前5位是否是flag{std::string::~string(v24);if ( v5 )goto LABEL_15;v6 = v29;if ( (v27 & 1) == 0 )v6 = v28;if ( v6[37] != '}' ) // 判断最后一位是否是 }goto LABEL_15;v7 = *&v28[3];if ( (v27 & 1) == 0 )v7 = v27 >> 1;if ( v7 == 38 ) // 判断输入字符串长度是否是38 ,即 flag{32位} ,主要就是除去flag{} 还剩的32位{sub_967E(v26, &v27, 5, 16); // 输入字符串,下标5,长度16,即除去flag{ 的前16位sub_967E(v25, &v27, 21, 16); // 输入字符串的下标21 长度 16,即后16位std::string::basic_string(v23, v26);std::string::basic_string(v22, v25);CheckM::CheckM(v24, v23, v22); //CheckM 函数处理std::string::~string(v22);std::string::~string(v23);CheckM::check1(&v19); //Check1函数处理for ( i = 0; ; i += 8 ){v9 = *&v20[3];v10 = v19 & 1;if ( !v10 )v9 = v19 >> 1;if ( i >= v9 )break;v11 = v21;if ( !v10 )v11 = v20;encrypt(&v11[i], &k); // TEA加密}v14 = v21;if ( !v10 )v14 = v20;base64_encode(v18, v14); // base64v15 = sub_97EC(v18, "e)n*pNe%PQy!^oS(@HtkUu+Cd$#hmmK&ieytiWwYkIA=");// 判断加密后的字符是否等于指定字符串std::string::~string(v18);NewStringUTF = (*a1)->NewStringUTF;if ( v15 )v17 = NewStringUTF(a1, "Congratulations,you found it!!!\n");elsev17 = NewStringUTF(a1, "error");v12 = v17;std::string::~string(&v19);CheckM::~CheckM(v24);std::string::~string(v25);std::string::~string(v26);}else{
LABEL_15:v12 = (*a1)->NewStringUTF(a1, "error");}std::string::~string(&v27);return v12;
}
初步分析代码流程
通过初步的分析可以看出大概的流程,得到的信息:
1、flag 长度是 38位,flag{xxx} , {}内的字符串是32位,主要的加密字符串是{}里面的32位
2、32位字符串经过了CheckM 和 check1 函数处理。
3、经过了一个encrypt 函数处理,识别出来是TEA加密。
4、经过base64_encode函数 ,base64编码处理。
5、最后与指定的密文字符串比较,密文字符串是"e)n*pNe%PQy!^oS(@HtkUu+Cd$#hmmK&ieytiWwYkIA="
frida hook 分析代码流程
先上sktrace hook 函数打印一下寄存器的值
python sktrace.py -m attach -l libnative-lib.so -i 0xF5D4 com.example.myapplication
对照指令和寄存器的值,再来分析代码就清晰很多
1、第1部分代码分析
判断输入字符串前5位是否是“flag{” ,中间32位分成前16位和后16位, 最后一位是否是 “}”
v56 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);v4 = (char *)(*a1)->GetStringUTFChars(a1, a3, 0LL);std::string::basic_string<decltype(nullptr)>(&v53, v4);v5 = (unsigned __int64)(unsigned __int8)v53 >> 1;// v54 = 0x31 , v54 >> 1 = 0x31 >> 1 = 0x18if ( (v53 & 1) != 0 ) // v54 & 1 = 1v5 = v54; // 0x26if ( v5 >= 5 )v6 = 5LL; // v7 = 5elsev6 = v5;v7 = 2 * v6; // v8 = 0xav45 = 0LL;ptr = 0LL;result = (unsigned __int8)(2 * v6); // v45 = 0xaif ( v6 ){__memcpy_chk(); // 复制v6 的 v7长度到 v45+1 ,也就是复制输入字符串的前5位长度v7 = result;}*((_BYTE *)&result + v6 + 1) = 0;v8 = v45;if ( (v7 & 1) == 0 ) // v8 = 0xa , v8 & 1 = 0 这里判断为truev8 = v7 >> 1; // v9 = 0xa >> 1 = 5if ( v8 == 5 ){if ( (v7 & 1) != 0 ) // 这里为false 走else,v10 = 输入字符串的前5位v9 = (char *)ptr;elsev9 = (char *)&result + 1;v10 = memcmp(v9, "flag{", 5u) != 0; // 输入字符串前5位与 “flag{” 比较if ( (v7 & 1) == 0 )goto LABEL_18; // 跳到 label 21goto LABEL_17;}v10 = 1;if ( (v7 & 1) != 0 )
LABEL_17:operator delete(ptr);
LABEL_18:if ( v10 )goto LABEL_44; // 输入字符串前5位不是"flag{" 走 label 43v11 = v55; // v12 = x8 = 0x0->0x79168ba750 , 是输入字符串
// 对应指令.text:000000000000F6FC A8 03 5B F8 LDUR X8, [X29,#var_50]
//inlinehook打印x8 的值即可验证if ( (v53 & 1) == 0 ) // 前面有v54 & 1 = 1v11 = (char *)&v53 + 1;if ( v11[37] != '}' || ((v53 & 1) != 0 ? (v12 = v54) : (v12 = (unsigned __int64)(unsigned __int8)v53 >> 1), v12 != 38) )// 判断输入字符串最后一位 是 “}”{
LABEL_44: // 验证失败逻辑,显示errorv23 = (*a1)->NewStringUTF(a1, "error");goto LABEL_45;}v33[0] = 32; // v33 是长度17的数值,v33[0] = 32v32[0] = 32; // v32 是长度17的数值,v32[0] = 32*(_OWORD *)&v33[1] = *(_OWORD *)(v11 + 5); // v33 = 输入字符串除去前5位flag{ 之后的前16位*(_OWORD *)&v32[1] = *(_OWORD *)(v11 + 21); // v32 = 输入字符串的21位开始,也就是除去flag{的 后16位v42 = *(_OWORD *)v33;v43 = (void *)v33[16];v40 = *(_OWORD *)v32;v41 = (void *)v32[16];CheckM::CheckM((unsigned __int8 *)&result, (unsigned __int8 *)&v42, (unsigned __int8 *)&v40); //CheckM函数if ( (v40 & 1) != 0 )operator delete(v41);if ( (v42 & 1) != 0 )operator delete(v43);CheckM::check1((CheckM *)&result); //check1函数
2、第二部分代码分析,CheckM 和check1函数
hook 了CheckM 和check1函数 发现主要处理逻辑在check1函数;
sktrace 对照arm指令,逐行分析代码,处理逻辑是循环16次,取前16位的每个值的高4位和后16位的每个值的低4位合并后的值放到后16位;取后16位的每个值的高4位和前16位的每个值的低4位合并后放到前16位,组合成新的32位字符串。
这里有点拗口难懂,直接看示例就明白了。
示例:输入字符串
前16位 abcdefghijklmnop
后16位 qrstuvwxyz123456
循环16次分别取前16位的值和后16的值
第一次取值 分别是 ‘a’ 和 ‘q’ , 经过运算 ‘q’ & 0xf0 | ‘a’ & 0xf = 0x71 = ‘q’ 得到 ‘q’
这个运算 ‘q’ & 0xf0 就是取 q 的高4位,‘a’ & 0xf 就是取 a 的低4位,然后或运算就是组合在一起,得到值 q
第二次取值 分别是 ‘b’ 和 ‘r’ , 经过运行 ‘r’ & 0xf0 | ‘b’ & 0xf = 0x72 = ‘r’ 得到 ‘r’
这样循环16次,最后得到字符串是 qrstuvwxyz;<=>?0 ,这个字符串是新字符串的前16位;
同理
循环16次分别取前16位的值和后16的值
第一次取值 分别是 ‘a’ 和 ‘q’ , 经过运算 ‘a’ & 0xf0 | ‘q’ & 0xf = 0x61 = ‘a’
这个运算和上面是反过来的 ‘a’ & 0xf0 是取的 a的高4位 , ‘q’ & 0xf 取的是q 的低4位,组合得到 ‘a’
这样循环16次,最后得到字符串是abcdefghijabcdev , 这个字符串是新字符串的后16位;
经过check1处理后
前16位 abcdefghijklmnop 变成 qrstuvwxyz;<=>?0
后16位 qrstuvwxyz123456 变成 abcdefghijabcdev
__int64 __fastcall CheckM::check1(CheckM *this)
{_BYTE *v1; // x19_BYTE *v3; // x22unsigned __int64 v4; // x23_BYTE *v5; // x24unsigned __int64 v6; // x8char *v7; // x8char *v8; // x8unsigned __int64 v9; // x9char *v10; // x10char v11; // w10char *v12; // x8char v13; // w11unsigned __int64 v14; // x8unsigned __int64 v15; // x20unsigned __int64 v16; // x1char v17; // w27char *v18; // x8char *v19; // x8char *v20; // x8char v21; // w9char *v22; // x8char v23; // w10unsigned __int64 v24; // x8char v25; // w9char v26; // w10unsigned __int64 v27; // x20unsigned __int64 v28; // x1char v29; // w27// statistics x0:0x7fe7e11af0 x1:0x7fe7e11ab1 x2:0x10 x3:0x7b x4:0x7fe7e11ac1 x5:0x7fe7e11b19 x6:0x7877767574737271 x7:0x3635343332317a79 x8:0x7fe7e11a98 x9:0x36 x10:0x35343332317a7978 x11:0x10 x12:0x7978777675747372 x13:0x7d3635343332317a x14:0x78e20dec34 x15:0xebad6a89 x16:0x78c8c56c78 x17:0x78c8c567b0 x18:0x7b90faa000 x19:0x79a686e190 x20:0x0 x21:0x5 x22:0x7b90047000 x23:0x7fe7e11b51 x24:0xa x25:0x7fe7e11af0 x26:0x70aa1738 x27:0x1 x28:0x7fe7e11bd0 x29:0x7fe7e11bb0 x30:0x7b89ec240cv1 = (char *)this+ 0x30; // x19=0x79a686e190->0x7fe7e11b20, x19 = x0 + 0x30 = 0x7fe7e11af0 + 0x30 = 0x7fe7e11b20std::string::assign((_DWORD)this + 0x30, "", 0);v3 = (char *)this + 0x48;std::string::assign((_DWORD)this + 0x48, "", 0);v4 = 0LL;v5 = (char *)this + 0x18; // x24=0xa->0x7fe7e11b08v6 = *(unsigned __int8 *)this; // v6 = 0x20if ( (v6 & 1) == 0 ) // truegoto LABEL_5; // 跳到label 5 执行while循环
LABEL_14:if ( *((_QWORD *)this + 1) > v4 ){do{v9 = (unsigned __int8)*v5; // x9=0x10->0x20if ( (v9 & 1) != 0 ) // false{if ( *((_QWORD *)this + 4) <= v4 )return std::operator+<char>(v1, (char *)this + 0x48);}else if ( v9 >> 1 <= v4 ) // false{return std::operator+<char>(v1, (char *)this + 0x48);}v10 = (char *)this + 1; // x10=0x10->0x7fe7e11af1if ( (v6 & 1) != 0 ) // falsev10 = (char *)*((_QWORD *)this + 2);v11 = v10[v4]; // v11 是第一个字符 v11 = 'a'
//循环取前16位v12 = (char *)this + 25; // v12 指向输入字符的后十六位if ( (v9 & 1) != 0 ) // falsev12 = (char *)*((_QWORD *)this + 5);v13 = v12[v4]; // v13 =后16位的第一位 , v16 = 'q'
//循环取后16位v14 = (unsigned __int8)*v1; // v14 = 0x0if ( (v14 & 1) != 0 ) // false{v15 = *((_QWORD *)this + 7);v16 = (*((_QWORD *)this + 6) & 0xFFFFFFFFFFFFFFFELL) - 1;}else{v15 = v14 >> 1;v16 = 22LL;}v17 = v13 & 0xF0 | v11 & 0xF; // v13 & 0xF0 = 0x71 & 0xf0 = 0x70 , v11 = 0x61, v11 & 0xf = 1 , v17 = 0x70 | 0x1 = 0x71//代码对应的地址是0x78c8c56d98 , 循环16次对应16个值,对应下面第一个图,这里就是核心算法,取后16位的值 & 0xF0, 也就是取高4位,然后 | 前16位 & 0xF 是取低4位,最后的结果就是v17 的值将是 v13 的高 4 位与 v11 的低 4 位的组合。if ( v15 == v16 ) // false{((void (__fastcall *)(_BYTE *, unsigned __int64, __int64, unsigned __int64, unsigned __int64, _QWORD, _QWORD))std::string::__grow_by)(v1,v16,1LL,v16,v16,0LL,0LL);if ( (*v1 & 1) != 0 ){
LABEL_21:v18 = (char *)*((_QWORD *)this + 8);*((_QWORD *)this + 7) = v15 + 1;goto LABEL_24;}}else if ( (v14 & 1) != 0 ) // false{goto LABEL_21;}*v1 = 2 * v15 + 2; // 2v18 = (char *)this + 49; // v18 = x8=0x2->0x7fe7e11b21
LABEL_24:v19 = &v18[v15]; // x8=0x7fe7e11b21->0x7fe7e11b22*v19 = v17; // 0x71 , 即 ‘q’v19[1] = 0;v20 = (char *)this + 25;if ( (*v5 & 1) != 0 ) // falsev20 = (char *)*((_QWORD *)this + 5);v21 = v20[v4]; // x9=0x20->0x71 , 后16位第一个‘q’
//这里是循环取后16位v22 = (char *)this + 1;if ( (*(_BYTE *)this & 1) != 0 )v22 = (char *)*((_QWORD *)this + 2);v23 = v22[v4]; // x10=0x20->0x61, 前16位第一个‘a’;
//这里就是循环取前16位v24 = (unsigned __int8)*v3; // x8=0x7fe7e11af1->0x0v25 = v21 & 0xF; // 1
//取后16位的每个值的低4位v26 = v23 & 0xF0; // 0x60
//取前16位的每个值的高4位if ( (v24 & 1) != 0 ) // false{v27 = *((_QWORD *)this + 10);v28 = (*((_QWORD *)this + 9) & 0xFFFFFFFFFFFFFFFELL) - 1;}else{v27 = v24 >> 1;v28 = 22LL;}v29 = v26 | v25; // 0x61
//这里或运算就是合并后16位的每个值的低4位 和 前16位的每个值的高4位
//代码对应地址是 0x78c8c56e48循环16次对应16个值,对应下面第二个图if ( v27 == v28 ){((void (__fastcall *)(char *, unsigned __int64, __int64, unsigned __int64, unsigned __int64, _QWORD, _QWORD))std::string::__grow_by)((char *)this + 0x48,v28,1LL,v28,v28,0LL,0LL);LOBYTE(v24) = *v3;}if ( (v24 & 1) != 0 ) // false{v7 = (char *)*((_QWORD *)this + 11);*((_QWORD *)this + 10) = v27 + 1;}else{*v3 = 2 * v27 + 2; // 2v7 = (char *)this + 73; // x8=0x2->0x7fe7e11b39}v8 = &v7[v27]; // x8=0x7fe7e11b39->0x7fe7e11b3a*v8 = v29; // 0x61, 即‘a’v8[1] = 0;++v4; // 循环+1v6 = *(unsigned __int8 *)this;if ( (v6 & 1) != 0 )goto LABEL_14;
LABEL_5:;}while ( v6 >> 1 > v4 );}return std::operator+<char>(v1, (char *)this + 0x48);
}
3、TEA 加密分析
ida32位是解析成encrypt 函数,ida64位是直接解析成代码,check1 和base64 直接的代码就是TEA 加密函数。
TEA加密算法的常数DELTA值0x9E3779B9 和 0x61C88647 是明显的TEA算法标识。
注意k 的值是 0x42, 0x37, 0x2C, 0x21 , 是在JNI_OnLoad 重新赋值。
CheckM::check1((CheckM *)&v45);v14 = (unsigned __int64)v37 >> 1; // v14 = x8=0x31->0x18if ( (v37 & 1) != 0 ) // v37 = 0x31, v37 & 1 = 1v14 = v39; // v14 = x8=0x18->0x20v15 = (v37 & 1) == 0; // v15 = 0if ( v14 ) // true{v16 = 0LL;do{if ( v15 )v17 = v38; // x12=0x0->0x79168ba790, 输入字符串经过check1处理后字符串elsev17 = v40;v18 = (unsigned int *)&v17[v16]; // 0x78cbbd47fc add x12, x12, x8 ; x12=0x79168ba790->0x79168ba798,每次取8个字符v20 = *v18; // 0x78cbbd4800 ldp w16, w14, [x12] ; x14=0x79168ba790->0x78777675, x16=0x78cbbf9eb8->0x74737271,8个字符分别给到v20 和 v19v19 = v18[1]; //字符串长度是32,每次取8位,这里会循环取4次,sktrace 搜索 0x78cbbd4800 就可以看到4次的取值v21 = 32;v22 = 0x9E3779B9; // TEA加密算法的常数DELTA值do{v20 += (DWORD1(k) + (v19 >> 5)) ^ (k + 16 * v19) ^ (v19 + v22);// k 的值是 0x42, 0x37, 0x2C, 0x21 , 是在JNI_OnLoad 赋值--v21;v19 += (DWORD2(k) + 16 * v20) ^ (v22 + v20) ^ (HIDWORD(k) + (v20 >> 5));v22 -= 0x61C88647; // TEA加密算法的常数DELTA值等价0x9E3779B9}while ( v21 ); // 循环加密32轮*v18 = v20; // 加密后的数据再给到v18v18[1] = v19;v16 += 8LL; // 取下一个 8个字符if ( (v37 & 1) != 0 )v23 = v39; // v23 = 0x20elsev23 = (unsigned __int64)v37 >> 1;v15 = (v37 & 1) == 0; // v15 = 0}while ( v23 > v16 ); // 循环条件是 v16 < 32 , 32就是待加密的字符串长度,v16是递增8,就是每次取待加密的字符串8位}else{LODWORD(v23) = 0;}
if ( v15 )v26 = v38; // x0=0x21->0x79168ba790,加密完的字符串给到v26elsev26 = (const unsigned __int8 *)v40;base64_encode(v26, v23); // 又调用base64_encode 进行加密
4、base64_encode 函数分析
base64_encode 是一个改了码表并且有换位的base64算法,不是标准的base64。
分析过程
先hook 这个函数看下参数,参数就是base64入参
或者inlinehook打印寄存器的值也是一样的
TEA加密后的值,也是base64_encode 函数处理前的值
90 58 7e 8f 74 30 4a ec 25 74 dd 96 ea cc a4 8b 56 54 f8 3c bc 71 c4 a9 59 86 fe 22 00 15 51 26
然后再hook打印一下base64_encode 函数处理后的值
fA+Tq)MKMsBnSKaTeX parse error: Expected 'EOF', got '#' at position 14: wIOWARz(udO+Y#̲s#Fy@w+&aivsyu=…%^&()ABCDEFGHIJKLMNOPQRSTUVWXYZ+/
码表是在start函数里赋的值。
修改码表之后的base64编码结果是
Af*+)TqMsKMB$nSwWIOA(Rzu+dOY##sFwy@+i&avusy=
和上面hook的结果还是不一样,对比发现是有换位。
然后还是sktrace 结果对照arm指令逐行分析,发现base64编码后确实是有换位。
示例:
编码前是 0x90, 0x58, 0x7e
转base64下标,正常应该是 0x24, 0x5, 0x21, 0x3e
这个代码换了位置,变成了 0x5 ,0x21, 0x24, 0x3e
换位逻辑:
1 ==>> 3
2 ==>> 1
3 ==>>2
4 不变
char *__usercall base64_encode@<X0>(char *result@<X0>, int a2@<W1>, _QWORD *a3@<X8>)
{unsigned int v4; // w27int v5; // w20char *v6; // x21int v7; // w24char v8; // t1__int64 v9; // x24unsigned __int8 v10; // w8__int64 v11; // x11unsigned __int64 v12; // x8char *v13; // x9char v14; // w22unsigned __int64 v15; // x26unsigned __int64 v16; // x1char *v17; // x8char *v18; // x8unsigned int v19; // w8char *v20; // x21__int64 i; // x25__int64 v22; // x11unsigned __int64 v23; // x8char *v24; // x9char v25; // w22unsigned __int64 v26; // x26unsigned __int64 v27; // x1char *v28; // x8char *v29; // x8char v30; // t1int v31; // w21unsigned __int64 v32; // x8char *v33; // x8bool v34; // cfunsigned __int64 v35; // x22unsigned __int64 v36; // x1char v37; // [xsp+10h] [xbp-60h]char v38; // [xsp+11h] [xbp-5Fh] BYREFchar v39; // [xsp+12h] [xbp-5Eh]char v40; // [xsp+13h] [xbp-5Dh]unsigned __int8 v41; // [xsp+14h] [xbp-5Ch] BYREFunsigned __int8 v42; // [xsp+15h] [xbp-5Bh]unsigned __int8 v43; // [xsp+16h] [xbp-5Ah]__int64 v44; // [xsp+18h] [xbp-58h]v44 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);*a3 = 0LL;a3[1] = 0LL;a3[2] = 0LL;if ( a2 ){LOBYTE(v4) = v37;v5 = a2; // x20=0x0->0x20v6 = result;v7 = 0;do{v8 = *v6++; // ldrb w8, [x21], #1 ; x8=0x90->0x58, x21=0x79168ba791->0x79168ba792,x8取待加密字符,循环执行32次 ,取值是90 58 7e 8f 74 30 4a ec 25 74 dd 96 ea cc a4 8b 56 54 f8 3c bc 71 c4 a9 59 86 fe 22 00 15 51 26*(&v41 + v7++) = v8;if ( v7 == 3 ) // 取3个值后进行base64转换成4字节,第一次取值是90587e{v9 = 1LL;v4 = (v42 >> 4) & 0xFFFFFFCF | (16 * (v41 & 3));v10 = (v42 >> 4) & 0xCF | (16 * (v41 & 3));// lsr w27, w8, #4 ; x27=0x38->0x5,取v41的后2位与v42的前4位拼接,这是base64编码的第2个值,但是被放到了第1个v39 = v41 >> 2; // lsr w12, w10, #2 ; x12=0x0->0x24 , v41 = w10 = 0x90 , 0x90 >>2 = 0x24 , 这里应该是第一个值,被放到了第3个v40 = v43 & 0x3F; // and w9, w9, #0x3f ; x9=0x7e->0x3e ,取v43的低6位,这个是base64的第4个值,位置没变v38 = (v43 >> 6) & 0xC3 | (4 * (v42 & 0xF));// bfi w11, w8, #2, #4 ; x11=0x1->0x21,取v42的后4位和v43的前2位拼接,这个是base64编码的第3个值
//正常base64编码处理,0x90,0x58,0x7e , 转换成4个base64下标应该是 0x24,0x5,0x21,0x3e , 但是到下面查码表的时候顺序变了while ( 1 ){v11 = v10; // 码表下标,0x78cbbd421c and x11, x8, #0xff ; x11=0x21->0x5 ,前4个值是0x5 ,0x21, 0x24, 0x3ev12 = *(unsigned __int8 *)a3;if ( (qword_35050 & 1) != 0 )v13 = *(char **)&dword_35060; // v13 = x9 =0x51->0x79368654b0 , 码表abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ+/elsev13 = (char *)&qword_35050 + 1;v14 = v13[v11]; // 查码表后编码的值 对应指令 0x78cbbd422c ldrb w22, [x9, x11] ; x22=0x7b90047000->0x66 ,前4个值是0x66 0x2a 0x41 0x2bif ( (v12 & 1) != 0 ){v15 = a3[1];v16 = (*a3 & 0xFFFFFFFFFFFFFFFELL) - 1;if ( v15 != v16 )goto LABEL_12;}else{v15 = v12 >> 1;v16 = 22LL;if ( v12 >> 1 != 22 ){
LABEL_12:if ( (v12 & 1) != 0 )goto LABEL_13;goto LABEL_16;}}result = (char *)((__int64 (__fastcall *)(_QWORD *, unsigned __int64, __int64, unsigned __int64, unsigned __int64, _QWORD, _QWORD))std::string::__grow_by)(a3,v16,1LL,v16,v16,0LL,0LL);if ( (*(_BYTE *)a3 & 1) != 0 ){
LABEL_13:v17 = (char *)a3[2];a3[1] = v15 + 1;goto LABEL_17;}
LABEL_16:*(_BYTE *)a3 = 2 * v15 + 2;v17 = (char *)a3 + 1;
LABEL_17:v18 = &v17[v15];*v18 = v14;v18[1] = 0;if ( v9 == 4 ){v7 = 0;break;}v10 = *(&v37 + v9++); // 循环取下标}}--v5;}while ( v5 );v37 = v4;if ( v7 ){if ( v7 <= 2 )result = (char *)memset(&v41 + v7, 0, 3 - v7);v19 = (v42 >> 4) & 0xFFFFFFCF | (16 * (v41 & 3));v39 = v41 >> 2;v37 = (v42 >> 4) & 0xCF | (16 * (v41 & 3));v38 = (v43 >> 6) & 0xC3 | (4 * (v42 & 0xF));v40 = v43 & 0x3F;if ( (v7 & 0x80000000) == 0 ){v20 = &v38;for ( i = (unsigned int)(v7 + 1) - 1LL; ; --i ){v22 = (unsigned __int8)v19;v23 = *(unsigned __int8 *)a3;if ( (qword_35050 & 1) != 0 )v24 = *(char **)&dword_35060;elsev24 = (char *)&qword_35050 + 1;v25 = v24[v22];if ( (v23 & 1) != 0 ){v26 = a3[1];v27 = (*a3 & 0xFFFFFFFFFFFFFFFELL) - 1;if ( v26 != v27 )goto LABEL_29;}else{v26 = v23 >> 1;v27 = 22LL;if ( v23 >> 1 != 22 ){
LABEL_29:if ( (v23 & 1) != 0 )goto LABEL_30;goto LABEL_33;}}result = (char *)((__int64 (__fastcall *)(_QWORD *, unsigned __int64, __int64, unsigned __int64, unsigned __int64, _QWORD, _QWORD))std::string::__grow_by)(a3,v27,1LL,v27,v27,0LL,0LL);if ( (*(_BYTE *)a3 & 1) != 0 ){
LABEL_30:v28 = (char *)a3[2];a3[1] = v26 + 1;goto LABEL_34;}
LABEL_33:*(_BYTE *)a3 = 2 * v26 + 2;v28 = (char *)a3 + 1;
LABEL_34:v29 = &v28[v26];*v29 = v25;v29[1] = 0;if ( !i )break;v30 = *v20++;LOBYTE(v19) = v30;}}if ( v7 <= 2 ){v31 = v7 - 3;v32 = *(unsigned __int8 *)a3;if ( (v32 & 1) != 0 )goto LABEL_42;
LABEL_44:v35 = v32 >> 1;v36 = 22LL;if ( v32 >> 1 != 22 )goto LABEL_46;
LABEL_45:result = (char *)((__int64 (__fastcall *)(_QWORD *, unsigned __int64, __int64, unsigned __int64, unsigned __int64, _QWORD, _QWORD))std::string::__grow_by)(a3,v36,1LL,v36,v36,0LL,0LL);LOBYTE(v32) = *(_BYTE *)a3;
LABEL_46:while ( 1 ){if ( (v32 & 1) != 0 ){v33 = (char *)a3[2];a3[1] = v35 + 1;}else{*(_BYTE *)a3 = 2 * v35 + 2;v33 = (char *)a3 + 1;}v34 = __CFADD__(v31++, 1);*(_WORD *)&v33[v35] = '=';if ( v34 )break;v32 = *(unsigned __int8 *)a3;if ( (v32 & 1) == 0 )goto LABEL_44;
LABEL_42:v35 = a3[1];v36 = (*a3 & 0xFFFFFFFFFFFFFFFELL) - 1;if ( v35 == v36 )goto LABEL_45;}}}}return result;
}
至此加密流程分析完毕。
接下来是解密流程。
解密流程
最后指定的密文是 e)n*pNe%PQy!^oS(@HtkUu+CdKaTeX parse error: Expected 'EOF', got '#' at position 1: #̲hmmK&ieytiWwYkI…hKmm&yietwiWYAkI=
然后是base64解码 3448e110fc5e633d1ad9f3a24dbacafb8526703747b8c320608113588ebc90ab
然后是TEA解密 6560343634356738373535653135306232323361603136343962316361673535
最后是高低位替换 3530346664353738376535656165303262623331303166343932316331373565
得到flag是 flag{504fd5787e5eae02bb3101f4921c175e}