360手机助手关于签名校验的分析

360手机助手中软件更新

0x01:分析过程

使用wireshark抓包分析更新时请求的是一个配置文件,请求url:update.api.sj.360.cn/mintf/getAppsByPackNames  后面是参数,请求方式为POST,内容如下:

可以看到其中包含了几个关键的参数down_url,apk_md5,signature_md5,size,其中比较关键的一个校验是signature_md5,其他都可以轻松替换,所以我们的重点就是对signature_md5,校验的破解,去分析他对signature_md5加密的方式,以及如何模拟算法,

0x02:知识补充

关于安卓apk数字签名校验的知识补充,安卓apk数字校验使用的是rsa非对称加密算法也就是我们常说的公钥私钥加密,这个算法

指数运算谁都懂,不必说了,先说说模运算。模运算是整数运算,有一个整数m,以n为模做模运算,即m mod n。怎样做呢?让m去被n整除,只取所得的余数作为结果,就叫做模运算。例如,10 mod 3=1;26 mod 6=2;28 mod 2 =0等等。 
模指数运算就是先做指数运算,取其结果再做模运算。如
好,现在开始正式讲解RSA加密算法。
算法描述:
(1)选择一对不同的、足够大的素数p,q。
(2)计算n=pq。
(3)计算f(n)=(p-1)(q-1),同时对p, q严加保密,不让任何人知道。
(4)找一个与f(n)互质的数e,且1<e<f(n)。
(5)计算d,使得de≡1 mod f(n)。这个公式也可以表达为d ≡e-1 mod f(n)
这里要解释一下,≡是数论中表示同余的符号。公式中,≡符号的左边必须和符号右边同余,也就是两边模运算结果相同。显而易见,不管f(n)取什么值,符号 右边1 mod f(n)的结果都等于1;符号的左边d与e的乘积做模运算后的结果也必须等于1。这就需要计算出d的值,让这个同余等式能够成立。
(6)公钥KU=(e,n),私钥KR=(d,n)。
(7)加密时,先将明文变换成0至n-1的一个整数M。若明文较长,可先分割成适当的组,然后再进行交换。设密文为C,则加密过程为:
(8)解密过程为:

Android的签名机制,通过分析signapk.jar这个可执行包可以知晓签名APK的整个过程

1.程序遍历整个apk文件包中的所有文件(entry),对非文件夹非签名文件的文件,逐个生成SHA1的数字签名信息,再用Base64进行编码,之后将生成的签名写入MANFEST.MF文件

2.对前一步生成的Manifest,使用SHA1-RSA算法,用私钥进行签名,生成CERT.SF文件

3.生成MANIFEST.MF没有使用密钥信息,生成CERT.SF文件使用了私钥文件。那么我们可以很容易猜测到,CERT.RSA文件的生成肯定和公钥相关CERT.RSA文件中保存了公钥、所采用的加密算法等信息

知晓了加密流程,我们可以认识到

1、 Android签名机制其实是对APK包完整性和发布机构唯一性的一种校验机制。
    2、 Android签名机制不能阻止APK包被修改,但修改后的再签名无法与原先的签名保持一致。(拥有私钥的情况除外)。
    3、 APK包加密的公钥就打包在APK包内,且不同的私钥对应不同的公钥。换句话言之,不同的私钥签名的APK公钥也必不相同。所以我们可以根据公钥的对比,来判断私钥是否一致。

也就是说360手机助手也不可能知道需要升级软件数字签名的私钥,所以可以猜测它进行的是对公钥的加密,那么接下来要验证这个观点

0x03逆向分析

这里我们使用一个强大的安卓反编译软件ApkIDE.exe,然后把360手机助手逆向分析一下:

1.搜索signature_md5我们发现了另一个量sign_md5_default_value,那么我们来定位一下:在Apk改之理中搜索sign_md5_default_value 我们分析出现的位置

分析中在d.smali文件中有一段很关键

