返回列表 发新帖

【应用安全】船新PackageInfo.CREATOR代理检测方案

  [复制链接]

162

主题

8064

回帖

2万

积分

博士后

滑稽

Rank: 8Rank: 8

金币
754
好评
474
信誉
210

MT论坛新人MT论坛最佳新人MT论坛活跃会员考神MT论坛帅哥MT论坛灌水老大

QQ
发表于 2025-8-15 15:18:04 来自手机  | 显示全部楼层 | 阅读模式  来自 福建
本帖最后由 逗比_滑稽 于 2025-8-15 15:30 编辑

此贴的由来还得从很久之前的帖子说起
【应用安全】对抗DEX字符串解密的两种方案

正好最近我在重写我的SignatureCheck项目,顺便分享下新项目的一个去签对抗方案思路,正好也可以围绕android签名校验写一篇帖子。


——————前言——————

说起Android应用安全最常见的防护,各位能第一时间想到谁。

我相信大部分人第一时间想起是“签名校验”,这也就是今天的主角,签名校验可以说是开发者为了防止自己的程序被非法修改而做的最常见的防护。

但为什么是最常见的手段,为什么会有签名校验这一说法?为什么能够信任签名机制不会被绕过?

这是因为签名文件是无法被伪造的,这就好比公司的公章,它是独一无二的,只有公司才知道这个公章的形状以及纹路,除非你去偷,不然你无法得知这个公章的形状以及纹路。
而给APK签名,就好比公司用公章在一张纸上盖了章,你也不可能仅凭这个纸上的印(RSA签名证书)来仿造一个一模一样的公章吧

既然签名无法被伪造,那为什么不能够直接去绕过APK完整性验证机制,这里面和签名又有什么关系?

这就得说起Google的APK完整性验证核心机制和签名验证机制,我们先来讲一讲APK的V1签名机制,你或许就能够明白为什么Google能够通过这个机制来验证APK是否完整,开发者又为什么能够信赖签名不会被伪造且修改APK只能对APK重新签名。

V1签名生成机制:
对APK进行V1签名后会产生三个文件,这三个文件也是Google用来验证APK是否完整的核心


1.MANIFEST.MF(清单文件)
该文件记录了每个文件的签名哈希值,哈西值生成原理是对整体文件进行SHA256计算

2.ANDROID.SF(签名文件)
这个相当于对MANIFEST.MF的信息二次验证
头部SHA-256-Digest-Manifest记录MANIFEST.MF整体文件SHA256哈希值

另外下面记录的每个文件摘要是MANIFEST.MF对应的文件摘要内容(包含换行符和回车符)进行二次SHA256加密得来的

3.ANDROID.RSA(证书文件)
该文件则是包含证书颁发者信息。比如:创建时间,过期时间,其中还包含公钥等...

在签名时最后会对ANDROID.SF文件使用RSA私钥进行签名加密,并把加密数据和RSA公钥一起存储至ANDROID.RSA。


这就是V1签名生成原理,说完这些可能看得有些云里雾里,不太知道这怎么验证APK完整性的,那不妨换个角度想。

在修改某个文件时会发生什么?

首先MANIFEST.MF文件会触发校验,文件整体和MANIFEST.MF记录的值不一致,所以验证失败。

如果你不服,去把MANIFEST.MF记录的值换成文件修改后的新值,那么ANDROID.SF就会验证失败,因为文件头记录了MANIFEST.MF整体文件的SHA256值,并且下面还记录了每个文件对应条目摘要二次加密的SHA256值。

如果你还不死心,计算哈希值去把ANDROID.SF文件也去改了,那么这样ANDROID.RSA对ANDROID.SF的签名验证也就失败了。

上面讲到过证书文件(ANDROID.RSA)会记录ANDROID.SF整体文件通过RSA私钥加密后的数据,以及RSA公钥。
在安装时会提取证书文件的RSA公钥将加密数据进行解密,然后把解密出的数字签名与ANDROID.SF的数字签名进行对比,如果不一致也就验证失败了。

那么就不能够修改ANDROID.RSA文件中加密数据的ANDROID.SF数字签名吗,答案是不能,因为你没有私钥。

