本来想从第一行开始看起,但是突然发现下面有一行
[code]parse_dex((_JNIEnv *)env, &v43);[/code]
解析dex?
那这个估计是重点,跟进去
[code]signed int fastcall parse_dex(_JNIEnv *a1, int64 a2)
{
int v2; // r7@3
char v3; // r0@4
const char v4; // r1@4
char v5; // r0@6
char v6; // r9@16
_BYTE v7; // r3@17
int v8; // r2@17
int v9; // t1@19
int fd; // ST14_4@23
unsigned int v11; // r8@24
int v12; // r7@24
int v13; // r5@26
int v14; // r5@25
int v15; // r7@25
int v16; // r1@25
int v17; // r5@25
int v18; // ST04_4@27
int (fastcall *v19)(int, signed int); // r5@27
int v20; // r5@27
unsigned int8 v21; // r8@27
const char v22; // r3@27
char v23; // r0@29
char v24; // r0@28
char v25; // r6@30
ali::EncFile v26; // r7@30
int v27; // r0@31
int v28; // r0@34
char v29; // r0@34
int v30; // r10@35
void v31; // r7@36
int (__fastcall v32)(unsigned int8 , int, signed int ); // r9@36
int (fastcall v33)(_DWORD); // r7@36
const char v34; // r1@37
const char v35; // r2@37
int v36; // r9@38
signed int v37; // r7@38
_DWORD v38; // r9@41
_BYTE v39; // r5@41
unsigned __int8 v40; // r3@41
void v41; // r0@42
JNINativeMethod v42; // r0@42
unsigned int8 v43; // r3@42
signed int v44; // r3@42
signed int result; // r0@44
_JNIEnv env; // [sp+8h] [bp-2A0h]@1
int64 v47; // [sp+10h] [bp-298h]@1
int v48; // [sp+24h] [bp-284h]@30
unsigned __int8 v49; // [sp+28h] [bp-280h]@30
unsigned int8 *v50; // [sp+2Ch] [bp-27Ch]@30
void (cdecl v51)(const unsigned int , jvalue ); // [sp+30h] [bp-278h]@42
char v52; // [sp+34h] [bp-274h]@3
signed int v53[2]; // [sp+38h] [bp-270h]@17
char s; // [sp+40h] [bp-268h]@23
char v55; // [sp+54h] [bp-254h]@2
int v56; // [sp+64h] [bp-244h]@23
int v57; // [sp+68h] [bp-240h]@23
char v58; // [sp+6Ch] [bp-23Ch]@2
const char v59; // [sp+80h] [bp-228h]@2
char v60; // [sp+84h] [bp-224h]@2
const char v61; // [sp+98h] [bp-210h]@25
char v62; // [sp+9Ch] [bp-20Ch]@27
unsigned int v63; // [sp+B0h] [bp-1F8h]@27
char v64; // [sp+B4h] [bp-1F4h]@3
char v65; // [sp+CCh] [bp-1DCh]@6
int v66; // [sp+E0h] [bp-1C8h]@23
char v67; // [sp+E4h] [bp-1C4h]@23
char v68; // [sp+FCh] [bp-1ACh]@23
const char v69; // [sp+110h] [bp-198h]@23
char v70; // [sp+114h] [bp-194h]@23
char v71; // [sp+12Ch] [bp-17Ch]@23
const char v72; // [sp+140h] [bp-168h]@23
char v73; // [sp+144h] [bp-164h]@23
char v74; // [sp+154h] [bp-154h]@23
char v75; // [sp+158h] [bp-150h]@23
char v76; // [sp+15Ch] [bp-14Ch]@23
char v77; // [sp+174h] [bp-134h]@23
char v78; // [sp+18Ch] [bp-11Ch]@23
char v79; // [sp+1A4h] [bp-104h]@23
char v80; // [sp+1BCh] [bp-ECh]@23
char v81; // [sp+1D4h] [bp-D4h]@23
char v82; // [sp+1ECh] [bp-BCh]@23
char v83; // [sp+204h] [bp-A4h]@23
char v84; // [sp+21Ch] [bp-8Ch]@23
char v85; // [sp+234h] [bp-74h]@17
int v86; // [sp+244h] [bp-64h]@17
_BYTE v87; // [sp+248h] [bp-60h]@17
char v88; // [sp+24Ch] [bp-5Ch]@22
char v89; // [sp+264h] [bp-44h]@22
char v90; // [sp+274h] [bp-34h]@22
char v91; // [sp+278h] [bp-30h]@22
int v92; // [sp+27Ch] [bp-2Ch]@1
env = a1;
v47 = a2;
v92 = _stack_chk_guard;
_android_log_print(3, "debug", "enter parse_dex");
if ( ali::isDalvik )
{
v48 = 0;
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v89, &ali::g_filePath, "/cls.jar");
v25 = v91;
v26 = operator new(0xCu);
ali::EncFile::EncFile(v26, v25);
v49 = 0;
v50 = 0;
if ( ali::sdk_int > 13 )
{
v27 = ali::EncFile::openWithHeader(v26, &v49, &v48, 0x10u);
v50 = v49 + 16;
}
else
{
v27 = ali::EncFile::open(v26, &v50, &v48);
}
if ( v50 == -1 )
{
v28 = _errno(v27);
v29 = strerror(v28);
_android_log_print(3, "debug", "mmap dex file :%s", v29);
LABEL_45:
v24 = &v89;
goto LABEL_46;
}
v30 = (v50 + 8);
if ( ali::sdk_int > 13 )
{
v41 = dlopen("libdvm.so", 1);
v42 = dlsym(v41, "dvm_dalvik_system_DexFile");
v51 = 0;
lookup(v42, "openDexFile", "([B)I", &v51);
v43 = v49;
(v49 + 2) = v48;
&v52 = v43;
(v51)();
v44 = v53[0];
v47 = v53[0];
(((v44 + 8) + 4) + 32) = (v44 + 16);
(((v44 + 8) + 4) + 36) = v48;
ali::EncFile::~EncFile(v26);
operator delete(v26);
}
else
{
v31 = dlopen("libdvm.so", 1);
v32 = dlsym(v31, "dvmDexFileOpenPartial");
v33 = dlsym(v31, "dexCreateClassLookup");
v53[0] = 0;
if ( v32(v50, v30, v53) == -1 )
{
v34 = "debug";
v35 = "dvmDexFileOpenPartial error";
LABEL_40:
_android_log_print(3, v34, v35);
goto LABEL_45;
}
v36 = v53[0];
(v36 + 36) = v33(v53[0]);
v37 = v53[0];
if ( !(v53[0] + 36) )
{
v34 = "debug";
v35 = "dexCreateClassLookup error";
goto LABEL_40;
}
v38 = malloc(0x2Cu);
v39 = malloc(0x14u);
strdup(&unk_4CEE9);
v39[4] = 0;
v39[5] = 0;
(v39 + 2) = 0;
v40 = v50;
v39 = v39;
(v39 + 3) = v38;
v38[10] = v37;
(v37 + 32) = v40;
(v37 + 36) = v48;
v47 = v39;
}
v23 = &v89;
goto LABEL_44;
}
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v55, &ali::g_filePath, "/cls.jar");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v58, &ali::g_filePath, "/cls.dex");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v60, &ali::g_filePath, "/fak.jar");
_android_log_print(3, "debug", "before oat gen");
if ( !access(v59, 0) )
goto LABEL_24;
v2 = android_getCpuFamily();
std::string::string(&v64, "arm", &v52);
if ( v2 == 1 )
{
v3 = &v64;
v4 = "arm";
LABEL_5:
std::string::operator=(v3, v4);
goto LABEL_6;
}
if ( v2 == 2 )
{
v3 = &v64;
v4 = "x86";
goto LABEL_5;
}
if ( v2 == 3 || v2 == 6 )
{
v3 = &v64;
v4 = "mips";
goto LABEL_5;
}
if ( v2 == 4 )
{
v3 = &v64;
v4 = "arm64";
goto LABEL_5;
}
if ( v2 == 5 )
{
v3 = &v64;
v4 = "x86_64";
goto LABEL_5;
}
LABEL_6:
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v65, &ali::g_libPath, "/libhack.so");
v5 = getenv("LD_PRELOAD");
v6 = v5;
if ( v5 )
{
_android_log_print(3, "debug", "the system already define LD_PRELOAD=%s", v5);
std::string::string(&v85, v6, v53);
v7 = v87;
v8 = v86;
while ( v7 != v8 )
{
v9 = v7++;
if ( v9 == 32 )
(v7 - 1) = 58;
}
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v88, &v85, ":");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v89, &v88, &v65);
std::string::_M_assign(&v65, v91, v90);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v89);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v88);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v85);
}
_android_log_print(3, "debug", "the new LD_PRELOAD is %s", v66);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v67, &ali::g_filePath, "/juice.data");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v68, &ali::g_filePath, "/fak.jar");
fd = open(v69, 0);
memset(&s, 0, 0x14u);
sprintf(&s, "%d", fd);
std::string::string(&v70, &s, v53);
v74 = &v73;
v75 = &v73;
std::priv::_String_base<char,std::allocator<char>>::_M_allocate_block(&v73, v56 - v57 + 10);
v74 = 0;
std::string::_M_appendT<char const>(&v73);
std::string::append(&v73, &v55);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v76, &v73, " JUICE_FILE=");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v77, &v76, &v67);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v78, &v77, " LD_PRELOAD=");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v79, &v78, &v65);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(
&v80,
&v79,
" /system/bin/dex2oat \t\t\t\t \t --runtime-arg -Xms64m \t\t\t\t\t --runtime-arg -Xmx64m \t\t\t\t"
"\t --boot-image=/system/framework/boot.art --zip-fd=");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v81, &v80, &v70);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v82, &v81, "\t\t\t\t\t --zip-location=");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v83, &v82, &v68);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v84, &v83, "\t\t\t\t\t --oat-file=");
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v85, &v84, &v58);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v71, &v85, " ");
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v85);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v84);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v83);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v82);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v81);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v80);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v79);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v78);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v77);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v76);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v73);
_android_log_print(3, "debug", "cmd is %s", v72);
system(v72);
close(fd);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v71);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v70);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v68);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v67);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v65);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v64);
LABEL_24:
_android_log_print(3, "debug", "after oat gen");
v11 = ali::JDexFile;
v12 = unk_54140;
if ( ali::sdk_int <= 19 )
{
v13 = _JNIEnv::NewStringUTF(env, v61);
_JNIEnv::NewStringUTF(env, v59);
v15 = _JNIEnv::CallStaticIntMethod(env, v11, v12, v13);
v17 = 0;
}
else
{
v14 = _JNIEnv::NewStringUTF(env, v61);
_JNIEnv::NewStringUTF(env, v59);
v15 = _JNIEnv::CallStaticLongMethod(env, v11, v12, v14);
v17 = v16;
}
v18 = v17;
_android_log_print(3, "debug", "cookie is %llx");
v47 = v15;
(v47 + 1) = v17;
v19 = dlsym(0xFFFFFFFF, "_ZNK3art7DexFile12FindClassDefEt");
_android_log_print(3, "debug", "DexFile::FindClassDefFn is %p", v19, v15, v18);
v20 = v19(v15, 1);
_android_log_print(3, "debug", "call FindClassDefFn(%p,%d) => %p", v15, 1, v20);
_android_log_print(3, "debug", "dex position is %p", v20 - 572);
_android_log_print(3, "debug", "dex head is %08x %08x", (v20 - 572), (v20 - 568));
v21 = (v20 - 540);
_android_log_print(3, "debug", "dex size is %d", v21);
MemEnableWrite((v20 - 572), &v21[v20 - 572]);
std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v62, &ali::g_filePath, "/juice.data");
if ( !ali::dex_juicer_patch((v20 - 572), v21, v63, v22) )
{
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v62);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v60);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v58);
v23 = &v55;
LABEL_44:
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(v23);
_android_log_print(3, "debug", "exit parse_dex");
result = 0;
goto LABEL_47;
}
_android_log_print(6, "debug", "fail to patch dex");
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v62);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v60);
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(&v58);
v24 = &v55;
LABEL_46:
std::priv::_String_base<char,std::allocator<char>>::_M_deallocate_block(v24);
_android_log_print(3, "debug", "exit parse_dex error");
result = -1;
LABEL_47:
if ( v92 != _stack_chk_guard )
_stack_chk_fail(result);
return result;
}[/code]
惊喜的发现还有好多log啊(^__^)
整理一下看看
[code]signed int fastcall parse_dex(_JNIEnv *a1, int64 a2)
{
_android_log_print(3, "debug", "enter parse_dex");
if ( ali::isDalvik )
{
if ( v50 == -1 )
{
_android_log_print(3, "debug", "mmap dex file :%s", v29);
LABEL_45:
goto LABEL_46;
}
if ( ali::sdk_int > 13 )
{}
else
{
if ( v32(v50, v30, v53) == -1 )
{
v34 = "debug";
v35 = "dvmDexFileOpenPartial error";
LABEL_40:
_android_log_print(3, v34, v35);
goto LABEL_45;
}
if ( !(v53[0] + 36) )
{
v34 = "debug";
v35 = "dexCreateClassLookup error";
goto LABEL_40;
}
}
goto LABEL_44;
}
_android_log_print(3, "debug", "before oat gen");
LABEL_6:
if ( v5 )
{
_android_log_print(3, "debug", "the system already define LD_PRELOAD=%s", v5);
}
_android_log_print(3, "debug", "the new LD_PRELOAD is %s", v66);
_android_log_print(3, "debug", "cmd is %s", v72);
LABEL_24:
_android_log_print(3, "debug", "after oat gen");
_android_log_print(3, "debug", "cookie is %llx");
_android_log_print(3, "debug", "DexFile::FindClassDefFn is %p", v19, v15, v18);
_android_log_print(3, "debug", "call FindClassDefFn(%p,%d) => %p", v15, 1, v20);
_android_log_print(3, "debug", "dex position is %p", v20 - 572);
_android_log_print(3, "debug", "dex head is %08x %08x", (v20 - 572), *(v20 - 568));
_android_log_print(3, "debug", "dex size is %d", v21);
if ( !ali::dex_juicer_patch((v20 - 572), v21, v63, v22) )
{
LABEL_44:
_android_log_print(3, "debug", "exit parse_dex");
goto LABEL_47;
}
_android_log_print(6, "debug", "fail to patch dex");
LABEL_46:
_android_log_print(3, "debug", "exit parse_dex error");
LABEL_47:
}[/code]
稍微的保留了一下if-else逻辑,第一个log很明确的显示了开始解析dex
入口处判断了是否是Dalvik模式,这个变量在init_classes()方法里有进行赋值操作
[code]ali::isDalvik = 1;
ali::isArt = 0;[/code]
我们这里使用Dalvik模式来分析,包括接下来动态调试脱壳也一样是使用Dalvik模式
根据逻辑整理一下Dalvik模式下的执行流程
[code]signed int fastcall parse_dex(_JNIEnv *a1, int64 *a2)
{
env = a1;
v47 = a2;
v92 = _stack_chk_guard;
_android_log_print(3, "debug", "enter parse_dex");
if ( ali::isDalvik )
{
v48 = 0;
std::operator+<char, std::char_traits<char>, std::allocator<char>>(&v89, &ali::g_filePath, "/cls.jar");
v25 = v91;
v26 = operator new('\f');
ali::EncFile::EncFile(v26, v25);
v49 = 0;
v50 = 0;
if ( ali::sdk_int > 13 )
{
v27 = ali::EncFile::openWithHeader(v26, &v49, &v48, 0x10u);
v50 = v49 + 16;
}
else
{
v27 = ali::EncFile::open(v26, &v50, &v48);
}
v30 = (v50 + 8);
if ( ali::sdk_int > 13 )
{
v41 = dlopen("libdvm.so", 1);
v42 = dlsym(v41, "dvm_dalvik_system_DexFile");
v51 = 0;
lookup(v42, "openDexFile", "([B)I", &v51);
v43 = v49;
(v49 + 2) = v48;
&v52 = v43;
(v51)();
v44 = v53[0];
v47 = v53[0];
(((v44 + 8) + 4) + 32) = (v44 + 16);
((*(v44 + 8) + 4) + 36) = v48;
ali::EncFile::~EncFile(v26);
operator delete(v26);
}
v23 = &v89;
goto LABEL_44;
}
LABEL_44:
std::priv::_String_base<char, std::allocator<char>>::_M_deallocate_block(v23);
_android_log_print(3, "debug", "exit parse_dex");
result = 0;
goto LABEL_47;
LABEL_47:
if ( v92 != _stack_chk_guard )
_stack_chk_fail(result);
return result;
}[/code]
如果当前手机系统是Dalvik模式且sdk_int大于13,执行
[code]v27 = ali::EncFile::openWithHeader(v26, &v49, &v48, 0x10u);[/code]
如果sdk_int小于13,执行
[code]v27 = ali::EncFile::open(v26, &v50, &v48);[/code]
sdk_int等于13那得安卓3.x了,大家应该都是至少安卓4.x,所以这里进入openWithHeader()方法
[code]int fastcall ali::EncFile::openWithHeader(ali::EncFile *this, unsigned int8 a2, unsigned int a3, unsigned int a4)
{
ali::EncFile v4; // r5@1
unsigned __int8 v5; // r11@1
unsigned int v6; // r6@1
unsigned int v7; // r7@1
const char v8; // r1@2
const char v9; // r2@2
int v10; // r8@3
int v11; // r10@3
__blksize_t v13; // r3@6
ali v14; // r4@6
JNIEnv v15; // r0@6
int v16; // r1@6
int v17; // r2@6
signed __int64 v18; // ST18_8@6
unsigned int v19; // r3@6
JNIEnv v20; // r0@6
int v21; // r1@6
int v22; // r2@6
signed int64 v23; // r0@6
int v24; // r8@6
int v25; // r3@6
char v26; // r2@7
unsigned int64 v27; // r0@7
JNIEnv v28; // r0@8
int v29; // r9@8
JNIEnv v30; // ST24_4@8
int v31; // r1@8
int v32; // r2@8
signed int64 v33; // r0@8
signed int64 v34; // ST18_8@8
JNIEnv v35; // r0@8
int v36; // r1@8
int v37; // r2@8
signed __int64 v38; // r0@8
size_t v39; // [sp+2Ch] [bp-9Ch]@8
int v40; // [sp+30h] [bp-98h]@8
struct stat buf; // [sp+38h] [bp-90h]@3
v4 = this;
v5 = a2;
v6 = a3;
v7 = a4;
if ( !(this + 2) )
{
v8 = "debug";
v9 = "file path is null";
LABEL_5:
_android_log_print(6, v8, v9);
return 0;
}
v10 = open((this + 2), 0);
v11 = fstat(v10, &buf);
if ( v11 )
{
v8 = "debug";
v9 = "fstat failed";
goto LABEL_5;
}
v13 = buf.st_blksize;
v6 = buf.st_blksize;
v4 = v13;
v14 = mmap(0, v6, 3, 2, v10, 0);
(v4 + 1) = v14;
close(v10);
v15 = _android_log_print(
3,
"debug",
"dex magic %c %c %c %c %c %c %c",
v14,
(v14 + 1),
(v14 + 2),
(v14 + 3),
(v14 + 4),
(v14 + 5),
(v14 + 6));
v18 = ali::NanoTime(v15, v16, v17);
ali::decryptRc4(v14, v14, v6, v19);
v23 = ali::NanoTime(v20, v21, v22);
ali::PrettyDuration((v23 - v18), v23 - v18);
_android_log_print(3, "debug", "decrypted len:%u", v6);
v24 = 0;
_android_log_print(
3,
"debug",
"after decrypt dex magic %c %c %c %c %c %c %c",
v14,
(v14 + 1),
(v14 + 2),
(v14 + 3),
(v14 + 4),
(v14 + 5),
(v14 + 6));
v25 = v14 + 4;
do
{
v26 = 8 v11++;
v27 = (v25++ + 1) << v26;
v24 += v27;
}
while ( v11 != 8 );
_android_log_print(3, "debug", "unpackSize: %u", v24);
v4 = v7 + v24;
v28 = mmap(0, v7 + v24, 3, 34, -1, 0);
(v4 + 1) = v28;
v29 = v28 + v7;
v30 = v28;
v33 = ali::NanoTime(v28, v31, v32);
v39 = v6;
v34 = v33;
v40 = v24;
v35 = LzmaDecode(v29, &v40, v14 + 13, &v39);
v38 = ali::NanoTime(v35, v36, v37);
ali::PrettyDuration((v38 - v34), v38 - v34);
munmap(v14, buf.st_blksize);
_android_log_print(
3,
"debug",
"after uncompressed dex magic %c %c %c %c %c %c %c",
(v30 + v7),
(v29 + 1),
(v29 + 2),
(v29 + 3),
(v29 + 4),
(v29 + 5),
(v29 + 6));
v6 = v24;
if ( v5 )
v5 = (v4 + 1);
return *(v4 + 1);
}[/code]
简单上下浏览一下,还是比较简单的,可以看到两个比较关键的字眼ali::decryptRc4()和LzmaDecode()
RC4不用说,Lzma是一个压缩解压相关的算法
第一想法是等这两个方法运行完后,看一下处理过后的数据区域是什么样的,这是一个关键的地方
而且这代码里有两个log,第一个是解密后的dexmagic,第二个是解压过后的dexmagic,所以这两处的log可以看到非常关键的信息
或者说,在openWithHeader()这个方法结束后,查看返回值,可以看到一些比较有用的数据
回到parse_dex()方法,接下来还是通过判断sdk_int是否大于13来决定程序执行流程
因为我们是Dalvik模式,所以进入if,这个分支执行的就是解析完dex之后的代码
这部分执行完就退出了parse_dex()函数
再根据attachBaseContext()函数的log信息,可以看到这里差不多就是解析完成dex了
那么这些都是我们在静态分析时的猜想,肯定有对的,有错的,都是个人思路
至于哪些是对的,哪些是错的,我们接下来进行动态调试验证一下
0x03 动态调试libmobisec.so
准备一个Root过后的Android系统手机,系统是4.x
先将IDA的dbgsrv文件夹里的android_server复制到手机的/data/local
[code]C:\Users\wangz\Desktop>adb push android_server /data/local[/code]
打开/data/local
[code]C:\Users\wangz\Desktop>adb shell
root@jflte:/ #cd data/local[/code]
然后加权限
[code]root@jflte:/data/local #chmod 777 android_server[/code]
安装jscrack.apk
[code]C:\Users\wangz\Desktop>adb install jscrack.apk
[100%] /data/local/tmp/jscrack.apk
WARNING: linker: app_process has text relocations. This is wasting memory and is a security risk. Please fix.
WARNING: linker: app_process has text relocations. This is wasting memory and is a security risk. Please fix.
pkg: /data/local/tmp/jscrack.apk
Success[/code]
开启android_server服务
[code]root@jflte:/data/local # ./android_server
IDA Android 32-bit remote debug server(ST) v1.19. Hex-Rays (c) 2004-2015
Listening on port #23946...[/code]
这个cmd窗口可以放一边了,接下来讲讲调试Android应用的小Tip
我们要想调试Android应用,就要在AndroidManifest.xml插入
[code]android:debuggable="true"[/code]
这种办法有些情况不太好用,想修改AndroidManifest.xml,就避免不了反编译和重打包,但是有些壳做了对抗apktool这类工具的保护,比如鹅厂的乐固,虽然ShaKaapktool可以解决这些问题但是经常会有各式各样的错误出现
所以这里介绍一种个人觉得还是很好用的工具mprop,这个工具搜一下就可以搜到
Android系统在跑起来后,会将default.prop文件里的配置信息写进内存
[code]C:\Users\wangz>adb shell
root@jflte:/ # cat default.prop
#
ADDITIONAL_DEFAULT_PROPERTIES
#
ro.secure=0
ro.allow.mock.location=0
ro.debuggable=0
ro.adb.secure=0
persist.sys.usb.config=mtp,adb
persist.radio.add_power_save=1
androidboot.selinux=0[/code]
这个文件里ro.debuggable字段的值为0,它的作用就是用来标记是否可调试,当这个字段为1的时候,说明所有应用都可以调试
我们来看正常情况下内存里这个字段的信息
[code]root@jflte:/data # getprop ro.debuggable
0[/code]
我们来执行这个文件,新开cmd窗口
[code]root@jflte:/data # ./mprop ro.debuggable 1
......
0001ffa0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0001ffb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0001ffc0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0001ffd0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0001ffe0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0001fff0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
patching it at:0xb6e48290[0xb6f8d288], value:[0x00000031 -> 0x00000031]
patching it at:0xb6e4828c[0xb6f8d284], value:[0x01000000 -> 0x01000000]
patched, reread: [0xb6f8d288] => 0x00000031
patched, reread: [0xb6f8d284] => 0x01000000[/code]
再看内存里的信息
[code]root@jflte:/data # getprop ro.debuggable
1[/code]
要说明一下的是,这个只是对内存中的值做一个patch,并不会修改default.prop文件的信息,所以在系统重启后内存里的值依旧会变回来
打开Android Device Monitor
新开cmd,开启端口转发
[code]C:\Users\wangz>adb forward tcp:23946 tcp:23946[/code]
调试模式打开应用
[code]C:\Users\wangz>adb shell am start -D -n com.ali.tg.testapp/com.ali.tg.testapp.MainActivity
WARNING: linker: app_process has text relocations. This is wasting memory and is a security risk. Pl
ease fix.
WARNING: linker: app_process has text relocations. This is wasting memory and is a security risk. Pl
ease fix.
Starting: Intent { cmp=com.ali.tg.testapp/.MainActivity }[/code]
此时手机上应该出现Waiting For Debugger
新开IDA,attach进程

