qtfreet00 发表于 2016-3-3 00:03

简单分析某大牛的2016zctf_android1-200 writeup

原帖地址:2016zctf_android1-200 writeup-【详细】http://www.52pojie.cn/thread-469259-1-1.html
(出处: 吾爱破解论坛)

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


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


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

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

   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就行,反正不影响,能有多简单就多简单呗

.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

.text:00001B78               LDR   R1,
.text:00001B7A               MOVS    R3, #0x300
.text:00001B7E               LDR   R6,
.text:00001B80               MOVS    R0, R4
.text:00001B82               MOVS    R1, R7
.text:00001B84               LDR   R2,
.text:00001B86               MOVS    R3, #0
.text:00001B88               BLX   R6
.text:00001B8A               BL      sub_14B0
.text:00001B8E               STR   R0,
.text:00001B90               CMP   R0, #0
.text:00001B92               BEQ   loc_1BA4
.text:00001B94               LDR   R2,
.text:00001B96               MOVS    R1, #0x29C

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

int sub_14B0()
{
__pid_t v0; // r6@1
signed int v1; // r6@1
FILE *v2; // r5@1
int result; // r0@3
unsigned int v4; // @3
char v5; // @3
char s; // @2
char v7; // @1
int v8; // @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即可,通过下面这个也可以知道

if ( sub_14B0() )
{
    v11 = *(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 668);
    v12 = v3;
    v13 = "you miss sth";
}

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

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

代码
.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层


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;
            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 != v6) {
                  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

先静态分析
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附加手机动态调试,下面就直接拿原楼主的图了


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

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















UNCLE 发表于 2016-3-3 10:26

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

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

已学习,支持一下
页: [1] 2 3
查看完整版本: 简单分析某大牛的2016zctf_android1-200 writeup