吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9012|回复: 19
收起左侧

[Android CTF] 简单分析某大牛的2016zctf_android1-200 writeup

[复制链接]
qtfreet00 发表于 2016-3-3 00:03
原帖地址:2016zctf_android1-200 writeup-【详细】http://www.52pojie.cn/thread-469259-1-1.html
(出处: 吾爱破解论坛)

楼主写的很详细,不过看上去就觉得有点复杂了,个人就想出了比较简单易懂的方法


看过原楼主帖子的应该知道这个软件有1处反调试和检测模拟器的小地方,原楼主通过修改流程中的返回值达到目标,不过呢,一般的情况下,往往我们需要更多的时间来尝试一些更加重要的操作,如dump内存,分析算法,不应该花太多的时间再操作反调试上


0x1 去除反调试
反调试,往往我们会在脱壳的时候遇到,一般情况下有三种处理手法
(1)直接修改apk程序中的必要文件,去除反调试
(2)IDA调试时,对ptrace关键处进行下断,修改返回值,部分代码达到去除反调试
(3)最直接的,修改手机rom,内核,从最底层直接去除反调试

我们最常接触的是第二种,当然也是这三个里面最简单易用的

[Java] 纯文本查看 复制代码
   public boolean CheckOperatorNameAndroid(Context context) throws InterruptedException {
        long v6 = 3500;
        if(context.getSystemService("phone").getNetworkOperatorName().toLowerCase().equals(this.getString(
                2131099675))) {
            Toast.makeText(this.getApplicationContext(), this.getString(2131099678), 1).show();
            new Timer().schedule(this.task, v6);
        }
        else {
            Toast.makeText(this.getApplicationContext(), this.getString(2131099681), 1).show();
            new Timer().schedule(this.task, v6);
        }

        return 0;
    }


第一处是对模拟器进行检测,如果是则返回true,程序强制退出,导致我们后面测试中断,很简单,直接删除所有代码,留下return false即可

第二处,是在so里,看到程序一共有三个包,而往往我们调试的手机都是arm架构,那么直接可以删除armv7和x86,留下一个arm就行,反正不影响,能有多简单就多简单呗

[Java] 纯文本查看 复制代码
.text:00001AD8
.text:00001AD8                 EXPORT Java_com_zctf_app_JNIclass_add
.text:00001AD8 Java_com_zctf_app_JNIclass_add
.text:00001AD8                 PUSH    {R3,LR}
.text:00001ADA                 BL      sub_14B0
.text:00001ADE                 POP     {R3,PC}
.text:00001ADE ; End of function Java_com_zctf_app_JNIclass_add


