Android逆向工程实验指南:从入门到实践
Android逆向工程实验指南:从入门到实践
Android逆向工程是一种通过对已编译的Android应用进行分析,以获取其源代码、算法实现等信息的技术。这种技术在安全审计、功能分析、二次开发等领域有着广泛的应用。本文将通过四个具体的案例,详细介绍Android逆向分析的基本方法和技巧。
Android 逆向分析实验
Task 1:Case Study 1
将task1.apk
安装至安卓设备后,发现需要对某个输入进行校验:
随机尝试输入并进行校验,发现提示flag错误,可见需要输入正确的flag才能够通过校验。
下载dex2jar和jd-gui并为对应文件添加可执行权限。使用dex2jar将task1.apk
转换为JAR文件格式:
d2j-dex2jar.sh ./task1.apk
可以看到以Java代码形式展示的task1.apk
:
分析可得当flag的位数与s
相同(31),且每一位i
均满足flag[i] ^ 0x17 == s[i]
(^:如果相应位值相同,则结果为0,否则为1)。按照提示写Python代码暴力破解:
s = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 替换为实际的s值
flag = ""
for i in range(len(s)):
flag += chr(ord(s[i]) ^ 0x17)
print(flag)
验证:
Task 2:Case Study 2
使用dex2jar和jd-gui读取task2.apk
中的Java代码。首先检查MainActivity.class
,发现其中的onClick
函数检查了一个名为a
的类中的一个名为a
的函数的返回值,而函数a
的参数为用户的输入:
public class MainActivity extends c {
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130968603);
EditText editText = (EditText)findViewById(2131427422);
findViewById(2131427423).setOnClickListener(new
View.OnClickListener(this, editText, (Context)this) {
public void onClick(View param1View) {
if (a.a(this.a.getText().toString())) {
Toast.makeText(this.b, "You get it~",1).show();
return;
}
Toast.makeText(this.b, "Sorry its wrong",1).show();
}
});
}
}
检查a.class
,其中的函数a
如下所示:
public static boolean a(String paramString) {
if (paramString.length() == b.length) {
int[] arrayOfInt = new int[a.length];
arrayOfInt[0] = 0;
byte[] arrayOfByte = paramString.getBytes();
int k = arrayOfByte.length;
int i = 0;
int j = 1;
while (i < k) {
arrayOfInt[j] = arrayOfByte[i];
j++;
i++;
}
i = 0;
while (i < c.length) {
if (a[i] == b[i] * arrayOfInt[i] * arrayOfInt[i] + c[i] *
arrayOfInt[i] + d[i] && a[i + 1] == b[i] * arrayOfInt[i + 1] * arrayOfInt[i+ 1] + c[i] *arrayOfInt[i + 1] + d[i]) {
i++;
continue;
}
return false;
}
return true;
}
return false;
}
分析该函数。该函数中将用户输入按字节存入一个字节数组arrayOfByte
,然后将arrayOfByte
逐元素转存到int
数组arrayOfInt
中(从arrayOfInt
的第二位开始,arrayOfInt
的第一位为0)。arrayOfInt
的长度为数组a
的长度,因此可知用户输入的长度比数组a
的长度少一个字节,为34个字节。最后逐元素验证数组arrayOfInt
的元素是否满足16行中的数量关系,若均满足则返回true
。
编写Python代码破解:
b = [xxx, xxx, xxx, ...] # 替换为实际的b数组
c = [xxx, xxx, xxx, ...] # 替换为实际的c数组
d = [xxx, xxx, xxx, ...] # 替换为实际的d数组
a = [xxx, xxx, xxx, ...] # 替换为实际的a数组
flag = ""
for i in range(34):
for j in range(256):
if a[i] == b[i] * j * j + c[i] * j + d[i] and a[i + 1] == b[i] * (j + 1) * (j + 1) + c[i] * (j + 1) + d[i]:
flag += chr(j)
break
print(flag)
输入验证:
Task 3:Case Study 3
使用file
命令查看task3.txt
的文件类型,发现为Zip压缩文件。
使用Unzip
命令解压缩后按上两个Task的方法查看Java代码。查看onClickTest
函数发现函数对比了用户输入与i
函数的返回值:
public void onClickTest(View paramView) {
if (this.n.getText().toString().equals(i())) {
this.o.setText(2131099685);
return;
}
this.o.setText(2131099683);
}
将i
函数放在在线Java IDE中运行,并将返回值转换为String类型输出,得到flag:
Task 4:Case Study 4
同样使用dex2jar和jd-gui查看task4.apk
的源代码,可以发现onCreate
函数中设置了某个按钮为不可点击状态,猜测这个按钮就是查看flag的按钮。
使用AndroidKiller对task4.apk
进行反编译。
查看onCreate
函数的smali代码,可以找到Java代码中设置按钮状态的对应代码。其中v5
寄存器在前方代码中被置为0,猜测该寄存器传递的就是参数false
。将一个新寄存器v4
的值置为1并替代传参语句中的v5
,使传递的参数修改为true
。
使用AndroidKiller将修改后的文件重新编译。将生成的task4_killer.apk
在安卓中运行,此时直接点击"爬到了,看Flag"按钮即可看到界面上显示出flag。