配置好代理,打开app抓包,点击登录,发现数据被加密
从Charles可以看到,发送的数据是加密的,发送回来的数据也是加密的
发送的数据包
POST /api/user/login HTTP/1.1 If-Modified-Since: Thu, 09 Feb 2023 05:17:17 GMT Content-Type: application/json; Charset=utf-8 User-Agent: Dalvik/2.1.0 (Linux; U; Android 10; Pixel 4 XL Build/QD1A.190821.011.C4) Host: api.dodovip.com Connection: Keep-Alive Accept-Encoding: gzip Content-Length: 249 {"Encrypt":"NIszaqFPos1vd0pFqKlB42Np5itPxaNH\/\/FDsRnlBfgL4lcVxjXii\/UNcdXYMk0EaHz9LRZCfkpz\nazbo8zb2jdzkGqEAe9VKNk8FmwSoaur1PhpwV+iCeHcftk6ZlsGui5zmyjMBX\/Ng7c701M7sY6iW\noWntyfWevnsJrN+wPARNX35V+O1JquZqhvoK8em4\/UxIVR3NO6WGr6ITQMXjfn1INGMnUAtT\n"}
返回的数据包
HTTP/1.1 200 OK Date: Thu, 09 Feb 2023 05:17:17 GMT Content-Type: application/json;Charset=utf-8 Transfer-Encoding: chunked Server: Nginx Content-Encoding: gzip Connection: keep-alive 2v+DC2gq7RuAC8PE5GZz5wH3/y9ZVcWhFwhDY9L19g9iEd075+Q7xwewvfIN0g0ec/NaaF43/S0=
为了伪造登录请求,我们就要对app进行逆向
首先先查一下壳
没有加壳
将app导入jadx进行反编译
在进行逆向之前,我们先分析一下界面,确定app是原生的还是h5app
可以确定是原生的app
先尝试通过搜索字符串定位关键加密代码,可以搜索链接,可以搜索加密的参数名,也可以搜索同一个数据包中,没有加密的参数名
直接搜索数据包中的Encypt,有105个结果
再加上双引号,变成了24个
其中,与包名相关的,我们可以优先关注
我们先进入到第一个函数
关键字encodeDesMap和sign关键字可以知道,相关性较大
进入paraMap函数,这里对参数进行了处理
我们进入encodeDesMap函数,可以看出进行了des加密处理
进入第二个函数,我们看到des加密和sign,相关性也比较大
通过搜索POST数据包里的链接,我们同样搜索出了两个函数,我们可以基本定位登录函数
登陆函数
函数的分析
private void login(String userName, String pwd) { this.DEFAULT_TYPE = new TypeToken() { // from class: com.dodonew.online.ui.LoginActivity.1 }.getType(); this.para.clear(); this.para.put("username", userName);//放入用户名 this.para.put("userPwd", pwd);//放入密码 if (TextUtils.isEmpty(DodonewOnlineApplication.devId)) { DodonewOnlineApplication.devId = Utils.getDevId(DodonewOnlineApplication.getAppContext()); } this.para.put("equtype", Config.equtype); this.para.put("loginImei", "Android" + DodonewOnlineApplication.devId); requestNetwork("user/login", this.para, this.DEFAULT_TYPE);//通过requestNetwork函数进行提交 }
提交函数
private void requestNetwork(final String cmd, Map para, Type type) { showProgress(); this.request = new JsonRequest(this, "http://api.dodovip.com/api/" + cmd, "", new Response.Listener() { // from class: com.dodonew.online.ui.LoginActivity.2//发送请求,回调函数 public void onResponse(RequestResult requestResult) { if (!requestResult.code.equals(a.e)) { LoginActivity.this.showToast(requestResult.message); } else if (cmd.equals("user/login")) { DodonewOnlineApplication.loginUser = (User) requestResult.data; DodonewOnlineApplication.loginLabel = "mobile"; Utils.saveJson(LoginActivity.this, DodonewOnlineApplication.loginLabel, Config.LOGINLABEL_JSON); LoginActivity.this.intentMainActivity(); } LoginActivity.this.dissProgress(); } }, this, type);//回调函数结束 this.request.addRequestMap(para, 0);//将带有用户名,密码等信息的para传入addRequestMap中 DodonewOnlineApplication.addRequest(this.request, this); }
进入addRequestMap
public void addRequestMap(Map addMap, int a) { String time = System.currentTimeMillis() + "";//取时间戳 if (addMap == null) { addMap = new HashMap<>(); } addMap.put("timeStamp", time);//将时间戳加入到addMap中 String encrypt = RequestUtil.encodeDesMap(RequestUtil.paraMap(addMap, Config.BASE_APPEND, "sign"), this.desKey, this.desIV);//addMap先传到paraMap中进行第一步处理,然后将deskey和desiv传入到encodeDesMap中进行des加密。这两个函数是分析的重点 JSONObject obj = new JSONObject(); try { obj.put("Encrypt", encrypt);//输出最终结果进行提交 this.mRequestBody = obj + ""; } catch (JSONException e) { e.printStackTrace(); } }
跟进paraMap函数
public static String paraMap(Map addMap, String append, String sign) { try { Set keyset = addMap.keySet(); StringBuilder builder = new StringBuilder(); List list = new ArrayList<>(); for (String keyName : keyset) {//加强for循环,将addMap全部放入list中 list.add(keyName + "=" + addMap.get(keyName)); } Collections.sort(list);//对参数进行排序 for (int i = 0; i < list.size(); i++) {//转成字符串,以便进行MD5加密 builder.append(list.get(i)); builder.append("&"); } builder.append("key=" + append); addMap.put("sign", Utils.md5(builder.toString()).toUpperCase());//使用MD5进行加密,得到签名的值 String result = new Gson().toJson(sortMapByKey(addMap));//将addMap转成Json字符串 Log.w(AppConfig.DEBUG_TAG, result + " result"); return result; } catch (Exception e) { e.printStackTrace(); return ""; } }
跟进encodeDesMap
继续跟进DesSecurity
public class DesSecurity { Cipher deCipher; Cipher enCipher; public DesSecurity(String key, String iv) throws Exception { if (key == null) { throw new NullPointerException("Parameter is null!"); } InitCipher(key.getBytes(), iv.getBytes());//将key和iv转字节后传入InitCipher中 } private void InitCipher(Byte[] secKey, Byte[] secIv) throws Exception { MessageDigest md = MessageDigest.getInstance("MD5");//将key进行了MD5加密 md.update(secKey); SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(new DESKeySpec(md.digest()));//将key加密后MD5的值直接传入DESKeySpec中作为des加密的密钥,注意,digest()是字节,取加密结果16个字节中的前8个字节 IvParameterSpec iv = new IvParameterSpec(secIv);//iv直接传入 this.enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");//DES加密模式 this.deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); this.enCipher.init(1, key, iv); this.deCipher.init(2, key, iv); } public String encrypt64(Byte[] data) throws Exception { return Base64.encodeToString(this.enCipher.doFinal(data), 0);//对加密后的结果进行doFinal,并进行Base64编码 } public Byte[] decrypt64(String data) throws Exception { return this.deCipher.doFinal(Base64.decode(data, 0)); } }
静态分析后,我们要使用Firda进行Hook了
我们首先Hook paraMap函数
编写hook代码
Java.perform(function () { console.log("xxx") var jsonRequest= Java.use("com.dodonew.online.http.JsonRequest"); jsonRequest.paraMap.implementation=function (a) { console.log("jsonRequest.paraMap is called"); return this.paraMap(a); } })
app点击登陆,发现函数并未被调用
我们再hook addRequestMap函数
Java.perform(function () { console.log("xxx") var jsonRequest= Java.use("com.dodonew.online.http.JsonRequest"); jsonRequest.paraMap.implementation=function (a) { console.log("jsonRequest.paraMap is called"); return this.paraMap(a); } jsonRequest.addRequestMap.implementation=function (addMap,a) { console.log("jsonRequest.addRequestMap is called"); return this.addRequestMap(addMap,a); } })
再次hook发现报错,函数有三个重载,很明显是第二个
修改hook代码,再次hook
Java.perform(function () { console.log("xxx") var jsonRequest= Java.use("com.dodonew.online.http.JsonRequest"); jsonRequest.paraMap.implementation=function (a) { console.log("jsonRequest.paraMap is called"); return this.paraMap(a); } jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation=function (addMap,a) { console.log("jsonRequest.addRequestMap is called"); return this.addRequestMap(addMap,a); } })
再次点击登陆,addRequestMap函数触发
再hook encodeDesMap函数
编写hook代码
Java.perform(function () { console.log("xxx") var jsonRequest= Java.use("com.dodonew.online.http.JsonRequest"); var requestUtil= Java.use("com.dodonew.online.http.RequestUtil"); jsonRequest.paraMap.implementation=function (a) { console.log("jsonRequest.paraMap is called"); return this.paraMap(a); } jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation=function (addMap,a) { console.log("jsonRequest.addRequestMap is called"); return this.addRequestMap(addMap,a); } requestUtil.encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation=function (data,deskey,desIv) { console.log("requestUtil.encodeDesMap is called"); console.log("data:",data,"deskey:",deskey,"desIv:",desIv); var encodeDesMap=this.encodeDesMap(data,deskey,desIv); console.log("encodeDesMap:",encodeDesMap) return encodeDesMap; } })
点击登陆按钮,成功hook
将hook到的数据与抓包数据对比一致
jsonRequest.addRequestMap is called requestUtil.encodeDesMap is called data: {"equtype":"AndROID","loginImei":"Androidnull","sign":"5CDC2DA6AB93DAAE28BB8B8F9B9A5396","timeStamp":"1675951114924","userPwd":"123456","username":"13258556988"} deskey: 65102933 desIv: 32028092 encodeDesMap: NIszaqFPos1vd0pFqKlB42Np5itPxaNH//FDsRnlBfgL4lcVxjXii/UNcdXYMk0Er+dH33DGoDbR vwi+0/JK4jFMtDufcIdMyMoUuHGtsYRGE5pi5gHNJVE51UAaJ4ZS2gFFoiuzvQALhou4Uyl/Cjgc bufrQMwC0DVacym7aemdRNuNgr4QPTi4WpUDvu3zUgSP05lfYaFJiO4RlyuiDxpjia7V7/S2
抓包数据
POST /api/user/login HTTP/1.1 If-Modified-Since: Thu, 09 Feb 2023 13:55:01 GMT Content-Type: application/json; Charset=utf-8 User-Agent: Dalvik/2.1.0 (Linux; U; Android 10; Pixel 4 XL Build/QD1A.190821.011.C4) Host: api.dodovip.com Connection: Keep-Alive Accept-Encoding: gzip Content-Length: 250 {"Encrypt":"NIszaqFPos1vd0pFqKlB42Np5itPxaNH\/\/FDsRnlBfgL4lcVxjXii\/UNcdXYMk0Er+dH33DGoDbR\nvwi+0\/JK4jFMtDufcIdMyMoUuHGtsYRGE5pi5gHNJVE51UAaJ4ZS2gFFoiuzvQALhou4Uyl\/Cjgc\nbufrQMwC0DVacym7aemdRNuNgr4QPTi4WpUDvu3zUgSP05lfYaFJiO4RlyuiDxpjia7V7\/S2\n"}
目前,我们还有一个sign的参数是未知的
跟进paraMap函数,我们发现传入了三个参数,该函数之前静态分析已经分析过
我们从上面的函数中跟进找到了append的值
继续跟进MD5函数
我们接下来hook MD5函数
Java.perform(function () { console.log("xxx") var jsonRequest= Java.use("com.dodonew.online.http.JsonRequest"); var requestUtil= Java.use("com.dodonew.online.http.RequestUtil"); var utils=Java.use("com.dodonew.online.util.Utils") jsonRequest.paraMap.implementation=function (a) { console.log("jsonRequest.paraMap is called"); return this.paraMap(a); } jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation=function (addMap,a) { console.log("jsonRequest.addRequestMap is called"); return this.addRequestMap(addMap,a); } requestUtil.encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation=function (data,deskey,desIv) { console.log("requestUtil.encodeDesMap is called"); console.log("data:",data,"deskey:",deskey,"desIv:",desIv); var encodeDesMap=this.encodeDesMap(data,deskey,desIv); console.log("encodeDesMap:",encodeDesMap) return encodeDesMap; } utils.md5.implementation=function (a) { console.log("sign data:",a); var md5 =this.md5(a); console.log("sign:",md5); return md5; } })
成功hook
jsonRequest.addRequestMap is called sign data: equtype=AndROID&loginImei=Androidnull&timeStamp=1675953260605&userPwd=123456&username=13258556988&key=sdlkjsdljf0j2fsjk sign: cb925fbd6ba322eda4afa035b1feca97 requestUtil.encodeDesMap is called data: {"equtype":"AndROID","loginImei":"Androidnull","sign":"CB925FBD6BA322EDA4AFA035B1FECA97","timeStamp":"1675953260605","userPwd":"123456","username":"13258556988"} deskey: 65102933 desIv: 32028092 encodeDesMap: NIszaqFPos1vd0pFqKlB42Np5itPxaNH//FDsRnlBfgL4lcVxjXii/UNcdXYMk0EbKdY0NLSwwFz rDi3e6GNtkV6lxF18rOwC0ljfaCvUVSspPnzB2mfNvJPDutqTnTsPwcaqzXNKmmhnHNPJd7s5bIH OeqgVNfdlJ/rTZK7koJWj3zqiVHksUkI1PYRm9HccAeksGWW9XF1K9TxWgTyZ3sYUUl7d0wX
抓包数据
POST /api/user/login HTTP/1.1 If-Modified-Since: Thu, 09 Feb 2023 13:56:49 GMT Content-Type: application/json; Charset=utf-8 User-Agent: Dalvik/2.1.0 (Linux; U; Android 10; Pixel 4 XL Build/QD1A.190821.011.C4) Host: api.dodovip.com Connection: Keep-Alive Accept-Encoding: gzip Content-Length: 248 {"Encrypt":"NIszaqFPos1vd0pFqKlB42Np5itPxaNH\/\/FDsRnlBfgL4lcVxjXii\/UNcdXYMk0EbKdY0NLSwwFz\nrDi3e6GNtkV6lxF18rOwC0ljfaCvUVSspPnzB2mfNvJPDutqTnTsPwcaqzXNKmmhnHNPJd7s5bIH\nOeqgVNfdlJ\/rTZK7koJWj3zqiVHksUkI1PYRm9HccAeksGWW9XF1K9TxWgTyZ3sYUUl7d0wX\n"}
#如无特别声明,该文章均为 原创,转载请遵循
署名-非商业性使用 4.0 国际(CC BY-NC 4.0) 协议,即转载请注明文章来源。
#最后编辑时间为: 2023-02-09 22:35:32