如图勾选三个选项

勾上红框里三个选项
在接下来出现的框里选择本次要调试的app

跑一会儿会断下
jdb挂上
[code]C:\Users\wangz>jdb -connect com.sun.jdi.SocketAttach:port=8700,hostname=localhost[/code]
挂上后点击IDA左上角的三角形运行程序再跑起来,又断下,来设置断点
查看Segments

可以看到libmobisec.so在内存里的加载地址是0x776E9000

回到最开始我们静态分析的IDA界面,我们在这里下个断点,偏移是0x00026A84,那么加上在内存里的加载基地址就是0x7770FA84
[code]text:00026A84 01 F0 B4 FE BL _ZN3ali7EncFile14openWithHeaderEPPhPjj; ali::EncFile::openWithHeader(uchar *,uint ,uint)[/code]
使用快捷键g跳转到这个地址,按一下C就可以看到代码了,下好断点

F9跑起来,断下,F7跟进去,按p创建函数

这里有个小技巧,如果想直观的看函数调用图的话,可以在函数入口第一句下断点,这样断下后,直接按p就可以创建函数,按一下空格就可以看到整个比较完整的调用图了
F8跑起来,中间各种log输出,使用tag:debug即可
在解压函数执行完后我们惊喜的发现

dex的magicnumber出现了,那么很大情况已经dex已经被释放了,我们执行完这个函数,返回