[Java] 纯文本查看 复制代码
.text:00001B78                 LDR     R1, [R4]
.text:00001B7A                 MOVS    R3, #0x300
.text:00001B7E                 LDR     R6, [R1,R3]
.text:00001B80                 MOVS    R0, R4
.text:00001B82                 MOVS    R1, R7
.text:00001B84                 LDR     R2, [SP,#0x40+src]
.text:00001B86                 MOVS    R3, #0
.text:00001B88                 BLX     R6
.text:00001B8A                 BL      sub_14B0
.text:00001B8E                 STR     R0, [SP,#0x40+src]
.text:00001B90                 CMP     R0, #0
.text:00001B92                 BEQ     loc_1BA4
.text:00001B94                 LDR     R2, [R4]
.text:00001B96                 MOVS    R1, #0x29C


在这两处都调用了sub_1480这个函数,而这个函数就是进行反调试的地方,进去看看

[Java] 纯文本查看 复制代码
int sub_14B0()
{
  __pid_t v0; // r6@1
  signed int v1; // r6@1
  FILE *v2; // r5@1
  int result; // r0@3
  unsigned int v4; // [sp+4h] [bp-1F4h]@3
  char v5; // [sp+8h] [bp-1F0h]@3
  char s; // [sp+1Ch] [bp-1DCh]@2
  char v7; // [sp+E4h] [bp-114h]@1
  int v8; // [sp+1E4h] [bp-14h]@1

  v8 = _stack_chk_guard;
  v0 = j_j_getpid();
  j_j_memset(&v7, 0, 0xFFu);
  j_j_sprintf(&v7, "/proc/%d/status", v0);
  v1 = 5;
  v2 = j_j_fopen(&v7, "r");
  do
  {
    --v1;
    j_j_fgets(&s, 150, v2);
  }
  while ( v1 );
  j_j_fscanf(v2, "%s %d", &v5, &v4);
  j_j_fclose(v2);
  result = (__PAIR__(v4, v4) - __PAIR__(v4 - 1, 1)) >> 32;
  if ( v8 != _stack_chk_guard )
    j_j___stack_chk_fail(result);
  return result;
}


这里调用fopen打开数据流,并从中获取到状态码和tracerpid值,正常情况下,tracerpid默认为0,调试状态下为一直不为0的值,通过检测这个值可以到反调试的目的

那这里直接强制返回0即可,通过下面这个也可以知道

[Java] 纯文本查看 复制代码
  if ( sub_14B0() )
  {
    v11 = *(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 668);
    v12 = v3;
    v13 = "you miss sth";
  }


不为0会直接走到错误流程上

那么我的做法是,直接返回0即可

代码
[Java] 纯文本查看 复制代码
.text:000014B0                 PUSH    {R4-R6,LR}
.text:000014B2                 MOVS    R0, #0
.text:000014B4                 POP     {R4-R6,PC}


对应16进制0020 70BD

打包后重新签名就可以正常的进行调试了


0x2 分析程序
我们看到app的assets文件夹下有一个db文件,观察后发现数据并未加密,直接用软件查看即可,省的通过代码分析 - -

查看后是zctf2016


接下来这个数据会做为des的key对数据进行加密,加密后的数据直接传入native层


[Java] 纯文本查看 复制代码
 public int auth(Context context, String input, String password1) {
        int v2;
        byte[] v6 = Auth.encrypt(new StringBuffer(input).reverse().toString().getBytes(), password1);
        try {
            InputStream v3 = context.getAssets().open(context.getString(2131099679));
            byte[] v0 = new byte[64];
            do {
            }
            while(v3.read(v0) > 0);

            v3.close();
            v2 = 0;
            while(true) {
            label_18:
                if(v2 >= v0.length) {
                    return 1;
                }

                if(v2 >= v6.length) {
                    return 1;
                }

                if(v2 < v0.length && v2 < v6.length && v0[v2] != v6[v2]) {
                    break;
                }

                goto label_31;
            }
        }
        catch(IOException v1) {
            goto label_34;
        }

        int v9 = 0;
        return v9;
    label_31:
        ++v2;
        goto label_18;
    label_34:
        v1.printStackTrace();
        return 1;
    }





这里判断的是加密后的数据是否等于flag.bin文件数据,那数据直接用16进制编辑器查看就行,然后调用des的解密方法可以获取到未加密的值,由于调用了reverse()方法,所以需要对字符串反转一下才是真正的未加密的值,解密后是zctf{Notthis},由于逻辑上并没有对用户名长度进行限制,所以这里的用户名密码组合是任意的,原楼主这里没有交代清楚

0x3 寻找flag

先静态分析
[Java] 纯文本查看 复制代码
  v14 = j_j_malloc(0x1460u);
    v15 = v14;
    if ( v14 )
    {
      j_j_memset(v14, 0, 0x1460u);
      j_j_memcpy(v15, &top, 0x100u);
      v16 = j_j_fopen("/data/data/com.zctf.app/files/bottom", "rb");
      v17 = v16;
      if ( v16 )
      {
        v20 = j_j_fread((char *)v15 + 256, 1u, 0x1360u, v16);
        j_j_fclose(v17);
        j_j_memset(&v26, 0, 0x10u);
        v21 = *(_DWORD *)v10;
        v22 = *((_DWORD *)v10 + 1);
        v26 = v21;
        v27 = v22;
        v23 = j_j_malloc(0x1460u);
        j_j_memset(v23, 0, 0x1460u);
        DES_Decrypt(v15, v20 + 256, &v26, v23);
        j_j_free(v15);
        j_j_free(v23);
        v18 = "System.out";
        v19 = "Too late, Boy";
      }
      else
      {
        v18 = "System.out";
        v19 = "Botton lost";
      }
      j_j___android_log_print(4, v18, v19);
    }


此处读取了assets下的bottom文件,然后调用了des_decrypt对数据进行解密,解密key恰好就是我们传入native的数据,解密之后立刻就释放了内存,

所以这里我们直接在j_j_free(v15)处下断

IDA附加手机动态调试,下面就直接拿原楼主的图了
152615ipqzk9q0em8869ez.png

由于程序中提示的是图片损坏,那么相信真正的内存中的数据也是一个图片,切换到r5后发现头数据是89 50 4E 47 0D 0A 1A 0A 00

标准的png格式,那直接找结尾就行 AE 42  60 82 ,用IDC脚本直接抠出来,用stegsolve即可看到图片的flag















免费评分

参与人数 4热心值 +4 收起 理由
yzh2004 + 1 谢谢@Thanks!
UNCLE + 1 我很赞同!
fenghaoda + 1 我很赞同!
hidekill + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

UNCLE 发表于 2016-3-3 10:26
非常不错⊙▽⊙,感谢您的补充。因为时间原因,原帖我没做太多完善,也存在很多问题,我在这里也说明一下:我的帖子只是我的解题思路,方法肯定不是最佳。但是必须要说的是,在解决ctf题目时哪种方法顺手就用哪种,和平常学习还是不同的。比如lz说查看db文件不用看代码,但是如果不看代码我怎么知道这个文件有什么信息,如何打开,打开后信息有什么用。
应该是因为我描述不够清楚(原谅本人语文不好),原帖中反调试就是修改了返回值。
最后,不同人喜欢不同的方法,都各有风骚,既不存在某一种方法一无是处,也不存在某一种方法各处通用。十分谢谢lz看了我的帖子

免费评分

参与人数 1热心值 +1 收起 理由
qtfreet00 + 1 我很赞同!

查看全部评分

Peach 发表于 2016-3-3 00:11
q9107472 发表于 2016-3-3 01:02
枫MapleLCG 发表于 2016-3-3 07:00
占个前排。大家可以根据这贴理解原文一下
sandikast 发表于 2016-3-3 08:17
方法很棒,简单也方便
cowboys2010 发表于 2016-3-3 08:43
学习大神,支持教程。
yzh2004 发表于 2016-3-3 09:43
谢谢大神的分析,涨见识
windwing1883 发表于 2016-3-3 09:58
我也正准备对这个apk下手 哈哈,支持一下
ai枫 发表于 2016-3-4 09:57
已学习,支持一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:38

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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