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到一个数组中然后保存到了本地,虽然这次入侵结果是失败了,不过过程很迷人,结论就是:安卓公钥私钥的数字签名校验确保了安卓软件的安全性。
转载于:https://www.cnblogs.com/jiancanxuepiao/p/4103414.html
360手机助手关于签名校验的分析相关推荐
- 应用商店调研-360手机助手
360手机助手 背景 国内360手机助手广告主信息的抓取 分析 分类分析 在360中将应用分成了软件(工具类)和游戏(游戏类) 游戏中分为角色扮演101587.休闲益智19.动作冒险20.网络游戏10 ...
- 360 android 应用市场,360手机助手在安卓应用市场占大份额
日前,奇虎360发布了2014财年第三季度未经审计财报.据报告显示,奇虎360第三季度营收为3.764亿美元,比去年同期的1.879亿美元增长100.3%.奇虎董事长周鸿祎在电话会议中表示,我们还在继 ...
- Python爬取360手机助手评论——以百度地图为例
想做竞品分析,打算先从应用市场爬一些应用的用户用户评论作为素材:这次爬取的是360手机助手网站,结尾附爬取完的百度地图和高德地图的用户评论文件~ 网页链接:http://zhushou.360.cn/ ...
- 360手机助手显示手机型号错误
今天客户反馈,在使用360手机助手的时候,显示的手机型号有时候错误,比如你用的华为的手机,而显示的是THC的手机型号,需要我们修改,我先分析了一下,根本不可能啊,因为我认为显示的型号应该是读系统的属性 ...
- 360安卓_数据 | TalkingData:360手机助手份额32.27%居第一
2015年春节刚过,各大手机应用市场都没闲着,360手机助手在春节期间发出2亿现金红包,腾讯应用宝也开启扫码抢红包活动,百度手机助手则亮相江苏卫视.据独立的移动互联网数据服务商TalkingData的 ...
- 玻璃心?App Store下架PP助手与360手机助手,做aso会被苹果下架吗
柚鸥ASO 在别人的场子混,码头得拜,高香得烧,可即便如此,毕竟是人在屋檐下,纵然低了头,弯了腰,也免不了东家将你扫地出门,为哪般?业务有冲突嘛,在利益面前,哪怕租客背景强大,也照下架不误,谁叫你抢我 ...
- 360手机助手2016年度嗨APP全民榜入围名单
如果说评选2016年最火热的APP,那么一定不能不提直播类APP.近期在360手机助手"2016年度嗨APP全民榜"入围榜单中,映客.花椒.斗鱼等多款直播类都名列前茅.那么哪些年龄 ...
- 360手机助手又开启“全民分发”时代
360手机助手又开启"全民分发"时代 2015-04-23 10:44 生活杂记 标签:360 科技 883 发表评论 2015年的移动应用分发市场正在走向&q ...
- 应用发布——发布应用程序到360手机助手
360手机助手发布android app 1.首先登录360移动开放平台 http://dev.360.cn/ 注册开发者账号 a)首先注册360账号 用手机号,设置密码,验证码验证就OK了 b)注册 ...
- 360手机助手游戏怎么实名认证 360手机助手下载的游戏怎么关了悬浮窗
360手机助手除了我们日常的传输文件,分享资源之外,上面还是有海量的游戏资源供我们下载的,而且平台还提供360币可以进行充值,不过很多小伙伴在下载游戏之后不知道在哪实名认证,哪里可以改实名认证?下面一 ...
最新文章
- 内存分配的原理__进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)
- 【转】(原創) 如何使用ModelSim-Altera對Nios II仿真? (SOC) (Nios II) (SOPC Builder) (ModelSim) (DE2)...
- 如何利用1%推广费用提高50%的用户增长
- 【学生信息管理系统】——优化篇(一)
- 【牛客 - 280A】勘测(fib数列,思维,打表)
- 数组中的键值对去重_javascript利用对象键值对中键的唯一性实现数组去重
- VS2012 UPDATE 2 发布了离线包
- 《西点军校的经典法则》序 -- 責任(せきにん)、栄誉(えいよ)、国家(こっか)
- 1062. 洪水填充
- 【外挂编程】外挂编程技术揭秘(一)
- 胡昊—第6次作业—static关键字、对象
- 趋势预测方法(五)Holt-Winters模型_时序递推预测
- python分支机构_基于Python爬取天眼查网站的企业信息!Python无所不能!
- Word 注重页面细节才专业(添加页眉页脚,调整页边距,消灭孤行) | 职场人就应该这样用 Word
- 客户端+调用+matlab,基于web的android图像处理示范(Win7+Apache+PHP+Matlab+Android)
- 第一篇Mac上fluter开发环境配置
- python 解压缩文件中文名字乱码解决
- Cython配置安装(ubuntu)
- 猿辅导python辅导老师_猿辅导哪些老师比较好?
- 有趣好玩的带音乐24H全自动在线要饭系统源码
热门文章
- python语音识别终极指南_Python语音识别终极指南
- 将微信表情包保存为图片
- 爱加密和梆梆加固的破解方法
- 普通话水平测试用朗读作品60篇-(练习版)
- JDK动态代理和CGLIB动态代理介绍
- redis MySQL 脏读_redis多线程情况下避免读脏数据的悲观锁解决方案
- springmvc/ssm框架详细图文解说流程图及运行原理_附源码
- 华为服务器培训文档,华为机架服务器渠道培训.ppt
- 华硕bios更改固态硬盘启动_华硕主板BIOS设置固态硬盘启动顺序为第一启动硬盘教程...
- charles安卓抓包步骤详解