阅读本文前,应先了解反编译工具的使用,和smali代码的插桩修改

前提:应用厂商为防止二次打包发布,一般会校验签名,甚至使用签名作为网络请求起算参数,防止服务器资源被盗用
作用:避免二次打包后签名校验导致无法运行问题,破解签名后,可插桩日志代码,分析应用请求接口
破解方法:很简单,伪造正确签名,欺骗程序读取正确签名

步骤:

  1. 判断是否使用了签名校验
  2. 获取正确签名并保存
  3. 欺骗程序获取正确签名

判断是否使用了签名校验

使用~/.android/debug.keystore对应用进行重新签名,安装,检测能否正常使用

1
2
3
4
5
# 重新签名脚本
apk=xxx.apk
apk_sign=${apk%.*}_sign.${apk##*.}
jarsigner -verbose -keystore ~/.android/debug.keystore -signedjar $apk_sign $apk androiddebugkey -storepass android
adb install -r $apk_sign

如果还能正常使用,代表没作签名校验,如果不能正常使用,解压apk,grep "signatures" -r .找出所有signatures的,举例

image

可见,应用可能在java和so中进行校验签名,由于欺骗java获取签名,使用baksmalismali工具即可,难度在于欺骗so,下文主要讲如何欺骗so获取正确应用签名

获取正确签名并保存

安装过程,会调用PagekManagerServiceinstallPackage方法,接着调用PackageParser对安装包进行解释,查看相应源码,我们可以知道,PackageInfosignatures数组长度正常情况下为1,即只对AndroidManifest.xml签名进行保存,另外签名缓存路径为:/data/system/packages.xml

1
2
3
4
5
6
7
8
private void logSignature(String path){
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SIGNATURES);
Signatures[] signatures = packageInfo.signatures;
String signature = signatures[0].toCharsString();
Log.e("signature", signature); //将signature字符串保存下来
}

欺骗程序获取正确签名

通过apktool反编译的apk,smali可以随便改,而so不能随便修改,从获取签名函数可以看出,so获取系统签名,必须往下传Context实例,通过反射调用java方法,获取签名。因此,只需要对传给so的Context实例进行封装下,即可达到欺骗目的

1 使用ida打开相关so文件,查找signatures,由于本文只是为了欺骗,不打算看汇编代码(主要是知识还老师了)

image

2 查看Context相关反射调用,看so中的字符串常量即可,记录下来,封装时需要用到

image

上图可见会调用ContextgetSystemServicegetPackageNamegetPackageManager方法

3 封装与调用

3.1 封装Context对象,由于Application集成了Context,直接继承Application,重写相应方法比较方便,重写获取系统签名方法,需要继承三个类,分别是ApplicationPackageManagerPackageInfo,代码如下

FateApplication继承Application

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
27
public class FakeApplication extends Application {
private Context context;
public FakeApplication(Context context) {
this.context = context;
}
@Override
public Object getSystemService(String name) {
return context.getSystemService(name);
}
@Override
public ApplicationInfo getApplicationInfo() {
return context.getApplicationInfo();
}
@Override
public PackageManager getPackageManager() {
return new FakePackageManager();
}
@Override
public String getPackageName() {
return context.getPackageName();
}
}

FakePackageManager继承PackageManager

1
2
3
4
5
6
7
8
9
public class FakePackageManager extends PackageManager {
@Override
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
return new FakePackageInfo();
}
// 此处省略一堆无关紧要的abstract方法的重写
}

FakePackageInfo继承PackageInfo

1
2
3
4
5
6
7
8
9
public class FakePackageInfo extends PackageInfo {
private final static String SIGNATURE = "${第二步保存的签名字符串}";
public FakePackageInfo() {
this.signatures = new Signature[]{new Signature(SIGNATURE)};
}
}

3.2 调用

使用apktool反编译apk,然后sublime text打开,搜索loadLibrary,找到java类,再查找传递context对象native函数,再调用此native函数时,创建FakeApplication对象

image