发新帖

《2015移动安全挑战赛----自毁程序密码(第二题)》分析

[复制链接]
32906 17
本帖最后由 nsnj 于 2015-1-29 12:38 编辑

这是阿里巴巴和看雪一起搞的一起活动, 活动主页: http://msc.pediy.com/timu.htm
第二个题目要求分析程序, 获取正确的密码. 这里就不贴截图了(因为论坛限制帖子最多1w字节, 已经超了-_-!)(更新: 现在已经支持100w字节了, 感谢鬼哥, 感谢 Hm)

APK文件下载链接: http://pan.baidu.com/s/1ntiKXg1 密码: 37xw
其中, AliCrackme_2.apk是原安装包, signed.apk是包含修改后so文件的安装包.

这是第一次分析调试so, 并且该程序还使用了反调试手段, 因此写在这里记录一下, 发在这里也可以让大家指点一下, 谢谢~

调试so:
1, 将so拖到IDA中;
2, 启动调试: adb shell am start -D -ncom.yaotong.crackme/com.yaotong.crackme.MainActivity;
3, 将IDA 中的 dbgsrv 目录下的 android_server 上传到Android中并以root权限执行:
[Bash shell] 纯文本查看 复制代码
adb push IDA安装目录\dbgsrv\android_server /data/local/tmp
adb shell
su
chmod 774 /data/local/tmp/android_server
/data/local/tmp/android_server

4, 端口转发: adb forward tcp:23946tcp:23946;
5, 设置IDA的 Debugger Options, 选中在加载so时暂停程序, 然后附加到被调试进程中, 之后按 F9 开始执行程序;
6, 执行命令 "c:\ProgramFiles\Java\jdk1.7.0_71\bin\jdb.exe" -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700, 稍等一下IDA就会有反应了, 此时在 JNI_OnLoad 函数上下断点即可.

分析 JNI_OnLoad 函数可知,其在函数 sub_750ADCA8 获取 dlsym, getpid, sprintf, fopen, kill 等函数地址.
然后通过以上函数检查当前进程是否被调试, 检测方法: 读取 /proc/<pid>/status 的 TracePid 字段, 如果程序没有被调试, 该字段值为0, 否则就是调试器的进程PID.如果该值不为0, 则给当前进程发送信号 SIGKILL,结束进程.

OK, 我们修改so文件, 将 SIGKILL 信号修改为 0 信号(0信号没有任何副作用, 只用来检测进程是否存在), 将偏移为 0x15D8 的 B G E loc_1600 (机器码 为 08 00 00 AA) 修改为 BLE loc_1600(机器码为 08 00 00 DA ).
重新打包并调试, 在验证用户输入的函数 Java_com_yaotong_crackme_MainActivity_securityCheck 下断点, 等程序停下来后, 查看原字符串为 wojiushidaan 的位置, 这里就是密钥了~
密钥是 aiyou,bucuoo

另外, IDA分析so时, IDA不能识别参数类型, 需要手动设置一下, 例如导出表中java调用的函数的前两个参数固定是 JNIEnv* 和 jobject, 然后像这种 v5 = (*(int (__fastcall**)(int, int, _DWORD))(*(_DWORD *)v3 + 676))(v3, v4, 0); 就会识别为 v5 = ((int (__fastcall*)(JNIEnv *, int, _DWORD))(*v3)->GetStringUTFChars)(v3, v4, 0);, 否则根本没法看, 你说是不?(求大牛轻喷~)


参考链接:
·        Ida调试so文件


