apk逆向

on under 二进制
5 minute read

0x01 工具准备

1.apktool,brew install apktool[可得到关键smali文件]

apktool d target.apk -o target
与apktool相同功能的工具但更新更多的是[baksmali][3]

2.dex2jar(kali自带)

dex2jar target.apk[得到jar文件]

3.jd-gui,brew cask install jd-gui[可得到java源码]

4.jad,如果jd-gui看到的代码不全,可尝试jad看能否看到更多源代码

jar -xvf target.jar[得到.class文件]
for each in $(find . -name "*.class");do echo y | jad $each;done
for each in $(find . -name "*.jad");do copy $each /tmp/source;done
其中.jad为jad将.class反编译得到的java代码文件

0x02 apk逆向好文

1.smali插桩

2.smali批量插桩

3.自动化工具

0x03 实例破解

apk下载地址

a)随便输入一个字符串,报出错信息”密码错误”

b)apktool d money.apk -o money

c)cd money && ack -r '密码错误' .得到如下结果

res/values/strings.xml
38:    <string name="dialog_error_tips">密码错误,请继续破解</string>

d)查看strings.xml文件发现关键字符串

<string name="dialog_good_tips">密码正确但没钱!!!</string>

e)ack -r 'dialog_good_tips' .得到关键id为0x7f060024

471:    <public type="string" name="dialog_good_tips" id="0x7f060024" />

f)将apk中的classes.dex在ida中打开,搜索这个id,如下图

g)后发现这里不用ida,直接用jd-gui看java源码

h)kali中dex2jar money.apk得到money_dex2jar.jar文件

i)jd-gui打开money_dex2jar.jar得到java源码

j)在jd-gui中搜索id=”0x7f060024”中对应的10进制值”2131099684”打开关键代码处逻辑如下

public class MainActivity
  extends c
  implements View.OnClickListener
{
  String l = null;
  EditText m = null;
  byte[] n = { 69, 0, -40, -3, -109, -89, -84, 114, 83, 99, -69, 7, 107, -31, -113, -4, 20, -122, 3, -33, -72, -4, -20, 65, 44, -36, 117, 4, 81, -70, 31, 125 };
  
  public void onClick(View paramView)
  {
    switch (paramView.getId())
    {
    default: 
      return;
    }
    this.l = this.m.getText().toString();
    if (this.l.isEmpty())
    {
      Toast localToast5 = Toast.makeText(getApplicationContext(), getString(2131099683), 1);
      localToast5.setGravity(17, 0, 0);
      localToast5.show();
      return;
    }
    try
    {
      if (Arrays.equals(nosence.a(this.l.getBytes()), this.n))
      {
        Toast localToast4 = Toast.makeText(getApplicationContext(), getString(2131099684), 1);
        localToast4.setGravity(17, 0, 0);
        localToast4.show();
        return;
      }
    }

k)查看关键对比处的nosence的a函数

public class nosence
{
  static
  {
    System.loadLibrary("pingan");
  }
  
  public static a a(String paramString)
  {
    byte[] arrayOfByte = paramString.getBytes("UTF-8");
    return new a(new SecretKeySpec(Arrays.copyOf(MessageDigest.getInstance("SHA-1").digest(arrayOfByte), 16), "AES"));
  }
  
  public static byte[] a(byte[] paramArrayOfByte)
  {
    return a(paramArrayOfByte, a(generatekey()));
  }
  
  public static byte[] a(byte[] paramArrayOfByte, a parama)
  {
    byte[] arrayOfByte = generateIv();
    Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    localCipher.init(1, parama.a(), new IvParameterSpec(arrayOfByte));
    return localCipher.doFinal(paramArrayOfByte);
  }
  
  public static native byte[] generateIv();
  
  public static native String generatekey();
  
  public static class a
  {
    private SecretKey a;
    private SecretKey b;
    
    public a(SecretKey paramSecretKey)
    {
      a(paramSecretKey);
    }
    
    public SecretKey a()
    {
      return this.a;
    }
    
    public void a(SecretKey paramSecretKey)
    {
      this.a = paramSecretKey;
    }
    
    public SecretKey b()
    {
      return this.b;
    }
    
    public boolean equals(Object paramObject)
    {
      if (this == paramObject) {}
      a locala;
      do
      {
        return true;
        if (paramObject == null) {
          return false;
        }
        if (getClass() != paramObject.getClass()) {
          return false;
        }
        locala = (a)paramObject;
        if (!this.b.equals(locala.b)) {
          return false;
        }
      } while (this.a.equals(locala.a));
      return false;
    }
    
    public int hashCode()
    {
      return 31 * (31 + this.a.hashCode()) + this.b.hashCode();
    }
    
    public String toString()
    {
      return Base64.encodeToString(a().getEncoded(), 2) + ":" + Base64.encodeToString(b().getEncoded(), 2);
    }
  }
}

相应可算出flag

apk
home
github
archive
category