查看R0所指向的内存区域,发现确实已经释放了dex
这时候我们可以高高兴兴的写个脚本dump这个dex了
0x04 Dump源dex文件
dump脚本,dex长度在偏移0x20的位置
[code]auto fp, begin, end, dexbyte;
fp = fopen("E:\dump.dex", "wb");
begin = 0x77841010;
end = begin + 0x000941FC;
for ( dexbyte = begin; dexbyte < end; dexbyte ++ )
fputc(Byte(dexbyte), fp);[/code]
执行

跑完脚本后我们可以在目录下看到一个dump.dex,使用JEB查看
可以看到效果不错

那么到这里,其实可以先告一段落了,东扯西扯讲了一大堆,最终还是把真实dex脱下来了,但是我们来思考一下,网上也有不少关于阿里加固这个版本的脱壳文章,他们并非在此处下断点,而是在libdvm.so里的dvmDexFileOpenPartial()函数下断
我们来对比一下这两种脱壳方法的不同:首先是断点下的不一样,我们需要在libmobisec.so加载起来后,通过计算相关函数偏移下断点,而dvmDexFileOpenPartial()是在libdvm.so中,只需要在attach上进程后直接在加载的Module list里找到libdvm.so,然后找到dvmDexFileOpenPartial()函数下断点,跑起来断下即可脱壳,总体来说还是第二种方法比较简单
源码路径
[code]/dalvik/vm/DvmDex.cpp[/code]
第一个参数是内存里dex的起始地址,第二个参数是dex长度,所以有的脱壳脚本直接就是用R0和R1的值来执行,因为R0就是起始地址,R1就是dex长度
前面调用dexFileParse()函数解析程序的dex文件,后面还有allocateAuxStructures()函数用于设置辅助的数据字段等等,这一系列的解析优化和验证过程还是比较复杂的,我们暂时不需要这部分知识,就不过多的讲解,感兴趣的同学可以先去阅读Android的相关源码,以后我们会详细的来讲解这部分的内容
[code]/*
- Create a DexFile structure for a "partial" DEX. This is one that is in
- the process of being optimized. The optimization header isn't finished
- and we won't have any of the auxillary data tables, so we have to do
- the initialization slightly differently.
*
- Returns nonzero on error.
/
int dvmDexFileOpenPartial(const void addr, int len, DvmDex* ppDvmDex)
{
DvmDex pDvmDex;
DexFile* pDexFile;
int parseFlags = kDexParseDefault;
int result = -1;
/ -- file is incomplete, new checksum has not yet been calculated
if (gDvm.verifyDexChecksum)
parseFlags |= kDexParseVerifyChecksum;
/
pDexFile = dexFileParse((u1*)addr, len, parseFlags);
if (pDexFile == NULL) {
ALOGE("DEX parse failed");
goto bail;
}
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
goto bail;
}
pDvmDex->isMappedReadOnly = false;
*ppDvmDex = pDvmDex;
result = 0;
bail:
return result;
}[/code]
0x05 修复Application
使用AndroidKiller反编译加固后的apk,找到AndroidManifest.xml,删除Application的android:name
[code]android:name="com.ali.mobisecenhance.StubApplication"[/code]
回编译后,找到生成的apk,压缩软件打开,替换我们dump出来的classes.dex,同时删除assets文件夹,至于lib文件夹,因为这个样本自身带了so,所以留着不去动好了
修改完后重新签名,安装运行,妥妥的
0x06 小结
这种文章写起来确实是很不容易,因为很多东西都要写的很详细,而且都需要结合源码来讲解,所以本文贴了比较多的源码,希望大家在看到源码的时候不要跳过去,稍微看一下源码是怎么写的,很多东西或者说有相当一部分东西很难讲解清楚但是看源码就会有一个很直观的理解,有些同学看完这篇文章可能会有疑问,我在这来先回答一部分:有些壳确实是会检测android_server,所以需要改一下名字,比如as,然后端口检测,数字壳会检测23946端口,这个端口是android_server在监听,所以我们需要在执行android_server的时候加上参数-p1995,这样就可以绕过端口检测,有的so会进行混淆,或者对so加固,还有一些反调试这个样本并没有使用到,所以我就没有提及,脱壳点不止这一处,后面还有,脱壳方法需要根据样本实际情况来,这些知识点我是希望在以后的样本里慢慢的写,所以想学习脱壳又不知道怎么学习的同学把这篇文章好好的理解一下,一定是能够学习到不少东西的,整体dump现在已经不存在了,之所以第一篇讲这些,是希望能有一种循序渐进学习的感觉,如果第一篇就讲阿里近期的加固,一来很费劲,二来大伙看着也比较晕
预告一下:下一篇文章的样本会出现阿里加固非常明显的特征
本文提供pdf和文中提到的Android相关源码下载,pdf的阅读体验比较好
最后,我已经尽量优化文章的阅读体验了,包括代码的缩进等等,但是效果好像不是很好,所以大伙如果觉得网页版阅读起来费劲的话,可以下载pdf版本
看不懂,略过,前面卡壳就没办法进行下去了,我回去找找表哥的视频学习一下