首页 文章详情

某麦最新 analysis 算法逆向分析(多种语言实现)

FightingCoder | 137 2022-10-24 11:42 0 0 0
UniSMS (合一短信)
这是「进击的Coder」的第 737  篇技术分享 作者:TheWeiJun 来源:逆向与爬虫的故事

阅读本文大概需要 13 分钟。




      特别声明:本公众号文章只作为学术研究,不作为其它不法用途;如有侵权请联系作者删除。

15deba4685c5e331224f48499469004b.webp


 目录


一、前言介绍 二、 参数分析 三、断点调试 四、算法分析 五、思路总结




趣味模块


      小明是一名工程师,最近小明遇到了一个棘手的问题:小明发现自己曾经熬了几个通宵搞定的某麦算法居然升级了。小明惆怅进行中....,按照之前的套路进行加密定位和还原,发现都不好使了。这篇文章中,将解决小明遇到的困境,让我们一起去看小明遇到的问题并通过多种语言去实现算法还原吧!




一、前言介绍


前言:  想必 大家都了解什么是JS逆向吧?我们在以往的文章中涉及到JS逆向的网站,采用的方式都是去还原JS代码,然后使用Python去调用JS源码传递参数返回结果。这个过程虽然没有问题,但是对于爬取效率的提升、代码的性能都会受到影响。虽然爬虫的职责是首先确保能不能拿到数据,但是我觉得还有一个问题,那就是爬取效率。如果这两者都具备了,那将是最完美的!对于一些加密逻辑不是太复杂的网站,我们完全可以进行源码跨语言还原。耐心看完本文,你会受益匪浅的!




二、参数分析


备注 :如果想看旧版文章的,可以点击此链接:某麦analysis参数算法分析


1、首先打开我们今天要模拟的网站,刷新当前页面,首页截图如下:

bd8fec9e85fba1d9a59ab1da796f715d.webp

2、然后打开开发者工具,查找指定的XHR请求数据包,截图如下所示:

49f873e4178a92b192ba14dfb18fa7c5.webp

3、接下来,我们对该接口参数进行初判断及整理分析:

2ef336967a6af4fc65f32aa857ccd986.webp

params参数分析:

  • analysis              初步怀疑是base64加密

  • country               固定值

  • version                搜索版本

  • search、advise  搜索关键字


headers参数分析:

说明: 由于headers参数没有重要参数影响,故不作说明。




三、断点调试


1、使用最简单的方式,查询指定关键字、加密方法,定位加密参数具体坐标文件,截图如下:

4f5782a0e15f6f1e3ae4c9c08f8aee1f.webp

说明: 经过查询,我们可以肯定的是代码中没有用到这个变量名,然后我们去搜索加密方法,发现能搜到结果,但是和我们的加密参数关联不大,截图如下:

ee76f8972167b27c99092c46f32711f7.webp


3、接下来,我们还是使用XHR打断点,回溯堆栈的方式查找吧,截图如下:

7381ae4124041e651572a81b9ca09f5c.webp

说明:查看断点发包的请求函数,我们发现标红的地方已经计算出了加密参数,显然我们的断点是置后的,不过没有关系,我们查看函数执行堆栈进行回溯查找。


4、由于需要堆栈调试,过滤掉一些没用的流程,我们直接定位到加密地方,截图如下:

84142fc48649b1bb5c72a8642eb727fb.webp

总结:此时上图中参数e即为我们想要还原的加密参数,整个流程贯通后,我们只需要对js算法进行还原即可。




四、算法还原


1、JS版本算法还原

      
        var n = {};
      
      
        
          
