IDA中分析liboo000oo.so,可以看出
.datadiv_decode5009363700628197108在程序开始时对数据解码:
.init_array:00003E78 ; ELF Initialization Function Table
.init_array:00003E78 ; ===========================================================================
.init_array:00003E78
.init_array:00003E78 ; Segment type: Pure data
.init_array:00003E78 AREA .init_array, DATA
.init_array:00003E78 ; ORG 0x3E78
.init_array:00003E78 DCD .datadiv_decode5009363700628197108+1 ; 对数据解码
.init_array:00003E78 ; .init_array ends
.init_array:00003E78
// 对数据解码
char *datadiv_decode5009363700628197108()
{
int v0; // r0
int v1; // r0
int v2; // r0
char *result; // r0
v0 = 0;
do
{
a650f909c721736[v0] ^= 0xA5u; // 解码串 "650f909c-7217-3647-9331-c82df8b98e98",0
++v0;
}
while ( v0 != 0x25 );
v1 = 0;
do
aAbcdefghijklmn[v1++] ^= 0xA5u; // 解码 Base64 编码表:
// "!:#",0x24,"%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';",0
while ( v1 != 0x42 );
v2 = 0;
do
aAndroidSupport[v2++] ^= 0x84u; // 解码串 "android/support/v7/app/AppCompiatActivity",0
while ( v2 != 0x2A );
result = &byte_40D0;
byte_40CA ^= 0xFCu;
byte_40CB ^= 0xFCu;
byte_40CC ^= 0xFCu;
byte_40D0 ^= 0x62u;
byte_40D1 ^= 0x62u;
byte_40D2 ^= 0x62u;
byte_40D3 ^= 0x62u;
byte_40D4 ^= 0x62u;
byte_40D5 ^= 0x62u;
word_40D6 = __PAIR__(HIBYTE(word_40D6), (word_40D6 ^ 0x62)) ^ 0x6200;
byte_40D8 ^= 0x62u;
byte_40D9 ^= 0x62u;
byte_40DA ^= 0x62u;
byte_40DB ^= 0x62u;
byte_40DC ^= 0x62u;
byte_40DD ^= 0x62u;
byte_40DE ^= 0x62u;
byte_40DF ^= 0x62u;
byte_40E0 ^= 0x62u;
byte_40E1 ^= 0x62u;
byte_40E2 ^= 0x62u;
byte_40E3 ^= 0x62u;
byte_40E4 ^= 0x62u;
byte_40E5 ^= 0x62u;
return result;
}
再来看
signed int __fastcall JNI_OnLoad(_JavaVM *a1)
{
signed int result; // r0
_JNIEnv *v2; // r5
int v3; // r6
_JNIEnv *env; // [sp+0h] [bp-18h]
int v5; // [sp+4h] [bp-14h]
env = 0;
if ( !(a1->functions->GetEnv)() )
goto LABEL_4;
LABEL_2:
result = -1;
while ( _stack_chk_guard != v5 )
{
LABEL_4:
v2 = env;
v3 = (env->functions->FindClass)(env, off_4010);// "android/support/v7/app/AppCompiatActivity"
dword_4110 = (v2->functions->NewGlobalRef)(v2, v3);
if ( !v3 || (v2->functions->RegisterNatives)(v2, v3, &JNINativeMethodEq, 1) <= -1 )// 注册 boolean NativeMethod 函数 eq(String)
goto LABEL_2;
result = 65542;
}
return result;
}
其中:.data:00004014 JNINativeMethodEq JNINativeMethod <aEq, aLjavaLangStrin, eq+1>
可以看到,在函数JNI_OnLoad中注册了一个NativeMethod boolean eq(String),这个就是验证核心
// 注册码核心验证代码
int __fastcall eq(_JNIEnv *a1)
{
size_t v1; // r10
unsigned __int8 *v2; // r6
_BYTE *v3; // r8
_BYTE *v4; // r11
int v5; // r0
size_t v6; // r2
char *v7; // r1
int v8; // r3
int v9; // r1
unsigned int v10; // r2
int v11; // r3
int v12; // r0
int v13; // r4
unsigned __int8 v14; // r0
_BYTE *v15; // r3
_BYTE *v16; // r5
char *v17; // r4
int v18; // r5
int v19; // r1
int v20; // r0
signed int v21; // r1
int v22; // r2
size_t v23; // r0
unsigned int lenSN; // r8
unsigned int v25; // r5
_BYTE *buf; // r0
int v27; // r3
int v28; // r10
unsigned int v29; // r2
int v30; // r12
bool v31; // zf
_BYTE *v32; // r1
bool v33; // zf
int v34; // r3
int v35; // r1
unsigned __int8 v36; // r11
unsigned int v37; // lr
char v38; // r1
char *v39; // r2
int v40; // t1
unsigned int v42; // [sp+4h] [bp-234h]
unsigned int v43; // [sp+8h] [bp-230h]
unsigned int v44; // [sp+10h] [bp-228h]
char *SN; // [sp+14h] [bp-224h]
char v46[256]; // [sp+18h] [bp-220h]
char v47[256]; // [sp+118h] [bp-120h]
int v48; // [sp+218h] [bp-20h]
SN = (a1->functions->GetStringUTFChars)(); // 取SN
v1 = strlen(a650f909c721736); // 用 "650f909c-7217-3647-9331-c82df8b98e98" 初始加密表
// 算法没看太出来,不过不要紧,最后XOR的序列是不变的,只要得到那个序列就可以算出SN
v2 = malloc(v1);
v3 = malloc(v1);
v4 = malloc(v1);
_aeabi_memclr(v2, v1);
_aeabi_memclr(v3, v1);
_aeabi_memclr(v4, v1);
if ( v1 )
{
v5 = 0;
v6 = v1;
v7 = a650f909c721736;
do
{
v8 = *v7++;
if ( v8 != '-' )
v3[v5++] = v8;
--v6;
}
while ( v6 );
if ( v5 >= 1 )
{
v9 = v5 - 1;
v10 = -8;
v11 = 0;
v12 = 0;
do
{
if ( (v11 | (v10 >> 2)) > 3 )
{
v13 = v12;
}
else
{
v13 = v12 + 1;
v2[v12] = '-';
}
v14 = v3[v9--];
v11 += 0x40000000;
v2[v13] = v14;
++v10;
v12 = v13 + 1;
}
while ( v9 != -1 );
if ( v13 >= 0 )
{
v15 = v4;
while ( 1 )
{
v16 = *v2;
if ( (v16 - 97) <= 5u )
break;
if ( (v16 - 48) <= 9u )
{
v16 = &unk_23DE + v16 - 48;
goto LABEL_18;
}
LABEL_19:
*v15++ = v16;
--v12;
++v2;
if ( !v12 )
goto LABEL_20;
}
v16 = &unk_23D8 + v16 - 97;
LABEL_18:
LOBYTE(v16) = *v16;
goto LABEL_19;
}
}
}
LABEL_20:
_aeabi_memcpy8(v46, &unk_23E8, 256);
v17 = v47;
v18 = 0;
do
{
sub_D20(v18, v1);
v47[v18++] = v4[v19];
}
while ( v18 != 256 );
v20 = (v47[0] - 41);
v46[0] = v46[v20];
v46[v20] = -41;
v21 = 1;
do
{
v22 = v46[v21];
v20 = (v20 + v47[v21] + v22) % 256;
v46[v21++] = v46[v20];
v46[v20] = v22;
}
while ( v21 != 256 );
v23 = strlen(SN);
lenSN = v23;
v25 = v4[3];
v43 = 8 * (3 - -3 * (v23 / 3));
v42 = v25 + v43 / 6;
buf = malloc(v42 + 1);
if ( lenSN )
{
v28 = 0;
v29 = 0;
v30 = 0;
v44 = v25;
do
{
v28 = (v28 + 1) % 256;
v35 = v46[v28];
v30 = (v30 + v35) % 256;
v46[v28] = v46[v30];
v46[v30] = v35;
v17 = v46[v28];
v36 = v46[(v35 + v17)] ^ SN[v29]; // 用加密码表对SN XOR 后 再Base64编码(Base64码表非标准表)再对输出每4个字节一组第0,2个分别XOR 0x7 和 0xf
// 在这儿加密码表序列为一个常数序列,设断点可得到这个序列:
// {0x9b, 0x6b, 0xba, 0x25, 0x73, 0x82, 0xe0, 0x31, 0x86, 0x80, 0xf1, 0xc5, 0xda, 0x82, 0xda, 0x08, 0x38, 0x90, 0x50, 0xa8, 0x31, 0x63, 0x07, 0xa2, ...}
if ( v29 && (v27 = 0xAAAAAAAB * v29 >> 32, v37 = 3 * (v29 / 3), v37 != v29) )
{
v31 = v29 == 1;
if ( v29 != 1 )
v31 = v37 + 1 == v29;
if ( v31 )
{
v32 = aAbcdefghijklmn; // Base64码表 "!:#",0x24,"%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
buf[v44 + v29] = aAbcdefghijklmn[buf[v44 + v29] | (v36 >> 4)];
v17 = &buf[v44 + v29];
v27 = 4 * v36 & 0x3C;
v17[1] = v27;
if ( v29 + 1 >= lenSN )
goto LABEL_53;
}
else
{
v33 = v29 == 2;
if ( v29 != 2 )
v33 = v37 + 2 == v29;
if ( v33 )
{
v17 = (v36 & 0xC0);
v34 = v44++ + v29;
buf[v34] = aAbcdefghijklmn[buf[v34] | (v17 >> 6)] ^ 0xF;// 四个一组中的第二个 XOR 0x0f
v27 = &buf[v34];
*(v27 + 1) = aAbcdefghijklmn[v36 & 0x3F];
}
}
}
else
{
buf[v44 + v29] = aAbcdefghijklmn[v36 >> 2] ^ 7;// 四个一组中的第0个,XOR 0x07
v17 = &buf[v44 + v29];
v27 = 16 * v36 & 0x30;
v17[1] = v27;
if ( v29 + 1 >= lenSN )
{
v38 = aAbcdefghijklmn[v27];
*(v17 + 1) = 0x3B3B;
goto LABEL_43;
}
}
++v29;
}
while ( v29 < lenSN );
}
while ( 1 )
{
if ( v43 )
{
v32 = (&dword_0 + 1);
v17 = v42;
v39 = &a98gaLTnFjJG; // " {9*8ga*l!Tn?@#fj'j",0x24,"\g;;"
do
{
v27 = buf[v25++];
v40 = *v39++; // 最终的比较
if ( v40 != v27 ) // 最后结果要为 " {9*8ga*l!Tn?@#fj'j",0x24,"\g;;"
v32 = 0;
}
while ( v25 < v42 );
}
else
{
v32 = (&dword_0 + 1);
}
buf = (_stack_chk_guard - v48);
if ( _stack_chk_guard == v48 )
break;
LABEL_53:
v38 = v32[v27];
v17[2] = 52;
LABEL_43:
v17[1] = v38;
}
return v32;
}
由以上分析可以看出验证算法大致是:
BYTE xorKey[] = {0x7, 0, 0xf, 0};
BYTE xorKey2[] = {0x9b, 0x6b, 0xba, 0x25, 0x73, 0x82, 0xe0, 0x31, 0x86, 0x80, 0xf1, 0xc5, 0xda, 0x82, 0xda, 0x08, 0x38, 0x90, 0x50, 0xa8, 0x31, 0x63, 0x07, 0xa2};
char sEnd[] = " {9*8ga*l!Tn?@#fj'j$\\g;;"; //长度24
char SN[24/4*3+1]; //最长18位
char snBase64[25];
int i;
for(i = 0; i < lenSN; i++){
SN[i] ^= xorKey2[i];
}
Base64Encode(SN, snBase64);
for(i = 0; i < 24 - 2; i++){ //最后的';'不计算
snBase64[i] ^= xorKey[i%4];
}
if(!strncmp(snBase64, sEnd, 24)){
//验证通过
}
不过这儿的Base64码表不是标准的"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
而是"!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
如果用标准的Base64算法还需要小小的变换一下。
由验证算法不难得出SN:
LPCSTR p1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
LPCSTR p2 = "!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\\';";
for(i = 0; i < 24; i++){
char c = sEnd[i] ^ (i>=22 ? 0 : xorKey[i%4]); //最后的';'不计算
LPCSTR p = strchr(p2, c); //换成标准的Base64码
snBase64[i] = p1[p-p2];
}
snBase64[i] = 0;
int len = Base64Decode((PBYTE)SN, sizeof(SN), snBase64);
for(i = 0; i < len; i++){
SN[i] ^= xorKey2[i];
}
SN[len] = 0;
OutputDebugString(SN);
最终得到 SN[] = "fu0kzHp2aqtZAuY6";
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法