二进制学习(1)-FindMyDex

作者: 分类: 二进制 时间: 2017-06-18 浏览: 3614 评论: 1条评论

前言

好久没有写文章了,先废话几句。
前段时间在忙毕设和出题,虽然偶尔也挖挖洞,但毕竟是0day也不好发出来。
于是blog就长草了,想了想这样还是不太好。
当然也有一些其他原因,总之,各种拖着,也没有学习,没有写文章。
恍恍惚惚过了半年也想明白了很多事,开心还是要看自己。
so,just do it.
感谢@overload,@spine,@giglf等师傅们的耐心指导。

正文

pwnhub的一道apk逆向题,apk链接在此
直接丢进jeb,发现无法反编译。于是用apktools进行解压,发现没有classes.dex文件。
当时以为是动态脱壳,查了一些工具如DexHunter都没有效果。

AndroidManifest.xml内容如下:

xml.png

可以看到入口为android.app.NativeActivity,说明是ndk程序,但是下面又有MainActivity,可能是在运行过程中动态生成了dex。

反汇编一下libnative.so文件,在左边找到入口函数android_main
大致逻辑如下

  v1 = a1;
  v71 = _stack_chk_guard;
  destLen = 0x100000;
  dest = (Bytef *)malloc(0x100000u);
  v2 = (const Bytef *)malloc((size_t)off_48BF0);
  _aeabi_memcpy((int)v3, (int)aQxe, (int)v2);