method private static b(Landroid/content/Context;Lcom/qihoo/appstore/resource/app/App;)V

.locals 3 //  private static void b(Context paramContext, App paramApp)

invoke-virtual {p1}, Lcom/qihoo/appstore/resource/app/App;->Z()Ljava/lang/String;

move-result-object v0

invoke-static {v0}, Lcom/qihoo/appstore/j/d;->c(Ljava/lang/String;)Z

move-result v0

if-eqz v0, :cond_0

sget-object v0, Lcom/qihoo/appstore/j/d;->a:Landroid/content/Context;

invoke-virtual {p1}, Lcom/qihoo/appstore/resource/app/App;->Z()Ljava/lang/String;

move-result-object v1

invoke-static {v0, v1}, Lcom/qihoo/appstore/utils/de;->f(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;

move-result-object v0

invoke-virtual {p1}, Lcom/qihoo/appstore/resource/app/App;->bL()Ljava/lang/String;

move-result-object v1

const-string v2, "sign_md5_default_value"

invoke-virtual {v2, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v2

if-nez v2, :cond_1

const-string v2, ""

invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v2

if-nez v2, :cond_1

sget-object v2, Lcom/qihoo/appstore/j/d;->a:Landroid/content/Context;

invoke-static {v2, v0, v1}, Lcom/qihoo/appstore/utils/de;->a(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Z

move-result v0

if-nez v0, :cond_1

const/4 v0, 0x1

invoke-virtual {p1, v0}, Lcom/qihoo/appstore/resource/app/App;->r(Z)V

:cond_0

:goto_0

return-void

:cond_1

const/4 v0, 0x0

invoke-virtual {p1, v0}, Lcom/qihoo/appstore/resource/app/App;->r(Z)V

goto :goto_0

.end method

前面那几句大概意思就是用一个boolean方法判断App成员的Z()方法也就是说那个是不是一个String,换句话我们可以大胆猜测这里是判断有没有获取到程序的MD5,

所以定位到这一句关键的代码Lcom/qihoo/appstore/utils/de;->f(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;

其中使用了de的 f() 方法对Context, String 参数进行处理 我们可以猜测这里的String代表的就是我们的MD5:

.method public static f(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;

.locals 2

:try_start_0

invoke-virtual {p0}, Landroid/content/Context;->getPackageManager()Landroid/content/pm/PackageManager;

move-result-object v0

const/16 v1, 0x40

invoke-virtual {v0, p1, v1}, Landroid/content/pm/PackageManager;->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;

move-result-object v0

iget-object v0, v0, Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature;

const/4 v1, 0x0

aget-object v0, v0, v1

invoke-virtual {v0}, Landroid/content/pm/Signature;->toByteArray()[B

move-result-object v0

invoke-static {v0}, Ljava/util/Arrays;->toString([B)Ljava/lang/String;

move-result-object v0

invoke-static {v0}, Lcom/qihoo/appstore/utils/de;->e(Ljava/lang/String;)Ljava/lang/String;

move-result-object v0

invoke-virtual {v0}, Ljava/lang/String;->toLowerCase()Ljava/lang/String;

:try_end_0

.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

move-result-object v0

:goto_0

return-object v0

:catch_0

move-exception v0

sget-boolean v1, Lcom/qihoo360/mobilesafe/a/a;->a:Z

if-eqz v1, :cond_0

invoke-virtual {v0}, Ljava/lang/Exception;->printStackTrace()V

:cond_0

const/4 v0, 0x0

goto :goto_0

.end method

首先调用系统的Context.getPackageManager().getPackageInfo(String,256/4).signature[0].toByteArray() 然后返回一个String对象然后再调用de类的e方法

.method public static e(Ljava/lang/String;)Ljava/lang/String;

.locals 2

:try_start_0

const-string v0, "MD5"

invoke-static {v0}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;

move-result-object v0

invoke-virtual {p0}, Ljava/lang/String;->getBytes()[B

move-result-object v1

invoke-virtual {v0, v1}, Ljava/security/MessageDigest;->update([B)V

invoke-virtual {v0}, Ljava/security/MessageDigest;->digest()[B

move-result-object v0

invoke-static {v0}, Lcom/qihoo/appstore/utils/de;->a([B)Ljava/lang/String;

:try_end_0

.catch Ljava/security/NoSuchAlgorithmException; {:try_start_0 .. :try_end_0} :catch_0

move-result-object p0

:goto_0

return-object p0

:catch_0

move-exception v0

invoke-virtual {v0}, Ljava/security/NoSuchAlgorithmException;->printStackTrace()V

goto :goto_0

.end method

这段话就是对String进行一次MD5运算然后调用a方法,其中我们看到的是调用的a的byte[]参数的方法

method public static a([B)Ljava/lang/String;

.locals 4

new-instance v1, Ljava/lang/StringBuilder;

array-length v0, p0

mul-int/lit8 v0, v0, 0x2

invoke-direct {v1, v0}, Ljava/lang/StringBuilder;-><init>(I)V

const/4 v0, 0x0

:goto_0

array-length v2, p0

if-ge v0, v2, :cond_0

sget-object v2, Lcom/qihoo/appstore/utils/de;->f:[C

aget-byte v3, p0, v0

and-int/lit16 v3, v3, 0xf0

ushr-int/lit8 v3, v3, 0x4

aget-char v2, v2, v3

invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;

sget-object v2, Lcom/qihoo/appstore/utils/de;->f:[C

aget-byte v3, p0, v0

and-int/lit8 v3, v3, 0xf

aget-char v2, v2, v3

invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;

add-int/lit8 v0, v0, 0x1

goto :goto_0

:cond_0

invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

move-result-object v0

return-object v0

.end method

这段代码的作用就是对一个Byte[]数组进行格式化处理返回一个String对象

那么现在可以猜测360手机助手是对软件公钥的数字签名放到一个数组中,然后取它的第1个成员sinagture[0]进行MD5加密,下面我们来验证猜想

0x04 验证猜想

首先搭建好google sdk 环境 然后我们来写一个安卓程序来模拟360手机助手加密的过程,这里我们用google skd自带的Eclipse进行操作

其中代码如下:

import java.lang.reflect.Array;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.util.Arrays;

import android.app.Activity;

import android.content.pm.PackageInfo;

import android.content.pm.PackageManager;

import android.content.pm.PackageManager.NameNotFoundException;

import android.content.pm.Signature;

import android.os.Bundle;

import android.text.TextUtils;

import android.view.Menu;

import android.view.View;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;

public class MainActivity extends Activity {

private EditText et_pkgname;

private TextView tv_signature;

private PackageManager manager;

private PackageInfo packageInfo;

private Signature[] signs;

private StringBuilder builder;

private String signature;

private static final char[] f = new char[] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70 };

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

et_pkgname = (EditText) findViewById(R.id.et_pkgname);

tv_signature = (TextView) findViewById(R.id.tv_signature);

manager = getPackageManager();

builder = new StringBuilder();

}

public void getSignature(View view) {

String pkgname = et_pkgname.getText().toString();

boolean isEmpty = TextUtils.isEmpty(pkgname);

if (isEmpty) {

Toast.makeText(this, "应用程序的包名不能为空!", Toast.LENGTH_SHORT);

} else {

try {

/** 通过包管理器获得指定包名包含签名的包信息 **/

packageInfo = manager.getPackageInfo(pkgname, PackageManager.GET_SIGNATURES);

/******* 通过返回的包信息获得签名数组 *******/

signs = packageInfo.signatures;

/******* 循环遍历签名数组拼接应用签名 *******/

/************** 得到应用签名 **************/

String str = e(Arrays.toString(packageInfo.signatures[0].toByteArray()).toLowerCase());

builder.append(str);

signature = builder.toString();

tv_signature.setText(signature);

} catch (NameNotFoundException e) {

e.printStackTrace();

}

}

}

public static String e(String paramString)

{

try

{

MessageDigest localMessageDigest = MessageDigest.getInstance("MD5");

localMessageDigest.update(paramString.getBytes());

String str = a(localMessageDigest.digest());

return str;

}

catch (NoSuchAlgorithmException localNoSuchAlgorithmException)

{

localNoSuchAlgorithmException.printStackTrace();

}

return paramString;

}

public static String a(byte[] paramArrayOfByte)

{

StringBuilder localStringBuilder = new StringBuilder(2 * paramArrayOfByte.length);

for (int i1 = 0; i1 < paramArrayOfByte.length; i1++)

{

localStringBuilder.append(f[((0xF0 & paramArrayOfByte[i1]) >>> 4)]);

localStringBuilder.append(f[(0xF & paramArrayOfByte[i1])]);

}

return localStringBuilder.toString();

}

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.activity_main, menu);

return true;

}

}

我们要在onCreate()函数调用我们的方法也就是我们构造出来的计算signature[]的 MD5的方法

其中

localStringBuilder.append(f[((0xF0 & paramArrayOfByte[i1]) >>> 4)]);

localStringBuilder.append(f[(0xF & paramArrayOfByte[i1])]);

这两句代码是模拟360手机助手的格式化MD5的方法取前四位放到StringBuilder然后取后四位放到StringBuilder中。。

然后我们来用安卓模拟器进行模拟计算新浪微博的md5算法,获得新浪包名,adb shell下查看就行了:adb shell  , cd  data,cd   app, ls,

包名是:com.sina.weibo

运行我们写好的安卓程序来计算我们的签名:

我们来查看与360计算的结果是否相同:

0x05 后记

在后面的入侵测试中,由于本地签名保存,会导致360提示签名非法

从图中我们可以猜测360手机助手是在本地保存了原有的数字签名公钥,然后会对比公钥是否一致,而在实际的代码分析中360的确是有读取sigaature到一个数组中然后保存到了本地,虽然这次入侵结果是失败了,不过过程很迷人,结论就是:安卓公钥私钥的数字签名校验确保了安卓软件的安全性。

时间: 2024-11-02 10:14:46

360手机助手关于签名校验的分析的相关文章

柔弱的APP如何自我保护,浅谈APP防御手段,使用360加固助手加固/签名/多渠道打包/应用市场发布

柔弱的APP如何自我保护,浅谈APP防御手段,使用360加固助手加固/签名/多渠道打包/应用市场发布 由于JAVA和Android的平台型,所以APP很容易被反编译,这对于我们开发者来说,是一个不想要的结果,对于用户来说,就是一个噩耗,而安全性,一直是我们关注的焦点,今天,我们来聊聊这个安全性,和一起玩玩Apk加固! 一.我们为什么要提高APP的安全性 手机已经是不会离开身边了,APP更是重中之重的环节,我们衣食住行,基本上大部分都是靠APP来完成的,这样的话,APP的安全就是一个很大的挑战了,

360手机助手内部资料曝光,63张PPT纯干货

360手机助手内部资料曝光,63张PPT纯干货 日前,国内最大的安卓应用商店360手机助手发布了<2016年手机软件行业趋势绿皮书>,这份绿皮书对2015年以来移动互联网的趋势做了总结,展望了2016年的发展方向.从这份绿皮书所反映的现状来看,当前,国内移动互联网或来到了一个十字路口. 这份报告的每一页PPT都是干货,对于移动互联网从业者.政府决策层来说绝对不能错过. 1.2015年移动端用户增速放缓并渐趋饱和,市场竞争迈入零和时代 随着中国人口结构的变化,人口红利出现下降,影响至移动智能终端

[软件测试] 用过360手机助手,豌豆荚的,可以考虑永远这个...

引导语 : 好久没有吃水果了,想买点桔子解解馋."老板,桔子甜不甜?""甜!""我尝尝,可以吧?""谢绝品尝!""那我怎么知道甜不甜?""我吃给你看!你看我表情就可以了!" 今天小编给大家带来一款软件,是小编在无意间在360手机助手下载到的,大家可以在360,豌豆荚搜索一下"亿动手机助手"看看...     大家复制这条链接下载一下看看,跟我们一起来吐槽这个软件 ht

Android静默安装实现方案,仿360手机助手秒装和智能安装功能

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149 之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢?所谓的静默安装,就是不用弹出系统的安装界面,在不影响用户任何操作的情况下不知不觉地将程序装好.虽说这种方式看上去不打搅用户,但是却存在着一个问题,因为Android系统会在安装界面当中把程序所声明的权限展示给用户看,用户来评估一下这些权限然后决定是否要安装该程序,但如果使用了静默安装的方式,也就没

Slidingmenu 仿 网易 360手机助手 框架 直接拿来用

天气太热了,上班也没什么精神头.索性  整理下项目中用到的 框架 抽出来,方便下次直接用吧. 大家应该都知道 slidingmenu的.就是github 上的一个开源框架  可以实现 左右滑动, 时下 好多app都用到了,比如 网易新闻 ,360手机助手, 糗事百科,ireader. 应该算是很火的一个框架了. no exception  我们公司的项目也用到了. 废话有点多.下面直奔主题吧 首先介绍下 框架的结构 先上几张图,不会做gif ..    再看下包结构 主要实现侧滑的功能代码 在

android 浮动窗口学习笔记及个人理解(仿360手机助手)

非常感谢原文作者 http://blog.csdn.net/guolin_blog/article/details/8689140 经自己理解 程序运行界面如下图: 1.程序入口界面 2.小浮动窗口 3.大浮动窗口 由上图可看出,可以看出我们基本需要: 1.一个主Activity 2.小浮动窗口view界面 3.大浮动窗口view界面 对于浮动窗口的管理我们还需要 4.一个Service(在后台监控管理浮动窗口的状态) 5.窗口管理类(创建/消除浮动窗口) 代码: package com.ww.

实现360手机助手TabHost的波纹效果

现在新版360手机助手的界面都做得挺漂亮的,在切换底部导航时的波纹效果也很好看,刚好最近看了个开源项目才了解到原来Drawable做动画效果也怎么好用,所以就仿照360实现了下带波纹的TabHost.源代码地址:https://github.com/Rukey7/XFragmentTabHost 先来看一下实现后的效果: 说明一下实现要点: 1. 因为我们项目之前用的是FragmentTabHost,所以我直接继承FragmentTabHost来实现动画效果更方便: 2. 波纹动画的实现其实是自

360手机助手使用问题

作为一个测试人员,要懂得将错误重现 问题描述一: 目标:手机通过USB线连接电脑 问题: 手机明明已经连接了电脑: 却提示: 要么就一直是无线连接(大哥,我要真机测试玩安卓啊,你给我无线连接有P用啊) 先断开无线连接,然后点击连接: 我还以为是USB线或者电脑的USB插口坏掉了,幸好不是 解决办法: 在手机[设置]->[开发人员选项]->[连接USB后启用调试模式]->勾选,即可 问题解决 问题描述二: 目标:更新手机上软件 问题: 手机连接到电脑后,发现只提示部分软件更新,接着发现内存

360手机助手(二):侧拉栏drawerLayout+Tab+Viewpager+ListView+Pulltorefresh+imageloder

整个项目的框架在ContentPager,要熟悉这个类. 简介 完成侧拉栏的显示,侧拉首页对应的:首页+应用+游戏这3个 tab,包含的知识点有:请求数据 +pullTofresh + ImageLoader +轮播图 + listView 效果图: 侧拉栏的实现 V4包中的DrawerLayout实现侧拉效果,每个侧拉的item对应一个fragment,点击该item时,先把所有的fragment隐藏 + 文字设置未选中,然后才创建对应的fragment并显示 怎么实现侧拉栏的显示与隐藏? 点