function i(e) { var t, a = (t = "",         ["66""52""6d""6d""43""68""61""72""43""6f""64""65"].forEach((function (e{ t += unescape("%u00" + e) } )), t); return String[a](e) }
function h(e) { return function (e) { try { return btoa(e) } catch (t) { return Buffer.from(e).toString("base64") } }(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g, (function (e, t) { return i("0x" + t) } ))) }
function g(e, t) { t || (t = s()); for (var a = (e = e.split("")).length, n = t.length, o = "charCodeAt", r = 0; r < a; r++) e[r] = i(e[r][o](0) ^ t[(r + 10) % n][o](0)); return e.join("") }
n.cv = h; n.oZ = g;
function get_enCdata(keyword, path) { var l = "xyz517cda96abcd"; var d = "@#"; var s = 687; var o = +new Date - (s || 0) - 1661224081041; var r = ["cn", "ios14", keyword, keyword]; r = r.sort().join(""); r = n.cv(r); r += d + path + d + o + d + 3; a = (0, n.cv)((0, n.oZ)(r, l)); console.log(a); return a } get_enCdata("王者荣耀", "/search/index")

console打印结果如下:

ec01b241907eb1aa7d2af15faff6a7e9.webp

Python执行JS代码编写如下:

      
        
          def js_load():
        
      
      
            with open('encrypt.js', 'r', encoding='UTF-8') as f:
      
      
                lines = f.read()
      
      
                ctx = execjs.compile(lines)
      
      
            return ctx
      
      
        
          