附录:
1.    JNI_OnLoad函数:
[C] 纯文本查看 复制代码
signed int __fastcall JNI_OnLoad(JavaVM *a1)
{
int v1; // r4@0
int v2; // r5@0
int v3; // r6@0
int v4; // r7@0
int v5; // r8@0
int v6; // r9@0
int v7; // lr@0
JavaVM *vm; // r4@1
int v9; // r5@1
int v10; // r6@3
int v11; // r6@5
int v12; // r0@7
signed int v13; // r6@7
_DWORD handle[8]; // [sp+0h] [bp-20h]@1
char vars0; // [sp+20h] [bp+0h]@1
int _24; // [sp+24h] [bp+4h]@1

handle[2] = v1;
handle[3] = v2;
handle[4] = v3;
handle[5] = v4;
handle[6] = v5;
handle[7] = v6;
*(_DWORD *)&vars0 = &vars0;
_24 = v7;
vm = a1;
dword_752612C8 = 0;
v9 = dword_752612C4;
if ( dword_752612C4 )
{
do
{
if ( *(_DWORD *)v9 >= 1 )
{
v10 = 0;
do
(*(void (**)(void))(v9 + 4 + 4 * v10++))(); // 获取 getpid, sprintf, fopen, kill, strstr等函数地址
while ( v10 < *(_DWORD *)v9 );
}
v11 = *(_DWORD *)(v9 + 44);
free((void *)v9);
v9 = v11;
}
while ( v11 );
dword_752612C4 = 0;
}
handle[-2] = 0;
v12 = func_pthread_create(handle, 0, sub_7525C6A4, 0); // 创建新线程, 死循环检测是否被调试
sub_7525C7F4(v12); // 解密密钥
v13 = 65540;
if ( ((int (__fastcall *)(JavaVM *, _DWORD *, signed int))(*vm)->GetEnv)(vm,&handle[-2], 65540) )
v13 = -1;
return v13;
}