RSA是非对称加密,有两把密钥
一把公钥,一把私钥
公钥加密的密文只能由私钥解密
私钥加密的密文只有由公钥解密

公钥在RSA文件里面会记录着,但私钥还是在开发者手里,所以即使你用公钥能解密出数字签名,但你无法将数字签名加密回去,这就形成一个闭环,现在能够明白为什么V1签名验证机制为什么不能够绕过了吧

有的人可能会说,那我把公钥和数字签名数据给换了不就好了,那样的话这个签名证书就不是原来的了。

所以说这就是为什么开发者一般直接从签名进行一个校验,因为你伪造不了开发者的签名证书,你修改APK后也必须要签名才能通过Google的证书验证和APK完整性验证,而开发者只要做好对签名校验的防护,就能够比较大程度阻止APK被篡改。


但随着时代进步,什么工具都层出不穷。

比如对Google的APK完整性验证进行破坏的核心破解
以及破坏Android获取签名逻辑的过签工具

这使以往的签名校验变得很脆弱不再安全,防守方需要采取更多的校验手段,比如:
Binder通信获取签名
验证APK整体文件MD5哈希值

但攻击方也能够有应对方案
反射代理PackageInfo.CREATOR字段
IO读写文件重定向

同样防守方能够对这些做出反击
检测CREATOR字段是否被代理
SVC读取签名文件

攻击方也能够对反击做出反击
Unsafe修改CREATOR的Class实例类名字符串内存
Hook SVC

不知你发现了没有,无论做出什么对策,双方都能够一一化解掉彼此的招式,这就好比两个不分胜负的江湖大侠,双双只能打成平手,没有绝对的矛与绝对的盾,只有暂时找不到的对策方案,这只是时间问题。


而本帖主题我以防守方说说
如何应对攻击方的
Unsafe修改CREATOR的Class实例内存
采取新的CREATOR代理检测反击方案

以及又以攻击方说说如何反击本次方案

参考文章:
Android应用程序签名过程和解析过程
android 生成签名apk源码 安卓生成签名文件

——————正文——————
这是一份Android签名校验代码

  1. //获取包管理器
  2.                 PackageManager PackManager = context.getPackageManager();
  3.                 //获取当前包名
  4.                 String PackageName = context.getPackageName();
  5. int flag = PackageManager.GET_SIGNATURES;
  6.                         //解析当前包信息
  7.                         PackageInfo PackInfo = PackManager.getPackageInfo(PackageName, flag);
  8.                         //获取签名
  9.                         Signature Sign = PackInfo.signatures[0];
复制代码

通过Context上下文获取包裹管理器,
然后调用getPackageInfo方法解析当前包名的包裹信息,最后返回PackageInfo实例,这个实例里面就包含签名值。

使用这份代码写一个签名校验Demo,让我们看看签名之后会发生什么

答案是触发了校验,校验获取了自身签名哈希值

使用MT普通版去签对我们的受害者Demo进行处理,看看又发生了什么变化

我们的受害者瑟瑟发抖,Sign签名校验竟然直接不起作用了,但这是什么原因造成的?

通过解析去签代码得知,代码会对PackageInfo.CREATOR字段进行java反射,将实例替换由KillerApplication$l充当代理人

(给不懂这里指的代理是什么意思的人一个简单的解释:这好比找工作你会通过中介去了解到某个公司,而中介就相当于代理人,每个中介介绍的公司都大同小异)

进一步查看KillerApplication$l类,
该类实现接口Parcelable.Creator并重写方法,其中去签核心就在重写的createFromParcel方法。

方法将参数传入原PackageInfo.CREATOR实例的createFromParcel方法进行封装,封装出来会得到一个PackageInfo实例,然后判断PackageInfo实例包名是否为自身应用包名,如果条件达成则修改Signature值并返回PackageInfo实例。

现在我们知道了去签代码干了什么,大致简略下原理:通过java反射代理PackageInfo.CREATOR,代理类对PackageInfo实例的Signature进行了修改。

说完去签原理,现在另外一个新问题出现了,为什么PackageInfo.CREATOR被java反射代理后,签名校验代码会失效?这个CREATOR和获取签名有什么关系?

