wifi定位算法 java_记一次APP的so层算法逆向(七)
“ 前言:初学逆向 请多多指教 好累 感觉每天这样肝 人有点受不了了...”
学习到的内容
—
1、新学习到IDA的一些分析时候的小技巧
2、算法还原代码实现的练习(有个参数没有分析出来,后面知道了会补上的)
3、在Frida中使用命令行调试的方便方法
分析过程
—
APP登陆界面:
请求包:
POST /api/adult/check_guest HTTP/1.1Content-Type: application/x-www-form-urlencodedUser-Agent: Dalvik/2.1.0 (Linux; U; Android 8.1.0; Pixel Build/OPM1.171019.011)Host: service.kuyangsh.cnConnection: Keep-AliveAccept-Encoding: gzipContent-Length: 1108a=6ih0KN8TFL5%2FQT%2FN3JY63ZovsWQyeIxHjBLHp1GGwjUNYVaGJLC%2FYZenRFKbeqsgIoI4rD1atROr%0Ajkl1p7eobNMARMel19oiGkl5hRD72vOn9zyNbERMe8Cj3b24Ru4wc2mWbbnamKVKPepkaa2mqpJl%0Akp4%2Fa5udSz0UbDR6cTwLCRWeKb60H%2Fir4vzZv1OfwQF%2FXJQsuBmxH2F6wp9CJkk9WYchx4LQU%2FS3%0AjQfpQY2iZWwsmHAiyZGVsfZIgXTvhJygpT8vH268Py5JspYZoho0RRrx4BjUfs5boif6rEMpd5PC%0AiZTLhIPovbPpoZQJC7d%2BWPFtwjPT7Ljyzgz5QxtctnZqa4qMMkfFwAIeA7lj2wJZeZBD%2F%2BoU5R45%0AMEFK6OMrMXB1M%2BC4dBt7Rd384SYhe%2BAEp0gKNHGkrpxWImFcPAaalajyijs6V4Wl4res9CWuEE5W%0Ayj5ehtmPc6uZGuo5ns6THfDwnho8BiFOnH0QoqJEeyVdTsCzOiMwfBJJldB7qbsTfUlLbSnpq4tf%0AurPbVMVKQbk4ui1XgDH5v%2FptUHirNK0IHT%2BBms8wQ%2BSX3BcMLKFiWI0OBAjUydqcJpIi0sPSWpfh%0A2k1nmMlvPnljfc7P12iF8nFHoFWRYQPVie46K%2Bhd4%2FttkyrZ4Gy3WM6zWdmnED3h6CCgZ4rEe0DB%0AN5Dj8lJJbsOAE%2FxWcYDguyj8WkUET3yLB%2FBZQx%2BsOn9otWzjwROdhfi6V7OObXZ5XoGUIDffKaFu%0ADnvTinsAh%2FvjcSDVq%2BHyD%2FzUeRceMf6uQruBhHRzikSb2Oz0Zfxld7rqmWYZ8aIBe1DMRJuXecB%2F%0AAsBu4VVuVVfQf4hlCpVNmKnX6huuMkHtCptCLaD0pkmuY7X7OEfCsudtFIco%2F7gXaQ5aXgfCs7GJ%0AzAMxfpCRm4vnF0kc8nQ4OWlexOV5t65k%2B4eDt8wY91%2BIFHcq%2BIwZPR3e41oeKriHlbsPdocNOkeg%0AeyUw%2FXlAY97IpZA%3D%0A
返回包:
{ "ret": 0, "msg": "没有错误", "data": { "needs_check": "0", "needs_bind": "0", "needs_guest_bind": 0, "user_is_guest": -1 }}
这里定位很简单,直接全局搜索关键词:"a" 就可以了,因为可以看到POST数据包中只有一个字段就是a,但是这里需要带上双引号,来到如下
可以看到最关键的加密代码就是:String a = m3385a((Map) hashMap, initFromXML.getAppKey());
initFromXML.getAppKey则正常返回一个定值
跟进m3385a函数可以发现,定义如下:
public static String m3385a(Map<String, String> map, String str) { if (C1074a.f2394c || TivicloudController.f2343a) { map.put("testing", "1"); } try { JSONObject jSONObject = new JSONObject(map); Debug.m3346d("params : " + jSONObject.toString()); String encode = URLEncoder.encode(jSONObject.toString(), "UTF-8"); String encryptString = EncryptUtil.encryptString(encode + str); JSONObject jSONObject2 = new JSONObject(); try { jSONObject2.put("sign", encryptString); jSONObject2.put(C0882di.C0883a.DATA, encode); } catch (JSONException e) { Debug.m3354w((Exception) e); } return new String(Base64.encode(EncryptUtil.nativeAES(jSONObject2.toString()), 0), "UTF-8"); } catch (UnsupportedEncodingException e2) { Debug.m3354w((Exception) e2); return null; } }
注意:这里发现JSONObject是处于org.json.JSONObject,系统自带的库中的类,这里也能作为一种hook的方法定位
这里可以直接对m3385a这个函数进行观察,流程就是将传入的数据先进行一个url编码,然后拼接initFromXML.getAppKey作为参数,调用EncryptUtil.encryptString
继续跟到encryptString中去,代码如下,那么也就需要进libgavesec.so中的nativeEncrypt函数进行分析
这里遇到了一个问题,比如下面的两个函数和注入代码,一个函数会调用native中的函数,但是如果我hook encryptString这个函数,返回值写的是调用native层的nativeEncrypt函数,这样的写法就会导致程序结束,具体原因不知道
public static String encryptString(String str) { return nativeEncrypt(str); } private static native String nativeEncrypt(String str); Java.perform(function () { var EncryptUtil = Java.use("com.tivicloud.utils.EncryptUtil"); EncryptUtil.encryptString.implementation = function (a) { send("EncryptUtil.encryptString args[0]: " + a); var result = this.nativeEncrypt(a); return result; } });
因为上面那样写就导致程序结束,所以这里就直接hook nativeEncrypt,惊奇的发现这样子就不会导致程序结束了
setImmediate(function(){ Java.perform(function () { var EncryptUtil = Java.use("com.tivicloud.utils.EncryptUtil"); EncryptUtil.nativeEncrypt.implementation = function (a) { send("EncryptUtil.nativeEncrypt args[0]: " + a); var result = this.nativeEncrypt(a); return result; } });});
这里可以对EncryptUtil.encryptString(encode + str),java层进行一次hook来获取对应的参数先
[*] EncryptUtil.encryptString args[0]: {"mobile_operator":"","app_versioncode":"4","app_version":"2.1.0.22277","connection_type":"WIFI","os_version":"8.1.0","version_code":"4","version_name":"2.1.0.22277","os_lang":"zh","sdk_version":"3.1.4","package_name":"com.lm.lm","imei":"359906070277673","os_name":"Android","lang":"zh","udid":"10e1ef4509a2340eb0276bb99d186506","nudid":"155c2b70-fc97-4005-abc7-6d6c4329ed23","app_id":"10054","channel_id":"10006","tdid":"39fb282a761c17e80c069332fc6a2ffa9"}fff18b83431fa3a83b9de80c1e413bde
那么的initFromXML.getAppKey值就为fff18b83431fa3a83b9de80c1e413bde
然后接着继续跟native层进行分析,将libgavesec.so拖入到IDA中,并且找到对应的函数
1、在通过导出library库之后,有些jni的函数无法识别参数,你可以直接右键该函数选择Force call type来进行重新分析,一般都可以成功识别参数
2、有时候ida中的强制转换类型太多,可以右键选择Show casts来隐藏强制转换,然后进行分析
主要进行了encrypt函数的调用
接着就是来到加密函数encrypt中进行分析
先是进行一次sha1加密
所以这里要hook两个地方
encrypt:Base + 0x1C8C ,获取第一个参数
SHA1::Result:Base + 0x2AA4,获取该函数调用完之后的第二个参数的结果
这里分享的frida的调试方法,通过命令行注入js脚本进入到frida的命令行中进行操作
frida -RF -l hooktest.js
1、通过主动调用获取对应的类
2、调用类对应的静态/非静态方法调试数据的时候会很方便,代码如下:
function data_test() { Java.perform(function () { var EncryptUtil = Java.use("com.tivicloud.utils.EncryptUtil"); var res = EncryptUtil.nativeEncrypt('%7B%22mobile_operator%22%3A%22%22%2C%22app_versioncode%22%3A%224%22%2C%22app_version%22%3A%222.1.0.22277%22%2C%22connection_type%22%3A%22WIFI%22%2C%22os_version%22%3A%228.1.0%22%2C%22source%22%3A%22SDK%22%2C%22user_id%22%3A%2216073535%22%2C%22os_lang%22%3A%22zh%22%2C%22sdk_version%22%3A%223.1.4%22%2C%22imei%22%3A%22359906070277673%22%2C%22os_name%22%3A%22Android%22%2C%22login_token%22%3A%2285700bcdf4a41164ab7406e8445479ed%22%2C%22lang%22%3A%22zh%22%2C%22udid%22%3A%2210e1ef4509a2340eb0276bb99d186506%22%2C%22nudid%22%3A%22155c2b70-fc97-4005-abc7-6d6c4329ed23%22%2C%22app_id%22%3A%2210054%22%2C%22channel_id%22%3A%2210006%22%2C%22tdid%22%3A%2239fb282a761c17e80c069332fc6a2ffa9%22%7Dfff18b83431fa3a83b9de80c1e413bde'); console.log(res); });}
接着上面的分析,打印了sha1加密过后的数据v12变量和最终的Sign值(这里也就是nativeEncrypt的返回值),你会发现结果是不一样的
接着你会看到下面的循环的操作,所以sha1加密的结果应该被二次修改了,这里有两个大循环的操作
首先第一段循环:
SHA1::Result((SHA1 *)&v13, (unsigned int *)v12);// sha1加密的结果v4 = 0LL; // long long v5 = 7;do // 进行循环操作 { v6 = *(_DWORD *)&v12[v4]; // 0X0C v7 = &dest_cstr_1[v5]; while ( 1 ) { v8 = v6 & 0xF; // 0X0C v6 >>= 4; // 0X00 *v7-- = hexDigits[v8]; // if ( !(v5 & 7) ) break; --v5; } *(_DWORD *)&v12[v4] = v6; // 赋值操作 v4 += 4LL; v5 += 15; } while ( v4 != 20 );
你会发现hexDigits是data段的一段数据:
最后分析,其实就是将其原封不动的转换为16进制的字符,然后一起拼接起来,分析注释如下:
然后就是第二段循环了,循环次数也可以看出来会对每个16进制字符进行处理,循环次数为40次
hook了char2hexInt处理过后的数据会发现,char2hexInt这个函数才是二次处理的函数
do { v10 = char2hexInt(dest_cstr_1[v9]); dest_cstr_1[v9] = hexDigits[(signed int)((unsigned __int64)char2hexInt(a211034f8af4e6b[v9]) ^ v10)]; //异或的值为v10 ++v9; }
还需要char2hexInt的参数来观察,该地址为0x1B34,HOOK结果如下
其实就是两个返回值进行异或处理,比如0x2^0x2就为0,最后还会通过hexDigits数组转成对应的16进制在内存中保存为0x30,因为字符 '0' 对应的ASCII 十六进制就是 0x30 是相等的!
最终的sign值分析的注释:
sign这里也分析完了,还有个整体加密的分析,发现整体会进行一次Base64.encode,但是nativeAES可以跟进去观察
发现该函数依旧是native层的函数,所以这里继续去看分析
来到如下进行分析,看到名字就知道是AES算法加密了,这里的话用findcrypt插件通过特征码也可以进行识别
然后接着就是先对传入的数据进行字符串复制
然后就是AES对象和密钥的初始化,这里的AES需要hook,来确认v19的值,还有该函数最终的返回值dest_array_bytes的地址来进行打印
AES::AES:0x3274,Cipher:0x397C,然后hook到的数据如下,一个是AES密钥,最终要进行AES字符串加密的字符串
最终hook的代码:
function hook_test() { Java.perform(function () { var EncryptUtil = Java.use("com.tivicloud.utils.EncryptUtil"); EncryptUtil.nativeEncrypt.implementation = function (a) { console.log("=============================") // console.log("EncryptUtil.nativeEncrypt args[0] 被加密的字符串: ", a); var result = this.nativeEncrypt(a); console.log("Sign的结果:", result); return result; } var b64 = Java.use("android.util.Base64"); var str = Java.use("java.lang.String"); EncryptUtil.nativeAES.implementation = function (a) { console.log("=============================") // console.log("EncryptUtil.nativeEncrypt args[0] 被加密的字符串: ", a); var result = this.nativeAES(a); console.log("base64数据", str.$new(b64.encode(result, 0))); return result; } var libgavesec = Module.findBaseAddress("libgavesec.so"); // encrypt Interceptor.attach(libgavesec.add(0x1C8C), { onEnter: function (args) { console.log("encrypt args[0] 被加密的字符串: ", Memory.readCString(args[0])); this.args1 = args[1]; }, onLeave: function (retVal) { console.log("encrypt args[1] 处理过后的数据:", hexdump(this.args1, { offset: 0, length: 32, header: true, ansi: false })); } }) // SHA1::Result Interceptor.attach(libgavesec.add(0x2AA4), { onEnter: function (args) { // console.log("SHA1::Result args[1] 加密前的数据", hexdump(args[1], { // offset: 0, // length: 64, // header: true, // ansi: false // })); this.args1 = args[1]; }, onLeave: function (retVal) { console.log("SHA1::Result args[1] 加密后的数据", hexdump(this.args1, { offset: 0, length: 32, header: true, ansi: false })); } }) // char2hexInt Interceptor.attach(libgavesec.add(0x1B34), { onEnter: function (args) { // console.log("char2hexInt 参数:", args[0]); this.args1 = args[1]; }, onLeave: function (retVal) { // console.log("char2hexInt 返回值:", retVal); } }) // AES::AES Interceptor.attach(libgavesec.add(0x3274), { onEnter: function (args) { console.log("AES密钥:", hexdump(args[1], { offset: 0, length: 32, header: true, ansi: false })); }, onLeave: function (retVal) { } }) // AES::Cipher Interceptor.attach(libgavesec.add(0x397C), { onEnter: function (args) { console.log("AES::Cipher args[1]:", Memory.readCString(args[1])); this.args1 = args[1]; }, onLeave: function (retVal) { } }); });};setImmediate(function () { Java.perform(function () { hook_test(); });});
简单的加密代码的实现:
from Crypto.Cipher import AESfrom urllib import parsefrom binascii import b2a_hex, a2b_hex, b2a_base64import hashlib"""aes加密算法ECB模式"""class Aes128_(object): def __init__(self): self.key = b"14ca829f017c0357" self.mode = AES.MODE_ECB def add_to_16(self, text): if len(text.encode('utf-8')) % 16: add = 16 - len(text.encode('utf-8')) % 16 else: add = 0 text = text + ("\0"*add) # 明文 + \00填充 return text.encode('utf-8') def encrypt(self, text): text = self.add_to_16(text) cryptos = AES.new(self.key, self.mode) cipher_text = cryptos.encrypt(text) # return b2a_hex(cipher_text) return b2a_base64(cipher_text) def decrypto(self, text): cryptor = AES.new(self.key, self.mode) plain_text = cryptor.decrypt(a2b_hex(text)) return bytes.decode(plain_text).rstrip('\0')def getSign(data): sign = '' a211034f8af4e6b = '211034f8af4e6b9546c19ae13ed099553319b6c3' for i in range(40): print(hex(int(data[i], 16) ^ int(a211034f8af4e6b[i], 16))) sign += str(hex(int(data[i], 16) ^ int(a211034f8af4e6b[i], 16)))[2:3] return signif __name__ == "__main__": data = bytes(parse.quote('{"mobile_operator":"","app_versioncode":"4","app_version":"2.1.0.22277","connection_type":"WIFI","os_version":"8.1.0","source":"SDK","user_id":"16073535","os_lang":"zh","sdk_version":"3.1.4","imei":"359906070277673","os_name":"Android","login_token":"9f6037c931db9a9cfcbb991966fad614","lang":"zh","udid":"10e1ef4509a2340eb0276bb99d186506","nudid":"155c2b70-fc97-4005-abc7-6d6c4329ed23","app_id":"10054","channel_id":"10006","tdid":"39fb282a761c17e80c069332fc6a2ffa9"}fff18b83431fa3a83b9de80c1e413bde'), encoding="utf-8") sha1 = hashlib.sha1(data) sha1_data = sha1.hexdigest() sign = getSign(sha1_data) aes_data = str(Aes128_().encrypt('{"sign":"'+sign+'","data": "' + parse.quote('{"mobile_operator":"","app_versioncode":"4","app_version":"2.1.0.22277","connection_type":"WIFI","os_version":"8.1.0","source":"SDK","user_id":"16073535","os_lang":"zh","sdk_version":"3.1.4","imei":"359906070277673","os_name":"Android","login_token":"9f6037c931db9a9cfcbb991966fad614","lang":"zh","udid":"10e1ef4509a2340eb0276bb99d186506","nudid":"155c2b70-fc97-4005-abc7-6d6c4329ed23","app_id":"10054","channel_id":"10006","tdid":"39fb282a761c17e80c069332fc6a2ffa9"}"}')), encoding='utf8').replace("\n", "") print(aes_data)
这个其实不是最终的代码,因为login_token没有分析出来,感觉有点难,我继续试试,可以的话再写一篇!
wifi定位算法 java_记一次APP的so层算法逆向(七)相关推荐
- 红米k30 允许调用gpu调试层_记一次APP的so层算法逆向(六)
" 前言:初学逆向 请多多指教" 学习到的内容 - 1.在java层,对容器类的对象进行hook来进行快速定位 2.ida的findcrypt插件对so层的算法快速识别 3.文章分 ...
- 逆向so_记一次APP的so层算法逆向(七)
" 前言:初学逆向 请多多指教 好累 感觉每天这样肝 人有点受不了了..." 学习到的内容 - 1.新学习到IDA的一些分析时候的小技巧 2.算法还原代码实现的练习(有个参数没有分 ...
- 使用frida破解native层算法
使用frida破解native层算法(原文) 使用frida破解native层算法 案例:就不放了 抓包分析 这里抓包分析的过程不详细说了 可以看到主要有两个参数 sign和signV1 反编译 1. ...
- wifi定位算法 java_几种室内定位技术方案对比,室内定位种类的优缺点一目了然...
最近几年,随着物联网技术的发展,定位技术也随之发展起来,室内定位技能非常实用,运用广泛,如图书馆,养老院,体育馆,地下车库,学校,仓库等都能够完成对人员及物品的快速定位.目前,在苏州新导室内定位体系中 ...
- android wifi定位不了,h5网页使用高德地图定位正常,网页嵌入安卓app后wifi定位正常,4g网络无法定位?...
问题描述 h5网页使用高德地图定位正常,网页嵌入安卓app后wifi定位正常,4g网络无法定位 问题出现的环境背景及自己尝试过哪些方法 h5网页手机浏览器打开没有问题,嵌入app后打包,安卓9.0系统 ...
- android 网络wifi定位服务器,基于位置指纹算法的Android平台WiFi定位系统
近年来,随着城域无线基础网络的发展,热点(AP)的覆盖范围已大大增加. 由于对定位服务的需求增加以及WiFi应用的扩展,WiFi定位已成为一种有效的定位方法. GPS卫星定位是最重要的定位方法. 它需 ...
- ios wifi 定位_一种IOS设备的集中式Wifi室内定位方法
一种IOS设备的集中式Wifi室内定位方法 [技术领域] [0001] 本发明属于无线通信室内定位技术领域,具体涉及一种IOS设备的集中式Wifi室 内定位方法. [背景技术] [0002] 近年来, ...
- LBS位置服务中GPS定位、基站定位、wifi定位的区别
1.卫星定位 其原理是接收机接收卫星广播,通过解析可见卫星的位置.距离等信息以及相应算法得出自己的位置信息,误差在15米范围内,当前可视卫星数量将影响定位精度,可见卫星数量越多,精度越高,实际测试中在 ...
- 定位技术GPS/A-GPS/LBS/WIFI定位介绍
定位技术GPS/A-GPS/LBS/WIFI定位介绍 来源:本站整理 作者:08LR.CN 更新时间:2013年05月03日人气:本日:9 本周:19 本月:34 总数:2134 次 生活中,我们经常 ...
最新文章
- shell中的数值判断
- java 实现部门树_(java实现)哈夫曼(Huffman)树编码(自编压缩项目基础)
- 『码蛋』Android 周刊第1期
- 不是内部或外部命令也不是可运行的程序?
- MySQL主从复制的原理:IO用于交互 SQL用于解析执行;主bin-log 从relay-log;
- 通过WM_COPYDATA消息完成进程间通信
- SAP OData Total = 80是这样计算出来的
- 信息学奥赛一本通 1020:打印ASCII码 | OpenJudge NOI 1.2 07
- php操作pdo,PHP PDO操作API
- sql azure 语法_Azure Data Studio中SQL Server Profiler
- mac terminal ssh client shuttle 免输密码
- android 类似按键精灵脚本_脚本编辑器 -- 按键精灵 #Android
- EasyUI-基本框架
- CRITIC权重指标如何计算?
- 浅谈 JNIEnv 和 JavaVM
- SLAM 中evo的使用(二) (evaluation of odometry) evo_traj/ape rpe/evo_ape说明与示例
- HTML做一个传统节日端午节 带设计报告4500字
- 1521 一维战舰(区间)
- 深度学习需要的显卡配置
- 反思:安全需要新体系
热门文章
- 在SQL Server上该做的和不该做的
- 【CyberSecurityLearning 44】iptables包过滤与网络地址转换
- npm package.json中的dependencies和devDependencies的区别
- document.ready和onload的区别——JavaScript文档加载完成事件
- 9.png(9位图)在android中作为background使用导致居中属性不起作用的解决方法
- git reset 命令详解(二)—— Git 学习笔记 08
- Unity3D 深度图
- 「 每日一练,快乐水题 」258. 各位相加
- 日常生活 -- 数据结构与算法告一段落
- linux搜狗输入法配置,liunx----配置搜狗输入法