首先声明了一段大小的空间,然后将.data7004偏移开始,off_48BF0指向的地址的大小的内容复制到里面。
中间是一波看不懂的操作,大致意思可能是通过api来获取手机震动次数???
接下来是重点

    v13 = shaketimes;
    if ( (unsigned int)(shaketimes - 1) <= 88 )
    {
      _R0 = 0x66666667;
      v13 = shaketimes;
      __asm { SMMUL.W         R0, R4, R0 }
      v19 = ((signed int)_R0 >> 2) + (_R0 >> 31);
      if ( shaketimes - 10 * v19 == 9 )
      {
        _R1 = 0x66666667;
        _R0 = off_48BF0;
        __asm { SMMUL.W         R1, R0, R1 }
        v23 = ((signed int)_R1 >> 2) + (_R1 >> 31);
        v24 = (v19 + 1) * v23;
        v25 = v19 * v23;
        if ( v25 < v24 )
        {
          v26 = &v3[v25];
          do
          {
            --v23;
            *v26++ ^= shaketimes;
          }
          while ( v23 );
        }
        if ( shaketimes == 89 )
        {
          while ( v24 < (signed int)_R0 )
            v3[v24++] ^= 0x59u;
        }
        v13 = shaketimes + 1;
      }
    }
    ...
     if ( shaketimes == 100 )
    {
      if ( (signed int)(time(0) - v31) > 9 )
      {
        _android_log_print(4, "FindMyDex", "OH~ You are too slow. Please try again");
        _aeabi_memcpy((int)v3, (int)aQxe, (int)off_48BF0);
        v13 = 0;
      }
      else
      {
        if ( uncompress(dest, &destLen, v3, (uLong)off_48BF0) )
          _android_log_print(5, "FindMyDex", "Dangerous operation detected.");
        v27 = open((const char *)&filename, 577, 511);
        if ( !v27 )
          _android_log_print(5, "FindMyDex", "Something wrong with the permission.");
        write(v27, dest, destLen);
        close(v27);
    

这里一开始就难到我了...
_R0 = 0x66666667;
v13 = shaketimes;
__asm { SMMUL.W R0, R4, R0 }
v19 = ((signed int)_R0 >> 2) + (_R0 >> 31);
请教了好基友后才知道这4行的意思是v19=shaketimes/10 (当时我的内心一脸蒙蔽 = = )
于是自己测试一波

#include <stdio.h>
#include <stdint.h>
int main(){
    uint64_t s = 0x66666667;
    int32_t v2,shakecount;

    shakecount=100;
    s = (s*shakecount) >> 32;
    v2 = ((signed int)s>>2)+((unsigned int)s>>31);
    printf("%d\n",v2);
}

结果还真的是除10~
后面就是常规操作了。于是可以写一个脚本dump出dex。
这里又有坑。

一开始是直接打开了libnative.so文件,然后读取data[0x7004:0x7004+0x41BE8]
结果发现libnative.so文件都没有0x7001+0x41BE8这么大。 现在还是不懂..有没有大佬能指导下

后来基友说要用ida python来dump
于是

Python>t=[]
Python>for i in range(0x7004,0x7004+0x41BE8):
Python>    t+=[chr(Byte(i))]
Python>
Python>open("data.bin","wb").write("".join(t))

dump出data.bin,然后用python脚本转换出dex。

import zlib

data = open('data.bin','rb').read()
data = list(data)
print '%x'%len(data)
for shakecount in xrange(90):
    v19 = shakecount/10
    if shakecount%10==9:
        v23 = 0x41be8/10
        v24 = (v19+1)*v23
        v25 = v19*v23
        if (v25<v24):
            for i in range(v25,v24):
                data[i]=chr(shakecount^ord("".join(data[i])))
                
        if shakecount==89:
            for i in xrange(v24,0x41be8):
                data[i]=chr(89^ord("".join(data[i])))
            
                
data2=zlib.decompress("".join(data))
open('3.dex','wb').write(data2) 

拿着3.dex就可以在jeb里面反汇编了。

protected void onCreate(Bundle arg4) {
        super.onCreate(arg4);
        this.setContentView(2130968603);
        this.findViewById(2131427415).setOnClickListener(new View$OnClickListener() {
            public void onClick(View arg5) {
                if(Arrays.equals(MainActivity.a(this.a.getText().toString(), this.c.getString(2131099683)), 
                        MainActivity.i())) {
                    Toast.makeText(this.b, this.c.getString(2131099685), 1).show();
                }
                else {
                    Toast.makeText(this.b, this.c.getString(2131099682), 1).show();
                }
            }
        });
    }

主要是对比
MainActivity.a(this.a.getText().toString(), this.c.getString(2131099683))
和MainActivity.i()
MainActivity.i()的是硬编码的,内容为:

MainActivity.m = new byte[]{3, -91, 61, -29, -87, -21, 101, -57, 27, -19, -27, -10, -61, 15, -85, 12, -32, -102, 106, 92, -39, 100, -122, 112, 18, -100, -55, 44, 60, -118, -97, 127, 76, 113, -35, 68, -17, -33, -93, -62, -57, -84, 36, -96, 97, -15, 20, 69};

写个python脚本转换一下:

s = [3, -91, 61, -29, -87, -21, 101, -57, 27, -19, -27, -10, -61, 15, -85, 12, -32, -102,106, 92, -39, 100, -122, 112, 18, -100, -55, 44, 60, -118, -97, 127, 76, 113, -35, 68, -17, -33, -93, -62, -57, -84, 36, -96, 97, -15, 20, 69]
ss=[]
for i in s :
    if i>0:
        ss+=[chr(i)]
    else:
        ss+=[chr(256+i)]
result = "".join(ss)
print result

this.c.getString(2131099683)的内容是这么找的。首先计算2131099683的16进制 0x7f060023
然后在res/values/public.xml中找到

最后在res/values/strings.xml找到
I have a male fish and a female fish.

this.a.getText().toString()是用户输入的值。
看一下MainActivity.a():

static byte[] a(String arg1, String arg2) {
        return MainActivity.b(arg1, arg2);
    }

    private static byte[] b(String arg7, String arg8) {
        byte[] v0_1;
        try {
            BufferedInputStream v2 = new BufferedInputStream(new ByteArrayInputStream(arg7.getBytes()));
            byte[] v1 = new byte[16];
            ArrayList v3 = new ArrayList();
            Object v4 = a.a(arg8.getBytes());
            while(v2.read(v1, 0, 16) != -1) {
                v3.add(a.a(v1, 0, v4));
                v1 = new byte[16];
            }

            ByteBuffer v2_1 = ByteBuffer.allocate(v3.size() * 16);
            Object[] v3_1 = v3.toArray();
            int v4_1 = v3_1.length;
            int v1_1;
            for(v1_1 = 0; v1_1 < v4_1; ++v1_1) {
                v2_1.put(v3_1[v1_1]);
            }

            v0_1 = v2_1.array();
        }
        catch(Exception v0) {
            v0_1 = new byte[1];
        }

        return v0_1;
    }

后面就看不懂啦,应该是个分组的加密。(大佬们能看出是twofish加密,是一个分组对称的加密算法)

最后用python脚本

from twofish import Twofish
fish = Twofish(b'I have a male fish and a female ')
print fish.decrypt(result[0:16])

可以跑出flag

dump dex还有另外几种方法

1是giglf大佬的方法。
用安卓的调试工具ddms连接手机,然后adb进手机dump出odex
然后用baksmali把odex解出来smali
java ‐jar baksmali.jar de ‐‐classpath‐dir <framework‐dir> classes.dex
再用smali
java ‐jar smali.jar ass out
生成out.dex

自己动手尝试

5FC35B40-05B5-46DF-89FE-609E9C3F92A5.png

发现进不了data目录,可能是手机没有root的原因

2是LeadroyaL师傅用ida在remove下断点,然后拖出真正的dex

遗留问题

dump文件data段的时候为什么会缺失。
dex中在加密过程的具体处理
如何用ida下断点
有空root手机后尝试一下dump odex文件
从自己提取data段释放出dex的so是如何编写的

标签: none

订阅本站(RSS)

仅有 1 条评论

  1. zwhubuntu

    bin爷好!

    时间: 2017-06-19 at 21:49 回复

添加新评论