借助浏览器插件hook一个 知乎网页端 焦点加密算法 做成签名服务器 ...

打印 上一主题 下一主题

主题 797|帖子 797|积分 2391

以下内容仅用于技术交换和提高 如有侵权 私信我 我会立即删除

最近在找工作,写写文章记载点能力吧,要否则就真是菜鸟了,我劝以后的萌新也 没事儿研究时写写记载一下 都是吹牛的资源.
前些年在抖音做短视频小说 SD AI绘画 语音合成了 虽然全系列做成了一个统一软件可以比现在市场很多类似的优秀,但是无奈举债多,没有能力再创业了,所以分享一下内容给后来朋友们,后续可能会分享一下抖音剪映语音辨认的接口流程,和抖音豆包AI 全链路API,白嫖算力,看各人热情吧,原业务是抓取知乎的盐故事及时上新内容, 来为做小说创作的朋友们提供一个小说热度帅选的功能.主要维度有两个一个是点赞 一个是评论量,很简单抓取汗青和秒更上新便是.
写了个GUI 界面貌寝勿喷 但是功能实用,这里主要是为了先容知乎的算法的.下面进入正题

抓取之前先先容下大概思路和用到的工具操作流程

知乎的app 很多内容照旧直接使用的web 只是知乎在app中自界说了浏览器头,辨认浏览器让一些内容显示和屏蔽

知乎的web 隐蔽了很多app 的内容但是却暴漏了他们的一些算法缺陷,让我们可以借助web加密来和通过app的api

下面我们将在手机端抓包知乎 使用的工具 有HttpCanary ,VMOS Pro 和 知乎APP

HttpCanary 俗称小黄鸟是手机端的抓包工具,有的时间比pc上的软件好用,情况证书安装 何使用啥的自己去搜吧,

VMOS Pro 是虚拟机可以模仿一个完备的手机系统 这里模仿我们需要留意选择模仿安卓7.1的 64位 这个虚拟机还支持root 一键安装xposed 再加上Frida 什么的爽的不要

为什么要这么做呢就是高版本安卓系统修改加强了ssl 体系认证 导致三方证书无法方便正常使用 ,
如果直接在原机器安装后抓包 抓包工具是无发抓取到知乎任何数据的,
上面这套下来基本市场9成的app都可以抓到了(之前我测试微信竟然可以),但是某些大厂照旧抓不到的 比如抖音 他自己重写了底层网络内容在so里 包罗一些ssl证书认证的内容,可以绕开虚拟机直接和外部通讯.纵然你外部有抓包工具也抓不到,
所以说知乎技术有待提高. 另外再次声明一下 我抓取的知乎数据访问不高哈,不会对服务器造成影响,只是模仿人类正常访问 然后搜集信息用于热度分析,别给我发律师函
看我标题为是那么要借助浏览器插件hook呢,原因很简单,
代码成本低和逆向成本相对低,
操作成本低(有时间测试和测试逆向过成 我们可以动态修改代码 刷新一下就直接进入浏览器情况了,如果用其他方式替换js可能需要借助抓包工具,或者自己写转发抓包拦截工具,比力麻烦)
只需要一个焦点插装就把问题解决了,插件自己 其实也可以直接举行数据处理和网络请求 但是为了节流资源就只有一个浏览器了.不过确实也可以这么做,根据具体场景自己选吧
还有就是为了测试这个方案的可行性,原因是知乎核默算法中包罗很多浏览器情况的信息,所以这是综合选择的结果,所以提取hook他的算法做成服务器,
这样也还可以使加密解耦合 为更大范围的抓取做服务支持如果算法有变 我们都不用改爬虫源代码只要改加密服务器就可以了,不过知乎更新并不是很勤快,用了1年多了这个方法,而且这个方法貌似防爬机制都触发的概率比力低,
来上两章手机截图吧 不一步步演示了

下边这张图就是 抓包到的数据了 我圈出的api 就是我们目标的api 我筛选的是一些点赞 评论和简洁的信息 其他自己
分析吧,我只是做信息集会,

点进去 切换到请求那一栏 所有请求信息都能看到了 多抓几次你会发现真正需要的和变化参数没几个