通过翻阅Android框架的getPackageInfo方法获取原理就能知道,IPackageManager$Stub$Proxy才是获取包裹信息的最终位置。

这里面实现Binder跨进程通信获取签名
(这里解释下Binder跨进程通信:就好比两个人生处异地,无法面对面联系(不是同一个进程),但通过电话能够让这两个人互相传达信息,这个过程就相当于Binder跨进程通信)

_data相当于请求方数据,将信息打包传递给另外一个人

用令牌来识别这个人身份,也就是服务端(负责解析APK签名)
  1. _data.writeInterfaceToken("android.content.pm.IPackageManager");
复制代码


将信息打包好通过Binder与服务端进行跨进程通信,如果通信成功就会将结果传递回_reply
  1. this.mRemote.transact(3, _data, _reply, 0);
复制代码


最终通过PackageInfo.CREATOR代理人将通信返回的数据进行打包返回PackageInfo实例

而PackageInfo.CREATOR代理人其实就是对PackageInfo进行了实例化


这就是getPackageInfo获取包裹信息的原理,说了这么一大串可能会有点绕,我再简单换个说法:

比方说你是买家,你在商家那边下单了一样物品,但商家不在工厂仓库那边(不是同一个进程),所以通过电话与工厂联系(Binder通信),告诉工厂你下单的物品,然后由工厂将你要的物品给快递员,由快递员(PackageInfo.CREATOR)打包(PackageInfo实例)亲自送到你手里。

而MT的SigKill方案就是把快递员给更换了,在你还没拿到物品前,MT的快递员对物品动了手脚,将包裹信息的签名替换会原包签名,这也就是为什么我们的签名校验失效的具体原因。

想要做出对策方案也很简单,我们只需对快递员身份进行检测即可,如果不是原来的快递员则触发校验,这也是我上面提到的防守方对策方案,对PackageInfo.CREATOR字段实例类名进行判断。

  1. //获取PackageInfo数据封装类实例
  2.                 Parcelable.Creator Creator = PackageInfo.CREATOR;
  3.                 //获取类实例名称
  4.                 String Name = Creator.getClass().getName();
  5.                 //如果类实例名称与期望名称不符则认定不是原代理人
  6.                 if(!Name.equals("android.content.pm.PackageInfo$1")){
  7.                         //触发校验
  8.                 }
复制代码

再尝试使用MT去签可以看到这个校验触发了



以上,就是关于MT去签对抗的一种方案


但上有政策 下有对策,一种新的反击方案出现了

Unsafe修改PackageInfo.CREATOR的Class实例内存

这会导致无法通过类名验证PackageInfo.CREATOR其代理身份,这个东西的来龙去脉还得从前几天说起

前几天我开始重写SignCheck项目,想找几个去签工具试试校验,正好论坛前段时间有位大佬分享了自己写的过签工具,这不正好拿来测试

当我拿工具对我的dome进行过签后全部通过,理所应当。

不得不说大佬的工具确实厉害,因为项目中我有写了个Shell读签名文件的校验,没想到过签把Shell的读写也给重定向了(具体的没去看,大概猜测)

但有一个让我惊讶的点,当我拆包编译去签代码我发现了个老熟人,这个字段实例是PackageInfo.Creator,也就是getPackageInfo封装实例数据的代理人。

我开始疑惑这个家伙怎么会在去签代码里,如果它对PackageInfo.Creator进行了代理为什么我的Creator类名检测查不到其身份。

这里我做了假设去签一定会对Creator进行了代理,但可能通过某种方式抹去了身份信息。

后面在翻开其它包我看到了这个家伙
sun.misc.Unsafe

这样的话就解释的通了,因为这个类让我想起了eirv大佬曾经对我的SignatureCheck旧项目7.0版本进行一个纯java去签,里面的代码就有使用Unsafe。
【帖子】纯Java移除SignatureCheck7.0签名校验

代码通过代理PackageInfo.Creator绕过了Signature签名校验

但为什么会没有触发我的PackageInfo.Creator类名检测?

