apk逆向
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插桩
3.自动化工具
0x03 实例破解
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