2. 获取 getpid, sprintf, fopen, kill, strstr等函数地址的函数:
[C] 纯文本查看 复制代码
int (*sub_4BE36CA8())(void)
{
    void *v0; // r4@3
    int (*func_lrand48)(void); // r0@27
 
 
    if ( !byte_4BE3B35F )
    {
        sub_4BE374F4((int)&str_dlsym, 6, (int)&unk_4BE39509, (int)"9HbB", 4u, 197);
        byte_4BE3B35F = 1;
    }
    v0 = dlsym((void *)0xFFFFFFFF, (const char *)&str_dlsym);
    if ( !flag_getpid )
    {
        sub_4BE374F4((int)&str_getpid, 7, (int)&unk_4BE3948B, (int)"m7", 2u, 213);
        flag_getpid = 1;
    }
    func_getpid = (int (*)(void))((int (__fastcall *)(signed int, _UNKNOWN *))v0)(-1, &str_getpid);
    if ( !flag_sprintf )
    {
        sub_4BE3739C((int)&str_sprintf, 8, (int)&unk_4BE394F3, (int)"LNAt", 4u);
        flag_sprintf = 1;
    }
    func_sprintf = ((int (__fastcall *)(signed int, _UNKNOWN *))v0)(-1, &str_sprintf);
    if ( !flag_fopen )
    {
        sub_4BE3739C((int)&str_fopen, 6, (int)"0磢w湧", (int)"cOXt", 4u);
        flag_fopen = 1;
    }
    func_fopen = ((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_fopen);
    if ( !flag_fgets )
    {
        sub_4BE374F4((int)&str_fgets, 6, (int)"┑阜罸", (int)"BMT", 3u, 1);
        flag_fgets = 1;
    }
    func_fgets = (int (__fastcall *)(_DWORD, _DWORD, _DWORD))((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_fgets);
    if ( !flag_strstr )
    {
        sub_4BE3739C((int)&str_strstr, 7, (int)&unk_4BE394C4, (int)&unk_4BE394C1, 2u);
        flag_strstr = 1;
    }
    func_strstr = ((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_strstr);
    if ( !flag_sscanf )
    {
        sub_4BE3754C((int)&str_sscanf, 7, (int)"构73€", (int)&unk_4BE394FC, 0, 1);
        flag_sscanf = 1;
    }
    func_sscanf = ((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_sscanf);
    if ( !flag_kill )
    {
        sub_4BE3754C((int)&str_kill, 5, (int)"66€", (int)&unk_4BE394FC, 0, 1);
        flag_kill = 1;
    }
    func_kill = ((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_kill);
    if ( !flag_sleep )
    {
        sub_4BE3754C((int)&str_sleep, 6, (int)"9恫28€", (int)&unk_4BE394FC, 0, 1);
        flag_sleep = 1;
    }
    func_sleep = (int (__fastcall *)(_DWORD))((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_sleep);
    if ( !flag_pthread_create )
    {
        sub_4BE3758C(&str_pthread_create, 15, "3:*8(-&\x1B\"@%%7)B", "BMAE");
        flag_pthread_create = 1;
    }
    func_pthread_create = (int (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(
            -1,
            &str_pthread_create);
    if ( !flag_mprotect )
    {
        sub_4BE3758C(&str_mprotect, 9, &unk_4BE394E4, "7?s");
        flag_mprotect = 1;
    }
    func_mprotect = (int (__fastcall *)(_DWORD))((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_mprotect);
    if ( !flag_cacheflush )
    {
        sub_4BE3754C((int)&str_cacheflush, 11, (int)"卑13逗94€", (int)&unk_4BE394FC, 0, 1);
        flag_cacheflush = 1;
    }
    *(_DWORD *)func_cacheflush = ((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_cacheflush);
    if ( !flag_lrand48 )
    {
        sub_4BE3758C(&str_lrand48, 8, &unk_4BE394B8, ".6gq");
        flag_lrand48 = 1;
    }
    func_lrand48 = (int (*)(void))((int (__fastcall *)(_DWORD, _UNKNOWN *))v0)(-1, &str_lrand48);
    lrand48__dword_4BE3D2C0 = func_lrand48;
    return func_lrand48;
}

3. 检测是否被调试的函数:
[C] 纯文本查看 复制代码
int sub_4BE3830C()
{
int v0; // r4@0
int v1; // r5@0
int v2; // r6@0
int v3; // r7@0
int v4; // r8@0
int v5; // r9@0
int v6; // r10@0
int v7; // r11@0
int v8; // lr@0
int pid; // r8@1
int func_sprintf_; // r4@1
int (__fastcall *func_fopen_)(_DWORD, _DWORD); // r5@3
unsigned int v12; // r6@5
char *v13; // r4@6
int (__fastcall *func_strstr_)(_DWORD, _DWORD); // r5@7
char *v15; // r11@8
unsigned int v16; // r4@8
char *v17; // r6@8
void (__fastcall *func_sscanf_)(_DWORD, _DWORD, _DWORD, _DWORD); // r11@10
int result; // r0@16
int fd; // [sp+34h] [bp-314h]@5
int v21; // [sp+38h] [bp-310h]@10
char v22; // [sp+3Ch] [bp-30Ch]@6
char buf; // [sp+BCh] [bp-28Ch]@5
char filename; // [sp+2BCh] [bp-8Ch]@1
int v25; // [sp+320h] [bp-28h]@1
int v26; // [sp+324h] [bp-24h]@1
int v27; // [sp+328h] [bp-20h]@1
int v28; // [sp+32Ch] [bp-1Ch]@1
int v29; // [sp+330h] [bp-18h]@1
int v30; // [sp+334h] [bp-14h]@1
int v31; // [sp+338h] [bp-10h]@1
int v32; // [sp+33Ch] [bp-Ch]@1
int v33; // [sp+340h] [bp-8h]@1
int v34; // [sp+344h] [bp-4h]@1


v26 = v0;
v27 = v1;
v28 = v2;
v29 = v3;
v30 = v4;
v31 = v5;
v32 = v6;
v33 = v7;
v34 = v8;
v25 = _stack_chk_guard;
_aeabi_memset(&filename, 100, 0);
pid = func_getpid();
func_sprintf_ = func_sprintf;
if ( !byte_4BE3D35B )
{
sub_4BE3939C((int)&unk_4BE3D349, 16, (int)&unk_4BE3B550,(int)"s!#L", 4u);
byte_4BE3D35B = 1;
}
((void (__fastcall *)(char *, _UNKNOWN *, int))func_sprintf_)(&filename,&unk_4BE3D349, pid);
func_fopen_ = (int (__fastcall *)(_DWORD, _DWORD))func_fopen;
if ( !unk_4BE3D35C )
{
sub_4BE3954C((int)&fopen_mode, 2, (int)&unk_4BE3B54A,(int)&unk_4BE3B4FC, 0, 1);
unk_4BE3D35C = 1;
}
fd = func_fopen_(&filename, &fopen_mode);
_aeabi_memset(&buf, 512, 0);
v12 = 0x2D4u;
if ( func_fgets(&buf, 512, fd) )
{
v13 = &v22;
while ( 1 )
{
func_strstr_ = (int (__fastcall *)(_DWORD, _DWORD))func_strstr;
if ( !unk_4BE3D35D )
{
v15 = v13;
v16 = v12;
v17 = (char *)&GLOBAL_OFFSET_TABLE_ + v12;
sub_4BE394F4((int)(v17 + 149), 10, (int)"9X,+(X=!),(int)&unk_4BE3B493, 2u, 157);
v17[205] = 1;
v12 = v16;
v13 = v15;
}
if ( func_strstr_(&buf, &unk_4BE3D325) )
{
_aeabi_memset(v13, 128, 0);
v21 = 0;
func_sscanf_ = (void (__fastcall *)(_DWORD, _DWORD, _DWORD,_DWORD))func_sscanf;
if ( !unk_4BE3D35E )
{
sub_4BE3939C((int)((char *)&GLOBAL_OFFSET_TABLE_ + v12 + 65), 6,(int)&unk_4BE3B461, (int)"L79", 3u);
*((_BYTE *)&GLOBAL_OFFSET_TABLE_ + v12 + 206) = 1;
}
func_sscanf_(&buf, &unk_4BE3D2D1, v13, &v21);
if ( v21 >= 1 )
break;
}
if ( !func_fgets(&buf, 512, fd) )
goto LABEL_16;
}
(*(void (__fastcall **)(int, signed int))((char *)&GLOBAL_OFFSET_TABLE_ +v12 + 28))(pid, 9);
}
LABEL_16:
result = _stack_chk_guard - v25;
if ( _stack_chk_guard != v25 )
_stack_chk_fail(result);
return result;
}

4. 验证用户输入是否正确的函数:
[C] 纯文本查看 复制代码
signed int __fastcall Java_com_yaotong_crackme_MainActivity_securityCheck(JNIEnv*env, jobject thiz, int str)
{
JNIEnv *env_; // r5@1
int str_; // r4@1
int user_input_str; // r0@5
char *key; // r2@5
int key_char; // r3@6
signed int v8; // r1@7


env_ = env;
str_ = str;
if ( !byte_4BE3D359 )
{
sub_4BE39494(&unk_4BE3D304, 8, &unk_4BE3B46B, &unk_4BE3B468);
byte_4BE3D359 = 1;
}
if ( !unk_4BE3D35A )
{
sub_4BE394F4(&unk_4BE3D36C);
unk_4BE3D35A = 1;
}
user_input_str = ((int (__fastcall *)(JNIEnv *, int,_DWORD))(*env_)->GetStringUTFChars)(env_, str_, 0);
key = off_4BE3D28C;
while ( 1 )
{
key_char = (unsigned __int8)*key;
if ( key_char != *(_BYTE *)user_input_str )
break;
++key;
++user_input_str;
v8 = 1;
if ( !key_char )
return v8;
}
return 0;
}

5. 解密密钥的函数:
[C] 纯文本查看 复制代码
int sub_4BE387F4()
{
    signed int v0; // r0@1
    int v1; // r0@1
    int v2; // r0@1
    int v3; // r0@1
    signed int v4; // r2@1
    int v5; // r1@1
    signed int v6; // r0@1
    int (**v7)(void); // r0@4
    int (*v8)(void); // r3@16
    unsigned int v9; // r0@16
    int v10; // r0@16


    func_mprotect((unsigned int)&jolin & -_page_size& 0xFFFFFFFE);
    v0 = lrand48__dword_4BE3D2C0();
    v1 = _floatsidf(8 * (v0 % 100) + 184);
    v2 = _muldf3(v1);
    v3 = _fixdfsi(v2);
    v4 = ((v3 + 3) * v3 + 2) * v3;
    v5 = v4 % 6 + 4;
    v6 = 0u;
    if ( v5 != 4 && (unsigned int)(v4 % 6) <0xFFFFFFFC )
    {
        do
        {
LABEL_3:
            *(_BYTE *)(v6 + ((unsignedint)&jolin & 0xFFFFFFFE)) ^= byte_4BE3D070[v6 % 108];
            ++v6;
        }
        while ( v6 != 212 );
        v7 = &GLOBAL_OFFSET_TABLE_;
    }
    else
    {
        switch ( v5 )
        {
            case 0:
                do
                {
                   *(_BYTE *)(v6 + ((unsigned int)&jolin & 0xFFFFFFFE)) ^=byte_4BE3D1B4[v6 % 108];
                   ++v6;
                }
                while ( v6 != 212);
                v7 =&GLOBAL_OFFSET_TABLE_;
                break;
            case 1:
                do
                {
                   *(_BYTE *)(v6 + ((unsigned int)&jolin & 0xFFFFFFFE)) ^=byte_4BE3D148[v6 % 108];
                   ++v6;
                }
                while ( v6 != 212);
                v7 =&GLOBAL_OFFSET_TABLE_;
                break;
            case 2:
                do
                {
                   *(_BYTE *)(v6 + ((unsigned int)&jolin & 0xFFFFFFFE)) ^=byte_4BE3D220[v6 % 108];
                   ++v6;
                }
                while ( v6 != 212);
                v7 =&GLOBAL_OFFSET_TABLE_;
                break;
            case 3:
                do
                {
                   *(_BYTE *)(v6 + ((unsigned int)&jolin & 0xFFFFFFFE)) ^=byte_4BE3D0DC[v6 % 108];
                   ++v6;
                }
                while ( v6 != 212);
                v7 =&GLOBAL_OFFSET_TABLE_;
                break;
            case 4:
                do
                {
                   *(_BYTE *)(v6 + ((unsigned int)&jolin & 0xFFFFFFFE)) ^=byte_4BE3D004[v6 / 108];
                   ++v6;
                }
                while ( v6 != 212);
                v7 =&GLOBAL_OFFSET_TABLE_;
                break;
            default:
                goto LABEL_3;
        }
    }
    v8 = v7[192];
    v9 = *(unsigned int *)((char *)v7 + 0xFFFFFFE0) &0xFFFFFFFE;
    v10 = v8();
    return ((int (__fastcall *)(_DWORD))jolin)(v10);
}
已有5人评分 威望 NB 贡献 荣获致谢 理由
JackIO + 2 + 1 很给力!
花墨 + 1 + 1 + 1 很给力!
Lnju + 1 很给力!
Dawn + 1 + 1 + 1 神马都是浮云
peterdocter + 1 + 1 赞一个!

查看全部评分 总评分: 威望 +2  NB +5  贡献 +2  荣获致谢 +3 

举报 使用道具

回复

精彩评论18

听鬼哥说故事    发表于 2015-1-28 23:05:17 来自手机  | 显示全部楼层
前排支持,很详细,不错

举报 使用道具

回复 支持 反对
fammer    发表于 2015-1-28 23:23:12 来自手机  | 显示全部楼层
很好很强大!!

举报 使用道具

回复 支持 反对
fammer    发表于 2015-1-28 23:23:28 来自手机  | 显示全部楼层
回头仔细看看!!

举报 使用道具

回复 支持 反对
花墨    发表于 2015-1-28 23:29:28 | 显示全部楼层
看来我写博客的时候得参考你的了,好详细的说!

举报 使用道具

回复 支持 反对
JackIO    发表于 2015-1-29 08:58:12 | 显示全部楼层
写得很详细,前来学习。

举报 使用道具

回复 支持 反对
peterdocter    发表于 2015-1-29 09:45:00 | 显示全部楼层
多谢分享!论坛有你更精彩!

举报 使用道具

回复 支持 反对
单翅的天使ylj    发表于 2015-1-29 09:50:24 | 显示全部楼层
小白路过纯支持

举报 使用道具

回复 支持 反对
nsnj    发表于 2015-1-29 09:59:38 | 显示全部楼层
谢谢大家支持, 但是看来我的功力还是不足, 大家一起加油

举报 使用道具

回复 支持 反对
Dawn    发表于 2015-1-29 10:01:39 | 显示全部楼层
PT哥,你抢了我的台词,让我怎么说。

举报 使用道具

回复 支持 反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表