前言

有一段时间没看安卓了,找几个软件练练手。

这是一个考驾照用的 app.

官方网址: http://www.******baodian.com/

本文就分析一下在 重置密码时对 数据包 进行签名来防篡改的方案。

正文

burp抓取htttps数据

首先导入 burp 证书到手机, 装上 xposed , 然后安装

https://github.com/WooyunDota/DroidSSLUnpinning

这个插件应该就可以抓到 https 数据了。

然后触发 找回密码 逻辑,抓包

这里有一个 sign 的参数,可以推测这个是用来 保存签名的参数。

如果正常发送的数据包得到的响应类似于下面(这里是验证码输错的情况)

如果重放数据包

经过测试发现 _r 参数用于标识数据包的唯一性,如果修改其他的字段会提示 重复的 URL, 但是如果修改了 _r, 则会提示 URL签名错误

于是大概可以推测服务端校验请求的流程

  • 拿到  _r 判断是否是重复的请求
  • 计算请求的 签名,与签名字段进行校验
  • 如果前面两步均通过,服务端则认为数据包没有被篡改,于是开始校验 验证码。

定位关键代码

我列举一下我用了的方法

搜参数名

程序中可能会用到参数的名称来设置参数的值,因为我的目标是分析签名算法,sign 这个参数名这么的明显,就搜他了。

发现太多的文件里面有这个关键字,不可用

搜 url 路径

观察抓到的数据包,发现它是向 /api/open/v2/forgot-password/check.htm 发起了 POST 请求。

这个貌似靠谱,可以追踪到进行请求的代码,于是我从第一个结果开始分析,一路往下,追踪,大概搞清楚了发送 请求的流程,以及一些参数的得到方式,但是貌似是看花眼了,而且程序的混淆强度比较大,没用找到 _rsign 设置位置。 于是开始了其他的定位尝试

监控 系统层的 加密类和方法

进行签名常用的方案就是 对待签名数据,算 hash 或者 用一些加密算法。于是想着是不是可以通过监控  java 层常用的 加密 api 来定位 算法。

https://github.com/Chenyuxin/CryptoFucker

找到了这个插件, 通过 hook 的方式来监控, 同时会在 /sdcard/ydsec/包名.txt 留下日志。

试了试,发现也没有找到相关的数据,通过这样的方式被加密,于是推测应该是自己实现了相关的加密算法。

再次搜参数名

忽然想起,参数名如果在程序中被使用的话应该是直接存在于程序中的, 然后 smali 代码引用字符串 会加上 " 来包裹字符串

开始了另一种搜索尝试

发现搜 "sign"  和 "sign  得到的结果不多,但是貌似都不太相关,倒是 "_r" ,就两个结果。

想起 既然 _r 用于标识请求的唯一性,那么签名肯定是要用到它的,而且 _r 应该没次都是随机生成的,那么用到它的位置,应该离 生成 sign 的位置不远了。

进入第一个结果的代码(程序太大了,jeb打不开之前的我分析时的数据,只能重新看没有命名函数名的了)

可以知道 _r 通过

UUID.randomUUID().toString().replace("-", "")

生成随机字符串,来保证数据包不被重放

生成 _r 后 ,会调用 a.N ,其中会调用 aa.ao ,跟进去看看

根据 URL(url) 可以确定 ,第一个参数是一个 url, 接着对 url 上的参数进行了处理。

然后定位 sign 参数的起始位置

接着删除了 sign 参数

然后重新生成 sign 参数

调用了 Riddle.s 生成了 sign 参数,传入的参数是 处理后的 url_path 和 签名用的 key

Riddle.s 是一个 native 方法, 在 libtnpn.so 里面,去 lib\armeabi 目录下找就是了。

拖进 ida 发现 so 倒是挺友好的 ,没有混淆。

进入 Java_cn_mucang_android_core_jni_Riddle_s, 这就是  Riddle.s 方法在 so 中的命名,jni 函数的第一个参数类型为 JNIEnv_ * , 首先 需要导入 jni.h , 然后设置一下类型便于分析。

首先就是把传入的 参数变成 c 语言中的字符串类型,然后根据 key 的格式判断, 使用哪种签名方案,经过调试重置密码用的 key*#06#i3lrRYudcZZ2fIx9fI6VqJV8

所以会调用

ver = j_j_SignUrl1(path, sign_key_1 + 5, &v13);

跟进到 SignUrl1

首先对 key 进行解密,然后进入 j_j_SignUrl0