下面的代码会告诉我们答案,它使用了Unsafe对PackageInfo.Creator类实例地址偏移28写入了原代理类名,把字段值的类名给修改了,但这并不影响jvm正常运作,执行的方法依然是替换的新代理类,这也就是为什么我们无法检测的原因。

这份去签代码可以说很微妙也很新奇,使用Unsafe进行修改绕过代理检测,在此之前很少有这种案例(至少我是第一次见),因为旧项目SignatureCheck其实是一个连锁反应校验,你改的越多触发的校验也就越多。所以代码只做了小部分的修改以及一些我没预料到的代理方案。
况且这还是纯java的去签,我表示佩服

为了应对这种方案,我现在做出了新的反击。

使用PackageManager获取自身包裹信息,并给予一个虚假的签名值,再将包裹信息实例重新写入到一个新Parcel实例,利用含包裹信息的Parcel实例调用PackageInfo.Creator重新封装一个PackageInfo实例,并判断是否为虚假签名值。
  1. //获取PackageInfo实例重新封装一个Parcel,在把Parcel传入PackageInfo.CREATOR进行封装验证其签名值
  2.         private void PackInfoCREATORCheck2() throws Exception {
  3.                 //获取自身签名包裹信息
  4.                 PackageManager PackM = context.getPackageManager();
  5.                 String PackName = context.getPackageName();
  6.                 PackageInfo Info = PackM.getPackageInfo(PackName, PackageManager.GET_SIGNATURES);
  7.                 //并把包裹信息签名值更替成虚假签名
  8.                 byte[] FalseSign = "doubihuaji".getBytes();
  9.                 Info.signatures[0] = new Signature(FalseSign);
  10.                 //获取Parcel
  11.                 Parcel _data = Parcel.obtain();
  12.                 //对Parcel写入包裹信息数据
  13.                 Info.writeToParcel(_data, 0);
  14.                 //指针回0
  15.                 _data.setDataPosition(0);

  16.                 //获取CREATOR
  17.                 Parcelable.Creator<PackageInfo> CREATOR = PackageInfo.CREATOR;
  18.                 //重新对数据进行封装
  19.                 Info = CREATOR.createFromParcel(_data);
  20.                 //回收内存
  21.                 _data.recycle();

  22.                 //判断签名值是否为修改的签名值,不是则代表Creator已被代理
  23.                 int hash = Info.signatures[0].hashCode();
  24.                 int hash2 = new Signature(FalseSign).hashCode();
  25.                 if (hash != hash2) {
  26.                         //触发校验
  27.                 }
  28.         }
复制代码



如果PackageInfo.Creator没被代理,必然会获取到我们提供的虚假签名值。
但被代理了呢,如果代码没有对签名值做判断,那么肯定会无脑写死原签名值。

使用过签工具在测试一下我们的demo
我们的新代理检测校验被触发了
这也证实这工具确实采用了此方案绕过了我原本的PackageInfo.Creator代理检测


最后,想要应对我这种方案也很简单,再判断包名的同时进一步对签名值进行判断即可,如果签名值不是当前的签名值,则不给予修改,这样就不会触发我这个方案的校验。

另外,分享下本贴使用的dome,这是我新重写的ApkCheck项目,现在只是测试版本我并没有写多少校验,但多多少少可以拿来测试你的过签工具强度

【点我下载】ApkCheck-公测版

本帖到这就结束了,如果你喜欢本篇内容或者说给你有很大感悟,请麻烦点个赞或者投个币,这是对我帖子的支持以及动力,谢谢。
已有6人评分好评 金币 理由
ningyh + 1
黄金 + 1 + 1
氵衮 + 1 + 1
青春向上 + 1 + 1 感谢分享。
紫檀 + 1 + 1
redblack25 + 1 + 1 赞一个!

查看全部评分 总评分:好评 +5  金币 +6 

回复

使用道具 举报

162

主题

8064

回帖

2万

积分

博士后

滑稽

Rank: 8Rank: 8

金币
754
好评
474
信誉
210

MT论坛新人MT论坛最佳新人MT论坛活跃会员考神MT论坛帅哥MT论坛灌水老大

QQ
发表于 2025-8-15 15:18:57 来自手机  | 显示全部楼层  来自 福建
可能内容有点长了
回复

