分析锁机样本中发现加了壳,硬着头皮撸了一遍,样本所使用加固为腾讯乐固,壳版本为2.10.3.1。
使用乐固加固后会在Lib文件夹下生成如下文件。
加固的核心功能在libshella-2.10.3.1.so中,libBugly.so主要功能为监控程序异常数据,故不关注。
修复so后,直接将so,拖入ida,如下图所示,JNI_OnLoad函数被加密,.init 与.init_array也无法显示。
修复工具地址为:http://bbs.pediy.com/thread-192874.htm
直接动态挂上IDA,在linker的.init_array调用出下好断点。
关于如何定位这里简单说一下,搜索字符串[ Calling %s @ %p for '%s' ],BLX R4就是调用出。
断点触发后运行到init_array位置。
看一下流程图,应该是被llvm混淆过。
来到函数尾部粗略看了看代码,调用了如下图中几个函数,在mprotect_0处断下后,代码解密完毕。
其中createthread创建了一个反调试线程,这里我直接nop了,粗略看了几眼。
反调试线程函数伪代码如下:
int __fastcall sub_6232D0B4(int a1)
{
void *v1; // r1@48
signed int v2; // r2@48
int v4; // r0@54
int v5; // r0@54
int v6; // r1@54
int v7; // r0@54
int v8; // r0@55
void *v9; // r2@55
unsigned __int8 v10; // zf@55
signed int v11; // r0@55
void *v12; // r1@60
signed int v13; // r2@60
void *v14; // r1@65
signed int v15; // r2@65
int v16; // r2@71
int v17; // r0@71
int v18; // r0@73
int v19; // r0@74
void *v20; // r2@74
signed int v21; // r0@74
int v22; // r0@79
int v23; // r0@79
unsigned int v24; // r1@79
int v25; // r0@79
unsigned int v26; // r2@79
int v27; // r0@80
void *v28; // r2@80
unsigned __int8 v29; // nf@80
signed int v30; // r0@80
int v31; // r0@85
int *v32; // r1@86
int *v33; // r3@86
int v34; // r2@86
unsigned int v35; // r0@86
void *v36; // r2@86
signed int v37; // r0@86
int v38; // r0@91
int v39; // r0@91
int v40; // r0@92
int v41; // [sp+0h] [bp-4A0h]@54
int v42; // [sp+4h] [bp-49Ch]@92
int v43; // [sp+8h] [bp-498h]@91
int v44; // [sp+Ch] [bp-494h]@91
int v45; // [sp+10h] [bp-490h]@91
int v46; // [sp+14h] [bp-48Ch]@86
int v47; // [sp+18h] [bp-488h]@85
int v48; // [sp+1Ch] [bp-484h]@85
unsigned int v49; // [sp+20h] [bp-480h]@79
void *v50; // [sp+24h] [bp-47Ch]@79
int v51; // [sp+28h] [bp-478h]@79
int v52; // [sp+2Ch] [bp-474h]@79
int v53; // [sp+30h] [bp-470h]@79
int v54; // [sp+34h] [bp-46Ch]@79
int v55; // [sp+38h] [bp-468h]@73
int v56; // [sp+3Ch] [bp-464h]@71
int v57; // [sp+40h] [bp-460h]@71
int v58; // [sp+44h] [bp-45Ch]@54
int v59; // [sp+48h] [bp-458h]@54
int v60; // [sp+4Ch] [bp-454h]@54
int v61; // [sp+50h] [bp-450h]@54
void *v62; // [sp+54h] [bp-44Ch]@2
int v63; // [sp+58h] [bp-448h]@1
void *v64; // [sp+5Ch] [bp-444h]@1
char *v65; // [sp+60h] [bp-440h]@1
void *v66; // [sp+64h] [bp-43Ch]@1
void *v67; // [sp+68h] [bp-438h]@1
char v68; // [sp+6Ch] [bp-434h]@1
char v69; // [sp+6Dh] [bp-433h]@1
char v70; // [sp+6Eh] [bp-432h]@1
char v71; // [sp+6Fh] [bp-431h]@1
char v72; // [sp+70h] [bp-430h]@1
char v73; // [sp+71h] [bp-42Fh]@1
char v74; // [sp+72h] [bp-42Eh]@1
char v75; // [sp+73h] [bp-42Dh]@1
char v76; // [sp+74h] [bp-42Ch]@1
char v77; // [sp+75h] [bp-42Bh]@1
char v78; // [sp+76h] [bp-42Ah]@1
char v79; // [sp+77h] [bp-429h]@1
char v80; // [sp+78h] [bp-428h]@1
char v81; // [sp+79h] [bp-427h]@1
char v82; // [sp+7Ah] [bp-426h]@1
char v83; // [sp+7Bh] [bp-425h]@1
int v84; // [sp+7Ch] [bp-424h]@54
char v85; // [sp+80h] [bp-420h]@71
int v86; // [sp+480h] [bp-20h]@55
int v87; // [sp+484h] [bp-1Ch]@1
int v88; // [sp+488h] [bp-18h]@1
int v89; // [sp+48Ch] [bp-14h]@1
v88 = a1;
++dword_62330398;
v66 = &unk_6232FF38;
v65 = &v68;
v64 = &unk_6232FF38;
unk_623303A8 = malloc_0(1024);
v63 = sub_6232B17C();
unk_623303AC = sub_6232B4E8(0, v88, unk_623303A4);
v82 = unk_623302EF ^ 0xD7;
v74 = unk_623302E7 ^ 0x9D;
v72 = unk_623302E5 ^ 0xC6;
v77 = unk_623302EA ^ 0xC3;
v80 = unk_623302ED ^ 0xBB;
v69 = unk_623302E2 ^ 0x81;
v78 = unk_623302EB ^ 0xD7;
v75 = unk_623302E8 ^ 0x95;
v70 = unk_623302E3 ^ 0xB5;
v81 = unk_623302EE ^ 0xDA;
v68 = unk_623302E1 ^ 0xF5;
v76 = unk_623302E9 ^ 0x9B;
v79 = unk_623302EC ^ 0xF2;
v73 = unk_623302E6 ^ 0xEA;
v71 = unk_623302E4 ^ 0xB7;
v83 = unk_623302F0;
v87 = opendir(&v68);
v89 = v87;
v67 = (void *)-1296986714;
do
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v62 = v67;
if ( (signed int)v67 > -1437465226 )
break;
if ( v62 == (void *)-1866223142 )
v67 = (void *)812103489;
}
if ( (signed int)v62 > -1296986715 )
break;
if ( v62 == (void *)-1437465225 )
{
*((_BYTE *)&v41 - 7) = byte_62330331;
*((_BYTE *)&v41 - 18) = byte_62330326 ^ 0x8E;
*((_BYTE *)&v41 - 9) = byte_6233032F ^ 0xB4;
*((_BYTE *)&v41 - 19) = byte_62330325 ^ 0xD3;
*((_BYTE *)&v41 - 13) = byte_6233032B ^ 0xBB;
*((_BYTE *)&v41 - 24) = unk_62330320 ^ 0x90;
*((_BYTE *)&v41 - 22) = byte_62330322 ^ 0xE0;
*((_BYTE *)&v41 - 16) = byte_62330328 ^ 0xE9;
*((_BYTE *)&v41 - 11) = byte_6233032D ^ 0xF0;
*((_BYTE *)&v41 - 8) = byte_62330330 ^ 0xB8;
*((_BYTE *)&v41 - 20) = byte_62330324 ^ 0xF7;
*((_BYTE *)&v41 - 12) = byte_6233032C ^ 0xC2;
*((_BYTE *)&v41 - 14) = byte_6233032A ^ 0xED;
*((_BYTE *)&v41 - 17) = byte_62330327 ^ 0x9B;
*((_BYTE *)&v41 - 23) = byte_62330321 ^ 0x84;
*((_BYTE *)&v41 - 15) = byte_62330329 ^ 0xE5;
*((_BYTE *)&v41 - 10) = byte_6233032E ^ 0xDB;
*((_BYTE *)&v41 - 21) = byte_62330323 ^ 0xBC;
v19 = sub_6232B7B8(&v41 - 6);
v20 = (void *)936246879;
v10 = v19 == 0;
v21 = 0;
if ( !v10 )
v21 = 1;
if ( v21 )
v20 = &unk_53451D86;
v67 = v20;
}
}
if ( (signed int)v62 <= 2011994327 )
break;
if ( v62 == (void *)2011994328 )
{
v40 = ((int (__fastcall *)(signed int))unk_623223E4)(1);
v67 = (void *)-1437465225;
v42 = v40;
}
}
if ( (signed int)v62 <= 1892825734 )
break;
if ( v62 == (void *)1892825735 )
{
v16 = v86 + 19;
*((_BYTE *)&v41 - 21) = byte_62330303 ^ 0x83;
*((_BYTE *)&v41 - 17) = byte_62330307 ^ 0x95;
*((_BYTE *)&v41 - 13) = byte_6233030B ^ 0xD5;
*((_BYTE *)&v41 - 20) = byte_62330304 ^ 0xE0;
*((_BYTE *)&v41 - 11) = byte_6233030D ^ 0xB5;
*((_BYTE *)&v41 - 24) = unk_62330300 ^ 0xE4;
*((_BYTE *)&v41 - 3) = byte_62330315 ^ 0xAB;
*((_BYTE *)&v41 - 10) = byte_6233030E ^ 0x93;
*((_BYTE *)&v41 - 9) = byte_6233030F ^ 0xBD;
*((_BYTE *)&v41 - 15) = byte_62330309 ^ 0xE5;
*((_BYTE *)&v41 - 16) = byte_62330308 ^ 0xCB;
*((_BYTE *)&v41 - 22) = byte_62330302 ^ 0xE6;
*((_BYTE *)&v41 - 18) = byte_62330306 ^ 0xC8;
*((_BYTE *)&v41 - 12) = byte_6233030C ^ 0x8F;
*((_BYTE *)&v41 - 23) = byte_62330301 ^ 0x85;
*((_BYTE *)&v41 - 5) = byte_62330313 ^ 0xB1;
*((_BYTE *)&v41 - 6) = byte_62330312 ^ 0xE7;
*((_BYTE *)&v41 - 4) = byte_62330314 ^ 0xE0;
*((_BYTE *)&v41 - 14) = byte_6233030A ^ 0xB7;
*((_BYTE *)&v41 - 19) = byte_62330305 ^ 0x8F;
*((_BYTE *)&v41 - 7) = byte_62330311 ^ 0xAD;
*((_BYTE *)&v41 - 8) = byte_62330310 ^ 0xD1;
*((_BYTE *)&v41 - 2) = byte_62330316;
v57 = sprintf_0(&v85, &v41 - 6, v16);
v17 = add_watch(v84, &v85, 4095);
v67 = (void *)-1866223142;
v56 = v17;
}
}
if ( (signed int)v62 > -1215902312 )
break;
if ( v62 == (void *)-1296986714 )
{
v1 = (void *)-870557634;
v2 = 0;
if ( !v89 )
v2 = 1;
if ( v2 )
v1 = (void *)363810945;
v67 = v1;
}
}
if ( (signed int)v62 > -870557635 )
break;
if ( v62 == (void *)-1215902311 )
{
v32 = (int *)((char *)v66 + 1132);
v33 = (int *)((char *)v66 + 1140);
++*((_DWORD *)v66 + 280);
v34 = *v32;
v46 = *v33;
v35 = sub_6232B4E8(0, v88, v34);
v36 = (void *)2011994328;
v10 = v46 == v35;
v37 = 0;
if ( !v10 )
v37 = 1;
if ( v37 )
v36 = &unk_4791A5A5;
v67 = v36;
}
}
if ( (signed int)v62 > 248389379 )
break;
if ( v62 == (void *)-870557634 )
{
v61 = 4095;
v84 = init();
v60 = v84;
v4 = fcntl_0(v84, 3, 0);
v5 = fcntl_0(v60, 4, v4 | 0x800);
v6 = v84;
*((_BYTE *)&v41 - 14) = byte_623302F3 ^ 0xFB;
*((_BYTE *)&v41 - 7) = byte_623302FA ^ 0xC7;
*((_BYTE *)&v41 - 3) = byte_623302FE ^ 0xA4;
*((_BYTE *)&v41 - 6) = byte_623302FB ^ 0xCD;
*((_BYTE *)&v41 - 8) = byte_623302F9 ^ 0xDF;
*((_BYTE *)&v41 - 12) = byte_623302F5 ^ 0xC2;
*((_BYTE *)&v41 - 11) = byte_623302F6 ^ 0xAD;
*((_BYTE *)&v41 - 9) = byte_623302F8 ^ 0xB5;
*((_BYTE *)&v41 - 10) = byte_623302F7 ^ 0xBB;
*((_BYTE *)&v41 - 16) = unk_623302F1 ^ 0xDF;
*((_BYTE *)&v41 - 5) = byte_623302FC ^ 0xF0;
*((_BYTE *)&v41 - 4) = byte_623302FD ^ 0xE9;
*((_BYTE *)&v41 - 2) = byte_623302FF;
*((_BYTE *)&v41 - 13) = byte_623302F4 ^ 0xEA;
*((_BYTE *)&v41 - 15) = byte_623302F2 ^ 0x93;
v59 = v5;
v7 = add_watch(v6, &v41 - 4, v61);
v67 = (void *)812103489;
v58 = v7;
}
}
if ( (signed int)v62 > 363810944 )
break;
if ( v62 == (void *)248389380 )
{
*((_BYTE *)&v41 - 6) = byte_62330334 ^ 0x99;
*((_BYTE *)&v41 - 5) = byte_62330335 ^ 0xA1;
*((_BYTE *)&v41 - 4) = byte_62330336;
*((_BYTE *)&v41 - 8) = unk_62330332 ^ 0xA3;
*((_BYTE *)&v41 - 7) = byte_62330333 ^ 0xD8;
*((_BYTE *)&v41 - 10) = byte_6233034B ^ 0xE8;
*((_BYTE *)&v41 - 12) = byte_62330349 ^ 0x94;
*((_BYTE *)&v41 - 16) = unk_62330345 ^ 0x9B;
*((_BYTE *)&v41 - 13) = byte_62330348 ^ 0xA8;
*((_BYTE *)&v41 - 14) = byte_62330347 ^ 0x8C;
*((_BYTE *)&v41 - 11) = byte_6233034A ^ 0xDC;
*((_BYTE *)&v41 - 6) = byte_6233034F;
*((_BYTE *)&v41 - 8) = byte_6233034D ^ 0x9B;
*((_BYTE *)&v41 - 7) = byte_6233034E ^ 0xBF;
*((_BYTE *)&v41 - 15) = byte_62330346 ^ 0x89;
*((_BYTE *)&v41 - 9) = byte_6233034C ^ 0x80;
v48 = sub_62322228(6, &v41 - 2, &v41 - 4);
v31 = raise(9);
v67 = (void *)-1215902311;
v47 = v31;
}
}
if ( (signed int)v62 <= 812103488 )
break;
if ( (signed int)v62 > 936246878 )
{
if ( (signed int)v62 > (signed int)&unk_43EA34CA )
{
if ( (signed int)v62 > (signed int)&unk_57D1BE0A )
{
if ( v62 == &unk_57D1BE0B )
{
v18 = closedir(v87);
v67 = (void *)-1437465225;
v55 = v18;
}
}
else if ( (signed int)v62 > (signed int)&unk_53451D85 )
{
if ( v62 == &unk_53451D86 )
{
*((_BYTE *)&v41 - 4) = byte_62330336;
*((_BYTE *)&v41 - 6) = byte_62330334 ^ 0x99;
*((_BYTE *)&v41 - 7) = byte_62330333 ^ 0xD8;
*((_BYTE *)&v41 - 8) = unk_62330332 ^ 0xA3;
*((_BYTE *)&v41 - 5) = byte_62330335 ^ 0xA1;
*((_BYTE *)&v41 - 12) = byte_6233033B ^ 0xB0;
*((_BYTE *)&v41 - 6) = byte_62330341 ^ 0xE3;
*((_BYTE *)&v41 - 14) = byte_62330339 ^ 0xE9;
*((_BYTE *)&v41 - 16) = unk_62330337 ^ 0x94;
*((_BYTE *)&v41 - 10) = byte_6233033D ^ 0xF5;
*((_BYTE *)&v41 - 13) = byte_6233033A ^ 0xEE;
*((_BYTE *)&v41 - 7) = byte_62330340 ^ 0xDE;
*((_BYTE *)&v41 - 8) = byte_6233033F ^ 0x83;
*((_BYTE *)&v41 - 9) = byte_6233033E ^ 0xD8;
*((_BYTE *)&v41 - 3) = byte_62330344;
*((_BYTE *)&v41 - 11) = byte_6233033C ^ 0xD4;
*((_BYTE *)&v41 - 4) = byte_62330343 ^ 0x9C;
*((_BYTE *)&v41 - 5) = byte_62330342 ^ 0xE2;
*((_BYTE *)&v41 - 15) = byte_62330338 ^ 0xCE;
v54 = sub_62322228(6, &v41 - 2, &v41 - 4);
v22 = getpid_0();
v53 = kill_0(v22, 9);
v23 = raise(9);
v24 = *((_DWORD *)v66 - 8) & 0xFFFFF000;
v52 = v23;
v25 = mprotect_0(v24, 0x2000, 3);
v26 = *((_DWORD *)v66 - 8) & 0xFFFFF000;
v51 = v25;
v50 = (void *)936246879;
v49 = v26;
aeabi_memset8(1074606080, 0x2000, 0);
v67 = v50;
}
}
else if ( (signed int)v62 > (signed int)&unk_4B2A5020 )
{
if ( v62 == &unk_4B2A5021 )
{
v12 = (void *)-1866223142;
v13 = 0;
if ( *(_BYTE *)(v86 + 18) & 4 )
v13 = 1;
if ( v13 )
v12 = &unk_43EA34CB;
v67 = v12;
}
}
else if ( v62 == &unk_43EA34CB )
{
v14 = (void *)1892825735;
v15 = 0;
if ( 46 == *(_BYTE *)(v86 + 19) )
v15 = 1;
if ( v15 )
v14 = &unk_45BD5515;
v67 = v14;
}
else if ( v62 == &unk_45BD5515 )
{
v67 = (void *)812103489;
}
else if ( v62 == &unk_4791A5A5 )
{
*((_BYTE *)&v41 - 5) = byte_62330335 ^ 0xA1;
*((_BYTE *)&v41 - 4) = byte_62330336;
*((_BYTE *)&v41 - 6) = byte_62330334 ^ 0x99;
*((_BYTE *)&v41 - 8) = unk_62330332 ^ 0xA3;
*((_BYTE *)&v41 - 7) = byte_62330333 ^ 0xD8;
*((_BYTE *)&v41 - 5) = byte_62330353 ^ 0xB6;
*((_BYTE *)&v41 - 6) = byte_62330352 ^ 0xA8;
*((_BYTE *)&v41 - 8) = unk_62330350 ^ 0xCA;
*((_BYTE *)&v41 - 3) = byte_62330355 ^ 0xBF;
*((_BYTE *)&v41 - 7) = byte_62330351 ^ 0xD6;
*((_BYTE *)&v41 - 2) = byte_62330356;
*((_BYTE *)&v41 - 4) = byte_62330354 ^ 0xD9;
v45 = sub_62322228(6, &v41 - 2, &v41 - 2);
v38 = getpid_0();
v44 = kill_0(v38, 9);
v39 = raise(9);
v67 = (void *)2011994328;
v43 = v39;
}
}
else if ( v62 == (void *)936246879 )
{
v27 = read_0(v84, &v85, 1024);
v28 = (void *)-1215902311;
v10 = v27 == 0;
v29 = v27 < 0;
v30 = 0;
if ( !(v29 | v10) )
v30 = 1;
if ( v30 )
v28 = (void *)248389380;
v67 = v28;
}
}
else if ( v62 == (void *)812103489 )
{
v8 = readdir(v87);
v9 = &unk_57D1BE0B;
v86 = v8;
v10 = v8 == 0;
v11 = 0;
if ( !v10 )
v11 = 1;
if ( v11 )
v9 = &unk_4B2A5021;
v67 = v9;
}
}
}
while ( v62 != (void *)363810945 );
return 0;
}
之后根据偏移定位到JNI_OnLoad函数调用处,这中间好像还有两三处反调试,直接nop掉即可。
在JNI_OnLoad中发现有一个分支再次调用了JNI_OnLoad。
跟进,反汇编有些问题,不贴伪代码了。
其中init函数初始化了一些字符串,register_nativers这个函数大家应该都很熟悉了。
register_nativers函数原型如下:
jint RegisterNatives(jclass clazz,const JNINativeMethod * methods,jint nMethods)
根据参数可知注册了五个函数,函数名分别为。
在java层中首先调用Jni的load函数,load函数获取PID,获取sdk版本,获取vm.version, 判断是art还是dalvik虚拟机,执行不同的加载方案,我这里是dalvik虚拟机,执行方案2。
跟进,在执行解密dex文件前,首先获取原始dex文件的位置,之后获取文件的偏移位置,分配内存,解密的dex文件。在这里我尝试dump,dump出的dex文件没有内容,可能姿势不对,继续单步跟了下去。
根据Log信息定位dex加载到内存中的位置。
分配内存空间,初始化dex内存结构。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!