Android 签名漏洞分析
前面介绍了,Android 的签名在理论上可以防止别人破环丁软恩代码)
能以作者的名义发布.但是Android 的签名机制最近接连暴露了两个涌洞,导致整个签名机
第一个漏洞是由国外的安全公司 Bluebox Security 发现的,这个漏洞目 Android 1.6以来就
同虚设.
直存在,号称对 99%的Android 设各造成了影响.恶意软件制作者可以在不破坏原有apk
的前提下,利用这个漏洞来修改 apk 的代码并绕开 Android 应用的签名验证机制。
这个漏洞的原理是安装apk 文件时,如果apk 包中同时存在着两个 classes.dex 文件,安装
读到第二个 classcs.dex 时会覆盖掉第一个.这样实际进行签名检验的是第二个 classes.dex.
在运行时仍然执行的是第一个classes.dex,因此,只要设法在一个apk 文件中放置两个classes.de
并使它们按照恶意classes.dex在前,正常classes.dex在后的顺序出现在文件中,就可以绕开务
检验并安装成功.
这段出问题的代码位于 libcore/luni/src/main/java/java/util/zip/ZipFile.java中,看看下面这段从
版本Android 4.2.2中摘录的代码:
private void readCentralDir()throws IOException{
.
// Seek to the first CDE and read all entries.
RAFStream rafs=new RAFStream(mRaf,centralDiroffset);
BufferedInputStream bin=new BufferedInputStream(rafs,4096);
byte[] hdrBuf-new byte[CENHDR];// Reuse the same buffer for each entry.
for(int i=0;i< numEntries;++i){
ZipEntry newEntry =new ZipEntry(hdrBuf,bin);
mEntries.put(newEntry.getName(),newEntry);
很明显,最后这段for循环的代码有问题,循环中读取了压缩包的内容并逐项加入到 mEntries
中,而mEntries 是一个类型为LinkedHashMap的变量,调用 put0函数时如果有重名的项,会覆
盖前一项。
下面再看看Android5.0版本中的代码,就知道Google 如何修复这个漏洞了:
private void readCentralDir()throws IOException{
RAFStream rafStreamnew RAFStream(raf,centralDiroffset);
BufferedInputStream buf feredstream =new BufferedInputstream(rafstream,4096);
byte[] hdrBuf=new byte[CENHDR];//Reuse the same buffer for each entry.
for(int i0;i< numEntries;++i){
ZipEntry newEntry=new ZipEntry(hdrBuf,bufferedstream);
if(newEntry,localHeaderRelOffset > centralDiroffset){
Hinu aoitsxin
throw new ZipException("Local file header offset is after
central directory");
String entryNamenewEntry.getName())
if(entries,put(entryName,newEntry)!null){
throw new ZipException("Duplicate entry name:"+entryName);
}
}
}
新的代码会先检查 entries 中是否已经存在同名的项,如果有就会抛出异常。这个漏洞任
Android4.3中就已经修正了。
2.4 Android中的签名
可能有人会感兴趣,如何制造一个这样的 apk 文件呢?其实很简单,这里就不介绍了。当然
检测这种恶意程序也很简单,只要发现一个apk中有两个classes.dex就可以判定,正常的apk文
件不会包含两个 classes.dex 文件.
第二个Andorid 签名漏洞最早由国内的安全 team 发现并提交给 Google,Google 很快修复了
该漏洞。这个漏洞利用了在Android 签名验证过程中,对Zip 文件进行16位数的读取时,没有考
虑到大于2~15的情况。这样在将short 型表示的块大小转换成int型时,会把大于215的数转换成
int 型的负数。但是,在native 层执行时,并不会出错。因为Java的int,short,long都是有符号
数,而不像C/C++中还有无符号数。
具体的漏洞原理就不分析了,有兴趣的读者可以上网搜索。