使用道具 举报

8

主题

1151

回帖

4249

积分

大学生

Rank: 5Rank: 5

金币
1102
好评
8
信誉
117
发表于 2025-8-15 15:20:50 来自手机  | 显示全部楼层  来自 浙江
学习学习大佬思路
回复

使用道具 举报

69

主题

3011

回帖

8580

积分

硕士生

logking

Rank: 6Rank: 6

金币
2895
好评
10
信誉
117

MT论坛最佳新人MT论坛帅哥考神MT论坛新人

发表于 2025-8-15 15:22:38 来自手机  | 显示全部楼层  来自 广东
看看
回复

使用道具 举报

134

主题

3460

回帖

1万

积分

博士生

无敌最俊朗

Rank: 7Rank: 7Rank: 7

金币
3011
好评
49
信誉
105
发表于 2025-8-15 15:24:54 来自手机  | 显示全部楼层  来自 四川
这么长赶上作文了
回复

使用道具 举报

8

主题

4452

回帖

9781

积分

硕士生

Rank: 6Rank: 6

金币
4912
好评
5
信誉
159

考神

发表于 2025-8-15 15:25:02 | 显示全部楼层  来自 安徽
看看隐藏
回复

使用道具 举报

10

主题

1646

回帖

7998

积分

硕士生

Rank: 6Rank: 6

金币
259
好评
10
信誉
120
发表于 2025-8-15 15:25:04 来自手机  | 显示全部楼层  来自 四川
学习学习
回复

使用道具 举报

18

主题

1723

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
3610
好评
30
信誉
104

考神MT论坛新人MT论坛帅哥MT论坛最佳新人MT论坛活跃会员

QQ
发表于 2025-8-15 15:25:39 来自手机  | 显示全部楼层  来自 陕西
看看隐藏
回复

使用道具 举报

0

主题

2398

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
4831
好评
1
信誉
101

考神MT论坛帅哥MT论坛最佳新人MT论坛新人挂机大佬

发表于 2025-8-15 15:25:41 来自手机  | 显示全部楼层  来自 广东
看看~~
回复

使用道具 举报

8

主题

1260

回帖

5710

积分

硕士生

棺材板

Rank: 6Rank: 6

金币
2287
好评
51
信誉
103
发表于 2025-8-15 15:25:42 来自手机  | 显示全部楼层  来自 四川
回复

使用道具 举报

35

主题

6577

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
1502
好评
6
信誉
172

考神MT论坛新人MT论坛帅哥MT论坛最佳新人

QQ
发表于 2025-8-15 15:26:14 来自手机  | 显示全部楼层  来自 河北
感谢分享,
回复

使用道具 举报

162

主题

8064

回帖

2万

积分

博士后

滑稽

Rank: 8Rank: 8

金币
754
好评
474
信誉
210

MT论坛新人MT论坛最佳新人MT论坛活跃会员考神MT论坛帅哥MT论坛灌水老大

QQ
发表于 2025-8-15 15:26:49 来自手机  | 显示全部楼层  来自 福建
古月方源 发表于 2025-8-15 15:24
这么长赶上作文了

涉及的知识方面比较多,比如v1签名机制,android获取签名原理以及一些对策方案
回复

使用道具 举报

139

主题

859

回帖

4477

积分

大学生

Rank: 5Rank: 5

金币
968
好评
37
信誉
100

MT论坛帅哥考神MT论坛新人MT论坛最佳新人MT论坛活跃会员

QQ
发表于 2025-8-15 15:26:58 来自手机  | 显示全部楼层  来自 福建
看看隐藏
回复

使用道具 举报

23

主题

2392

回帖

7416

积分

硕士生

Rank: 6Rank: 6

金币
1435
好评
21
信誉
101

MT论坛新人考神

发表于 2025-8-15 15:27:57 来自手机  | 显示全部楼层  来自 湖南
看看隐藏
回复

使用道具 举报

0

主题

7273

回帖

2万

积分

博士后

Rank: 8Rank: 8

金币
2246
好评
8
信誉
124

考神

发表于 2025-8-15 15:29:19 来自手机  | 显示全部楼层  来自 广东
看看
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表