流程很清晰,就是 拼接 path 和 解密后的 key, 然后计算 md5, 存在 a3 地址,回到 SignUrl1

接着又对传入的 没有被解密的key 来了一个求和,然后进入

calc_sum(key_sum, 19);

这个函数有点复杂,我是直接抠了出来,用 python 重写了。先继续看后面。

v11 的值 附加到 之前刚刚计算好的 md5 的数值后面,v11 来自于 v14, 而 v14 并没有设置的地方,这里是 IDA F5 识别错了。直接看汇编把。

实际上 v14 就是调用 calc_sum 后的 r1 寄存器。

跟进到 sub_788640D4

首先对传入的参数和 lr 进行了保存,然后调用 sub_78864000 , 然后用之前保存的参数值 与 sub_78864000 的返回值进行运算,结果保存到 r1(专门用来迷惑 ida _).

伪代码:

  a1 = sub_78864000(a1,a2)
t = save_a2 * a1
ret = save_a1 - t
return ret

sub_78864000 看起来比较复杂,我就直接用 python, 照着重写了一遍。

其中有一个有意思的地方: ida 无法 对 clz 指令进行转换,所以用

v5 = __clz(a2) - __clz(v4);

表示

CLZ     R2, R1
CLZ R0, R3
SUB R0, R2, R0

这使得重写造成了困扰,我的解决办法是,根据 clz 的作用,自己实现。

实现的 python 代码如下:

def calc_clz(reg):
return 32 - len(str(bin(reg))[2:])

最后的脚本:

from urllib.parse import urlparse
from pprint import pprint
import base64
import hashlib # 示例数据包
# POST /api/open/v2/forgot-password/check.htm?_platform=android&_srv=t&_appName=jiakaobaodian&_product=%E9%A9%BE%E8%80%83%E5%AE%9D%E5%85%B8&_vendor=xiaomi&_renyuan=XYX&_version=6.9.8&_system=LMY48Z&_manufacturer=samsung&_systemVersion=5.1.1&_device=SM-G9350&_imei=862514326681779&_productCategory=jiakaobaodian&_operator=T&_androidId=0086049272635872&_mac=02%3A00%3A00%3A00%3A00%3A00&_appUser=ffd830178df6460e8b401c1421b1e148&_pkgName=com.handsgo.jiakao.android&_screenDpi=1.2&_screenWidth=720&_screenHeight=1280&_network=wifi&_launch=4&_firstTime=2018-03-09%2009%3A28%3A53&_apiLevel=22&_userCity=320100&_p=464D555059511A4751565E5F475A&_ipCity=320100&_j=1.0&schoolName=%E6%9C%AA%E6%8A%A5%E8%80%83%E9%A9%BE%E6%A0%A1&schoolCode=-1&_webviewVersion=4.7&_mcProtocol=4.0&_r=78ae8eb0a00b4d6d9451eecd3ee169a6&sign=8b5c95d8e839cce7588db0c3ee315c0501 HTTP/1.1
# User-Agent: Mozilla/5.0 (Linux Android 5.1.1 SM-G9350 Build/LMY48Z) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36
# Accept-Encoding: gzip, deflate
# Accept-Encoding: gzip, deflate
# Content-Type: application/x-www-form-urlencoded
# Content-Length: 83
# Host: auth.mucang.cn
# Connection: close
#
# phoneNumber=13333333333&captchaId=e5410186e8afa433e4a4e8019901ff40&captchaCode=5659 def calc_sum(a1, a2):
save_a1 = a1
save_a2 = a2
v3 = a1 ^ a2
if a2 < 0:
a2 = -a2
if a2 == 1:
if (v3 ^ a1) < 0:
a1 = -a1
else:
v4 = a1
if a1 < 0:
v4 = -a1
if v4 <= a2:
if v4 < a2:
a1 = 0
if v4 == a2:
a1 = (v3 >> 31) | 1
elif (a2 & (a2 - 1)):
v5 = calc_clz(a2) - calc_clz(v4)
v6 = a2 << v5
v7 = 1 << v5
a1 = 0
while True:
if v4 >= v6:
v4 -= v6
a1 |= v7
if v4 >= v6 >> 1:
v4 -= v6 >> 1
a1 |= v7 >> 1
if v4 >= v6 >> 2:
v4 -= v6 >> 2
a1 |= v7 >> 2
if v4 >= v6 >> 3:
v4 -= v6 >> 3
a1 |= v7 >> 3
v8 = v4 == 0
if v4:
v7 >>= 4
v8 = v7 == 0
if v8:
break
v6 >>= 4
if v3 < 0:
a1 = -a1
else:
a1 = v4 >> (31 - calc_clz(a2))
if v3 < 0:
a1 = -a1
t = save_a2 * a1
ret = save_a1 - t
return ret def decode_key(key):
key = base64.b64decode(key)
de_key = ''
for c in key:
de_key += chr((c - 42) ^ 0x2a)
return de_key def get_md5(src):
m = hashlib.md5()
m.update(src.encode('UTF-8'))
return m.hexdigest() def calc_clz(reg):
return 32 - len(str(bin(reg))[2:]) def sign_url(url, key):
o = urlparse(url)
key = key[5:]
# target = o.path + "?" + o.query query_list = o.query.split("&")
for q in query_list:
if "sign=" in q:
query_list.remove(q)
target = o.path + "?" + "&".join(query_list)
# target = "/api/open/v2/forgot-password/check.htm?_platform=android&_srv=t&_appName=jiakaobaodian&_product=%E9%A9%BE%E8%80%83%E5%AE%9D%E5%85%B8&_vendor=xiaomi&_renyuan=XYX&_version=6.9.8&_system=KRT16S&_manufacturer=LGE&_systemVersion=4.4&_device=AOSP%20on%20Mako&_imei=355136058307672&_productCategory=jiakaobaodian&_operator=&_androidId=c8af131c36bb666c&_mac=40%3Ab0%3Afa%3Ac1%3Aec%3A24&_appUser=99cfb9442b804b819ddfbc5a3f71d5fb&_pkgName=com.handsgo.jiakao.android&_screenDpi=2.0&_screenWidth=768&_screenHeight=1184&_network=unknown&_launch=3&_firstTime=2018-03-09%2012%3A23%3A26&_apiLevel=19&_userCity=440300&_p=&_j=1.0&schoolName=%E6%9C%AA%E6%8A%A5%E8%80%83%E9%A9%BE%E6%A0%A1&schoolCode=-1&_webviewVersion=4.7&_mcProtocol=4.0&_r=76aba50fd2fa46c292bbd6b6885cc886"
decoded_key = decode_key(key)
md5 = get_md5(target + decoded_key)
key_sum = 0
for k in key:
key_sum += ord(k) sum = hex(calc_sum(key_sum, 0x13))[2:]
if len(sum) % 2:
sum = "0" + sum
sign = md5 + sum
print(sum)
print(sign) if __name__ == '__main__':
sign_key = "*#06#i3lrRYudcZZ2fIx9fI6VqJV8"
url = "https://auth.mucang.cn/api/open/v2/forgot-password/check.htm?_platform=android&_srv=t&_appName=jiakaobaodian&_product=%E9%A9%BE%E8%80%83%E5%AE%9D%E5%85%B8&_vendor=xiaomi&_renyuan=XYX&_version=6.9.8&_system=LMY48Z&_manufacturer=samsung&_systemVersion=5.1.1&_device=SM-G9350&_imei=862514326681779&_productCategory=jiakaobaodian&_operator=T&_androidId=0086049272635872&_mac=02%3A00%3A00%3A00%3A00%3A00&_appUser=ffd830178df6460e8b401c1421b1e148&_pkgName=com.handsgo.jiakao.android&_screenDpi=1.2&_screenWidth=720&_screenHeight=1280&_network=wifi&_launch=4&_firstTime=2018-03-09%2009%3A28%3A53&_apiLevel=22&_userCity=320100&_p=464D555059511A4751565E5F475A&_ipCity=320100&_j=1.0&schoolName=%E6%9C%AA%E6%8A%A5%E8%80%83%E9%A9%BE%E6%A0%A1&schoolCode=-1&_webviewVersion=4.7&_mcProtocol=4.0&_r=66ae8eb0a00b4d6d9451eecd3ee169a1&sign=13413413413" sign_url(url, sign_key)
# print(calc_sum(0x82b, 0x13))

