首先抓包看下请求参数,这个app很简单,没有做什么防抓包的策略,可以看到请求中有个hnairSign,这个参数就是目标。
POST /mapp/webservice/v5/common/flight/list?hnairSign=234B7E54A9E83A19040FC3E66720B81AC7C9441A HTTP/1.1 User-Agent: okhttp/3.9.1 Content-Type: application/json; charset=utf-8 Content-Length: 595 Host: app.hnair.com Connection: Keep-Alive Accept-Encoding: gzip Pragma: no-cache Cache-Control: no-cache {"common":{"abuild":"55572","akey":"184C5F04D8BE43DCBD2EE3ABC928F616","aname":"com.rytong.hnair","atarget":"standard","aver":"7.5.0","did":"8XV5T15A23004277","gtcid":"151631a36b8e19d7235c3bea66bb36d7","mchannel":"xiaomi","schannel":"AD","slang":"zh-CN","sname":"google\/angler\/angler:6.0.1\/MTC20L\/3230295:user\/release-keys","stime":"1568599814440","sver":"6.0.1","szone":"+0800","riskToken":"5d7eee48p3BusPiNPOzOBjkRrfxxe6pncDiJK1y3","captchaToken":""},"data":{"adultCount":1,"cabins":["*"],"childCount":0,"depDate":"2019-09-17","dstCode":"CAN","infantCount":0,"orgCode":"SZX","tripType":1}}
java层的调用很简单,直接搜索hnairSign就能很快定位到关键函数,最后会调用getHNASignature
这个方法,是个native方法,在libsignature.so中
public class HNASignature { private static final String TAG = "HNASignature"; static { try { System.loadLibrary("signature"); } catch (Exception e) { Log.e(TAG, "HNASignature load signature error ", e); } } public static native String getHNASignature(String paramString1, String paramString2, String paramString3, String paramString4, String paramString5); }
ida打开这个so,先看下导出函数有哪些,
Java_com_rytong_hnair_HNASignature_getHNASignature对应的就是java层的getHNASignature(),先静态分析跟一下,点进去这个函数,从返回结果往上看,大概猜到算法在 HNASignature::HNASignature
这里面。
点进去,继续往下跟,很明显看到HNASignature::sign
继续点进去,很明显看到用到的算法了,猜测就是hmacSha
下面就用frida来hook做动态分析,验证上面分析。
首先写一个主动调用的函数,方便我们自定义简单的参数,分析起来方便些
function call_getSignFromJni() { //调用最外层函数 java层调用jni Java.perform(function () { var HNASignature = Java.use("com.rytong.hnair.HNASignature"); var result = HNASignature.getHNASignature("{}", "{}", '{"abuild":"55572"}', "ad", "cccc"); console.log("BitmapkitUtils.getSignFromJni:", result); }); }
再写个hook函数,这里对HNASignature::sign进行hook,先找到它的导出函数
下面是hook代码,
function hook_sign() { var a = Module.findExportByName("libsignature.so", "_ZN12HNASignature4signEPhS0_"); console.log('hook_sign: ',a); Interceptor.attach(a, { onEnter: function(args) { this.arg0 = args[0] console.log("hook_sign arg2==: "+ptr(args[2]).readCString()+" arg3==: "+ptr(args[3]).readCString()); }, onLeave: function (retval) { console.log('hook_sign onLeave: ', 'retval: ', Memory.readCString(this.arg0.readPointer())) } }); } function hook_hmacSha() { var base_libsinature = Module.findBaseAddress("libsignature.so"); console.log('base1: ',base_libsinature); var a = Module.findExportByName("libsignature.so", "_ZN12HNASignature7hmacShaEPhS0_"); console.log('hook_hmacSha: ',a); Interceptor.attach(a, { onEnter: function(args) { console.log("hook_hmacSha param: "+ptr(args[2]).readCString()); console.log("hook_hmacSha key: "+ptr(args[3]).readCString()); }, onLeave: function (retval) { // console.log('hook_hmacSha onLeave: ', 'retval: ', ptr(retval).readCString()) } }); console.log("hook_hmacSha..."); }
启动frida执行上面的脚本,
可以看到,HNASignature::sign就是算法的地方,也能看到执行算法返回后后面的拼接参数就是我们传进去的参数。
最后就是确定算法是不是标准的hmac_sha了,随便找个在线网站,发现计算的结果和我们动态hook的是一致的。