第一眼肯定是懵逼,多试几次会,然后再结合分享去的web端网页比如 https://www.zhihu.com/xen/market/remix/paid_column/1824095654672261120
1824095654672261120末了一串是小说集 的id
你会发现请求头直接 6个参数就可以了
  1. baseurl = "https://api.zhihu.com/pluton/products/" + ids + "/paid_column/section?offset=" + str( offset) + "&limit=100&order_by=global_idx"
  2. headers = {
  3.         "X-Requested-With": "Fetch",
  4.         # "X-Zse-96": "2.0_MbKocVal+L2oCq1+4AgxlJU7kBqZd==1/ES8UyXtgHtxbdF1=iCTvBPo3cXLwsEc",
  5.         "x-zse-96": "2.0_" + black_code,
  6.         "x-zse-93": os_vr,  # 不可修改
  7.         # user-agent 不重要 可以随意发送命令
  8.         "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
  9.         "accept": "*/*",
  10.       
  11.         "cookie": cookie
  12.     }
复制代码
x-zse-96 其中 black_code 是加密值 前边2.0固定版本 我上图是版的显示是1.0 而网页时2.0
x-zse-93 的 os_vr 是情况的版本 这里由于跟的web版本不一样我们选择从浏览器那边动态获取
cookie 是web情况初始化后的辨认信息,不是重点 也从浏览器情况获取就可以了
醒醒啦 主要任务来了
第一步 分析知乎的页面找到算法验证的关键点

在浏览器器调试源码里 找到 vendors.2d292cce3f0653fa0e5e.js
然后搜索 __g._encrypt 结果会出来两个 至于怎么找 不在本次教程范围内 新手可以看我一上篇
能搜索出来两个结果 这里我们只用到第一个 断点刷新一下你们就知道了,这是对上边代码中 baseurl 举行加密的算法
知乎此处比力弱了的点是把这种 关键点api 的值输入和输出 直接暴露了,虽然也是闭包存在,但是我们直接改这里代码就可以修改玉成局可用的算法,其实可以学学抖音 再加密肴杂一次 就不好找了 他这种应该vue编译出来直接上线了那种 毫无防护手段 作为大厂 照旧很不测的写个小工具源代码都能逆向出来

我们修改方案
  1. function(d){window._encrypt=__g._encrypt;return __g._encrypt(encodeURIComponent(d))})
复制代码
这样 就把加密算法 提取出来了 时间成本极低 然后 我们就可以用 window._encrypt全局方法做文章了
第二步 写一个浏览器插件 在知乎提取出关键js 放进去 修改指定关键行 然后替换


新建文件夹 zhihuchajian 这个文件夹地址 一会儿 浏览器 需要用到
然后按照上述目次创建文件布局
下面我给出文件内容
hoook_manager.js
  1. chrome.webRequest.onBeforeRequest.addListener(
  2.     // onBeforeRequest 可见 是再浏览器请求前替换
  3.     function(details) {
  4.         const url = details.url;
  5.         if (url === 'https://static.zhihu.com/xen/productDetailsApps~albumColumnAndMagazine/vendors.2d292cce3f0653fa0e5e.js') {
  6.             console.log("vendors.2d292cce3f0653fa0e5e.js")
  7.             return {redirectUrl: chrome.extension.getURL("hook_js/vendors.2d292cce3f0653fa0e5e.js")}; //hook.js是你要替换的js脚本
  8.         }
  9.         return {cancel: false}
  10.     },
  11.     {urls: ["<all_urls>"], types: ["script"]},
  12.     ["blocking"] //类型blocking为拦截,
  13.   );
复制代码
manifest.json
  1. {
  2.   "name": "知乎HooK",
  3.   "version": "1.1",
  4.   "manifest_version": 2,
  5.   "description": "插件描述.",
  6.   "permissions": [
  7.     "activeTab",
  8.     "storage",
  9.     "storage",
  10.     "contextMenus",
  11.     "notifications",
  12.     "tabs",
  13.     "webRequest",
  14.     "webRequestBlocking",
  15.     "https://static.zhihu.com/*",
  16.     "https://www.zhihu.com/*"
  17.   ],
  18.   "host_permissions": [
  19.     "http://127.0.0.1/*"
  20.   ],
  21.   "browser_action": {
  22.     "default_title": "Get Page HTML"
  23.   },
  24.   "background": {
  25.     "scripts": [
  26.       "hoook_manager.js"
  27.     ],
  28.     "persistent": true
  29.   },
  30.   "web_accessible_resources": [
  31.     "hook_js/vendors.2d292cce3f0653fa0e5e.js"
  32.   ]
  33. }
