第一次尝试APK的题目

Posted by rk700 on December 14, 2014

今天遇到一道APK逆向的题目,难度应该是入门级别的。由于之前见到这类题目直接就放弃了,这样还是不太好,于是试着从头开始,查资料把这道题解决了。

题目给的APK在这里。为了做这道题,我把apktool装好了,还翻墙把android sdk安上了。结果模拟器启动太慢了……以后有钱搞一台实机用好了。话说回来这次也基本没有用到模拟器,主要用了dex2jar和jd-gui。

将APK用unzip解压,再用dex2jar,最后用jd-gui得到了java代码。如下:

import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class JewelActivity extends Activity
{
  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130903040);
    String str1 = ((TelephonyManager)getSystemService("phone")).getDeviceId();
    try
    {
      MessageDigest localMessageDigest = MessageDigest.getInstance("SHA-256");
      localMessageDigest.update(str1.getBytes("ASCII"));
      String str2 = new BigInteger(localMessageDigest.digest()).toString(16);
      if (!str1.substring(0, 8).equals("99999991"))
      {
        new AlertDialog.Builder(this).setMessage("Your device is not supported").setCancelable(false).setPositiveButton("OK", new b(this)).show();
        return;
      }
      if (!str2.equals("356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5"))
      {
        new AlertDialog.Builder(this).setMessage("You are not a valid user").setCancelable(false).setPositiveButton("OK", new a(this)).show();
        return;
      }
    }
    catch (Exception localException)
    {
      Toast.makeText(this, localException.toString(), 1).show();
      return;
    }
    InputStream localInputStream = getResources().openRawResource(2130968576);
    byte[] arrayOfByte1 = new byte[localInputStream.available()];
    localInputStream.read(arrayOfByte1);
    SecretKeySpec localSecretKeySpec = new SecretKeySpec(("!" + str1).getBytes("ASCII"), "AES");
    IvParameterSpec localIvParameterSpec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes());
    Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    localCipher.init(2, localSecretKeySpec, localIvParameterSpec);
    byte[] arrayOfByte2 = localCipher.doFinal(arrayOfByte1);
    ImageView localImageView = new ImageView(this);
    localImageView.setImageBitmap(BitmapFactory.decodeByteArray(arrayOfByte2, 0, arrayOfByte2.length));
    setContentView(localImageView);
  }
}

由于没有环境,只能静态去分析代码了,好多不懂的地方现查了。从流程上来说,这段代码取出来了device id,检查其前8位是否是99999991,而且要求sha256的hash等于某个指定的值。如果符合要求,就用这个device id来构造key,对一个文件进行AES加密,得到一个BMP图片。

具体地,device id查了下是15 bytes,而前8 bytes确定后,还有7 bytes,大概有10^7种可能,这部分可以通过暴力破解。然后作为AES加密输入的文件,是一个resource,而且这个resource的id是2130968576=0x7f040000。在apktools解码得到的文件res/values/public.xml中发现了这个资源文件是raw/jewel_c.png

于是整体的思路就是,先暴力跑出device id,然后AES加密文件,得到BMP图像。之前也没有用过java,于是也第一次练习了……感觉java的文档写的很不错。下面是我的代码,复制了很多原来代码的东西

import java.io.*;
import java.math.BigInteger;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Main {
    public static void main(String[] args) {
        try{ 
            StringBuilder str1 = new StringBuilder(15);
            str1.append("99999991");

            for(int i=0; i<10000000; i++) {
                str1.replace(8, 15, String.format("%07d", i));
                MessageDigest localMessageDigest = MessageDigest.getInstance("SHA-256");
                localMessageDigest.update(str1.toString().getBytes("ASCII"));
                String str2 = new BigInteger(localMessageDigest.digest()).toString(16);
                
                if (str2.equals("356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5")) {
                    System.out.println(str1);

                    InputStream localInputStream = new FileInputStream(new File("jewel_c.png"));
                    byte[] arrayOfByte1 = new byte[localInputStream.available()];
                    localInputStream.read(arrayOfByte1);

                    SecretKeySpec localSecretKeySpec = new SecretKeySpec(("!" + str1.toString()).getBytes("ASCII"), "AES");
                    IvParameterSpec localIvParameterSpec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes());
                    Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                    localCipher.init(2, localSecretKeySpec, localIvParameterSpec);
                    byte[] arrayOfByte2 = localCipher.doFinal(arrayOfByte1);

                    FileOutputStream outfile = new FileOutputStream("1.bmp");
                    outfile.write(arrayOfByte2);

                    break;
                }
            }
        }
        catch(Exception e)  {  
            e.printStackTrace();  
        }  
    }
}

编译运行,得到正确的device id是999999913371337。查看得到的BMP图片,说是flag在comment里面。于是用strings查看,得到flag

总的来说,第一次做APK逆向的题目,考察的知识还是基本的。没有涉及到网络通信,单纯地静态分析下就可以了,所以其实与android关系并不大,只是旧的逆向题装了个新瓶子。但以后做其他的APK题目可能就不会这么容易了。不过对我来说,比windows下面的逆向题要方便多了,ida在我虚拟机里的xp下总是不能用,非常麻烦,所以到现在APK逆向做了这一道,windows的逆向题还没做过……