Android 程序的反破解技术

时间:2022-02-28 09:12:58

我们知道,利用 apktool 可以将 apk 反编译为 smali 文件,利用 dex2jar 也可以将 apk 反编译为 jar 文件。这样的话,破解者就可以根据关键代码(比如资源文件中的字符串),修改代码,然后再利用 apktool 重新编译,并运行 signapk.bat 重新签名打包为己所用,而你辛辛苦苦几个月的努力一下回到*!

Android 程序的反破解技术

最近看过《Android 软件安全与逆向分析》之后,又有了不少收获。

那么,怎样防止破解呢?其实之前介绍的利用 proguard 进行代码混淆就是一种方式,它可以有效增加利用 dex2jar 反编译后破解的难度。另外,也可以通过检测调试器、模拟器、签名的 hash 值和 classes.dex 文件的 crc 值来确定确认 apk 文件的完整性。

检测调试器

我们发布时将 AndroidManifest.xml 文件中 application 标签的 android:debuggable 属性设为 false ,程序运行时再去检测:

1
2
3
4
5
public void checkDebug(){
if((getApplicationInfo().flags&=ApplicationInfo.FLAG_DEBUGGABLE)!=0){
android.os.Process.killProcess(android.os.Process.myPid());
}
}

此外,Android SDK 还提供了一个专门检测 debugger 是否连接的方法:

1
android.os.Debug.isDebuggerConnected();

这样就不用在 AndroidManifest.xml 里配置字段了。

检测模拟器

通过 dab shell getprop 可以发现模拟器客真机这几个属性不一致:

  • ro.product.model : 模拟器中为 sdk ,真机中为具体型号;

  • ro.build.tags : 模拟器中为 test-keys ,真机中为 release-keys ;

  • ro.kernel.qemu : 模拟器中为 1 ,真机中不存在;

下面以第三个字段为例做检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public boolean isRunningInEmulator(){
boolean qemuKernel=false;
Process process=null;
DataOutpusStream os=null;
try{
process=Runtime.getRuntime().exec("get prop ro.kernel.qemu");
os=new DataOutputStream(process.getOutputStream());
BufferedReader in=new BufferedReader(
new InputStreamReader(process.getInputStream(), "GBK"));
os.writeBytes("exit\n");
os.flush();
process.waitFor();
qemuKernel=(Integer.valueOf(in.readLine())==1);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(os!=null){
os.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
return qemuKernel;
}

检查签名的 hash 值

Android 的 PackageManager 类提供了读取签名信息方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int getSignature(Context context, String packageName){
PackageManager pm=context.getPackageManager();
PackageInfo pi=null;
int sig=0;
try{
pi=pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
Signature[] s=pi.signatures;
sig=s[0].hashCode();
}catch(Exception e){
sig=0;
e.printStackTrace();
}
return sig;
}

打包发布前我们将这个 hash 值保存在 server 端,程序运行时再去比对。

检查 classes.dex 文件 CRC 值

apk 文件本质上是 zip 压缩文件,而 Android SDK 自带了读取 zip 压缩包 CRC 值的 API :

1
2
3
4
5
6
7
8
9
10
11
12
public long getDexCrc(Context context){
long crc=0;
ZipFile zf;
try{
zf=new ZipFile(context.getApplicationContext().getPackageCodePath());
ZipEntry ze=zf.getEntry("classes.dex");
crc=ze.getCrc();
}catch(Exception e){
e.printStackTrace();
}
return crc;
}

打包发布前我们也可以将这个 crc 值保存在 server 端,程序运行时再去比对。

将以上几种方式结合起来,就可以大大增强 apk 文件的破解难度。

最后,网上有些大神说还可以通过对 apktool 和 dex2jar 等反编译工具进行压力测试,以得到错误信息,而这些工具是开源的,这样我们就可以顺藤摸瓜地找到这些工具本身的漏洞,进而在我们的代码中加以利用,达到釜底抽薪的作用。例如对 dex2jar 运行这个批处理:

1
for %%i in (*.apk) do dex2jar %%i

这种思路理论上是行得通的,但我没有亲测过,这里就不再多说了。