复制代码
vendors.2d292cce3f0653fa0e5e.js 这个太大 不粘帖了,自己去改吧 .这个方法把我们插件放在编辑器里 修改js 到达了动态修改页面js 的目的 爽的不要不要的 比追栈服从高不少
第三步 启动 web 启动浏览器远程 debuger 模式 并且加载插件

这个浏览器地址是默认安装的位置 留意修改你名字 这是一个 命令行 在桌面新建一个bat 复制进去修改 生存 再启动
  1. C:\Users\你得主机名字\AppData\Local\Google\Chrome\Application\chrome.exe --remote-debugging-port=9222 --user-data-dir="C:selenumAutomationProfile"  --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" --load-extension=D:/txt_to_video/zhihu/zhihu_app_api/zhihuchajian
复制代码
–remote-debugging-port 指定浏览器开发端口 9222
–user-data-dir 指定浏览器情况存储的路径 设置这个可以把一个浏览器当作多个浏览器来用
–user-agent 自界说浏览器头
–load-extension 指定自己的浏览器插件路径
上述操作完成基本就你可以看到 自己的插件了

再去访问 知乎网址 https://www.zhihu.com/xen/market/remix/paid_column/1824095654672261120
调试期内 查察源码 就看到

我们的代码见效了,舒畅的玩耍吧, 记得我们的bat 要一直启动不能关闭
第四步 利用python web框 架 暴露一个简单服务,买通浏览器 实验js 完成我们算法认证