headers = { 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua-mobile': '?0', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36', 'sec-ch-ua-platform': '"macOS"', 'sec-fetch-site': 'same-site', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'accept-language': 'zh-CN,zh;q=0.9', } ctx = js_load() keyword = "王者荣耀" url = "https://xxxxxx/search/index" analysis = ctx.call("get_enCdata", keyword, urlparse(url).path) params = ( # ('analysis', ), ('country', 'cn'), ('search', keyword), ('advise', keyword), ('version', 'ios14'), ) # analysis = get_analysis(params, urlparse(url).path) params = dict(params) params.update({ "analysis":analysis, })
response = requests.get(url, headers=headers, params=params) print(response.text)

打印结果如下:

9c8059d141101f3625ce247ac8166b52.webp

2、Python版本算法还原完整代码:

      
        
          # -*- coding: utf-8 -*-
        
      
      
        
          # --------------------------------------
        
      
      
        
          # @author : 逆向与爬虫故事公众号
        
      
      
        
          # --------------------------------------
        
      
      
        import time
      
      
        
          
import requests import base64 from urllib.parse import urlparse

def get_analysis(params, path): salt = "xyz517cda96abcd" data = list(dict(params).values()) data.sort() first_ba64 = base64.b64encode("".join(data).encode()).decode() first_ba64 += f"@#{path}@#{int(time.time()) * 1000 - 687 - 1661224081041}@#3" base_chr = ''.join(chr(ord(v) ^ ord(salt[(r + 10) % len(salt)])) for r, v in enumerate(first_ba64)) analysis = base64.b64encode(base_chr.encode()).decode() return analysis
def start_requests(keyword):
headers = { 'authority': 'api.qimai.cn', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua-mobile': '?0', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36',         'sec-ch-ua-platform''"macOS"', 'sec-fetch-site': 'same-site', 'sec-fetch-mode': 'cors',         'sec-fetch-dest''empty', 'accept-language': 'zh-CN,zh;q=0.9', } url = "https://xxxxx/search/index" params = ( # ('analysis', ), ('country', 'cn'), ('search', keyword), ('advise', keyword), ('version', 'ios14'), ) analysis = get_analysis(params, urlparse(url).path) params = dict(params) params.update({ "analysis": analysis, })
response = requests.get(url, headers=headers, params=params) print(response.text)
if __name__ == '__main__': start_requests("王者荣耀")


Pycharm打印结果如下:

9c8059d141101f3625ce247ac8166b52.webp


3、GoLang版本算法还原完整代码:

      
        
          // Package main ------------------
        
      
      
        
          // @author    : 逆向与爬虫故事公众号
        
      
      
        
          // -------------------------------
        
      
      
        package main
      
      
        
          
import ( "encoding/base64" "fmt" "io/ioutil" "log" "net/http" "time" )
func getAnalysis(keyword, path string) string { salt := "xyz517cda96abcd" baseStr := fmt.Sprintf("cnios14%s%s", keyword, keyword) baseBase64 := base64.StdEncoding.EncodeToString([]byte(baseStr)) baseBase64 += fmt.Sprintf("@#%s@#%d@#3", path, time.Now().Unix()*1000-687-1661224081041) dataStr := "" for i := 0; i < len(baseBase64); i++ { dataStr += string(baseBase64[i] ^ salt[(i+10)%len(salt)]) } analysis := base64.StdEncoding.EncodeToString([]byte(dataStr)) return analysis }
func main() { client := &http.Client{} keyword := "王者荣耀" path := "/search/index" analysis := getAnalysis(keyword, path) fmt.Println(analysis) req, err := http.NewRequest("GET", "https://xxxx/search/index?analysis="+analysis+"&country=cn&version=ios14&search="+keyword+"&advise="+keyword+"", nil) if err != nil { log.Fatal(err) } req.Header.Set("accept", "application/json, text/plain, */*") req.Header.Set("accept-language", "zh-CN,zh;q=0.9") req.Header.Set("cache-control", "no-cache") req.Header.Set("cookie", "gr_user_id=6326ec0c-e4bb-40f4-80fa-f136f53ec138; qm_check=A1sdRUIQChtxen8tJ0NMOQkKWVIeEHhARFQEQi5VVFk1RVJcd3UQABZQS0FIWhoSUFRZEgMSBBRRTlNISFVKF0o%3D; Hm_lvt_ff3eefaf44c797b33945945d0de0e370=1665841750,1665843752,1665999455,1666149354; tgw_l7_route=d09474674af82c17375cfcdd775c0c28; PHPSESSID=c8sbl02kml6qbdktrgkf2l7iia; ada35577182650f1_gr_session_id=25228f5f-bbba-4660-86f5-d45ebd688bb6; ada35577182650f1_gr_session_id_25228f5f-bbba-4660-86f5-d45ebd688bb6=true; synct=1666149373.597; Hm_lpvt_ff3eefaf44c797b33945945d0de0e370=1666149374; syncd=-1222") req.Header.Set("pragma", "no-cache") req.Header.Set("sec-ch-ua", `"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"`) req.Header.Set("sec-ch-ua-mobile", "?0") req.Header.Set("sec-ch-ua-platform", `"macOS"`) req.Header.Set("sec-fetch-dest", "empty") //req.Header.Set("sec-fetch-mode", "cors") req.Header.Set("sec-fetch-site", "same-site") req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36") resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() bodyText, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", bodyText) }


GoLand打印结果如下:

0210001525617c00215bb0314f5e8eab.webp



五、思路总结


回顾整个分析流程,本次难点主要概括为以下几点:


  • 如何快速定位加密参数的位置
  • 堆栈回溯如何过滤无用代码
  • 如何扣JS代码并能run起来
  • Python还原加密算法实现
  • GoLang还原加密算法实现
  • 熟练掌握数据类型及逻辑运算
06a23b341dcd62bec73a3c36e3d4b1cc.webp

End

崔庆才的新书《Python3网络爬虫开发实战(第二版)》已经正式上市了!书中详细介绍了零基础用 Python 开发爬虫的各方面知识,同时相比第一版新增了 JavaScript 逆向、Android 逆向、异步爬虫、深度学习、Kubernetes 相关内容,‍同时本书已经获得 Python 之父 Guido 的推荐,目前本书正在七折促销中!

内容介绍:《Python3网络爬虫开发实战(第二版)》内容介绍


5db7cdf7875af232f5158c7f5ede9239.webp


扫码购买


5561492de82294fe8f7629f1f95672b2.webp


d1761100a09042dad5282ac10663a94d.webp

点个在看你最好看

037fb23c42d91fc261b58e8c630dcd9b.webp
good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter