关于android应用中的签名验证的查找过程
一切的工作全凭自我的摸索和实验,感觉啥问题都能遇到。小米的android手机有个叫做开发版的系统,刷机后自带root,不过感觉权限设置方面不稳定,会有莫名的错误出现。所有工具重启一遍好像有好了。模拟器虽然卡,不过验证一些东西还是可以的。至少不会一条道走到黑。
话题转回签名验证这块,andriod系统做的签名验证知识用来确定签名的存在与否。其实没啥大用,但如果应用本身也存在签名验证,就会有点坑。
蹩脚的思维之路
说实话,我自认为各种逆向思维我都大致有所了解,不过也只限于了解,真用起来感觉自己会的就是两大方法,静态分析,动态分析。至于,栈回溯,要是动态不起来,就用不起来了。然后开始各种找,本例中我就费了半天劲,但回头发现,其实是自己方法不得当。
静态分析开始
开始的时候是要分析一个关键字的加密情况,算法基本很清晰。但就是有一点,就是,有个_ROR_4的符号很奇怪,发现不是函数,那倒是宏。想要动态调试确认一下。
动态调试开始,挂起,呵呵,程序直接退出了。目测有反调试。找反调试的点,在加载动态库的时候终端程序,也中断不了。不科学,难道是因为没有开启debugable,但我目测这里应该不是。我自己写了个没有debugable:true的属性,也可以工作。
疯了。
重打包
开始的时候重打包各种失败,就不说了。最后成功了,发现装上还是运行不了。
要不是我自己谢了个验证的程序重打包可以工作,我还一位签名不能用。所以我断定,应用做了签名验证。各种分析又开始了,其实还是静态分析,各种signatures和getpackageinfo搜索,发现这是一条不归路。密密麻麻麻的搜索结果。
得到一道经验,只有低级的逆向分析者才只会用静态分析,我就位于其中。
这时候小米手机在应用崩溃的过程中的一条消息引起了我的注意。
Abort message: 'art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: jclass has wrong type: com.sina.weibo.WeiboApplication'
r0 00000000 r1 00006284 r2 00000006 r3 b6f6fb7c
r4 b6f6fb84 r5 b6f6fb34 r6 0000000b r7 0000010c
r8 b4abf378 r9 b4c6a800 sl 00000001 fp 00000001
ip 00000006 sp beae77b0 lr b6ccbc69 pc b6cce058 cpsr 40070010
backtrace:
#00 pc 00042058 /system/lib/libc.so (tgkill+12)
#01 pc 0003fc65 /system/lib/libc.so (pthread_kill+32)
#02 pc 0001c403 /system/lib/libc.so (raise+10)
#03 pc 000195b5 /system/lib/libc.so (__libc_android_abort+34)
#04 pc 00017508 /system/lib/libc.so (abort+4)
#05 pc 00333b29 /system/lib/libart.so (_ZN3art7Runtime5AbortEv+228)
#06 pc 000f48bb /system/lib/libart.so (_ZN3art10LogMessageD2Ev+2226)
#07 pc 0025ab2d /system/lib/libart.so (_ZN3art9JavaVMExt8JniAbortEPKcS2_+1552)
#08 pc 0025aedd /system/lib/libart.so (_ZN3art9JavaVMExt9JniAbortVEPKcS2_St9__va_list+64)
#09 pc 000fd491 /system/lib/libart.so (_ZN3art11ScopedCheck6AbortFEPKcz+32)
#10 pc 001014d9 /system/lib/libart.so (_ZN3art11ScopedCheck5CheckERNS_18ScopedObjectAccessEbPKcPNS_12JniValueTypeE.constprop.95+772)
#11 pc 0010627b /system/lib/libart.so (_ZN3art8CheckJNI8ThrowNewEP7_JNIEnvP7_jclassPKc+438)
#12 pc 00002ccb /data/app/com.sina.weibo-1/lib/arm/libutility.so (Java_com_sina_weibo_WeiboApplication_getDecryptionString+24)
#13 pc 03ed2b9d /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (java.lang.String com.sina.weibo.WeiboApplication.getDecryptionString(java.lang.String)+96)
#14 pc 03f78ff5 /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (java.lang.String com.sina.weibo.applicationInit.f.b(java.lang.String)+752)
#15 pc 03f7e98f /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.applicationInit.f.n(android.content.Context)+1066)
#16 pc 03f81e63 /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.applicationInit.f.c(android.content.Context)+950)
#17 pc 03f5f819 /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.applicationInit.c.a(android.content.Context, boolean)+1100)
#18 pc 03f81a7d /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.applicationInit.f.a(android.content.Context, boolean)+64)
#19 pc 03f87289 /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.applicationInit.i.a(android.content.Context)+1028)
#20 pc 03ed26e9 /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.WeiboApplication.f()+516)
#21 pc 03ed3771 /data/app/com.sina.weibo-1/oat/arm/base.odex (offset 0x3954000) (void com.sina.weibo.WeiboApplication.onCreate()+1332)
#22 pc 72cd159b /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x24c2000)
打开发现,原来是崩溃时的栈信息,高兴坏了。沿着这个路线在libutility.so (Java_com_sina_weibo_WeiboApplication_getDecryptionString寻找签名验证的代码。
int __fastcall Java_com_sina_weibo_WeiboApplication_getDecryptionString(int a1, int a2, int a3)
{
int v3; // r6
int v4; // r4
int v5; // r5
v3 = a3;
v4 = a1;
v5 = a2;
if ( !sub_2BA8() )
(*(void (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 56))(v4, v5, 0);
return sub_370E(v4, v5, v3);
}
别看这么几行代码,展开的时候,又到了我怀疑人生的时候了,看了半天哪有签名验证,那倒是因为栈信息所在的线程不对,但后来发现不是的。因为,终止程序的还输就是在这个线程中调用的。所以验证信息肯定也是在这里的。
但当时并不知道,只能理了理思路,发现还是一无所获。
我所在这段代码所在。so文件搜索了下signature,这回有发现了
signture调用的路径中有我们的getDecryptionString,这就说明开始的分析是没有错误的。只是因为这个函数里没用的信息太多了,所以导致没看到关键所在。
所以说,栈回溯之后找不到,之后再静态分析一下,应该就差不多了。
int __fastcall sub_4B7C(int a1, int a2)
{
int v2; // r6
int v3; // r4
int v4; // r5
int v5; // r7
int v6; // r5
int v7; // r6
int (__fastcall *v8)(int, int, int, int, signed int); // ST08_4
int v9; // r0
int v10; // r6
int v12; // r2
int v13; // r7
int v14; // r5
int v15; // r1
int v16; // r0
int v17; // r6
int v18; // r0
int v19; // r5
int v20; // r0
int v21; // ST0C_4
int v22; // [sp+8h] [bp-28h]
int v23; // [sp+10h] [bp-20h]
int v24; // [sp+14h] [bp-1Ch]
v2 = a2;
v3 = a1;
v24 = (*(int (**)(void))(*(_DWORD *)a1 + 24))();
if ( !v24 )
return 0;
v4 = (*(int (__fastcall **)(int, int, const char *, const char *))(*(_DWORD *)v3 + 132))(
v3,
v24,
"getPackageManager",
"()Landroid/content/pm/PackageManager;");
if ( !v4 )
return 0;
v23 = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 24))(v3, "android/content/pm/PackageManager");
if ( !v23 )
return 0;
v5 = (*(int (__fastcall **)(int, int, int))(*(_DWORD *)v3 + 136))(v3, v2, v4);
if ( !v5 )
return 0;
v6 = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 24))(v3, "android/content/pm/PackageInfo");
if ( !v6 )
return 0;
v7 = (*(int (__fastcall **)(int, int, const char *, const char *))(*(_DWORD *)v3 + 132))(
v3,
v23,
"getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
if ( !v7 )
return 0;
v8 = *(int (__fastcall **)(int, int, int, int, signed int))(*(_DWORD *)v3 + 136);
v9 = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 668))(v3, "com.sina.weibo");
v10 = v8(v3, v5, v7, v9, 64);
if ( (*(int (__fastcall **)(int))(*(_DWORD *)v3 + 60))(v3) )
{
(*(void (__fastcall **)(int))(*(_DWORD *)v3 + 68))(v3);
return 0;
}
if ( !v10 )
return 0;
v12 = (*(int (__fastcall **)(int, int, const char *, const char *))(*(_DWORD *)v3 + 376))(
v3,
v6,
"signatures",
"[Landroid/content/pm/Signature;");
if ( !v12 )
return 0;
v22 = (*(int (__fastcall **)(int, int, int))(*(_DWORD *)v3 + 380))(v3, v10, v12);
if ( !v22 )
return 0;
v13 = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 24))(v3, "android/content/pm/Signature");
if ( !v13 )
return 0;
v14 = (*(int (__fastcall **)(int, int, const char *, const char *))(*(_DWORD *)v3 + 132))(
v3,
v13,
"toByteArray",
"()[B");
if ( !v14 )
return 0;
v15 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v3 + 692))(v3, v22, 0);
if ( !v15 )
return 0;
v16 = (*(int (__fastcall **)(int, int, int))(*(_DWORD *)v3 + 136))(v3, v15, v14);
v17 = v16;
if ( !v16 )
return 0;
v18 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v3 + 736))(v3, v16, 0);
v19 = v18;
v20 = j_strlen(v18);
v21 = j_malloc(v20 + 1);
j_strcpy(v21, v19);
(*(void (__fastcall **)(int, int, int, _DWORD))(*(_DWORD *)v3 + 768))(v3, v17, v19, 0);
(*(void (__fastcall **)(int, int))(*(_DWORD *)v3 + 92))(v3, v22);
(*(void (__fastcall **)(int, int))(*(_DWORD *)v3 + 92))(v3, v13);
(*(void (__fastcall **)(int, int))(*(_DWORD *)v3 + 92))(v3, v24);
(*(void (__fastcall **)(int, int))(*(_DWORD *)v3 + 92))(v3, v23);
return v21;
}
最终定位到这里,应该差不多了。
小结
这些天的一些逆向经验告诉我,千万不要静态分析一条路走到黑,你可以从静态分析开始,但千万不要在静态分析上一味的走。要会用栈信息。
其实这期间我还学习如何在程序中发出相应的栈信息。可以通过异常,日志,还有个traceview的工具。
我在其他的博客上有记录了,如下:
http://blog.sina.com.cn/s/blog_a3f364920102x3zx.html