其实到这步已经没啥意思了 不过该写还要写 烂作者凑字儿 哈哈 我就贴代码了 看解释吧
主要使用 selenium ,如果selenium 情况和驱动缺失 自行下载安装 学习不做教程
  1. import os
  2. from selenium.webdriver import ChromeOptions
  3. from selenium import webdriver
  4. from flask import Flask
  5. app = Flask(__name__)
  6. chrome_options = ChromeOptions()
  7. chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
  8. browser = webdriver.Chrome(options=chrome_options)
  9. browser.get("https://www.zhihu.com")
  10. script = """
  11. console.log("#防止爬虫代码 ,最好在 浏览器环境中 使用油猴插件方法 全局在加载前加载此段代码")
  12. Object.defineProperty(navigator,"webdriver",{get:() => false,});
  13. """
  14. #运行Javascript
  15. browser.execute_script(script)
  16. browser.get("https://www.zhihu.com/xen/market/remix/paid_column/1518918494300667904")
  17. browser.execute_script(script)
  18. monidianji="""
  19.         // 选择具有特定class的div元素  
  20.         var element = document.querySelector('.CatalogModule-allSection-ctqBJ');  
  21.         // 检查元素是否存在  
  22.         if (element) {  
  23.             // 创建一个新的点击事件  
  24.             var clickEvent = new MouseEvent('click', {  
  25.                 'view': window,  
  26.                 'bubbles': true,  
  27.                 'cancelable': true  
  28.             });  
  29.          
  30.             // 触发点击事件  
  31.             element.dispatchEvent(clickEvent);  
  32.         } else {  
  33.             console.log('没有找到具有指定class的元素');  
  34.         }
  35. """
  36. browser.execute_script(monidianji)
  37. @app.route("/")
  38. def hello_world():
  39.     """Example Hello World route."""
  40.     api = """
  41.     /get_code/<light_code> return black_code
  42.     /get_cookie                     return current cookie
  43.     /get_header            return headers
  44.     """
  45.     return api
  46. @app.route('/get_cookie', methods=['GET'])
  47. def get_cookie():
  48.     javascript_code = """
  49.         return JSON.stringify({'cookie': document.cookie})
  50.         """
  51.     try:
  52.         result = browser.execute_script(javascript_code)
  53.         if result:
  54.             tata = {
  55.                 'data': result
  56.             }
  57.             return tata
  58.         else:
  59.             return "{'error':'浏览器端出现异常或者环境没有初始化'}"
  60.     except Exception as e:
  61.         print(e)
  62.         return f"{'error':'浏览器端出现异常或者环境没有初始化'}"
  63. @app.route('/get_header', methods=['GET'])
  64. def get_header():
  65.     javascript_code = """
  66.     var ttsa=new RegExp("d_c0=([^;]+)").exec(document.cookie)
  67.     return JSON.stringify({'xUDId':JSON.parse(document.getElementById("resolved").value).appContext.xUDId,'authId':JSON.parse(document.getElementById("resolved").value).appContext.authId,'otherId':ttsa[1],'cookie':document.cookie,'userAgent':navigator.userAgent})
  68.     """
  69.     try:
  70.         result = browser.execute_script(javascript_code)
  71.         if result:
  72.             tata={
  73.                 'data': result
  74.             }
  75.             return tata
  76.         else:
  77.             return "{'error':'浏览器端出现异常或者环境没有初始化'}"
  78.     except Exception as e:
  79.         print(e)
  80.         return "{'error':'浏览器端出现异常或者环境没有初始化'}"
  81. @app.route('/get_code/<light_code>', methods=['GET'])
  82. def get_data(light_code):
  83.     javascript_code = """
  84.     // JavaScript代码示例
  85.     //alert('Hello World!');
  86.     if(undefined!=window._encrypt){
  87.         var tt=window._encrypt(encodeURIComponent('"""
  88.     ttff = """'))
  89.         console.log(tt)
  90.         return tt
  91.     }else{
  92.       console.log('环境window._encrypt没有设置')
  93.       console.log('定义为代码   return __g._encrypt(encodeURIComponent(d))')
  94.       console.log('return __g._encrypt(encodeURIComponent(d))')
  95.       console.log('window._encrypt=__g._encrypt')
  96.       // 选择具有特定class的div元素  
  97.         var element = document.querySelector('.CatalogModule-allSection-ctqBJ');  
  98.       // 检查元素是否存在  
  99.         if (element) {  
  100.             // 创建一个新的点击事件  
  101.             var clickEvent = new MouseEvent('click', {  
  102.                 'view': window,  
  103.                 'bubbles': true,  
  104.                 'cancelable': true  
  105.             });  
  106.          
  107.             // 触发点击事件  
  108.             element.dispatchEvent(clickEvent);  
  109.         } else {  
  110.             console.log('没有找到具有指定class的元素');  
  111.         }
  112.     }
  113.     """
  114.     # zhihu = ZhiHu()
  115.     # code = zhihu.get_pinglun("1716821153832767488")
  116.     if light_code==None or light_code=='':
  117.         return "{'error':'light_code参数异常浏览器端出现异常或者环境没有初始化'}"
  118.     javascript_code = javascript_code + light_code + ttff
  119.     try:
  120.         result = browser.execute_script(javascript_code)
  121.         if result:
  122.             tata={
  123.                 'data': result
  124.             }
  125.             return tata
  126.         else:
  127.             return "{'error':'浏览器端出现异常或者环境没有初始化'}"
  128.     except Exception as e:
  129.         print(e)
  130.         return "{'error':'浏览器端出现异常或者环境没有初始化'}"
  131. if __name__ == "__main__":
  132.     print( 'return __g._encrypt(encodeURIComponent(d))')
  133.     print('window._encrypt=__g._encrypt')
  134.     # app.run(port=8080)
  135.     app.run(debug=False, host="0.0.0.0", port=int(os.environ.get("PORT", 7355)))
  136.     # app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
  137.     # web框架 使用请参考 https://blog.csdn.net/weixin_38037405/article/details/127149032
  138.     #app.run(host='0.0.0.0', port=80, debug=True)
  139. # browser.quit()
复制代码
以上代码弄完还不能正常运行 不过大的操作情况 已经就位 为了给知乎留点面子不写了
但是给各人一点提示
def get_data(light_code):
这个方法我们接收的 light_code 是有固定算法需要计算的不需要浏览器情况 ,不过我提供的关键点 你们向上追栈是可以找到的
流程是 大概就是这几个参数 说到这里自己去搜这几个关键参数就应该找到了 这些一起计算一个明码值 再传给浏览器加密依赖一些浏览器动态信息加密给出暗码 ,再通通过浏览器头发送get 请求,这里就完成了逆向 可以舒畅爬了
# os_vr = “101_3_3.1”
# story_id = “1716821153832767488”
# url_api = f"/pluton/products/{story_id}/paid_column/section"
# authId = “7481a31d25d73bfc1edf9240df687cf9”
这样我们就可以自己动态修改请求 也可以 还不去关注浏览器内登录信息了 暴露的服务还可以解耦给其他 分布式服务使用 就是一个快乐

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

半亩花草

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表