xxxx签名算法逆向&&python脚本实现的更多相关文章

  1. 使用NuGet打包并发布至ProGet过程 (步骤详细,附python脚本)【上篇】

    一.基本知识 (1)NuGet : NuGet是一个为大家所熟知的Visual Studio扩展,通过这个扩展,开发人员可以非常方便地在Visual Studio中安装或更新项目中所需要的第三方组件, ...

  2. 将Python脚本封装成exe可执行文件 转

    将Python脚本封装成exe可执行文件 http://www.cnblogs.com/renzo/archive/2012/01/01/2309260.html  cx_freeze是用来将 Pyt ...

  3. 使用Python脚本强化LLDB调试器

    LLDB是Xcode自带的调试器,作为一个iOS应用开发程序员,平时我在开发应用时会使用LLDB来调试代码.在逆向应用时,也会用到LLDB来跟踪应用的执行过程. LLDB还内置了一个Python解析器 ...

  4. 【NuGet】使用NuGet打包并发布至ProGet过程 (步骤详细,附python脚本)【上篇】

    一.基本知识 (1)NuGet : NuGet是一个为大家所熟知的Visual Studio扩展,通过这个扩展,开发人员可以非常方便地在Visual Studio中安装或更新项目中所需要的第三方组件, ...

  5. Android上执行python脚本-QPython

    看书,发现android可以跑python. 尝试了一下. 首先需要在手机上安装python环境,通过安装apk实现,这个apk叫QPython,还有同类的比如SL4A. QPython的官网:htt ...

  6. 阿里云zabbix的python脚本

    由于阿里云只能用465端口.所以这个zabbix的脚本修改成了465端口的python脚本. 修改于https://www.jianshu.com/p/9d6941dabb47 #!/usr/bin/ ...

  7. Python脚本打包成exe执行文件

    需求 一个教辅目录结构检查工具,目录结构是[书籍]-[章节]-[题目|答案]-[*.jpg],后台有个异步处理的服务,需要强依赖这个目录结构. 书籍解析是单独的pipeline,日志对用户不可见,这里 ...

  8. freeswitch嵌入python脚本

    操作系统:debian8.5_x64 freeswitch 版本 : 1.6.8 python版本:2.7.9 开启python模块 安装python lib库 apt-get install pyt ...

  9. python脚本后台运行

    问题描述: 环境: CentOS6.4 一个用python写的监控脚本test1.py,用while True方式一直运行,在ssh远程(使用putty终端)时通过以下命令启动脚本: python t ...

随机推荐

  1. 【bzoj3684】 大朋友和多叉树 生成函数+多项式快速幂+拉格朗日反演

    这题一看就觉得是生成函数的题... 我们不妨去推下此题的生成函数,设生成函数为$F(x)$,则$[x^s]F(x)$即为答案. 根据题意,我们得到 $F(x)=x+\sum_{i∈D} F^i(x)$ ...

  2. 【hdu6058】 Kanade's sum 模拟

    题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6058 题目大意:给你一个$1$到$n$的排列,请求出该序列所有区间中第$k$大之和,若该区间内少于$ ...

  3. POJ 2377

    #include<stdio.h> #define MAXN 1005 #include<iostream> #include<algorithm> #define ...

  4. jade直接写类似JavaScript语法的东西,不需要写script

    我们知道,html做计算都是在JavaScript中完成的,那么不用JavaScript行不行呢,可以直接在jade中一样的编写 如: -var a = 3 -var b = 4 div a+b = ...

  5. VSTO学习(五)——创建Word解决方案

    一.引言 在上一个专题中主要为大家介绍如何自定义我们的Excel 界面的,然而在这个专题中,我将为大家介绍如何用VSTO来创建Word项目,对于Word的VSTO开发和Excel的开发很类似,你同样也 ...

  6. python聚类算法实战详细笔记 (python3.6+(win10、Linux))

    python聚类算法实战详细笔记 (python3.6+(win10.Linux)) 一.基本概念:     1.计算TF-DIF TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库 ...

  7. 如何修复“sshd error: could not load host key”

    问题:当我尝试SSH到一台远程服务器时,SSH客户端登陆失败并提示“Connection closed by X.X.X.X”.在SSH服务器那端,我看到这样的错误消息:“sshd error: co ...

  8. Django中涉及金融的项目

    在Django中,如果一个项目涉及了金融,他的要求是十分严格的. 所以嘞,这里就有一些坑,很多坑,第一次开发的时候很容易出现一系列的错误 在涉及金融计算的地方,不能使用float类型 什么鬼,但事实就 ...

  9. Android 开发服务类 04_ServletForPOSTMethod

    ServletForPOSTMethod 业务类 package com.wangjialin.internet.servlet; import java.io.IOException; import ...

  10. mongodb二进制安装与yum安装

    一.什么是mongodb MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当 ...