├── .idea ├── .gitignore ├── crawler-guide-code.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── readme.md ├── update.txt ├── 第三章:Web Js逆向 ├── 3.10 常见协议分析 │ └── 3.10.2 protobuf协议 │ │ ├── 3.10.2.1 万方protobuf请求案例 │ │ ├── js3.10.2.1.py │ │ ├── parent_sample.py │ │ ├── protoc.exe │ │ ├── s.proto │ │ ├── s_parent_pb2.py │ │ └── s_pb2.py │ │ └── 3.10.2.2 抖音protobuf解析案例 │ │ ├── js.py │ │ ├── protoc.exe │ │ ├── s.proto │ │ ├── s.txt │ │ └── s_pb2.py ├── 3.13 反混淆AST │ ├── 3.13.4 用AST还原代码 │ │ ├── ast.js │ │ ├── ast补充 │ │ │ ├── readme.md │ │ │ ├── while-if转变为while-switch.js │ │ │ ├── 三目运算符.js │ │ │ ├── 函数调用处自动替换计算值.js │ │ │ ├── 删除多余的空行和空语句.js │ │ │ ├── 删除所有的代码注释.js │ │ │ ├── 删除未被使用的变量.js │ │ │ ├── 删除未被调用的函数.js │ │ │ ├── 去控制流(for-switch).js │ │ │ ├── 去控制流(while-switch).js │ │ │ ├── 合并定义在object对象外面的key、value.js │ │ │ ├── 处理eval函数.js │ │ │ ├── 处理条件已知的if语句.js │ │ │ ├── 处理条件已知的三元表达式.js │ │ │ ├── 处理没有实参的自执行函数.js │ │ │ ├── 对同一节点使用多个方法.js │ │ │ ├── 条件表达式拆分为if语句.js │ │ │ ├── 构造节点.js │ │ │ ├── 节点类型转换.js │ │ │ ├── 还原Array对象.js │ │ │ ├── 还原object对象.js │ │ │ ├── 还原定义的字面量.js │ │ │ ├── 还原成中文字符.js │ │ │ ├── 还原自执行函数的实参.js │ │ │ └── 逗号表达式.js │ │ ├── for_swith.js │ │ └── lx.js │ └── readme.md ├── 3.3 加密参数定位方法 │ └── 3.3.7 注入和Hook │ │ └── readme.md ├── 3.4 常见的压缩和混淆 │ ├── 3.4.1 webpack导出 │ │ ├── readme.md │ │ ├── webpack-export.js │ │ ├── 案例一.js │ │ ├── 案例三.js │ │ └── 案例二.js │ └── 3.4.7 lsb隐写 │ │ └── lsb.py ├── 3.5 常见的编码和加密 │ ├── 3.5.6 AES │ │ ├── aes_encrypt.js │ │ └── aes_encrypt.py │ └── 3.5.7 RSA │ │ ├── rsa_encrypt.js │ │ └── rsa_encrypt.py ├── 3.6 加密参数还原与模拟 │ ├── 3.6.1 virustotal逆向入门案例 │ │ └── js3.6.1.py │ ├── 3.6.2 newrank榜单逆向案例 │ │ └── js3.6.2.py │ ├── 3.6.3 MD5加密逆向案例 │ │ └── js3.6.3.py │ ├── 3.6.4 RSA参数加密逆向案例 │ │ └── js3.6.4.py │ ├── 3.6.5 AES数据加密逆向案例 │ │ └── run.py │ ├── 3.6.6 AES链接加密逆向案例 │ │ └── js3.6.6 │ │ │ ├── js3.6.6.py │ │ │ ├── node_modules │ │ │ ├── .package-lock.json │ │ │ └── crypto-js │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── aes.js │ │ │ │ ├── bower.json │ │ │ │ ├── cipher-core.js │ │ │ │ ├── core.js │ │ │ │ ├── crypto-js.js │ │ │ │ ├── docs │ │ │ │ └── QuickStartGuide.wiki │ │ │ │ ├── enc-base64.js │ │ │ │ ├── enc-base64url.js │ │ │ │ ├── enc-hex.js │ │ │ │ ├── enc-latin1.js │ │ │ │ ├── enc-utf16.js │ │ │ │ ├── enc-utf8.js │ │ │ │ ├── evpkdf.js │ │ │ │ ├── format-hex.js │ │ │ │ ├── format-openssl.js │ │ │ │ ├── hmac-md5.js │ │ │ │ ├── hmac-ripemd160.js │ │ │ │ ├── hmac-sha1.js │ │ │ │ ├── hmac-sha224.js │ │ │ │ ├── hmac-sha256.js │ │ │ │ ├── hmac-sha3.js │ │ │ │ ├── hmac-sha384.js │ │ │ │ ├── hmac-sha512.js │ │ │ │ ├── hmac.js │ │ │ │ ├── index.js │ │ │ │ ├── lib-typedarrays.js │ │ │ │ ├── md5.js │ │ │ │ ├── mode-cfb.js │ │ │ │ ├── mode-ctr-gladman.js │ │ │ │ ├── mode-ctr.js │ │ │ │ ├── mode-ecb.js │ │ │ │ ├── mode-ofb.js │ │ │ │ ├── package.json │ │ │ │ ├── pad-ansix923.js │ │ │ │ ├── pad-iso10126.js │ │ │ │ ├── pad-iso97971.js │ │ │ │ ├── pad-nopadding.js │ │ │ │ ├── pad-pkcs7.js │ │ │ │ ├── pad-zeropadding.js │ │ │ │ ├── pbkdf2.js │ │ │ │ ├── rabbit-legacy.js │ │ │ │ ├── rabbit.js │ │ │ │ ├── rc4.js │ │ │ │ ├── ripemd160.js │ │ │ │ ├── sha1.js │ │ │ │ ├── sha224.js │ │ │ │ ├── sha256.js │ │ │ │ ├── sha3.js │ │ │ │ ├── sha384.js │ │ │ │ ├── sha512.js │ │ │ │ ├── tripledes.js │ │ │ │ └── x64-core.js │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── test.js │ └── 3.6.7 cnvd加速乐分析案例 │ │ ├── run.py │ │ ├── t_md5.js │ │ ├── t_sha1.js │ │ └── t_sha256.js ├── 3.7 浏览器环境补充 │ ├── 3.7.0 浏览器环境补充 │ │ ├── js-hook.txt │ │ ├── readme.md │ │ ├── t0 浏览器指纹解读.md │ │ ├── t1.js │ │ ├── t2.js │ │ ├── t3.js │ │ └── t4.js │ └── 3.7.3 selenium环境模拟 │ │ └── test.py └── 3.9 加密方法远程调用 │ ├── 3.9.0 加密方法远程调用 │ ├── readme.md │ └── 头条系web-RPC.md │ ├── 3.9.1 微博登陆参数RPC │ ├── client.js │ └── server.py │ ├── 3.9.2 抖音直播数据RPC │ ├── 2022-05-25更新.md │ ├── client.js │ ├── readme.md │ └── server.py │ └── 3.9.3 巨量指数签名RPC │ ├── juliang_index20231108.py │ ├── juliang_index_0.py │ ├── juliang_index_1.py │ └── readme.md ├── 第九章:安卓逆向案例 ├── 9.0 书外新增案例 │ ├── readme.md │ ├── 趣头条sign.md │ └── 飞瓜sign.md ├── 9.1 某新闻加密参数分析和还原 │ └── frida1.py ├── 9.2 某瓣签名Frida还原 │ ├── frida1.py │ └── run.py ├── 9.4 某图参数Frida+Flask RPC │ └── run.py ├── 9.5 某东加密参数Unidbg生成 │ ├── lxServer.zip │ ├── readme.md │ └── unidbg-jd-sign-11.zip └── 9.6 某资讯加固脱壳和参数分析 │ ├── hook.py │ └── rpc.py ├── 第八章:抓包技巧汇总 ├── heytap软件商店抓包.md ├── readme.md ├── 夜神安卓7导入charles证书.md ├── 某物app抓包.md └── 点评app抓包案例.md ├── 第六章:安卓逆向 ├── Frida │ ├── Frida基本方法.md │ ├── Frida自吐算法.js │ ├── Frida自吐算法.py │ └── Frida过Root检测.js ├── Unidbg │ └── xgorgon.java └── 抖音 │ ├── libcms.so │ └── xposedhook.java ├── 第十一章:反爬虫补充 ├── 11.1 css动态字体反爬 │ └── 选哪儿网动态字体.py ├── 11.2 tls指纹识别 │ ├── aiohttp_ja3.py │ ├── requests_jar3.py │ └── scrapy_Ja3_download.py └── 11.3 http2 │ └── scrapy_http2_download.py ├── 第十章:验证码识别技术 ├── 10.2.1.1 邮箱滑块验证码.py ├── 10.2.1.2 邮箱滑块验证码.py └── 10.2.2 数美滑块验证码.py ├── 第四章:自动化工具的应用 ├── 4.2.4 Pyppeteer拦截器 │ ├── test.js │ └── test.py ├── 4.3.5 cefpython3爬虫实战 │ └── cefpython_demo.py └── 4.7.3 autojs指数查询案例 │ ├── main.js │ └── test.js └── 附录(一些经验) ├── readme.md ├── 检索技巧.md └── 面试之谈.md /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Default ignored files 3 | /workspace.xml -------------------------------------------------------------------------------- /.idea/crawler-guide-code.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 《爬虫逆向进阶实战》书籍代码库 (视频教程背面扫码!) 3 | 4 | 书籍内容介绍:http://t.csdn.cn/Y1wsV (视频教程在书背面扫码!) 5 | 6 | 官方旗舰店:https://item.jd.com/10050499209765.html 7 | 8 | ![爬虫逆向进阶实战](https://img-blog.csdnimg.cn/de25338bca1f45cebfe0acf8a81d1438.png?) 9 | 10 | 11 | ## 更新说明 12 | 13 | 01、(2022-05-23)因为某些在线工具下架了,给大家做了一个:http://cnlans.com/lx/tools 14 | 15 | 02、(2022-05-25)案例3.9.2,网站调整为wss协议,最新教程和代码已更新。 16 | 17 | 03、(2022-06-01)案例3.9.3,网站请求接口调整,最新教程和代码已更新。 18 | 19 | 04、(2022-06-02)新增3.9.0,新增附录,增加书外案例。 20 | 21 | 05、(2022-06-06)案例更新的代码在本代码库中,更新的视频在b站:[考古学家lx](https://space.bilibili.com/390499740)。 22 | 23 | 06、(2022-06-12)更新视频教程:[webpack逆向案例一](https://www.bilibili.com/video/BV1QS4y1i7vp?spm_id_from=333.999.0.0) 24 | 25 | 07、(2022-06-16)更新10.2.1代码。[邮箱滑块验证](https://www.bilibili.com/video/BV1ZB4y1W7nS) 26 | 27 | 08、(2022-06-18)增加案例 [某图片混淆的滑块协议验证](https://www.bilibili.com/video/BV16L4y1N7kk?)、[某站反调试和加解密](https://www.bilibili.com/video/BV1iB4y1S7Pk?) 28 | 29 | 09、(2022-07-14)增加第八章及部分内容。 30 | 31 | 10、(2022-07-23)增加视频教程:[webpack逆向案例二](https://www.bilibili.com/video/BV1Da411M7k7?spm_id_from=333.999.0.0) 32 | 33 | 11、(2022-07-24)增加视频教程:[webpack逆向案例三](https://www.bilibili.com/video/BV1MB4y1h7nK/?spm_id_from=333.999.0.0) 34 | 35 | ## 加群 36 | 37 | 可关注 **《pythonlx》公众号** 获取群聊二维码,购书的同学可以扫码背面二维码加客服进入读者群。 38 | 39 | 代码库中有需要补充的内容可以提Issues或者加群联系我。 40 | 41 | python、java、nodejs等环境大家自己安装一下。 42 | 43 | 44 | 45 | # 案例涉及的APK 46 | 47 | 一些APK的下载链接: https://pan.baidu.com/s/1odDoOh2JKlpF5t7MmH1QJg?pwd=n7ug 48 | 49 | # 逆向相关工具 50 | 51 | ### 查壳反编译 52 | 53 | - [查壳小工具](https://pan.baidu.com/s/1s1BoElAyQCnPaxb2T3QpEw?pwd=tmbs) 54 | 55 | - [AndroidKiller](https://down.52pojie.cn/Tools/Android_Tools/) 56 | 57 | - [Apktools2.5.0](https://pan.baidu.com/s/12qB4N_2Fg-IsTB2BcQuiDw?pwd=gjqs) 58 | 59 | - [超级Jadx](https://pan.baidu.com/s/1SHsJGfnGJJmcPfgcC_lnYA?pwd=9999) 60 | 61 | - [IDAPro7.0 调试工具Windows版本](https://pan.baidu.com/s/1_-PorRCwHDMpmUI1t_cKcQ?pwd=t39m) 62 | 63 | - [ddms](https://pan.baidu.com/s/1wdsZvTA-fAZ12o53Exw80A?pwd=wk3d) 64 | 65 | - [JEB3.0中文版](https://pan.baidu.com/s/1kCjw8dP9tq7kLBWkublHag?pwd=k2s4) 66 | 67 | - [JEB2.3.7](https://pan.baidu.com/s/1HgyyEomL72jLWY1XMtHv8g?pwd=zpha) 68 | 69 | ### 脱壳工具 70 | 71 | - [FDex2](https://pan.baidu.com/s/1e0zcp1IzA-u7UC-A3gaj8g?pwd=yds2) 72 | 73 | - [反射大师](https://pan.baidu.com/s/170oS04qoFdd-Btu9DanHfg?pwd=an39) 74 | 75 | - [BlackDex3.1.0](https://pan.baidu.com/s/18gijmyy5dgUCbwi-hnqtpg?pwd=433u) 76 | 77 | - [DumpDex](https://github.com/WrBug/dumpDex) 78 | 79 | - [FRIDA-DEXDump](https://github.com/hluwa/FRIDA-DEXDump) 80 | 81 | ### HOOK工具 82 | 83 | - [Xposed](https://pan.baidu.com/s/15WnJD8qj9UzSss55DWLNfA?pwd=7sgb) 84 | 85 | - [VAExposed](https://pan.baidu.com/s/1fd0r2fy4mm4jUArGE4MZvA?pwd=mu9q) 86 | 87 | - [Inspeckage](https://pan.baidu.com/s/1WfnVM7hKE76jNpQc3FnKWg?pwd=pvcs) 88 | 89 | - [SSLUnpinning 20](https://pan.baidu.com/s/1EZuv-JK0a-TLHhw4v6SkvQ?pwd=dsfj) 90 | 91 | 92 | ### 抓包工具 93 | 94 | - [httpCanary 安卓抓包工具](https://pan.baidu.com/s/1mdHHaXulnsM6Zxf335yMHA?pwd=tfhx) 95 | 96 | - [Postern安卓抓包工具](https://pan.baidu.com/s/1A-2kIVnYSxpgHqiDn4mqnw?pwd=1e5k) 97 | 98 | - [Drony_113](https://pan.baidu.com/s/14d6ezZXRWDQayL73d2E8gw?pwd=tyk7) 99 | 100 | - [HttpAnalyzerStd V7](https://pan.baidu.com/s/1p3ThL5yqqc5XwTrDdmmGCg?pwd=x9hg) 101 | 102 | - [fridaManager](https://pan.baidu.com/s/1u_P2P_kd_H2n2SYTaLB0hA?pwd=jovi) 103 | 104 | - [AppSignGet](https://pan.baidu.com/s/1_j2QTVFD6qHP3FKp_FVeCw?pwd=6qmu) 105 | 106 | 107 | ## chrome插件 108 | - [request-hook](https://pan.baidu.com/s/1OmMiE4rJrTNwarw3EJbz0A?pwd=thyl) 109 | 110 | - [Trace-dist](https://github.com/L018/Trace) 111 | 112 | 113 | 114 | ### 微信小程序 115 | 116 | - [UnpackMiniApp](https://pan.baidu.com/s/1dwUehOAnPka9eHjXN6Y-Lg?pwd=unp7) 117 | 118 | - [CrackMinApp](https://github.com/Cherrison/CrackMinApp) 119 | 120 | 121 | 122 | 123 | # 其他常用工具 124 | 125 | - [FontCreator英文版](https://pan.baidu.com/s/1Ek34ePZpJYTkmiCuKsqIMQ?pwd=hnku) 126 | 127 | - [鬼鬼JS调试工具](https://pan.baidu.com/s/1hjdgx3DOTJMp0wtYGAa67A?pwd=1s67) 128 | 129 | - [MT 管理器](https://pan.baidu.com/s/1AfBDHVvini4bweDOD9GoIw?pwd=9999) 130 | 131 | - [NP 管理器](https://pan.baidu.com/s/1X5g8loORq_WS0HLqeasLbg?pwd=9jk7) 132 | 133 | - [Autojs](https://pan.baidu.com/s/1bbjFWMjFU5m2RupRyIZcGw?pwd=4ikp) 134 | 135 | 136 | # 访问统计 137 | 138 | ![Visitor Count](https://profile-counter.glitch.me/lixi5338619/count.svg) 139 | -------------------------------------------------------------------------------- /update.txt: -------------------------------------------------------------------------------- 1 | 2 | ## Unable to access ... 443: Timed out 3 | 4 | ## OpenSSL SSL_connect: Connection was reset in connection to github.com:443 5 | 6 | 取消全局解决: 7 | 8 | git config --global --unset http.proxy 9 | 10 | git config --global --unset https.proxy 11 | 12 | 给pycharm挂上vpn再push 13 | 14 | 15 | 或者当前项目设置代理:(global,local) 16 | 17 | git config --local http.proxy 127.0.0.1:15732 18 | git config --local https.proxy 127.0.0.1:15732 19 | 20 | 21 | ## Unable to access xxx OpenSSL SSL_read: Connection was reset, errno 10054 22 | 23 | git config --global http.sslBackend "openssl" 24 | 25 | --- 26 | 27 | git config --global -l 28 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.1 万方protobuf请求案例/js3.10.2.1.py: -------------------------------------------------------------------------------- 1 | { 2 | "currentPage": 1, 3 | "pageSize": 20, 4 | "searchFilter": [0], 5 | "searchScope": 0, 6 | "searchSort": None, 7 | "searchType": "paper", 8 | "searchWord": "lxlx" 9 | } 10 | 11 | import s_pb2 as pb 12 | search_request = pb.SearchService.SearchRequest() 13 | search_request.commonrequest.searchType = "paper" 14 | search_request.commonrequest.searchWord = 'lxlx' 15 | search_request.commonrequest.searchScope = 0 16 | search_request.commonrequest.currentPage = 1 17 | search_request.commonrequest.pageSize = 20 18 | search_request.commonrequest.searchFilter.append(0) 19 | bytes_body = search_request.SerializeToString() 20 | bytes_head = bytes([0, 0, 0, 0, len(bytes_body)]) 21 | print((bytes_body+bytes_head).decode()) 22 | 23 | import requests,re 24 | url = 'https://s.wanfangdata.com.cn/SearchService.SearchService/search?' 25 | headers = { 26 | 'Content-Type': 'application/grpc-web+proto', 27 | } 28 | resp = requests.post(url=url,data=bytes_head+bytes_body,headers=headers) 29 | print(resp.text) 30 | 31 | # 解析可参考 parent_sample.py -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.1 万方protobuf请求案例/parent_sample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : lx 3 | 4 | # 专利详情页解析示例 5 | 6 | import requests 7 | import s_parent_pb2 as pb 8 | from lxpy import copy_headers_dict 9 | 10 | parent_id ='ChJQYXRlbnROZXdTMjAyMjAzMjMSEENOMjAyMDEwNjYxNDM1LjYaCGZ6YW1hbTNw' 11 | 12 | search_request = pb.SearchService2.CommonRequest() 13 | search_request.resourcetype = "Patent" 14 | search_request.id = parent_id 15 | search_request.referer = "" 16 | search_request.md5id = "" 17 | search_request.transaction = "" 18 | bytes_body = search_request.SerializeToString() 19 | bytes_head = bytes([0, 0, 0, 0, len(bytes_body)]) 20 | data=bytes_head+bytes_body 21 | 22 | url = 'https://d.wanfangdata.com.cn/Detail.DetailService/getDetailInFormation' 23 | patent_headers = copy_headers_dict(''' 24 | accept: */* 25 | accept-encoding: gzip, deflate, br 26 | accept-language: zh-CN,zhq=0.9 27 | cache-control: no-cache 28 | content-type: application/grpc-web+proto 29 | cookies: CASTGC=CASTGCSpecial= 30 | origin: https://d.wanfangdata.com.cn 31 | pragma: no-cache 32 | referer: https://d.wanfangdata.com.cn/patent/ChJQYXRlbnROZXdTMjAyMjAzMjMSEENOMjAyMDEwNjYxNDM1LjYaCGZ6YW1hbTNw 33 | sec-ch-ua: " Not ABrand"v="99", "Chromium"v="99", "Google Chrome"v="99" 34 | sec-ch-ua-mobile: ?0 35 | sec-ch-ua-platform: "Windows" 36 | sec-fetch-dest: empty 37 | sec-fetch-mode: cors 38 | sec-fetch-site: same-origin 39 | user-agent: Mozilla/5.0 (Windows NT 10.0 Win64 x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 40 | x-grpc-web: 1 41 | x-user-agent: grpc-web-javascript/0.1 42 | ''') 43 | detail_resp = requests.post(url=url,data=data,headers=patent_headers).content 44 | 45 | from blackboxprotobuf import protobuf_to_json 46 | import struct,json 47 | data_len = struct.unpack(">i", detail_resp[1:5])[0] 48 | data = json.loads(protobuf_to_json(detail_resp[5: 5 + data_len])[0])['1']['5'] 49 | print(data) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.1 万方protobuf请求案例/protoc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lixi5338619/lxBook/07d632eef3f7eab7258e83d22c9d9afa913c27d3/第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.1 万方protobuf请求案例/protoc.exe -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.1 万方protobuf请求案例/s.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message SearchService { 4 | enum SearchScope { 5 | A = 0; 6 | } 7 | enum SearchFilter { 8 | B = 0; 9 | } 10 | 11 | message CommonRequest { 12 | string searchType = 1; 13 | string searchWord = 2; 14 | int32 currentPage = 3; 15 | int32 pageSize = 4; 16 | SearchScope searchScope = 5; 17 | repeated SearchFilter searchFilter = 6; 18 | } 19 | 20 | message SearchRequest { 21 | CommonRequest commonrequest = 1; 22 | } 23 | } -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.2 抖音protobuf解析案例/js.py: -------------------------------------------------------------------------------- 1 | from google.protobuf.json_format import MessageToDict 2 | from s_pb2 import * 3 | import base64 4 | 5 | def on_message(data): 6 | danmu_resp = test() 7 | danmu_resp.ParseFromString(data) 8 | Message = MessageToDict(danmu_resp, preserving_proto_field_name=True) 9 | for message in Message["message"]: 10 | method = message["method"] 11 | payload = bytes(base64.b64decode(message["payload"].encode())) 12 | if method == "WebcastMemberMessage": 13 | menber_message = WebcastMemberMessage() 14 | menber_message.ParseFromString(payload) 15 | mes= MessageToDict(menber_message, preserving_proto_field_name=True) 16 | print(mes) 17 | elif method == "WebcastLikeMessage": 18 | menber_message = WebcastLikeMessage() 19 | menber_message.ParseFromString(payload) 20 | mes = MessageToDict(menber_message, preserving_proto_field_name=True) 21 | print(mes) 22 | 23 | with open('s.txt','rb') as f: 24 | data = f.read() 25 | on_message(data) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.2 抖音protobuf解析案例/protoc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lixi5338619/lxBook/07d632eef3f7eab7258e83d22c9d9afa913c27d3/第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.2 抖音protobuf解析案例/protoc.exe -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.2 抖音protobuf解析案例/s.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message test{ 4 | repeated Message message =1; 5 | string cursor =2; 6 | uint64 fetchInterval=3; 7 | uint64 now=4; 8 | string internalExt=5; 9 | int32 fetchType=6; 10 | } 11 | 12 | message Message{ 13 | string method=1; 14 | bytes payload=2; 15 | uint64 msgId=3; 16 | int32 msgType=4; 17 | uint64 offset=5; 18 | int64 rankScore = 7; 19 | int64 topUserNo = 8; 20 | int64 enterType = 9; 21 | int64 action = 10; 22 | int64 userId = 12; 23 | string popStr = 14; 24 | } 25 | 26 | message WebcastMemberMessage{ 27 | repeated Common common=1; 28 | repeated User user=2; 29 | uint64 memberCount=3; 30 | repeated User operator=4; 31 | bool isSetToAdmin=5; 32 | bool isTopUser=6; 33 | } 34 | 35 | message WebcastLikeMessage{ 36 | repeated Common common=1; 37 | uint64 count=2; 38 | uint64 total=3; 39 | uint64 color=4; 40 | repeated User user=5; 41 | string icon=6; 42 | } 43 | 44 | message WebcastChatMessage{ 45 | repeated Common common=1; 46 | repeated User user=2; 47 | string content=3; 48 | bool visibleToSender = 4; 49 | string fullScreenTextColor = 6; 50 | } 51 | 52 | message WebcastGiftMessage{ 53 | repeated Common common=1; 54 | uint64 giftId=2; 55 | uint64 fanTicketCount=3; 56 | uint64 groupCount=4; 57 | uint64 repeatCount=5; 58 | uint64 comboCount=6; 59 | repeated User user=7; 60 | repeated User toUser=8; 61 | } 62 | 63 | message Common{ 64 | string method=1; 65 | uint64 msgId=2; 66 | uint64 roomId=3; 67 | uint64 createTime=4; 68 | int32 monitor=5; 69 | bool isShowMsg=6; 70 | string describe=7; 71 | uint64 foldType=9; 72 | } 73 | 74 | message User{ 75 | int64 id = 1; 76 | int64 shortId = 2; 77 | string nickname = 3; 78 | int32 gender = 4; 79 | string signature = 5; 80 | int32 level = 6; 81 | int64 birthday = 7; 82 | string telephone = 8; 83 | bool verified = 12; 84 | int32 experience = 13; 85 | string city = 14; 86 | int32 status = 15; 87 | int64 createTime = 16; 88 | int64 modifyTime = 17; 89 | int32 secret = 18; 90 | string shareQrcodeUri = 19; 91 | int32 incomeSharePercent = 20; 92 | } -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.2 抖音protobuf解析案例/s.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lixi5338619/lxBook/07d632eef3f7eab7258e83d22c9d9afa913c27d3/第三章:Web Js逆向/3.10 常见协议分析/3.10.2 protobuf协议/3.10.2.2 抖音protobuf解析案例/s.txt -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast.js: -------------------------------------------------------------------------------- 1 | const parser = require("@babel/parser"); 2 | const template = require("@babel/template").default; 3 | const traverse = require("@babel/traverse").default; 4 | const t = require("@babel/types"); 5 | const generator = require("@babel/generator").default; 6 | const fs = require("fs"); 7 | const path = require('path'); 8 | // lx.js 是被混淆的js文件 9 | var jscode = fs.readFileSync("lx.js", { encoding: "utf-8"}); 10 | 11 | const visitor_string = { 12 | 'StringLiteral|NumericLiteral'(path) { 13 | delete path.node.extra 14 | } 15 | }; 16 | 17 | const visitor_number = { 18 | 'UnaryExpression'(path) { 19 | const {value} = path.evaluate(); 20 | switch (typeof value) { 21 | case 'boolean': 22 | path.replaceWith(t.BooleanLiteral(value)) 23 | break; 24 | case 'string': 25 | path.replaceWith(t.StringLiteral(value)) 26 | break; 27 | case 'number': 28 | path.replaceWith(t.NumericLiteral(value)) 29 | break; 30 | default: 31 | break; 32 | } 33 | } 34 | } 35 | const visitor_function = { 36 | MemberExpression(path) { 37 | let property = path.get('property') 38 | if (property.isStringLiteral()) { 39 | let value = property.node.value; 40 | path.node.computed = false 41 | property.replaceWith(t.Identifier(value)) 42 | } 43 | } 44 | }; 45 | const visitor_del_cons = 46 | { 47 | VariableDeclarator(path) { 48 | const {id} = path.node; 49 | const binding = path.scope.getBinding(id.name); 50 | if (!binding || binding.constantViolations.length > 0) { 51 | return; 52 | } 53 | if (binding.referencePaths.length === 0) { 54 | path.remove(); 55 | } 56 | }, 57 | } 58 | 59 | const visitor_eval = 60 | { 61 | CallExpression(path) 62 | { 63 | let {callee,arguments} = path.node; 64 | if (!t.isIdentifier(callee,{name:'eval'})) return; 65 | if (arguments.length !== 1 || !t.isStringLiteral(arguments[0])) return; 66 | let value = arguments[0].value; 67 | path.replaceWith(t.Identifier(value)); 68 | }, 69 | } 70 | 71 | // 将JS源码转换成语法树 72 | let ast = parser.parse(jscode); 73 | traverse(ast, visitor_string); //识别字符串 74 | traverse(ast, visitor_number); //计算表达式 !![] -> true 75 | traverse(ast, visitor_function); //将a["length"]转为a.length 76 | traverse(ast, visitor_del_cons); //删除未被调用的变量 77 | traverse(ast, visitor_eval); //处理eval函数 78 | let {code} = generator(ast, {jsescOption: {"minimal": true}}); 79 | fs.writeFile('lx_decoded.js', code, (err) => {}); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/readme.md: -------------------------------------------------------------------------------- 1 | 补充部分的内容收集于网络,内容并未出现在书籍中,如有侵权请联系作者进行删除。 2 | 3 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/while-if转变为while-switch.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = 9 | ` 10 | function test() { 11 | var index = 0, 12 | arr = [3, 0, 2, 1], 13 | pindex; 14 | 15 | while (1) { 16 | pindex = arr[index++]; 17 | 18 | if (pindex < 1) { 19 | console.log("this is index 0"); 20 | } else if (pindex < 2) { 21 | console.log("this is index 1"); 22 | return; 23 | } else if (pindex < 3) { 24 | console.log("this is index 2"); 25 | } else { 26 | console.log("Hello world!"); 27 | } 28 | } 29 | } 30 | `; 31 | 32 | let ast = parse(jscode); 33 | 34 | const visitor = 35 | { 36 | "WhileStatement"(path) { 37 | let {test, body} = path.node; 38 | 39 | //******************************************************特征判断开始 40 | if (!t.isNumericLiteral(test, {value: 1})) return; 41 | 42 | let block_body = body.body; 43 | 44 | if (block_body.length !== 2 || !t.isExpressionStatement(block_body[0]) || !t.isIfStatement(block_body[1])) { 45 | return; 46 | } 47 | //******************************************************特征判断结束 48 | 49 | let {left, right} = block_body[0].expression; 50 | let name = left.name; 51 | 52 | let if_arrs = []; 53 | path.traverse({ 54 | "IfStatement"(_path) { 55 | 56 | let {test, consequent, alternate} = _path.node; 57 | 58 | let {left, operator, right} = test; 59 | 60 | if (!t.isIdentifier(left, {name: name}) || operator !== '<' || !t.isNumericLiteral(right)) return; 61 | 62 | if (consequent.body.length === 1) { 63 | if_arrs[right.value - 1] = consequent.body[0]; 64 | } else { 65 | if_arrs[right.value - 1] = consequent; 66 | } 67 | 68 | if (!t.isIfStatement(alternate)) { 69 | if (consequent.body.length === 1) { 70 | if_arrs[right.value] = alternate.body[0]; 71 | } else { 72 | if_arrs[right.value] = alternate; 73 | } 74 | } 75 | }, 76 | }) 77 | 78 | if (if_arrs.length === 0) return; 79 | 80 | for (var i = 0; i < if_arrs.length - 1; i++) { 81 | consequent = [if_arrs[i], t.BreakStatement()]; 82 | if_arrs[i] = t.SwitchCase(test = t.NumericLiteral(i), consequent = consequent); 83 | } 84 | 85 | consequent = [if_arrs[if_arrs.length - 1], t.BreakStatement()]; 86 | if_arrs[i] = t.SwitchCase(test = null, consequent = consequent); 87 | 88 | let new_node = t.SwitchStatement(right, if_arrs); 89 | 90 | path.get('body.body.1').replaceInline(new_node); 91 | path.get('body.body.0').remove(); 92 | }, 93 | } 94 | 95 | 96 | //some function code 97 | 98 | 99 | traverse(ast, visitor); 100 | let {code} = generator(ast); 101 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/三目运算符.js: -------------------------------------------------------------------------------- 1 | /* 2 | desc : 把 a = m?11:22; 转成 m ? a = 11 : a = 22; 3 | */ 4 | 5 | const fs = require('fs'); 6 | const {parse} = require("@babel/parser"); 7 | const traverse = require("@babel/traverse").default; 8 | const t = require("@babel/types"); 9 | const generator = require("@babel/generator").default; 10 | 11 | let jscode = "a = m ? 11 : 22;"; 12 | 13 | let ast = parse(jscode); 14 | 15 | const visitor = 16 | { 17 | ConditionalExpression(path){ 18 | let {test, consequent, alternate} = path.node; 19 | const ParentPath = path.parentPath; 20 | if(t.isAssignmentExpression(ParentPath)){ 21 | let {operator, left} = ParentPath.node; 22 | if (operator === '='){ 23 | consequent = t.AssignmentExpression('=', left, consequent); 24 | alternate = t.AssignmentExpression('=', left, alternate); 25 | ParentPath.replaceWith(t.ConditionalExpression(test, consequent, alternate)) 26 | } 27 | } 28 | } 29 | } 30 | 31 | //some function code 32 | 33 | traverse(ast,visitor); 34 | let {code} = generator(ast); 35 | console.log(code); 36 | 37 | /* 38 | 39 | 1.节点类型变了,由 AssignmentExpression 类型变成了ConditionalExpression 类型。 40 | 2.ConditionalExpression 子节点的 consequent 和 alternate 都变成了 AssignmentExpression 类型。 41 | 42 | */ 43 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/函数调用处自动替换计算值.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | const fs = require('fs'); 5 | const {parse} = require("@babel/parser"); 6 | const traverse = require("@babel/traverse").default; 7 | const t = require("@babel/types"); 8 | const generator = require("@babel/generator").default; 9 | 10 | let jscode = "function add(a, b) {\n" + 11 | " return a + b;\n" + 12 | "}\n" + 13 | "\n" + 14 | "let c = add(1, 2);"; 15 | 16 | let ast = parse(jscode); 17 | 18 | const visitor = 19 | { 20 | FunctionDeclaration(path) { 21 | let {id} = path.node; 22 | let code = path.toString(); 23 | if (code.indexOf("try") !== -1 || code.indexOf("random") !== -1 || code.indexOf("Date") !== -1) { 24 | // 不是纯函数,不处理 25 | return 26 | } 27 | 28 | eval(code); 29 | 30 | let scope = path.scope; 31 | const binding = path.scope.parent.getBinding(id.name); 32 | 33 | if (!binding || binding.constantViolations.length > 0) { 34 | return 35 | } 36 | 37 | for (const refer_path of binding.referencePaths) { 38 | // 查找父节点 39 | let call_express = refer_path.findParent(p => p.isCallExpression()); 40 | 41 | let arguments = call_express.get("arguments"); 42 | let args = []; 43 | 44 | // 判断参数是否为 Literal 类型 45 | arguments.forEach(arg => { 46 | args.push(arg.isLiteral()) 47 | }) 48 | 49 | // 自行编写判断条件,example 50 | if (args.length === 0 || args.indexOf(false) !== -1) { 51 | continue 52 | } 53 | 54 | try { 55 | // 计算值 56 | let value = eval(call_express.toString()); 57 | value && call_express.replaceWith(t.valueToNode(value)); 58 | } catch (e) { 59 | 60 | } 61 | } 62 | 63 | } 64 | } 65 | 66 | 67 | //some function code 68 | 69 | 70 | traverse(ast, visitor); 71 | let {code} = generator(ast); 72 | console.log(code); 73 | 74 | 75 | /* 76 | 1.函数需要满足在任意位置都能执行。就是一个函数声明的代码,随便拷贝到任意的地方都能直接运行,不会报错。说白了其实也就是函数体内的所有变量或者对象,其作用域只在函数体里面。 77 | 2.定义的函数,对于实参是固定的,其结果也是固定的。请大家自行百度 纯函数 的概念 78 | 3.实参必须是字面量,因为对于遍历的节点来说。只有是字面量,才能给你计算出具体的字,如果不是字面量,实参对它来说就是 undefined 的,是无法计算的。 79 | 4.将函数定义eval到本地环境,然后根据作用域找到函数调用的位置。再eval该表达式即可。注意需要使用try...catch语句,避免错误。 80 | */ 81 | 82 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/删除多余的空行和空语句.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = "var a = 123;\n" + 9 | "\n" + 10 | ";\n" + 11 | "\n" + 12 | "var b = 456;"; 13 | 14 | let ast = parse(jscode); 15 | 16 | const visitor = 17 | { 18 | EmptyStatement(path) 19 | { 20 | path.remove(); 21 | }, 22 | } 23 | 24 | 25 | //some function code 26 | 27 | 28 | traverse(ast,visitor); 29 | let {code} = generator(ast); 30 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/删除所有的代码注释.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const fs = require('fs'); 4 | const {parse} = require("@babel/parser"); 5 | const traverse = require("@babel/traverse").default; 6 | const t = require("@babel/types"); 7 | const generator = require("@babel/generator").default; 8 | 9 | let jscode = "var a = 123; //this is single line comment\n" + 10 | "\n" + 11 | "/*\n" + 12 | "\n" + 13 | "This is a multiline comments;\n" + 14 | "\n" + 15 | "test\n" + 16 | "\n" + 17 | "test\n" + 18 | "\n" + 19 | "test\n" + 20 | "\n" + 21 | "*/\n" + 22 | "\n" + 23 | "var b = 456;"; 24 | 25 | let ast = parse(jscode); 26 | 27 | const visitor = 28 | { 29 | //TODO write your code here! 30 | } 31 | 32 | 33 | //some function code 34 | 35 | 36 | traverse(ast,visitor); 37 | let {code} = generator(ast, {comments:false}); 38 | console.log(code); 39 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/删除未被使用的变量.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = "var a = 123,b;\n" + 9 | "\n" + 10 | "let c = 4 + 5;\n" + 11 | "\n" + 12 | "d = a + 12;"; 13 | 14 | let ast = parse(jscode); 15 | 16 | const visitor = 17 | { 18 | VariableDeclarator(path) { 19 | 20 | const {id} = path.node; 21 | const binding = path.scope.getBinding(id.name); 22 | 23 | //如果变量被修改过,则不能进行删除动作。 24 | if (!binding || binding.constantViolations.length > 0) { 25 | return; 26 | } 27 | 28 | //长度为0,说明变量没有被使用过。 29 | if (binding.referencePaths.length === 0) { 30 | path.remove(); 31 | } 32 | }, 33 | } 34 | 35 | 36 | //some function code 37 | 38 | 39 | traverse(ast,visitor); 40 | let {code} = generator(ast); 41 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/删除未被调用的函数.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {parse} = require("@babel/parser"); 3 | const traverse = require("@babel/traverse").default; 4 | const t = require("@babel/types"); 5 | const generator = require("@babel/generator").default; 6 | 7 | let jscode = "function i()\n" + 8 | "\n" + 9 | "{\n" + 10 | "\n" + 11 | " var i = 123;\n" + 12 | "\n" + 13 | " i += 2;\n" + 14 | "\n" + 15 | " \n" + 16 | "\n" + 17 | " return 123;\n" + 18 | "\n" + 19 | "}"; 20 | 21 | let ast = parse(jscode); 22 | 23 | const visitor = 24 | { 25 | FunctionDeclaration(path) { 26 | // path.scope.dump(); 27 | 28 | const {id} = path.node; 29 | const binding = path.scope.parent.getBinding(id.name); 30 | 31 | if (!binding || binding.constantViolations.length > 0) { 32 | return; 33 | } 34 | 35 | if (binding.referencePaths.length === 0) { 36 | path.remove(); 37 | } 38 | }, 39 | } 40 | 41 | 42 | //some function code 43 | 44 | 45 | traverse(ast,visitor); 46 | let {code} = generator(ast); 47 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/去控制流(for-switch).js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = 9 | ` 10 | function test() { 11 | for (var index = 0; index != 5;) { 12 | switch (index) { 13 | case 0: 14 | console.log("This is case-block 0"); 15 | index = 3; 16 | continue; 17 | 18 | case 1: 19 | console.log("This is case-block 1"); 20 | return; 21 | index = 5; 22 | continue; 23 | 24 | case 2: 25 | console.log("This is case-block 2"); 26 | index = 1; 27 | continue; 28 | 29 | case 3: 30 | console.log("This is case-block 3"); 31 | index = 4; 32 | continue; 33 | 34 | case 4: 35 | console.log("This is case-block 4"); 36 | index = 2; 37 | continue; 38 | } 39 | 40 | break; 41 | } 42 | } 43 | `; 44 | 45 | let ast = parse(jscode); 46 | 47 | const visitor = 48 | { 49 | ForStatement(path) { 50 | //遍历for语句 51 | const { init, update, test, body } = path.node; 52 | 53 | //特征判断 54 | if ( 55 | !t.isVariableDeclaration(init) || 56 | !t.isBinaryExpression(test) || 57 | update !== null 58 | ) 59 | return; 60 | 61 | let declaration = init.declarations[0]; 62 | 63 | //获取控制循环的变量及其初始值 64 | const init_name = declaration.id.name; 65 | let init_value = declaration.init.value; 66 | let { left, right, operator } = test; 67 | 68 | //特征判断 69 | if ( 70 | !t.isIdentifier(left, { name: init_name }) || 71 | operator !== "!=" || 72 | !t.isNumericLiteral(right) 73 | ) 74 | return; 75 | 76 | let test_value = right.value; 77 | let switch_body = body.body[0]; 78 | 79 | //特征判断 80 | if (!t.isSwitchStatement(switch_body)) return; 81 | let { discriminant, cases } = switch_body; 82 | 83 | if (!t.isIdentifier(discriminant, { name: init_name })) return; 84 | let ret_body = []; 85 | let end_flag = false; 86 | 87 | //不断的拿到控制循环的变量值 88 | while (init_value !== test_value) { 89 | //如果没有与之匹配的值,直接跳出循环 90 | if (end_flag === true) { 91 | //如果遇到return语句,直接跳出循环 92 | break; 93 | } 94 | 95 | for (const each_case of cases) { 96 | let { test, consequent } = each_case; 97 | 98 | if (init_value !== test.value) { 99 | continue; 100 | } 101 | 102 | if (t.isContinueStatement(consequent[consequent.length - 1])) { 103 | //如果是continue语句,直接删除 104 | consequent.pop(); 105 | } 106 | 107 | if (t.isExpressionStatement(consequent[consequent.length - 1])) { 108 | //如果是表达式语句,则判断是否包含控制循环的变量的赋值语句 109 | let { expression } = consequent[consequent.length - 1]; 110 | if (t.isAssignmentExpression(expression)) { 111 | let { left, right, operator } = expression; 112 | if (t.isIdentifier(left, { name: init_name })) { 113 | //更新控制循环的变量值,并进行删除 114 | init_value = right.value; 115 | consequent.pop(); 116 | } 117 | } 118 | } 119 | 120 | if (t.isReturnStatement(consequent[consequent.length - 1])) { 121 | end_flag = true; 122 | } 123 | ret_body = ret_body.concat(consequent); 124 | break; 125 | } 126 | } 127 | path.replaceInline(ret_body); 128 | } 129 | 130 | } 131 | 132 | 133 | //some function code 134 | 135 | 136 | traverse(ast,visitor); 137 | let {code} = generator(ast); 138 | console.log(code); 139 | 140 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/去控制流(while-switch).js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = 9 | ` 10 | var arr = "3|0|1|2|4".split("|"); 11 | var cnt = 0; 12 | 13 | while (true) { 14 | switch (arr[cnt++]) { 15 | case "1": 16 | console.log("This is case-block 1"); 17 | continue; 18 | 19 | case "0": 20 | console.log("This is case-block 0"); 21 | continue; 22 | 23 | case "2": 24 | console.log("This is case-block 2"); 25 | continue; 26 | 27 | case "4": 28 | console.log("This is case-block 4"); 29 | continue; 30 | 31 | case "3": 32 | console.log("This is case-block 3"); 33 | continue; 34 | } 35 | 36 | break; 37 | } 38 | `; 39 | 40 | let ast = parse(jscode); 41 | 42 | const visitor = 43 | { 44 | WhileStatement(path) { 45 | const { test, body } = path.node; 46 | 47 | //特征语句判断,视情况而定,也可以不判断 48 | if (!t.isBooleanLiteral(test) || test.value !== true) return; 49 | 50 | //特征语句判断,body.body[0] 必须是 SwitchStatement 节点, 51 | //注意一定要先判断长度,避免index出错 52 | if (body.body.length === 0 || !t.isSwitchStatement(body.body[0])) return; 53 | 54 | let switch_state = body.body[0]; 55 | 56 | //获取discriminant及cases节点 57 | let { discriminant, cases } = switch_state; 58 | 59 | //特征语句判断,经过此判断后,基本可以确定是需要还原的while节点了。 60 | //如果出错了,可以继续增加判断,直到不出错即可 61 | if (!t.isMemberExpression(discriminant) || !t.isUpdateExpression(discriminant.property)) return; 62 | 63 | //获取数组名,用于查找该数组。 64 | let arr_name = discriminant.object.name; 65 | let arr = []; 66 | 67 | //在这里再加一个特征语句的判断:WhileStatement 节点前面有两个节点 68 | let all_pre_siblings = path.getAllPrevSiblings(); 69 | if (all_pre_siblings.length !== 2) return; 70 | 71 | all_pre_siblings.forEach((pre_path) => { 72 | //虽然知道是第0个节点,但这里还是做下判断取arr 73 | const { declarations } = pre_path.node; 74 | let { id, init } = declarations[0]; 75 | 76 | if (arr_name === id.name) { 77 | //如果是定义arr的节点,拿到该arr的值 78 | arr = init.callee.object.value.split("|"); 79 | } 80 | 81 | //没啥用的语句可以直接删除 82 | pre_path.remove(); 83 | }); 84 | 85 | //新建一个数组变量,用于存放 case 节点 86 | let ret_body = []; 87 | 88 | arr.forEach((index) => { 89 | //遍历数组,去case节点 90 | let case_body = []; 91 | for (const tmp of cases){ 92 | if(index === tmp.test.value){ 93 | case_body = tmp.consequent; 94 | break; 95 | } 96 | } 97 | 98 | if (t.isContinueStatement(case_body[case_body.length - 1])) { 99 | //删除 continue语句 100 | case_body.pop(); 101 | } 102 | 103 | //存放于数组变量中 104 | ret_body = ret_body.concat(case_body); 105 | }); 106 | 107 | //替换 108 | path.replaceInline(ret_body); 109 | } 110 | } 111 | 112 | 113 | //some function code 114 | 115 | 116 | traverse(ast,visitor); 117 | let {code} = generator(ast); 118 | console.log(code); 119 | 120 | 121 | /* 122 | 去控制流(while-switch), 代码不通用,存在优化空间, 123 | 比如将 case 做个键值对映射,这样直接取结果就比循环判断好一些 124 | */ 125 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/合并定义在object对象外面的key、value.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = 9 | ` 10 | var h = {}; 11 | h["aaa"] = "hello wolrd"; 12 | h["bbb"] = function (a,b) 13 | { 14 | return a | b; 15 | } 16 | `; 17 | 18 | let ast = parse(jscode); 19 | 20 | const visitor = 21 | { 22 | VariableDeclarator(path) 23 | { 24 | const {id,init} = path.node; 25 | if (!t.isObjectExpression(init)) return; 26 | let name = id.name; 27 | let properties = init.properties; 28 | let all_next_siblings = path.parentPath.getAllNextSiblings(); 29 | 30 | for (let next_sibling of all_next_siblings) 31 | { 32 | if (!next_sibling.isExpressionStatement()) break; 33 | 34 | let expression = next_sibling.get('expression'); 35 | if (!expression.isAssignmentExpression()) break; 36 | let {operator,left,right} = expression.node; 37 | 38 | if (operator !== '=' || !t.isMemberExpression(left) || !t.isIdentifier(left.object,{name:name})) 39 | { 40 | break; 41 | } 42 | properties.push(t.ObjectProperty(left.property,right)); 43 | next_sibling.remove(); 44 | } 45 | }, 46 | } 47 | 48 | 49 | //some function code 50 | 51 | 52 | traverse(ast,visitor); 53 | let {code} = generator(ast); 54 | console.log(code); 55 | 56 | 57 | /* 58 | 59 | 1.object对象使用var定义的,因此遍历 VariableDeclarator 节点即可 60 | 2.依次判断后续节点,是否为定义在外面的key和value 61 | 3.收集key和value,用于构造ObjectProperty 节点 62 | 4. properties 属性是Array对象,只用push方法来增加节点 63 | 5.处理完成后删除后续节点。 64 | 65 | */ -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/处理eval函数.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = "eval('var a = 123;');"; 9 | 10 | let ast = parse(jscode); 11 | 12 | const visitor = 13 | { 14 | CallExpression(path) 15 | { 16 | let {callee,arguments} = path.node; 17 | if (!t.isIdentifier(callee,{name:'eval'})) return; 18 | if (arguments.length !== 1 || !t.isStringLiteral(arguments[0])) return; 19 | let value = arguments[0].value; 20 | path.replaceWith(t.Identifier(value)); 21 | }, 22 | } 23 | 24 | 25 | //some function code 26 | 27 | 28 | traverse(ast,visitor); 29 | let {code} = generator(ast); 30 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/处理条件已知的if语句.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {parse} = require("@babel/parser"); 3 | const traverse = require("@babel/traverse").default; 4 | const t = require("@babel/types"); 5 | const generator = require("@babel/generator").default; 6 | 7 | // let jscode = "if (true)\n" + 8 | // "{\n" + 9 | // " //do something;\n" + 10 | // " funcA();\n" + 11 | // "}\n" + 12 | // "else\n" + 13 | // "{\n" + 14 | // " //do something;\n" + 15 | // " funcB();\n" + 16 | // "}"; 17 | 18 | let jscode = "if ('123'==='123')\n" + 19 | " a = 123;\n" + 20 | "else\n" + 21 | " a = 456;"; 22 | 23 | let ast = parse(jscode); 24 | 25 | const visitor1 = 26 | { 27 | BinaryExpression(path) { 28 | let {confident, value} = path.evaluate(); 29 | // console.log(path.type, confident, value) 30 | if (confident) { 31 | // console.log(path.node); 32 | path.replaceInline(t.valueToNode(value)) 33 | 34 | } 35 | } 36 | } 37 | 38 | const visitor2 = 39 | { 40 | IfStatement(path) { 41 | let {test, consequent, alternate} = path.node; 42 | 43 | if (!t.isBlockStatement(consequent)) {//添加中括号 44 | path.node.consequent = t.BlockStatement([consequent]); 45 | } 46 | 47 | if (alternate !== null && !t.isBlockStatement(alternate)) {//添加中括号 48 | path.node.alternate = t.BlockStatement([alternate]); 49 | } 50 | 51 | //特征判断,if语句里面的test是否为字面量 52 | if (!t.isLiteral(test)) return; 53 | 54 | let value = test.value; 55 | consequent = path.node.consequent; 56 | alternate = path.node.alternate; 57 | 58 | if (value) {//替换 59 | path.replaceInline(consequent.body); 60 | } else {//替换 61 | alternate === null ? path.remove() : path.replaceInline(alternate.body); 62 | } 63 | }, 64 | } 65 | 66 | //some function code 67 | 68 | 69 | traverse(ast, visitor1); 70 | traverse(ast, visitor2); 71 | let {code} = generator(ast); 72 | console.log(code); 73 | 74 | /* 75 | if (true) 76 | //do something; 77 | else 78 | //do something; 79 | 80 | 与 81 | if (true) 82 | { 83 | //do something; 84 | } 85 | else 86 | { 87 | //do something; 88 | } 89 | 90 | 解析出来的结构会有小小的差异,不利于处理,因此先将上面的代码转变为下面的代码,再进行处理即可 91 | 92 | 1.先将if语句块中没有 中括号的处理成 包含中括号的 93 | 2.判断if条件里面的值,获取应该执行的语句块 94 | 3.用语句块替换整个if表达式即可。 95 | 96 | */ 97 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/处理条件已知的三元表达式.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {parse} = require("@babel/parser"); 3 | const traverse = require("@babel/traverse").default; 4 | const t = require("@babel/types"); 5 | const generator = require("@babel/generator").default; 6 | 7 | let jscode = "a = 5 ? 11 : 22;\n" + 8 | "b = 0 ? 33 : 44;"; 9 | 10 | let ast = parse(jscode); 11 | 12 | const visitor = 13 | { 14 | ConditionalExpression(path) { 15 | let { test, consequent, alternate } = path.node; 16 | 17 | if (!t.isLiteral(test)) return; 18 | 19 | if (test.value) { 20 | path.replaceInline(consequent); 21 | } else { 22 | path.replaceInline(alternate); 23 | } 24 | } 25 | } 26 | 27 | //some function code 28 | 29 | traverse(ast, visitor); 30 | let {code} = generator(ast); 31 | console.log(code); 32 | 33 | // 和常见表达式的计算类似 -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/处理没有实参的自执行函数.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = "!function () {\n" + 9 | " a = 123;\n" + 10 | "}();"; 11 | 12 | let ast = parse(jscode); 13 | 14 | const visitor = { 15 | 16 | UnaryExpression(path) { 17 | const {operator, argument} = path.node; 18 | 19 | if (operator !== "!" || !t.isCallExpression(argument)) return; 20 | 21 | let {callee, arguments} = argument; 22 | 23 | if (!t.isFunctionExpression(callee) || arguments.length !== 0) return; 24 | 25 | path.replaceInline(callee.body.body); 26 | 27 | }, 28 | 29 | } 30 | 31 | 32 | //some function code 33 | 34 | 35 | traverse(ast,visitor); 36 | let {code} = generator(ast); 37 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/对同一节点使用多个方法.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const {parse} = require("@babel/parser"); 4 | const traverse = require("@babel/traverse").default; 5 | const t = require("@babel/types"); 6 | const generator = require("@babel/generator").default; 7 | 8 | let jscode = ""; 9 | 10 | let ast = parse(jscode); 11 | 12 | const visitor = 13 | { 14 | CallExpression: 15 | {// 注意顺序 16 | enter: [reduce_call_express, delete_empty_params] 17 | }, 18 | } 19 | 20 | 21 | //some function code 22 | 23 | function reduce_call_express(path) { 24 | } 25 | 26 | function delete_empty_params(path) { 27 | } 28 | 29 | traverse(ast,visitor); 30 | let {code} = generator(ast); 31 | console.log(code); 32 | 33 | /* 34 | 35 | 假设想对一个函数表达式的 CallExpression 节点进行进行实参还原, 36 | 如果还原后,没有了实参和形参,假如又可以将函数体里面的代码提取出来。 37 | 这个时候就需要两个方法来分别进行处理了 38 | 39 | reduce_call_express 还原函数的实参 40 | delete_empty_params 将参数为空的函数进行处理,将函数体里面的代码直接提取出来 41 | 42 | */ -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/条件表达式拆分为if语句.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | const fs = require('fs'); 5 | const {parse} = require("@babel/parser"); 6 | const traverse = require("@babel/traverse").default; 7 | const t = require("@babel/types"); 8 | const generator = require("@babel/generator").default; 9 | 10 | let jscode = 11 | ` 12 | r ? r > 1 ? e.apply(t, arguments) : e.call(t, n) : e.call(t); 13 | `; 14 | 15 | let ast = parse(jscode); 16 | 17 | const visitor = 18 | { 19 | ConditionalExpression(path) { 20 | let { test, consequent, alternate } = path.node; 21 | const ParentPath = path.parentPath; 22 | if (t.isExpressionStatement(ParentPath)) { 23 | if (!t.isExpressionStatement(consequent)) { 24 | consequent = t.BlockStatement([t.ExpressionStatement(consequent)]); 25 | } 26 | if (!t.isExpressionStatement(alternate)) { 27 | alternate = t.BlockStatement([t.ExpressionStatement(alternate)]); 28 | } 29 | ParentPath.replaceInline(t.IfStatement(test, consequent, alternate)); 30 | } 31 | } 32 | } 33 | 34 | 35 | //some function code 36 | 37 | 38 | traverse(ast,visitor); 39 | let {code} = generator(ast); 40 | console.log(code); 41 | 42 | /* 43 | 44 | r ? r > 1 ? e.apply(t, arguments) : e.call(t, n) : e.call(t); 45 | 46 | 47 | if (r) { 48 | if (r > 1) { 49 | e.apply(t, arguments); 50 | } else { 51 | e.call(t, n); 52 | } 53 | } else { 54 | e.call(t); 55 | } 56 | */ -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/构造节点.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 将 var a; 转换为 var a = 123 + 456; 3 | * 4 | * */ 5 | 6 | const fs = require('fs'); 7 | const {parse} = require("@babel/parser"); 8 | const traverse = require("@babel/traverse").default; 9 | const t = require("@babel/types"); 10 | const generator = require("@babel/generator").default; 11 | 12 | let jscode = "var a"; 13 | 14 | let ast = parse(jscode); 15 | 16 | const visitor = 17 | { 18 | // 方法一 19 | VariableDeclarator(path){ 20 | const {init} = path.node; 21 | let node = { 22 | type: "BinaryExpression", 23 | operator: "+", 24 | left: { 25 | type: "NumericLiteral", 26 | value: 123, 27 | }, 28 | right: { 29 | type: "NumericLiteral", 30 | value: 456, 31 | } 32 | } 33 | 34 | init || path.set("init", node) 35 | }, 36 | 37 | // 方法二 38 | /* VariableDeclarator(path){ 39 | const {init} = path.node; 40 | init || path.set("init", t.binaryExpression('+',t.valueToNode(123),t.valueToNode(456))) 41 | 42 | },*/ 43 | } 44 | 45 | 46 | 47 | //some function code 48 | 49 | 50 | traverse(ast,visitor); 51 | let {code} = generator(ast); 52 | console.log(code); 53 | 54 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/节点类型转换.js: -------------------------------------------------------------------------------- 1 | /* 2 | desc : 将 BinaryExpression 类型转换为 CallExpression 类型 3 | */ 4 | 5 | const fs = require('fs'); 6 | const {parse} = require("@babel/parser"); 7 | const traverse = require("@babel/traverse").default; 8 | const t = require("@babel/types"); 9 | const generator = require("@babel/generator").default; 10 | 11 | let jscode = "var a = 123 | 456;"; 12 | 13 | let ast = parse(jscode); 14 | 15 | const visitor = 16 | { 17 | "VariableDeclarator"(path) 18 | { 19 | const init_path = path.get('init'); 20 | if (!init_path.isBinaryExpression()) return 21 | 22 | init_path.node.type = "CallExpression"; 23 | let {operator,left,right} = init_path.node; 24 | init_path.node.arguments = [left,right]; 25 | 26 | let id = null; 27 | let frist_arg = t.Identifier('s'); 28 | let second_arg = t.Identifier('h'); 29 | let params = [frist_arg,second_arg]; 30 | 31 | let args = t.BinaryExpression(operator,frist_arg,second_arg); 32 | let return_state = t.ReturnStatement(args); 33 | 34 | let body = t.BlockStatement([return_state]); 35 | init_path.node.callee = t.FunctionExpression(id ,params,body); 36 | }, 37 | } 38 | 39 | 40 | //some function code 41 | 42 | 43 | traverse(ast,visitor); 44 | let {code} = generator(ast); 45 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/还原Array对象.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | const fs = require('fs'); 5 | const {parse} = require("@babel/parser"); 6 | const traverse = require("@babel/traverse").default; 7 | const t = require("@babel/types"); 8 | const generator = require("@babel/generator").default; 9 | 10 | let jscode = "var _2$SS = function (_SSz, _1111) {\n" + 11 | " var _l1L1 = [46222, '\x74\x61\x43\x61\x70\x74\x63\x68\x61\x42\x6c\x6f\x62', '\x74', '\x61', '\x73', '\x6c', '\x64', '\x69', .3834417654519915, '\x65\x6e\x63\x72\x79\x70\x74\x4a', '\x73\x6f', '\x6e', 49344];\n" + 12 | "\n" + 13 | " var _2Szs = _l1L1[5] + _l1L1[7] + (_l1L1[4] + _l1L1[2]),\n" + 14 | " _I1il1 = _l1L1[9] + (_l1L1[10] + _l1L1[11]);\n" + 15 | "\n" + 16 | " var _0ooQoO = _l1L1[0];\n" + 17 | " var _$Z22 = _l1L1[12],\n" + 18 | " _2sS2 = _l1L1[8];\n" + 19 | " return _l1L1[6] + _l1L1[3] + _l1L1[1];\n" + 20 | "};"; 21 | 22 | let ast = parse(jscode); 23 | 24 | const visitor = 25 | { 26 | VariableDeclarator(path){ 27 | // 还原数组对象 28 | const {id, init} = path.node; 29 | 30 | // 非Array或者没有元素, 返回 31 | if (!t.isArrayExpression(init) || init.elements.length===0) return; 32 | 33 | let elements = init.elements; 34 | 35 | // 获取binding实例 36 | const binding = path.scope.getBinding(id.name); 37 | 38 | for ( const ref_path of binding.referencePaths){ 39 | // 获取 MemberExpression 父节点 40 | let member_path = ref_path.findParent(p=>p.isMemberExpression()); 41 | let property = member_path.get('property'); 42 | 43 | // 索引值不是 NumericLiteral 类型的不处理 44 | if(!property.isNumericLiteral()){ 45 | continue; 46 | } 47 | 48 | // 获取索引值 49 | let index = property.node.value; 50 | 51 | // 获取索引值对应的节点, 并替换 52 | let arr_ele = elements[index]; 53 | member_path.replaceWith(arr_ele) 54 | } 55 | } 56 | } 57 | 58 | 59 | //some function code 60 | 61 | 62 | traverse(ast,visitor); 63 | let {code} = generator(ast); 64 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/还原object对象.js: -------------------------------------------------------------------------------- 1 | /* 2 | date : 2020/8/11 3 | desc : 4 | */ 5 | 6 | const fs = require('fs'); 7 | const {parse} = require("@babel/parser"); 8 | const traverse = require("@babel/traverse").default; 9 | const t = require("@babel/types"); 10 | const generator = require("@babel/generator").default; 11 | 12 | let jscode = "var a = {\n" + 13 | " \"YJJox\": \"object\",\n" + 14 | " \"sbTga\": function (b, c) {\n" + 15 | " return b | c;\n" + 16 | " },\n" + 17 | " \"iwvEK\": function (b, c) {\n" + 18 | " return b << c;\n" + 19 | " },\n" + 20 | " \"HqkiD\": function (b, c) {\n" + 21 | " return b(c);\n" + 22 | " }\n" + 23 | "};\n" + 24 | "b = a[\"iwvEK\"](1, 3), c = a[\"sbTga\"](111, 222), d = a[\"YJJox\"], e = a[\"HqkiD\"](String.fromCharCode, 49);"; 25 | 26 | let ast = parse(jscode); 27 | 28 | const visitor = 29 | { 30 | VariableDeclarator(path) { 31 | const {id, init} = path.node; 32 | 33 | //特征判断,对象为空则不处理 34 | if (!t.isObjectExpression(init) || init.properties.length === 0) return; 35 | 36 | let name = id.name; 37 | let scope = path.scope; 38 | 39 | for (const property of init.properties) {//遍历key、value 40 | let key = property.key.value; 41 | let value = property.value; 42 | 43 | //一般ob混淆,key长度都是5,也有是3的,自行调整即可。 44 | if (key.length !== 5) return; 45 | 46 | //如果是字面量 47 | if (t.isLiteral(value)) { 48 | scope.traverse(scope.block, { 49 | //遍历MemberExpression,找出与key相同的表达式 50 | MemberExpression(_path) { 51 | let _node = _path.node; 52 | if (!t.isIdentifier(_node.object, {name: name})) return; 53 | if (!t.isLiteral(_node.property, {value: key})) return; 54 | _path.replaceWith(value); 55 | }, 56 | }) 57 | } 58 | //如果是函数表达式 59 | else if (t.isFunctionExpression(value)) { 60 | let ret_state = value.body.body[0]; 61 | 62 | //特征判断,如果不是return表达式 63 | if (!t.isReturnStatement(ret_state)) continue; 64 | 65 | scope.traverse(scope.block, { 66 | CallExpression: function (_path) { 67 | 68 | //遍历CallExpression 69 | let {callee, arguments} = _path.node; 70 | if (!t.isMemberExpression(callee)) return; 71 | if (!t.isIdentifier(callee.object, {name: name})) return; 72 | if (!t.isLiteral(callee.property, {value: key})) return; 73 | if (t.isCallExpression(ret_state.argument) && arguments.length > 0) { 74 | 75 | //构造节点 76 | _path.replaceWith(t.CallExpression(arguments[0], arguments.slice(1))); 77 | } else if (t.isBinaryExpression(ret_state.argument) && arguments.length === 2) { 78 | 79 | //构造节点 80 | let replace_node = t.BinaryExpression(ret_state.argument.operator, arguments[0], arguments[1]); 81 | _path.replaceWith(replace_node); 82 | } else if (t.isLogicalExpression(ret_state.argument) && arguments.length === 2) { 83 | 84 | //构造节点 85 | let replace_node = t.LogicalExpression(ret_state.argument.operator, arguments[0], arguments[1]); 86 | _path.replaceWith(replace_node); 87 | } 88 | } 89 | }) 90 | } 91 | } 92 | }, 93 | } 94 | 95 | 96 | //some function code 97 | 98 | 99 | traverse(ast,visitor); 100 | let {code} = generator(ast); 101 | console.log(code); 102 | 103 | /* 104 | 1.这里是 VariableDeclarator 节点,子节点 init 是 ObjectExpression 类型的表达式,因此我们可以遍历VariableDeclarator节点,便于获取 对象名 及整个对象 105 | 2.遍历 ObjectExpression 节点的 properties 属性,它是一个 数组,遍历这个数组,获取key和value 106 | 3.判断 value 的节点类型,如果是字面量,则可以直接进行替换;如果是函数表达式,则需要通过返回的表达式类型构造相应的表达式。然后在作用域块内遍历 MemberExpression (PS:a["sbTga"]) 节点,如果是对象名,并且当前的key值也相等,则进行节点替换。 107 | 4.遍历 properties 完毕后,可以试着删除整个 VariableDeclarator 节点,如果不报错就没事。 108 | */ 109 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/还原定义的字面量.js: -------------------------------------------------------------------------------- 1 | /* 2 | date : 2020/8/11 3 | desc : 4 | */ 5 | 6 | const fs = require('fs'); 7 | const {parse} = require("@babel/parser"); 8 | const traverse = require("@babel/traverse").default; 9 | const t = require("@babel/types"); 10 | const generator = require("@babel/generator").default; 11 | 12 | let jscode = "var s = 92;\n" + 13 | "b = Z(1324801, 92);"; 14 | 15 | let ast = parse(jscode); 16 | 17 | const visitor = 18 | { 19 | "Identifier"(path) 20 | { 21 | const {confident,value} = path.evaluate(); 22 | confident && path.replaceInline(t.valueToNode(value)); 23 | }, 24 | 25 | // 替换完了就没用了,将其删除 26 | VariableDeclarator(path) 27 | { 28 | const {id,init} = path.node; 29 | 30 | if (!t.isLiteral(init)) return;//只处理字面量 31 | 32 | const binding = path.scope.getBinding(id.name); 33 | 34 | if (!binding || binding.constantViolations.length > 0) 35 | {//如果该变量的值被修改则不能处理 36 | return; 37 | } 38 | 39 | for (const refer_path of binding.referencePaths) 40 | { 41 | refer_path.replaceWith(init); 42 | } 43 | path.remove(); 44 | }, 45 | } 46 | 47 | 48 | //some function code 49 | 50 | 51 | traverse(ast,visitor); 52 | let {code} = generator(ast); 53 | console.log(code); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/还原成中文字符.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | const fs = require('fs'); 5 | const {parse} = require("@babel/parser"); 6 | const traverse = require("@babel/traverse").default; 7 | const t = require("@babel/types"); 8 | const generator = require("@babel/generator").default; 9 | 10 | let jscode = "var s = \"\u4f60\u597d\uff0c\u4e16\u754c;\"\n" + 11 | "var a = \"\u0068\u0065\u006c\u006c\u006f\u002c\u0020\u0077\u006f\u0072\u0064\";"; 12 | 13 | let ast = parse(jscode); 14 | 15 | const visitor = 16 | { 17 | StringLiteral(path) 18 | { 19 | path.get('extra').remove(); 20 | }, 21 | } 22 | 23 | //some function code 24 | 25 | 26 | traverse(ast,visitor); 27 | let {code} = generator(ast, {jsescOption:{"minimal":true}}); 28 | console.log(code); 29 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/还原自执行函数的实参.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | const fs = require('fs'); 5 | const {parse} = require("@babel/parser"); 6 | const traverse = require("@babel/traverse").default; 7 | const t = require("@babel/types"); 8 | const generator = require("@babel/generator").default; 9 | 10 | let jscode = "!function (a, b) {\n" + 11 | " c = a | b;\n" + 12 | "}(111, 222);"; 13 | 14 | let ast = parse(jscode); 15 | 16 | const visitor = 17 | { 18 | CallExpression(path){ 19 | let callee = path.get('callee'); 20 | let arguments = path.get('arguments'); 21 | 22 | if(!t.isFunctionExpression(callee) || arguments.length ===0){ 23 | // 实参的长度判断可以写死 24 | return; 25 | } 26 | 27 | // 获取形参 28 | let params = callee.get('params'); 29 | let scope = callee.scope; 30 | 31 | for ( let i =0; i< arguments.length; i++){ 32 | // 遍历实参, 因为形参可能比实参长 33 | let arg = params[i]; 34 | let {name} = arg.node; 35 | 36 | const binding = scope.getBinding(name); 37 | 38 | if(!binding || binding.constantViolations.length > 0){ 39 | // 形参发生改变,不能被还原 40 | continue; 41 | } 42 | 43 | for(refer_path of binding.referencePaths){ 44 | // 字面量可以直接替换 45 | refer_path.replaceWith(arguments[i]); 46 | } 47 | 48 | arg.remove(); 49 | arguments[i].remove(); 50 | } 51 | } 52 | } 53 | 54 | //some function code 55 | 56 | traverse(ast,visitor); 57 | let {code} = generator(ast); 58 | console.log(code); 59 | 60 | // 替换完之后可以通过无参的自执行插件再替换 -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/ast补充/逗号表达式.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | const fs = require('fs'); 5 | const {parse} = require("@babel/parser"); 6 | const traverse = require("@babel/traverse").default; 7 | const types = require("@babel/types"); 8 | const generator = require("@babel/generator").default; 9 | 10 | let jscode = 11 | ` 12 | b = (0,g.o)(a); 13 | 14 | c = (a=1,b=2,c=3,d=4,e=5,f); 15 | 16 | function get() 17 | { 18 | return a=1,b=2,a +=2,a; 19 | } 20 | `; 21 | 22 | let ast = parse(jscode); 23 | 24 | const visitor = 25 | { 26 | SequenceExpression: { 27 | exit(path){ 28 | let expressions = path.get('expressions'); 29 | let last_expression = expressions.pop(); 30 | 31 | let statement = path.getStatementParent(); 32 | 33 | if(statement){ 34 | for(let expression of expressions) 35 | { 36 | // 删除无用的干扰代码 37 | if(expression.isLiteral() ||expression.isIdentifier()) 38 | { 39 | expression.remove(); 40 | continue; 41 | } 42 | statement.insertBefore(types.ExpressionStatement(expression=expression.node)); 43 | } 44 | path.replaceInline(last_expression); 45 | } 46 | } 47 | } 48 | } 49 | 50 | 51 | //some function code 52 | 53 | 54 | traverse(ast,visitor); 55 | let {code} = generator(ast); 56 | console.log(code); 57 | 58 | 59 | /******************************************* 60 | b = (0,g.o)(a); 61 | 62 | c = (a=1,b=2,c=3,d=4,e=5,f); 63 | 64 | function get() 65 | { 66 | return a=1,b=2,a +=2,a; 67 | } 68 | 69 | ===> 70 | 71 | b = g.o(a); 72 | a = 1; 73 | b = 2; 74 | c = 3; 75 | d = 4; 76 | e = 5; 77 | c = f; 78 | 79 | function get() { 80 | a = 1; 81 | b = 2; 82 | a += 2; 83 | return a; 84 | } 85 | 86 | ******************************************/ 87 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/for_swith.js: -------------------------------------------------------------------------------- 1 | const visitor = 2 | { 3 | ForStatement(path) { 4 | const { init, update, test, body } = path.node; 5 | if ( 6 | !t.isVariableDeclaration(init) || 7 | !t.isBinaryExpression(test) || 8 | update !== null 9 | ) 10 | return; 11 | 12 | let declaration = init.declarations[0]; 13 | 14 | const init_name = declaration.id.name; 15 | let init_value = declaration.init.value; 16 | let { left, right, operator } = test; 17 | //判断特征 18 | if ( 19 | !t.isIdentifier(left, { name: init_name }) || 20 | operator !== "!=" || 21 | !t.isNumericLiteral(right) 22 | ) 23 | return; 24 | 25 | let test_value = right.value; 26 | let switch_body = body.body[0]; 27 | 28 | //判断特征 29 | if (!t.isSwitchStatement(switch_body)) return; 30 | let { discriminant, cases } = switch_body; 31 | 32 | if (!t.isIdentifier(discriminant, { name: init_name })) return; 33 | let ret_body = []; 34 | let end_flag = false; 35 | 36 | while (init_value !== test_value) { 37 | if (end_flag === true) { 38 | break; 39 | } 40 | 41 | for (const each_case of cases) { 42 | let { test, consequent } = each_case; 43 | if (init_value !== test.value) { 44 | continue; 45 | } 46 | if (t.isContinueStatement(consequent[consequent.length - 1])) { 47 | consequent.pop(); 48 | } 49 | if (t.isExpressionStatement(consequent[consequent.length - 1])) { 50 | let { expression } = consequent[consequent.length - 1]; 51 | if (t.isAssignmentExpression(expression)) { 52 | let { left, right, operator } = expression; 53 | if (t.isIdentifier(left, { name: init_name })) { 54 | init_value = right.value; 55 | consequent.pop(); 56 | } 57 | } 58 | } 59 | if (t.isReturnStatement(consequent[consequent.length - 1])) { 60 | end_flag = true; 61 | } 62 | ret_body = ret_body.concat(consequent); 63 | break; 64 | } 65 | } 66 | path.replaceInline(ret_body); 67 | } 68 | } -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/3.13.4 用AST还原代码/lx.js: -------------------------------------------------------------------------------- 1 | function test() { 2 | for (var index = 0; index != 5;) { 3 | switch (index) { 4 | case 0: 5 | console.log("This is case-block 0"); 6 | index = 3; 7 | continue; 8 | 9 | case 1: 10 | console.log("This is case-block 1"); 11 | return; 12 | index = 5; 13 | continue; 14 | 15 | case 2: 16 | console.log("This is case-block 2"); 17 | index = 1; 18 | continue; 19 | 20 | case 3: 21 | console.log("This is case-block 3"); 22 | index = 4; 23 | continue; 24 | 25 | case 4: 26 | console.log("This is case-block 4"); 27 | index = 2; 28 | continue; 29 | } 30 | break; 31 | } 32 | } -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.13 反混淆AST/readme.md: -------------------------------------------------------------------------------- 1 | 可能大家对ast不太理解,其实是通过生成语法树(AST),可快速修改代码中的一些混淆处理,从而简化代码,便于后续分析。 2 | 3 | 比如通过Python来把JS转为AST并进行简单的操作,内容很简单。 4 | 5 | 6 | 7 | 比如我们下图中的JS代码,有sum和minus两个函数,一个变量a,两个换行\n,以及一次sum函数的调用,参数为1和2。 8 | 9 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/ab7b48fbb46d4a6581082aeb4a6a60c8.png) 10 | 11 | 12 | 通过pyjsparser库将script代码转换成为json-ast格式。 13 | 14 | 15 | pyjsparser是目前用于 python 的最快和最易理解的 JavaScript 解析器。可将JavaScript翻译成Python,即在Python中运行JavaScript代码。 16 | 17 | 18 | 19 | import pyjsparser 20 | js_ast = pyjsparser.parse(script) 21 | 22 | 23 | 24 | 转换后用Json格式化工具打开。 25 | 26 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f909c4d84d7e4df79ffb228408ab72fa.png) 27 | 28 | 在body下的元素有这几种类型: 29 | 函数声明:FunctionDeclaration 30 | 空语句(\n):EmptyStatement 31 | 变量声明:VariableDeclaration 32 | 表达式语句:ExpressionStatement 33 | 34 | 根据Json可看到FunctionDeclaration中有基本的函数名、参数名、参数类型、块语句和返回语句等。 35 | 36 | 37 | 38 | 此外,表达式语句中还有调用表达式(CallExpression)、二元表达式(BinaryExpression)、赋值表达式(AssignmentExpression)等等。 39 | 40 | 41 | 42 | 43 | 那么我们就可以通过这些Type,以修改Json对象的方式去操作这棵语法树。比如根据是否被调用去删除一些无用的对象,删除未调用的函数,或根据规则去替换一些结构,修改一些节点。 44 | 45 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/3a2842229fc44be8aa512aae6b0830de.png) 46 | 47 | 假如这是一段时间长并且难以阅读的代码,我们需要先将其转为AST,然后遍历所有函数,来查找未被调用的方法,然后进行删除,再根据AST转回正常的JS代码。 48 | 49 | 50 | 51 | ```python 52 | # 完整代码如下,大家自己试试删除无用变量a吧! 53 | # js2py依赖于pyjsparser,所以安装js2py即可安装pyjsparser 54 | # pip install js2py 55 | script = ''' 56 | function sum(a,b){ 57 | c = minus(2,3) 58 | return a+c; 59 | }; 60 | 61 | function minus(a2,b2){ 62 | return a2-b2; 63 | }; 64 | 65 | function dddd(a2,b2){ 66 | return a2-b2; 67 | }; 68 | 69 | var a = 123; 70 | sum(1,2) 71 | ''' 72 | 73 | import pyjsparser 74 | js_ast = pyjsparser.parse(script) 75 | # 获取所有方法 76 | funcList = [] 77 | for i in js_ast['body']: 78 | if i['type'] =='FunctionDeclaration': 79 | name = i['id']['name'] 80 | funcList.append(name) 81 | 82 | # 查找未被调用的方法 83 | noCallList = [] 84 | for func in funcList: 85 | searchStatement = "{'type': 'CallExpression', 'callee': {'type': 'Identifier', 'name': '%s'}"%func 86 | if searchStatement not in str(js_ast): 87 | noCallList.append(func) 88 | 89 | # 删除未调用的方法 90 | for i in js_ast['body']: 91 | if i['type'] =='FunctionDeclaration': 92 | if i['id']['name'] in noCallList: 93 | js_ast['body'].remove(i) 94 | 95 | #js_ast['body'][0]['id']['name'] = 'pythonlx' # 修改一个函数名 96 | 97 | # 用AST重新生成js代码 98 | import js2py.py_node_modules.escodegen as escodegen 99 | escodegen = escodegen.var.get('escodegen') 100 | res = escodegen.get('generate')(js_ast) 101 | print(res.to_python() 102 | ``` 103 | 104 | 105 | 106 | 执行后,可以发现未被调用的dddd方法已经被删除。 107 | 108 | 代码很简单,大家试试如何删除无关变量a吧! 109 | 110 | 本文牛刀小试,更多内容我们后续再见! 111 | 112 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/d046fa03df384dbda997bbb0fd01944d.png) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.4 常见的压缩和混淆/3.4.1 webpack导出/readme.md: -------------------------------------------------------------------------------- 1 | 新增webpack逆向的案例 2 | 3 | 案例地址:https://space.bilibili.com/390499740 4 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.4 常见的压缩和混淆/3.4.1 webpack导出/webpack-export.js: -------------------------------------------------------------------------------- 1 | var lx; 2 | !function (e){ 3 | var report = {}; 4 | function o(n){ 5 | if (report[n]) 6 | return report[n].exports; 7 | var t = report[n] = { 8 | i:n, 9 | l:!1, 10 | exports:{} 11 | }; 12 | return e[n].call(t.exports,t,t.exports,o), 13 | t.l = !0, 14 | t.exports 15 | } 16 | lx = o; 17 | }({ 18 | // 添加webpack模块 19 | // "method":function(e){} 20 | }); 21 | // 调用模块函数 22 | // var t = lx("method"); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.4 常见的压缩和混淆/3.4.1 webpack导出/案例二.js: -------------------------------------------------------------------------------- 1 | function reqId() { 2 | var t = {}; 3 | var e = undefined; 4 | var n = undefined; 5 | var r = [123, 48, 115, 203, 231, 115]; 6 | var o = 9996; 7 | var d = 0; 8 | var h = Date.now(); 9 | 10 | var i = e && n || 0 11 | , b = e || [] 12 | , f = (t = t || {}).node || r 13 | , v = void 0 !== t.clockseq ? t.clockseq : o; 14 | if (null == f || null == v) { 15 | var m = l(); 16 | null == f && (f = r = [1 | m[0], m[1], m[2], m[3], m[4], m[5]]), 17 | null == v && (v = o = 16383 & (m[6] << 8 | m[7])) 18 | } 19 | var y = void 0 !== t.msecs ? t.msecs : (new Date).getTime() 20 | , w = void 0 !== t.nsecs ? t.nsecs : d + 1 21 | , dt = y - h + (w - d) / 1e4; 22 | 23 | if (dt < 0 && void 0 === t.clockseq && (v = v + 1 & 16383), 24 | (dt < 0 || y > h) && void 0 === t.nsecs && (w = 0), 25 | w >= 1e4) 26 | throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); 27 | h = y, 28 | d = w, 29 | o = v; 30 | var x = (1e4 * (268435455 & (y += 122192928e5)) + w) % 4294967296; 31 | b[i++] = x >>> 24 & 255, 32 | b[i++] = x >>> 16 & 255, 33 | b[i++] = x >>> 8 & 255, 34 | b[i++] = 255 & x; 35 | var _ = y / 4294967296 * 1e4 & 268435455; 36 | b[i++] = _ >>> 8 & 255, 37 | b[i++] = 255 & _, 38 | b[i++] = _ >>> 24 & 15 | 16, 39 | b[i++] = _ >>> 16 & 255, 40 | b[i++] = v >>> 8 | 128, 41 | b[i++] = 255 & v; 42 | for (var A = 0; A < 6; ++A) 43 | b[i + A] = f[A]; 44 | return e || c(b) 45 | } 46 | 47 | 48 | function c(t, e) { 49 | var n =["00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"]; 50 | var i = e || 0 51 | , r = n; 52 | return [r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]]].join("") 53 | } 54 | 55 | 56 | console.log(reqId()) 57 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.4 常见的压缩和混淆/3.4.7 lsb隐写/lsb.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | 3 | def plus(str): 4 | # 返回指定长度的字符串,原字符串右对齐,前面填充0。 5 | return str.zfill(8) 6 | 7 | def get_key(strr): 8 | str_ = "" 9 | for i in range(len(strr)): 10 | # 将要隐藏的文件内容转换为二进制并拼接起来 11 | str_ = str_ + plus(bin(ord(strr[i])).replace('0b', '')) 12 | return str_ 13 | 14 | def mod(x, y): 15 | return x % y 16 | 17 | def func(old_img, str2, new_img): 18 | # str1为载体图片路径,str2为隐写文件,str3为加密图片保存的路径 19 | im = Image.open(old_img) 20 | # 获取图片的宽和高 21 | width = im.size[0] 22 | height = im.size[1] 23 | count = 0 24 | key = get_key(str2) 25 | keylen = len(key) 26 | for h in range(0, height): 27 | for w in range(0, width): 28 | pixel = im.getpixel((w, h)) 29 | a = pixel[0] 30 | b = pixel[1] 31 | c = pixel[2] 32 | if count == keylen: 33 | break 34 | # 信息隐藏:分别将每个像素点的RGB值余2,去掉最低位的值 35 | # 再从需要隐藏的信息中取出一位,转换为整型,两值相加 36 | a = a - mod(a, 2) + int(key[count]) 37 | count += 1 38 | if count == keylen: 39 | im.putpixel((w, h), (a, b, c)) 40 | break 41 | b = b - mod(b, 2) + int(key[count]) 42 | count += 1 43 | if count == keylen: 44 | im.putpixel((w, h), (a, b, c)) 45 | break 46 | c = c - mod(c, 2) + int(key[count]) 47 | count += 1 48 | if count == keylen: 49 | im.putpixel((w, h), (a, b, c)) 50 | break 51 | if count % 3 == 0: 52 | im.putpixel((w, h), (a, b, c)) 53 | im.save(new_img) 54 | 55 | # 原图 56 | old_img = r"timg.jpg" 57 | new_img = r"timg2.jpg" 58 | func(old_img,"Lx Is Good Man",new_img) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.5 常见的编码和加密/3.5.6 AES/aes_encrypt.js: -------------------------------------------------------------------------------- 1 | let password = "lx123"; 2 | let key = "1234567890abcdef" 3 | // AES加密 4 | cfg = { 5 | mode: CryptoJs.mode.ECB, 6 | padding: CryptoJs.pad.Pkcs7 7 | } 8 | let encPwd = CryptoJs.AES.encrypt(password, key, cfg).toString() 9 | 10 | // AES解密 11 | let key = CryptoJs.enc.Utf8.parse("1234567890abcdef") 12 | cfg = { 13 | mode: CryptoJs.mode.ECB, 14 | padding: CryptoJs.pad.Pkcs7 15 | } 16 | encPwd = "+4X1GzDcLdd5yb3PiZLxdw==" 17 | decPwd = CryptoJs.AES.decrypt(encPwd, key, cfg).toString(CryptoJs.enc.Utf8) // 指定解码方式 18 | console.log(decPwd) // lx123 -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.5 常见的编码和加密/3.5.6 AES/aes_encrypt.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from Crypto.Cipher import AES 3 | 4 | # AES 5 | # 需要补位,str不是16的倍数那就补足为16的倍数 6 | def add_to_16(value): 7 | while len(value) % 16 != 0: 8 | value += '\0' 9 | return str.encode(value) # 返回bytes 10 | 11 | # 加密方法 12 | def encrypt(key, text): 13 | aes = AES.new(add_to_16(key), AES.MODE_ECB) # 初始化加密器 14 | encrypt_aes = aes.encrypt(add_to_16(text)) # 先进行aes加密 15 | encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') 16 | return encrypted_text 17 | 18 | # 解密方法 19 | def decrypt(key, text): 20 | aes = AES.new(add_to_16(key), AES.MODE_ECB) # 初始化加密器 21 | base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8')) 22 | decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '') # 执行解密密并转码返回str 23 | return decrypted_text -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.5 常见的编码和加密/3.5.7 RSA/rsa_encrypt.js: -------------------------------------------------------------------------------- 1 | window = global; 2 | const JSEncrypt = require('jsencrypt'); 3 | publickey = '公钥'; 4 | 5 | // 加密 6 | let jse = new JSEncrypt(); 7 | jse.setPublicKey(publickey); 8 | var encStr = jse.encrypt('username'); 9 | 10 | // 解密 11 | privatekey = '私钥'; 12 | jse.setPrivateKey(privatekey); 13 | var Str = jse.decrypt(encStr); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.5 常见的编码和加密/3.5.7 RSA/rsa_encrypt.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import rsa 3 | from rsa import common 4 | 5 | class RsaUtil(object): 6 | PUBLIC_KEY_PATH = 'public_key.pem' # 公钥 7 | PRIVATE_KEY_PATH = 'private_key.pem' # 私钥 8 | 9 | # 初始化key 10 | def __init__(self, 11 | company_pub_file=PUBLIC_KEY_PATH, 12 | company_pri_file=PRIVATE_KEY_PATH): 13 | 14 | if company_pub_file: 15 | self.company_public_key = rsa.PublicKey.load_pkcs1_openssl_pem(open(company_pub_file).read()) 16 | if company_pri_file: 17 | self.company_private_key = rsa.PrivateKey.load_pkcs1(open(company_pri_file).read()) 18 | 19 | def get_max_length(self, rsa_key, encrypt=True): 20 | """加密内容过长时 需要分段加密 换算每一段的长度. 21 | :param rsa_key: 钥匙. 22 | :param encrypt: 是否是加密. 23 | """ 24 | blocksize = common.byte_size(rsa_key.n) 25 | reserve_size = 11 # 预留位为11 26 | if not encrypt: # 解密时不需要考虑预留位 27 | reserve_size = 0 28 | maxlength = blocksize - reserve_size 29 | return maxlength 30 | 31 | def encrypt_by_public_key(self, message): 32 | """使用公钥加密. 33 | :param message: 需要加密的内容. 34 | 加密之后需要对接过进行base64转码 35 | """ 36 | encrypt_result = b'' 37 | max_length = self.get_max_length(self.company_public_key) 38 | while message: 39 | input = message[:max_length] 40 | message = message[max_length:] 41 | out = rsa.encrypt(input, self.company_public_key) 42 | encrypt_result += out 43 | encrypt_result = base64.b64encode(encrypt_result) 44 | return encrypt_result 45 | 46 | def decrypt_by_private_key(self, message): 47 | """使用私钥解密. 48 | :param message: 需要加密的内容. 49 | 解密之后的内容直接是字符串,不需要在进行转义 50 | """ 51 | decrypt_result = b"" 52 | 53 | max_length = self.get_max_length(self.company_private_key, False) 54 | decrypt_message = base64.b64decode(message) 55 | while decrypt_message: 56 | input = decrypt_message[:max_length] 57 | decrypt_message = decrypt_message[max_length:] 58 | out = rsa.decrypt(input, self.company_private_key) 59 | decrypt_result += out 60 | return decrypt_result 61 | 62 | def sign_by_private_key(self, data): 63 | """私钥签名. 64 | :param data: 需要签名的内容. 65 | 使用SHA-1 方法进行签名(也可以使用MD5) 66 | 签名之后,需要转义后输出 67 | """ 68 | signature = rsa.sign(str(data), priv_key=self.company_private_key, hash='SHA-1') 69 | return base64.b64encode(signature) 70 | 71 | def verify_by_public_key(self, message, signature): 72 | """公钥验签. 73 | :param message: 验签的内容. 74 | :param signature: 对验签内容签名的值(签名之后,会进行b64encode转码,所以验签前也需转码). 75 | """ 76 | signature = base64.b64decode(signature) 77 | return rsa.verify(message, signature, self.company_public_key) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.1 virustotal逆向入门案例/js3.6.1.py: -------------------------------------------------------------------------------- 1 | import execjs 2 | import requests 3 | from lxpy import copy_headers_dict 4 | 5 | url = 'https://www.virustotal.com/ui/search?limit=20&relationships%5Bcomment%5D=author%2Citem&query=1' 6 | headers = copy_headers_dict(''' 7 | accept: application/json 8 | accept-encoding: gzip, deflate, br 9 | accept-ianguage: en-US,en;q=0.9,es;q=0.8 10 | accept-language: zh-CN,zh;q=0.9 11 | content-type: application/json 12 | cookie: _ga=GA1.2.747208657.1642417044; _gid=GA1.2.988701706.1642417044 13 | referer: https://www.virustotal.com/ 14 | sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97" 15 | sec-ch-ua-mobile: ?0 16 | sec-ch-ua-platform: "Windows" 17 | sec-fetch-dest: empty 18 | sec-fetch-mode: cors 19 | sec-fetch-site: same-origin 20 | user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 21 | x-app-version: v1x57x0 22 | x-tool: vt-ui-main 23 | ''') 24 | 25 | js = ''' 26 | function get_anti(){ 27 | const e = Date.now() / 1e3; 28 | return Buffer.from((`${(()=>{ 29 | const e = 1e10 * (1 + Math.random() % 5e4); 30 | return e < 50 ? "-1" : e.toFixed(0) 31 | } 32 | )()}-ZG9udCBiZSBldmls-${e}`)).toString('base64'); 33 | } 34 | ''' 35 | xvt_anti = execjs.compile(js).call('get_anti') 36 | headers.update({'x-vt-anti-abuse-header':xvt_anti}) 37 | 38 | print(requests.get(url, headers=headers).text) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.3 MD5加密逆向案例/js3.6.3.py: -------------------------------------------------------------------------------- 1 | from lxpy.encrypt import md5 2 | 3 | print(md5.get_md5('11')) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.4 RSA参数加密逆向案例/js3.6.4.py: -------------------------------------------------------------------------------- 1 | from Cryptodome.PublicKey import RSA 2 | from Cryptodome.Cipher import PKCS1_v1_5 3 | import base64 4 | 5 | def encrypt_str(data): 6 | key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgDq4OqxuEisnk2F0EJFmw4xKa5IrcqEYHvqxPs2CHEg2kolhfWA2SjNuGAHxyDDE5MLtOvzuXjBx/5YJtc9zj2xR/0moesS+Vi/xtG1tkVaTCba+TV+Y5C61iyr3FGqr+KOD4/XECu0Xky1W9ZmmaFADmZi7+6gO9wjgVpU9aLcBcw/loHOeJrCqjp7pA98hRJRY+MML8MK15mnC4ebooOva+mJlstW6t/1lghR8WNV8cocxgcHHuXBxgns2MlACQbSdJ8c6Z3RQeRZBzyjfey6JCCfbEKouVrWIUuPphBL3OANfgp0B+QG31bapvePTfXU48TYK0M5kE+8LgbbWQIDAQAB" 7 | rsakey = RSA.import_key(base64.b64decode(key)) 8 | cipher = PKCS1_v1_5.new(rsakey) 9 | cipher_text = base64.b64encode(cipher.encrypt(data.encode(encoding="utf-8"))) 10 | return cipher_text 11 | 12 | 13 | password = encrypt_str("11") 14 | print(password) 15 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.5 AES数据加密逆向案例/run.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import base64,json,re 3 | from Crypto.Cipher import AES 4 | 5 | def decrypt(info: str) -> list: 6 | key = '3sd&d24h@$udD2s*'.encode(encoding='utf-8') 7 | cipher = AES.new(key, mode=AES.MODE_ECB) 8 | json_str = str(cipher.decrypt(base64.b64decode(info)), encoding='utf-8') 9 | data = re.sub('[\x00-\x09|\x0b-\x0c|\x0e-\x1f]', '', json_str) 10 | return json.loads(data) 11 | 12 | headers = {}# 需要把你的header复制进去 13 | 14 | url = "https://api.hanghangcha.com/hhc/tag" # 产业图谱接口 15 | res = requests.get(url,headers=headers) 16 | payload = json.loads(res.content)['data'] 17 | data = decrypt(payload) 18 | print(data) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/js3.6.6.py: -------------------------------------------------------------------------------- 1 | import execjs 2 | 3 | js = ''' 4 | // npm install crypto-js 5 | 6 | var CryptoJS = require("crypto-js"); 7 | 8 | function lx(hh) { 9 | var aa = hh.split("/"); 10 | var aaa = aa.length; 11 | var bbb = aa[aaa - 1].split('.'); 12 | var ccc = bbb[0]; 13 | var cccc = bbb[1]; 14 | var r = /^\+?[1-9][0-9]*$/; 15 | 16 | var srcs = CryptoJS.enc.Utf8.parse(ccc); 17 | var s = "qnbyzzwmdgghmcnm"; 18 | var k = CryptoJS.enc.Utf8.parse(s); 19 | var en = CryptoJS.AES.encrypt(srcs, k, { 20 | mode: CryptoJS.mode.ECB, 21 | padding: CryptoJS.pad.Pkcs7 22 | }); 23 | var ddd = en.toString(); 24 | ddd = ddd.replace(/\//g, "^"); 25 | ddd = ddd.substring(0, ddd.length - 2); 26 | var bbbb = ddd + '.' + bbb[1]; 27 | aa[aaa - 1] = bbbb; 28 | var uuu = ''; 29 | for (i = 0; i < aaa; i++) { 30 | uuu += aa[i] + '/' 31 | } 32 | uuu = uuu.substring(0, uuu.length - 1); 33 | return uuu; 34 | } 35 | ''' 36 | 37 | hh="http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/970369.jhtml" 38 | url = execjs.compile(js).call('lx',hh) 39 | print(url) 40 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/.package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "node_modules/crypto-js": { 7 | "version": "4.1.1", 8 | "resolved": "https://registry.npmmirror.com/crypto-js/download/crypto-js-4.1.1.tgz", 9 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution 2 | 3 | # Git Flow 4 | 5 | The crypto-js project uses [git flow](https://github.com/nvie/gitflow) to manage branches. 6 | Do your changes on the `develop` or even better on a `feature/*` branch. Don't do any changes on the `master` branch. 7 | 8 | # Pull request 9 | 10 | Target your pull request on `develop` branch. Other pull request won't be accepted. 11 | 12 | # How to build 13 | 14 | 1. Clone 15 | 16 | 2. Run 17 | 18 | ```sh 19 | npm install 20 | ``` 21 | 22 | 3. Run 23 | 24 | ```sh 25 | npm run build 26 | ``` 27 | 28 | 4. Check `build` folder -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/LICENSE: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | [The MIT License (MIT)](http://opensource.org/licenses/MIT) 4 | 5 | Copyright (c) 2009-2013 Jeff Mott 6 | Copyright (c) 2013-2016 Evan Vosberg 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-js", 3 | "version": "4.1.1", 4 | "description": "JavaScript library of crypto standards.", 5 | "license": "MIT", 6 | "homepage": "http://github.com/brix/crypto-js", 7 | "repository": { 8 | "type": "git", 9 | "url": "http://github.com/brix/crypto-js.git" 10 | }, 11 | "keywords": [ 12 | "security", 13 | "crypto", 14 | "Hash", 15 | "MD5", 16 | "SHA1", 17 | "SHA-1", 18 | "SHA256", 19 | "SHA-256", 20 | "RC4", 21 | "Rabbit", 22 | "AES", 23 | "DES", 24 | "PBKDF2", 25 | "HMAC", 26 | "OFB", 27 | "CFB", 28 | "CTR", 29 | "CBC", 30 | "Base64", 31 | "Base64url" 32 | ], 33 | "main": "index.js", 34 | "dependencies": {}, 35 | "browser": { 36 | "crypto": false 37 | }, 38 | "ignore": [] 39 | } 40 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/enc-base64.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var WordArray = C_lib.WordArray; 21 | var C_enc = C.enc; 22 | 23 | /** 24 | * Base64 encoding strategy. 25 | */ 26 | var Base64 = C_enc.Base64 = { 27 | /** 28 | * Converts a word array to a Base64 string. 29 | * 30 | * @param {WordArray} wordArray The word array. 31 | * 32 | * @return {string} The Base64 string. 33 | * 34 | * @static 35 | * 36 | * @example 37 | * 38 | * var base64String = CryptoJS.enc.Base64.stringify(wordArray); 39 | */ 40 | stringify: function (wordArray) { 41 | // Shortcuts 42 | var words = wordArray.words; 43 | var sigBytes = wordArray.sigBytes; 44 | var map = this._map; 45 | 46 | // Clamp excess bits 47 | wordArray.clamp(); 48 | 49 | // Convert 50 | var base64Chars = []; 51 | for (var i = 0; i < sigBytes; i += 3) { 52 | var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 53 | var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; 54 | var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; 55 | 56 | var triplet = (byte1 << 16) | (byte2 << 8) | byte3; 57 | 58 | for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { 59 | base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); 60 | } 61 | } 62 | 63 | // Add padding 64 | var paddingChar = map.charAt(64); 65 | if (paddingChar) { 66 | while (base64Chars.length % 4) { 67 | base64Chars.push(paddingChar); 68 | } 69 | } 70 | 71 | return base64Chars.join(''); 72 | }, 73 | 74 | /** 75 | * Converts a Base64 string to a word array. 76 | * 77 | * @param {string} base64Str The Base64 string. 78 | * 79 | * @return {WordArray} The word array. 80 | * 81 | * @static 82 | * 83 | * @example 84 | * 85 | * var wordArray = CryptoJS.enc.Base64.parse(base64String); 86 | */ 87 | parse: function (base64Str) { 88 | // Shortcuts 89 | var base64StrLength = base64Str.length; 90 | var map = this._map; 91 | var reverseMap = this._reverseMap; 92 | 93 | if (!reverseMap) { 94 | reverseMap = this._reverseMap = []; 95 | for (var j = 0; j < map.length; j++) { 96 | reverseMap[map.charCodeAt(j)] = j; 97 | } 98 | } 99 | 100 | // Ignore padding 101 | var paddingChar = map.charAt(64); 102 | if (paddingChar) { 103 | var paddingIndex = base64Str.indexOf(paddingChar); 104 | if (paddingIndex !== -1) { 105 | base64StrLength = paddingIndex; 106 | } 107 | } 108 | 109 | // Convert 110 | return parseLoop(base64Str, base64StrLength, reverseMap); 111 | 112 | }, 113 | 114 | _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' 115 | }; 116 | 117 | function parseLoop(base64Str, base64StrLength, reverseMap) { 118 | var words = []; 119 | var nBytes = 0; 120 | for (var i = 0; i < base64StrLength; i++) { 121 | if (i % 4) { 122 | var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); 123 | var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); 124 | var bitsCombined = bits1 | bits2; 125 | words[nBytes >>> 2] |= bitsCombined << (24 - (nBytes % 4) * 8); 126 | nBytes++; 127 | } 128 | } 129 | return WordArray.create(words, nBytes); 130 | } 131 | }()); 132 | 133 | 134 | return CryptoJS.enc.Base64; 135 | 136 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/enc-hex.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.enc.Hex; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/enc-latin1.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.enc.Latin1; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/enc-utf8.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.enc.Utf8; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/evpkdf.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/sha1"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./sha1", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var Base = C_lib.Base; 21 | var WordArray = C_lib.WordArray; 22 | var C_algo = C.algo; 23 | var MD5 = C_algo.MD5; 24 | 25 | /** 26 | * This key derivation function is meant to conform with EVP_BytesToKey. 27 | * www.openssl.org/docs/crypto/EVP_BytesToKey.html 28 | */ 29 | var EvpKDF = C_algo.EvpKDF = Base.extend({ 30 | /** 31 | * Configuration options. 32 | * 33 | * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) 34 | * @property {Hasher} hasher The hash algorithm to use. Default: MD5 35 | * @property {number} iterations The number of iterations to perform. Default: 1 36 | */ 37 | cfg: Base.extend({ 38 | keySize: 128/32, 39 | hasher: MD5, 40 | iterations: 1 41 | }), 42 | 43 | /** 44 | * Initializes a newly created key derivation function. 45 | * 46 | * @param {Object} cfg (Optional) The configuration options to use for the derivation. 47 | * 48 | * @example 49 | * 50 | * var kdf = CryptoJS.algo.EvpKDF.create(); 51 | * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 }); 52 | * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 }); 53 | */ 54 | init: function (cfg) { 55 | this.cfg = this.cfg.extend(cfg); 56 | }, 57 | 58 | /** 59 | * Derives a key from a password. 60 | * 61 | * @param {WordArray|string} password The password. 62 | * @param {WordArray|string} salt A salt. 63 | * 64 | * @return {WordArray} The derived key. 65 | * 66 | * @example 67 | * 68 | * var key = kdf.compute(password, salt); 69 | */ 70 | compute: function (password, salt) { 71 | var block; 72 | 73 | // Shortcut 74 | var cfg = this.cfg; 75 | 76 | // Init hasher 77 | var hasher = cfg.hasher.create(); 78 | 79 | // Initial values 80 | var derivedKey = WordArray.create(); 81 | 82 | // Shortcuts 83 | var derivedKeyWords = derivedKey.words; 84 | var keySize = cfg.keySize; 85 | var iterations = cfg.iterations; 86 | 87 | // Generate key 88 | while (derivedKeyWords.length < keySize) { 89 | if (block) { 90 | hasher.update(block); 91 | } 92 | block = hasher.update(password).finalize(salt); 93 | hasher.reset(); 94 | 95 | // Iterations 96 | for (var i = 1; i < iterations; i++) { 97 | block = hasher.finalize(block); 98 | hasher.reset(); 99 | } 100 | 101 | derivedKey.concat(block); 102 | } 103 | derivedKey.sigBytes = keySize * 4; 104 | 105 | return derivedKey; 106 | } 107 | }); 108 | 109 | /** 110 | * Derives a key from a password. 111 | * 112 | * @param {WordArray|string} password The password. 113 | * @param {WordArray|string} salt A salt. 114 | * @param {Object} cfg (Optional) The configuration options to use for this computation. 115 | * 116 | * @return {WordArray} The derived key. 117 | * 118 | * @static 119 | * 120 | * @example 121 | * 122 | * var key = CryptoJS.EvpKDF(password, salt); 123 | * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 }); 124 | * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 }); 125 | */ 126 | C.EvpKDF = function (password, salt, cfg) { 127 | return EvpKDF.create(cfg).compute(password, salt); 128 | }; 129 | }()); 130 | 131 | 132 | return CryptoJS.EvpKDF; 133 | 134 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/format-hex.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function (undefined) { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var CipherParams = C_lib.CipherParams; 21 | var C_enc = C.enc; 22 | var Hex = C_enc.Hex; 23 | var C_format = C.format; 24 | 25 | var HexFormatter = C_format.Hex = { 26 | /** 27 | * Converts the ciphertext of a cipher params object to a hexadecimally encoded string. 28 | * 29 | * @param {CipherParams} cipherParams The cipher params object. 30 | * 31 | * @return {string} The hexadecimally encoded string. 32 | * 33 | * @static 34 | * 35 | * @example 36 | * 37 | * var hexString = CryptoJS.format.Hex.stringify(cipherParams); 38 | */ 39 | stringify: function (cipherParams) { 40 | return cipherParams.ciphertext.toString(Hex); 41 | }, 42 | 43 | /** 44 | * Converts a hexadecimally encoded ciphertext string to a cipher params object. 45 | * 46 | * @param {string} input The hexadecimally encoded string. 47 | * 48 | * @return {CipherParams} The cipher params object. 49 | * 50 | * @static 51 | * 52 | * @example 53 | * 54 | * var cipherParams = CryptoJS.format.Hex.parse(hexString); 55 | */ 56 | parse: function (input) { 57 | var ciphertext = Hex.parse(input); 58 | return CipherParams.create({ ciphertext: ciphertext }); 59 | } 60 | }; 61 | }()); 62 | 63 | 64 | return CryptoJS.format.Hex; 65 | 66 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/format-openssl.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.format.OpenSSL; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-md5.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/md5"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./md5", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacMD5; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-ripemd160.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/ripemd160"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./ripemd160", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacRIPEMD160; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-sha1.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/sha1"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./sha1", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacSHA1; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-sha224.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/sha256"), require("crypto-js/sha224"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./sha256", "./sha224", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacSHA224; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-sha256.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/sha256"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./sha256", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacSHA256; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-sha3.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/x64-core"), require("crypto-js/sha3"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./x64-core", "./sha3", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacSHA3; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-sha384.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/x64-core"), require("crypto-js/sha512"), require("crypto-js/sha384"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./x64-core", "./sha512", "./sha384", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacSHA384; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac-sha512.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/x64-core"), require("crypto-js/sha512"), require("crypto-js/hmac")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./x64-core", "./sha512", "./hmac"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.HmacSHA512; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/hmac.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var Base = C_lib.Base; 21 | var C_enc = C.enc; 22 | var Utf8 = C_enc.Utf8; 23 | var C_algo = C.algo; 24 | 25 | /** 26 | * HMAC algorithm. 27 | */ 28 | var HMAC = C_algo.HMAC = Base.extend({ 29 | /** 30 | * Initializes a newly created HMAC. 31 | * 32 | * @param {Hasher} hasher The hash algorithm to use. 33 | * @param {WordArray|string} key The secret key. 34 | * 35 | * @example 36 | * 37 | * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key); 38 | */ 39 | init: function (hasher, key) { 40 | // Init hasher 41 | hasher = this._hasher = new hasher.init(); 42 | 43 | // Convert string to WordArray, else assume WordArray already 44 | if (typeof key == 'string') { 45 | key = Utf8.parse(key); 46 | } 47 | 48 | // Shortcuts 49 | var hasherBlockSize = hasher.blockSize; 50 | var hasherBlockSizeBytes = hasherBlockSize * 4; 51 | 52 | // Allow arbitrary length keys 53 | if (key.sigBytes > hasherBlockSizeBytes) { 54 | key = hasher.finalize(key); 55 | } 56 | 57 | // Clamp excess bits 58 | key.clamp(); 59 | 60 | // Clone key for inner and outer pads 61 | var oKey = this._oKey = key.clone(); 62 | var iKey = this._iKey = key.clone(); 63 | 64 | // Shortcuts 65 | var oKeyWords = oKey.words; 66 | var iKeyWords = iKey.words; 67 | 68 | // XOR keys with pad constants 69 | for (var i = 0; i < hasherBlockSize; i++) { 70 | oKeyWords[i] ^= 0x5c5c5c5c; 71 | iKeyWords[i] ^= 0x36363636; 72 | } 73 | oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes; 74 | 75 | // Set initial values 76 | this.reset(); 77 | }, 78 | 79 | /** 80 | * Resets this HMAC to its initial state. 81 | * 82 | * @example 83 | * 84 | * hmacHasher.reset(); 85 | */ 86 | reset: function () { 87 | // Shortcut 88 | var hasher = this._hasher; 89 | 90 | // Reset 91 | hasher.reset(); 92 | hasher.update(this._iKey); 93 | }, 94 | 95 | /** 96 | * Updates this HMAC with a message. 97 | * 98 | * @param {WordArray|string} messageUpdate The message to append. 99 | * 100 | * @return {HMAC} This HMAC instance. 101 | * 102 | * @example 103 | * 104 | * hmacHasher.update('message'); 105 | * hmacHasher.update(wordArray); 106 | */ 107 | update: function (messageUpdate) { 108 | this._hasher.update(messageUpdate); 109 | 110 | // Chainable 111 | return this; 112 | }, 113 | 114 | /** 115 | * Finalizes the HMAC computation. 116 | * Note that the finalize operation is effectively a destructive, read-once operation. 117 | * 118 | * @param {WordArray|string} messageUpdate (Optional) A final message update. 119 | * 120 | * @return {WordArray} The HMAC. 121 | * 122 | * @example 123 | * 124 | * var hmac = hmacHasher.finalize(); 125 | * var hmac = hmacHasher.finalize('message'); 126 | * var hmac = hmacHasher.finalize(wordArray); 127 | */ 128 | finalize: function (messageUpdate) { 129 | // Shortcut 130 | var hasher = this._hasher; 131 | 132 | // Compute HMAC 133 | var innerHash = hasher.finalize(messageUpdate); 134 | hasher.reset(); 135 | var hmac = hasher.finalize(this._oKey.clone().concat(innerHash)); 136 | 137 | return hmac; 138 | } 139 | }); 140 | }()); 141 | 142 | 143 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/index.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/x64-core"), require("crypto-js/lib-typedarrays"), require("crypto-js/enc-utf16"), require("crypto-js/enc-base64"), require("crypto-js/enc-base64url"), require("crypto-js/md5"), require("crypto-js/sha1"), require("crypto-js/sha256"), require("crypto-js/sha224"), require("crypto-js/sha512"), require("crypto-js/sha384"), require("crypto-js/sha3"), require("crypto-js/ripemd160"), require("crypto-js/hmac"), require("crypto-js/pbkdf2"), require("crypto-js/evpkdf"), require("crypto-js/cipher-core"), require("crypto-js/mode-cfb"), require("crypto-js/mode-ctr"), require("crypto-js/mode-ctr-gladman"), require("crypto-js/mode-ofb"), require("crypto-js/mode-ecb"), require("crypto-js/pad-ansix923"), require("crypto-js/pad-iso10126"), require("crypto-js/pad-iso97971"), require("crypto-js/pad-zeropadding"), require("crypto-js/pad-nopadding"), require("crypto-js/format-hex"), require("crypto-js/aes"), require("crypto-js/tripledes"), require("crypto-js/rc4"), require("crypto-js/rabbit"), require("crypto-js/rabbit-legacy")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./x64-core", "./lib-typedarrays", "./enc-utf16", "./enc-base64", "./enc-base64url", "./md5", "./sha1", "./sha256", "./sha224", "./sha512", "./sha384", "./sha3", "./ripemd160", "./hmac", "./pbkdf2", "./evpkdf", "./cipher-core", "./mode-cfb", "./mode-ctr", "./mode-ctr-gladman", "./mode-ofb", "./mode-ecb", "./pad-ansix923", "./pad-iso10126", "./pad-iso97971", "./pad-zeropadding", "./pad-nopadding", "./format-hex", "./aes", "./tripledes", "./rc4", "./rabbit", "./rabbit-legacy"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | root.CryptoJS = factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/lib-typedarrays.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Check if typed arrays are supported 18 | if (typeof ArrayBuffer != 'function') { 19 | return; 20 | } 21 | 22 | // Shortcuts 23 | var C = CryptoJS; 24 | var C_lib = C.lib; 25 | var WordArray = C_lib.WordArray; 26 | 27 | // Reference original init 28 | var superInit = WordArray.init; 29 | 30 | // Augment WordArray.init to handle typed arrays 31 | var subInit = WordArray.init = function (typedArray) { 32 | // Convert buffers to uint8 33 | if (typedArray instanceof ArrayBuffer) { 34 | typedArray = new Uint8Array(typedArray); 35 | } 36 | 37 | // Convert other array views to uint8 38 | if ( 39 | typedArray instanceof Int8Array || 40 | (typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) || 41 | typedArray instanceof Int16Array || 42 | typedArray instanceof Uint16Array || 43 | typedArray instanceof Int32Array || 44 | typedArray instanceof Uint32Array || 45 | typedArray instanceof Float32Array || 46 | typedArray instanceof Float64Array 47 | ) { 48 | typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); 49 | } 50 | 51 | // Handle Uint8Array 52 | if (typedArray instanceof Uint8Array) { 53 | // Shortcut 54 | var typedArrayByteLength = typedArray.byteLength; 55 | 56 | // Extract bytes 57 | var words = []; 58 | for (var i = 0; i < typedArrayByteLength; i++) { 59 | words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8); 60 | } 61 | 62 | // Initialize this word array 63 | superInit.call(this, words, typedArrayByteLength); 64 | } else { 65 | // Else call normal init 66 | superInit.apply(this, arguments); 67 | } 68 | }; 69 | 70 | subInit.prototype = WordArray; 71 | }()); 72 | 73 | 74 | return CryptoJS.lib.WordArray; 75 | 76 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/mode-cfb.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * Cipher Feedback block mode. 18 | */ 19 | CryptoJS.mode.CFB = (function () { 20 | var CFB = CryptoJS.lib.BlockCipherMode.extend(); 21 | 22 | CFB.Encryptor = CFB.extend({ 23 | processBlock: function (words, offset) { 24 | // Shortcuts 25 | var cipher = this._cipher; 26 | var blockSize = cipher.blockSize; 27 | 28 | generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); 29 | 30 | // Remember this block to use with next block 31 | this._prevBlock = words.slice(offset, offset + blockSize); 32 | } 33 | }); 34 | 35 | CFB.Decryptor = CFB.extend({ 36 | processBlock: function (words, offset) { 37 | // Shortcuts 38 | var cipher = this._cipher; 39 | var blockSize = cipher.blockSize; 40 | 41 | // Remember this block to use with next block 42 | var thisBlock = words.slice(offset, offset + blockSize); 43 | 44 | generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); 45 | 46 | // This block becomes the previous block 47 | this._prevBlock = thisBlock; 48 | } 49 | }); 50 | 51 | function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) { 52 | var keystream; 53 | 54 | // Shortcut 55 | var iv = this._iv; 56 | 57 | // Generate keystream 58 | if (iv) { 59 | keystream = iv.slice(0); 60 | 61 | // Remove IV for subsequent blocks 62 | this._iv = undefined; 63 | } else { 64 | keystream = this._prevBlock; 65 | } 66 | cipher.encryptBlock(keystream, 0); 67 | 68 | // Encrypt 69 | for (var i = 0; i < blockSize; i++) { 70 | words[offset + i] ^= keystream[i]; 71 | } 72 | } 73 | 74 | return CFB; 75 | }()); 76 | 77 | 78 | return CryptoJS.mode.CFB; 79 | 80 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/mode-ctr-gladman.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** @preserve 17 | * Counter block mode compatible with Dr Brian Gladman fileenc.c 18 | * derived from CryptoJS.mode.CTR 19 | * Jan Hruby jhruby.web@gmail.com 20 | */ 21 | CryptoJS.mode.CTRGladman = (function () { 22 | var CTRGladman = CryptoJS.lib.BlockCipherMode.extend(); 23 | 24 | function incWord(word) 25 | { 26 | if (((word >> 24) & 0xff) === 0xff) { //overflow 27 | var b1 = (word >> 16)&0xff; 28 | var b2 = (word >> 8)&0xff; 29 | var b3 = word & 0xff; 30 | 31 | if (b1 === 0xff) // overflow b1 32 | { 33 | b1 = 0; 34 | if (b2 === 0xff) 35 | { 36 | b2 = 0; 37 | if (b3 === 0xff) 38 | { 39 | b3 = 0; 40 | } 41 | else 42 | { 43 | ++b3; 44 | } 45 | } 46 | else 47 | { 48 | ++b2; 49 | } 50 | } 51 | else 52 | { 53 | ++b1; 54 | } 55 | 56 | word = 0; 57 | word += (b1 << 16); 58 | word += (b2 << 8); 59 | word += b3; 60 | } 61 | else 62 | { 63 | word += (0x01 << 24); 64 | } 65 | return word; 66 | } 67 | 68 | function incCounter(counter) 69 | { 70 | if ((counter[0] = incWord(counter[0])) === 0) 71 | { 72 | // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8 73 | counter[1] = incWord(counter[1]); 74 | } 75 | return counter; 76 | } 77 | 78 | var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({ 79 | processBlock: function (words, offset) { 80 | // Shortcuts 81 | var cipher = this._cipher 82 | var blockSize = cipher.blockSize; 83 | var iv = this._iv; 84 | var counter = this._counter; 85 | 86 | // Generate keystream 87 | if (iv) { 88 | counter = this._counter = iv.slice(0); 89 | 90 | // Remove IV for subsequent blocks 91 | this._iv = undefined; 92 | } 93 | 94 | incCounter(counter); 95 | 96 | var keystream = counter.slice(0); 97 | cipher.encryptBlock(keystream, 0); 98 | 99 | // Encrypt 100 | for (var i = 0; i < blockSize; i++) { 101 | words[offset + i] ^= keystream[i]; 102 | } 103 | } 104 | }); 105 | 106 | CTRGladman.Decryptor = Encryptor; 107 | 108 | return CTRGladman; 109 | }()); 110 | 111 | 112 | 113 | 114 | return CryptoJS.mode.CTRGladman; 115 | 116 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/mode-ctr.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * Counter block mode. 18 | */ 19 | CryptoJS.mode.CTR = (function () { 20 | var CTR = CryptoJS.lib.BlockCipherMode.extend(); 21 | 22 | var Encryptor = CTR.Encryptor = CTR.extend({ 23 | processBlock: function (words, offset) { 24 | // Shortcuts 25 | var cipher = this._cipher 26 | var blockSize = cipher.blockSize; 27 | var iv = this._iv; 28 | var counter = this._counter; 29 | 30 | // Generate keystream 31 | if (iv) { 32 | counter = this._counter = iv.slice(0); 33 | 34 | // Remove IV for subsequent blocks 35 | this._iv = undefined; 36 | } 37 | var keystream = counter.slice(0); 38 | cipher.encryptBlock(keystream, 0); 39 | 40 | // Increment counter 41 | counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 42 | 43 | // Encrypt 44 | for (var i = 0; i < blockSize; i++) { 45 | words[offset + i] ^= keystream[i]; 46 | } 47 | } 48 | }); 49 | 50 | CTR.Decryptor = Encryptor; 51 | 52 | return CTR; 53 | }()); 54 | 55 | 56 | return CryptoJS.mode.CTR; 57 | 58 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/mode-ecb.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * Electronic Codebook block mode. 18 | */ 19 | CryptoJS.mode.ECB = (function () { 20 | var ECB = CryptoJS.lib.BlockCipherMode.extend(); 21 | 22 | ECB.Encryptor = ECB.extend({ 23 | processBlock: function (words, offset) { 24 | this._cipher.encryptBlock(words, offset); 25 | } 26 | }); 27 | 28 | ECB.Decryptor = ECB.extend({ 29 | processBlock: function (words, offset) { 30 | this._cipher.decryptBlock(words, offset); 31 | } 32 | }); 33 | 34 | return ECB; 35 | }()); 36 | 37 | 38 | return CryptoJS.mode.ECB; 39 | 40 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/mode-ofb.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * Output Feedback block mode. 18 | */ 19 | CryptoJS.mode.OFB = (function () { 20 | var OFB = CryptoJS.lib.BlockCipherMode.extend(); 21 | 22 | var Encryptor = OFB.Encryptor = OFB.extend({ 23 | processBlock: function (words, offset) { 24 | // Shortcuts 25 | var cipher = this._cipher 26 | var blockSize = cipher.blockSize; 27 | var iv = this._iv; 28 | var keystream = this._keystream; 29 | 30 | // Generate keystream 31 | if (iv) { 32 | keystream = this._keystream = iv.slice(0); 33 | 34 | // Remove IV for subsequent blocks 35 | this._iv = undefined; 36 | } 37 | cipher.encryptBlock(keystream, 0); 38 | 39 | // Encrypt 40 | for (var i = 0; i < blockSize; i++) { 41 | words[offset + i] ^= keystream[i]; 42 | } 43 | } 44 | }); 45 | 46 | OFB.Decryptor = Encryptor; 47 | 48 | return OFB; 49 | }()); 50 | 51 | 52 | return CryptoJS.mode.OFB; 53 | 54 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-js", 3 | "version": "4.1.1", 4 | "description": "JavaScript library of crypto standards.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Evan Vosberg", 8 | "url": "http://github.com/evanvosberg" 9 | }, 10 | "homepage": "http://github.com/brix/crypto-js", 11 | "repository": { 12 | "type": "git", 13 | "url": "http://github.com/brix/crypto-js.git" 14 | }, 15 | "keywords": [ 16 | "security", 17 | "crypto", 18 | "Hash", 19 | "MD5", 20 | "SHA1", 21 | "SHA-1", 22 | "SHA256", 23 | "SHA-256", 24 | "RC4", 25 | "Rabbit", 26 | "AES", 27 | "DES", 28 | "PBKDF2", 29 | "HMAC", 30 | "OFB", 31 | "CFB", 32 | "CTR", 33 | "CBC", 34 | "Base64", 35 | "Base64url" 36 | ], 37 | "main": "index.js", 38 | "dependencies": {}, 39 | "browser": { 40 | "crypto": false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/pad-ansix923.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * ANSI X.923 padding strategy. 18 | */ 19 | CryptoJS.pad.AnsiX923 = { 20 | pad: function (data, blockSize) { 21 | // Shortcuts 22 | var dataSigBytes = data.sigBytes; 23 | var blockSizeBytes = blockSize * 4; 24 | 25 | // Count padding bytes 26 | var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes; 27 | 28 | // Compute last byte position 29 | var lastBytePos = dataSigBytes + nPaddingBytes - 1; 30 | 31 | // Pad 32 | data.clamp(); 33 | data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8); 34 | data.sigBytes += nPaddingBytes; 35 | }, 36 | 37 | unpad: function (data) { 38 | // Get number of padding bytes from last byte 39 | var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; 40 | 41 | // Remove padding 42 | data.sigBytes -= nPaddingBytes; 43 | } 44 | }; 45 | 46 | 47 | return CryptoJS.pad.Ansix923; 48 | 49 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/pad-iso10126.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * ISO 10126 padding strategy. 18 | */ 19 | CryptoJS.pad.Iso10126 = { 20 | pad: function (data, blockSize) { 21 | // Shortcut 22 | var blockSizeBytes = blockSize * 4; 23 | 24 | // Count padding bytes 25 | var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; 26 | 27 | // Pad 28 | data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)). 29 | concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1)); 30 | }, 31 | 32 | unpad: function (data) { 33 | // Get number of padding bytes from last byte 34 | var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; 35 | 36 | // Remove padding 37 | data.sigBytes -= nPaddingBytes; 38 | } 39 | }; 40 | 41 | 42 | return CryptoJS.pad.Iso10126; 43 | 44 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/pad-iso97971.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * ISO/IEC 9797-1 Padding Method 2. 18 | */ 19 | CryptoJS.pad.Iso97971 = { 20 | pad: function (data, blockSize) { 21 | // Add 0x80 byte 22 | data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1)); 23 | 24 | // Zero pad the rest 25 | CryptoJS.pad.ZeroPadding.pad(data, blockSize); 26 | }, 27 | 28 | unpad: function (data) { 29 | // Remove zero padding 30 | CryptoJS.pad.ZeroPadding.unpad(data); 31 | 32 | // Remove one more byte -- the 0x80 byte 33 | data.sigBytes--; 34 | } 35 | }; 36 | 37 | 38 | return CryptoJS.pad.Iso97971; 39 | 40 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/pad-nopadding.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * A noop padding strategy. 18 | */ 19 | CryptoJS.pad.NoPadding = { 20 | pad: function () { 21 | }, 22 | 23 | unpad: function () { 24 | } 25 | }; 26 | 27 | 28 | return CryptoJS.pad.NoPadding; 29 | 30 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/pad-pkcs7.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | return CryptoJS.pad.Pkcs7; 17 | 18 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/pad-zeropadding.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | /** 17 | * Zero padding strategy. 18 | */ 19 | CryptoJS.pad.ZeroPadding = { 20 | pad: function (data, blockSize) { 21 | // Shortcut 22 | var blockSizeBytes = blockSize * 4; 23 | 24 | // Pad 25 | data.clamp(); 26 | data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes); 27 | }, 28 | 29 | unpad: function (data) { 30 | // Shortcut 31 | var dataWords = data.words; 32 | 33 | // Unpad 34 | var i = data.sigBytes - 1; 35 | for (var i = data.sigBytes - 1; i >= 0; i--) { 36 | if (((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) { 37 | data.sigBytes = i + 1; 38 | break; 39 | } 40 | } 41 | } 42 | }; 43 | 44 | 45 | return CryptoJS.pad.ZeroPadding; 46 | 47 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/rc4.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/enc-base64"), require("crypto-js/md5"), require("crypto-js/evpkdf"), require("crypto-js/cipher-core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var StreamCipher = C_lib.StreamCipher; 21 | var C_algo = C.algo; 22 | 23 | /** 24 | * RC4 stream cipher algorithm. 25 | */ 26 | var RC4 = C_algo.RC4 = StreamCipher.extend({ 27 | _doReset: function () { 28 | // Shortcuts 29 | var key = this._key; 30 | var keyWords = key.words; 31 | var keySigBytes = key.sigBytes; 32 | 33 | // Init sbox 34 | var S = this._S = []; 35 | for (var i = 0; i < 256; i++) { 36 | S[i] = i; 37 | } 38 | 39 | // Key setup 40 | for (var i = 0, j = 0; i < 256; i++) { 41 | var keyByteIndex = i % keySigBytes; 42 | var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff; 43 | 44 | j = (j + S[i] + keyByte) % 256; 45 | 46 | // Swap 47 | var t = S[i]; 48 | S[i] = S[j]; 49 | S[j] = t; 50 | } 51 | 52 | // Counters 53 | this._i = this._j = 0; 54 | }, 55 | 56 | _doProcessBlock: function (M, offset) { 57 | M[offset] ^= generateKeystreamWord.call(this); 58 | }, 59 | 60 | keySize: 256/32, 61 | 62 | ivSize: 0 63 | }); 64 | 65 | function generateKeystreamWord() { 66 | // Shortcuts 67 | var S = this._S; 68 | var i = this._i; 69 | var j = this._j; 70 | 71 | // Generate keystream word 72 | var keystreamWord = 0; 73 | for (var n = 0; n < 4; n++) { 74 | i = (i + 1) % 256; 75 | j = (j + S[i]) % 256; 76 | 77 | // Swap 78 | var t = S[i]; 79 | S[i] = S[j]; 80 | S[j] = t; 81 | 82 | keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8); 83 | } 84 | 85 | // Update counters 86 | this._i = i; 87 | this._j = j; 88 | 89 | return keystreamWord; 90 | } 91 | 92 | /** 93 | * Shortcut functions to the cipher's object interface. 94 | * 95 | * @example 96 | * 97 | * var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg); 98 | * var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg); 99 | */ 100 | C.RC4 = StreamCipher._createHelper(RC4); 101 | 102 | /** 103 | * Modified RC4 stream cipher algorithm. 104 | */ 105 | var RC4Drop = C_algo.RC4Drop = RC4.extend({ 106 | /** 107 | * Configuration options. 108 | * 109 | * @property {number} drop The number of keystream words to drop. Default 192 110 | */ 111 | cfg: RC4.cfg.extend({ 112 | drop: 192 113 | }), 114 | 115 | _doReset: function () { 116 | RC4._doReset.call(this); 117 | 118 | // Drop 119 | for (var i = this.cfg.drop; i > 0; i--) { 120 | generateKeystreamWord.call(this); 121 | } 122 | } 123 | }); 124 | 125 | /** 126 | * Shortcut functions to the cipher's object interface. 127 | * 128 | * @example 129 | * 130 | * var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg); 131 | * var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg); 132 | */ 133 | C.RC4Drop = StreamCipher._createHelper(RC4Drop); 134 | }()); 135 | 136 | 137 | return CryptoJS.RC4; 138 | 139 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/sha1.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var WordArray = C_lib.WordArray; 21 | var Hasher = C_lib.Hasher; 22 | var C_algo = C.algo; 23 | 24 | // Reusable object 25 | var W = []; 26 | 27 | /** 28 | * SHA-1 hash algorithm. 29 | */ 30 | var SHA1 = C_algo.SHA1 = Hasher.extend({ 31 | _doReset: function () { 32 | this._hash = new WordArray.init([ 33 | 0x67452301, 0xefcdab89, 34 | 0x98badcfe, 0x10325476, 35 | 0xc3d2e1f0 36 | ]); 37 | }, 38 | 39 | _doProcessBlock: function (M, offset) { 40 | // Shortcut 41 | var H = this._hash.words; 42 | 43 | // Working variables 44 | var a = H[0]; 45 | var b = H[1]; 46 | var c = H[2]; 47 | var d = H[3]; 48 | var e = H[4]; 49 | 50 | // Computation 51 | for (var i = 0; i < 80; i++) { 52 | if (i < 16) { 53 | W[i] = M[offset + i] | 0; 54 | } else { 55 | var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; 56 | W[i] = (n << 1) | (n >>> 31); 57 | } 58 | 59 | var t = ((a << 5) | (a >>> 27)) + e + W[i]; 60 | if (i < 20) { 61 | t += ((b & c) | (~b & d)) + 0x5a827999; 62 | } else if (i < 40) { 63 | t += (b ^ c ^ d) + 0x6ed9eba1; 64 | } else if (i < 60) { 65 | t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; 66 | } else /* if (i < 80) */ { 67 | t += (b ^ c ^ d) - 0x359d3e2a; 68 | } 69 | 70 | e = d; 71 | d = c; 72 | c = (b << 30) | (b >>> 2); 73 | b = a; 74 | a = t; 75 | } 76 | 77 | // Intermediate hash value 78 | H[0] = (H[0] + a) | 0; 79 | H[1] = (H[1] + b) | 0; 80 | H[2] = (H[2] + c) | 0; 81 | H[3] = (H[3] + d) | 0; 82 | H[4] = (H[4] + e) | 0; 83 | }, 84 | 85 | _doFinalize: function () { 86 | // Shortcuts 87 | var data = this._data; 88 | var dataWords = data.words; 89 | 90 | var nBitsTotal = this._nDataBytes * 8; 91 | var nBitsLeft = data.sigBytes * 8; 92 | 93 | // Add padding 94 | dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); 95 | dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); 96 | dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; 97 | data.sigBytes = dataWords.length * 4; 98 | 99 | // Hash final blocks 100 | this._process(); 101 | 102 | // Return final computed hash 103 | return this._hash; 104 | }, 105 | 106 | clone: function () { 107 | var clone = Hasher.clone.call(this); 108 | clone._hash = this._hash.clone(); 109 | 110 | return clone; 111 | } 112 | }); 113 | 114 | /** 115 | * Shortcut function to the hasher's object interface. 116 | * 117 | * @param {WordArray|string} message The message to hash. 118 | * 119 | * @return {WordArray} The hash. 120 | * 121 | * @static 122 | * 123 | * @example 124 | * 125 | * var hash = CryptoJS.SHA1('message'); 126 | * var hash = CryptoJS.SHA1(wordArray); 127 | */ 128 | C.SHA1 = Hasher._createHelper(SHA1); 129 | 130 | /** 131 | * Shortcut function to the HMAC's object interface. 132 | * 133 | * @param {WordArray|string} message The message to hash. 134 | * @param {WordArray|string} key The secret key. 135 | * 136 | * @return {WordArray} The HMAC. 137 | * 138 | * @static 139 | * 140 | * @example 141 | * 142 | * var hmac = CryptoJS.HmacSHA1(message, key); 143 | */ 144 | C.HmacSHA1 = Hasher._createHmacHelper(SHA1); 145 | }()); 146 | 147 | 148 | return CryptoJS.SHA1; 149 | 150 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/sha224.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/sha256")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./sha256"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_lib = C.lib; 20 | var WordArray = C_lib.WordArray; 21 | var C_algo = C.algo; 22 | var SHA256 = C_algo.SHA256; 23 | 24 | /** 25 | * SHA-224 hash algorithm. 26 | */ 27 | var SHA224 = C_algo.SHA224 = SHA256.extend({ 28 | _doReset: function () { 29 | this._hash = new WordArray.init([ 30 | 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 31 | 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 32 | ]); 33 | }, 34 | 35 | _doFinalize: function () { 36 | var hash = SHA256._doFinalize.call(this); 37 | 38 | hash.sigBytes -= 4; 39 | 40 | return hash; 41 | } 42 | }); 43 | 44 | /** 45 | * Shortcut function to the hasher's object interface. 46 | * 47 | * @param {WordArray|string} message The message to hash. 48 | * 49 | * @return {WordArray} The hash. 50 | * 51 | * @static 52 | * 53 | * @example 54 | * 55 | * var hash = CryptoJS.SHA224('message'); 56 | * var hash = CryptoJS.SHA224(wordArray); 57 | */ 58 | C.SHA224 = SHA256._createHelper(SHA224); 59 | 60 | /** 61 | * Shortcut function to the HMAC's object interface. 62 | * 63 | * @param {WordArray|string} message The message to hash. 64 | * @param {WordArray|string} key The secret key. 65 | * 66 | * @return {WordArray} The HMAC. 67 | * 68 | * @static 69 | * 70 | * @example 71 | * 72 | * var hmac = CryptoJS.HmacSHA224(message, key); 73 | */ 74 | C.HmacSHA224 = SHA256._createHmacHelper(SHA224); 75 | }()); 76 | 77 | 78 | return CryptoJS.SHA224; 79 | 80 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/node_modules/crypto-js/sha384.js: -------------------------------------------------------------------------------- 1 | ;(function (root, factory, undef) { 2 | if (typeof exports === "object") { 3 | // CommonJS 4 | module.exports = exports = factory(require("crypto-js/core"), require("crypto-js/x64-core"), require("crypto-js/sha512")); 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | // AMD 8 | define(["crypto-js/core", "./x64-core", "./sha512"], factory); 9 | } 10 | else { 11 | // Global (browser) 12 | factory(root.CryptoJS); 13 | } 14 | }(this, function (CryptoJS) { 15 | 16 | (function () { 17 | // Shortcuts 18 | var C = CryptoJS; 19 | var C_x64 = C.x64; 20 | var X64Word = C_x64.Word; 21 | var X64WordArray = C_x64.WordArray; 22 | var C_algo = C.algo; 23 | var SHA512 = C_algo.SHA512; 24 | 25 | /** 26 | * SHA-384 hash algorithm. 27 | */ 28 | var SHA384 = C_algo.SHA384 = SHA512.extend({ 29 | _doReset: function () { 30 | this._hash = new X64WordArray.init([ 31 | new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), 32 | new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), 33 | new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), 34 | new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4) 35 | ]); 36 | }, 37 | 38 | _doFinalize: function () { 39 | var hash = SHA512._doFinalize.call(this); 40 | 41 | hash.sigBytes -= 16; 42 | 43 | return hash; 44 | } 45 | }); 46 | 47 | /** 48 | * Shortcut function to the hasher's object interface. 49 | * 50 | * @param {WordArray|string} message The message to hash. 51 | * 52 | * @return {WordArray} The hash. 53 | * 54 | * @static 55 | * 56 | * @example 57 | * 58 | * var hash = CryptoJS.SHA384('message'); 59 | * var hash = CryptoJS.SHA384(wordArray); 60 | */ 61 | C.SHA384 = SHA512._createHelper(SHA384); 62 | 63 | /** 64 | * Shortcut function to the HMAC's object interface. 65 | * 66 | * @param {WordArray|string} message The message to hash. 67 | * @param {WordArray|string} key The secret key. 68 | * 69 | * @return {WordArray} The HMAC. 70 | * 71 | * @static 72 | * 73 | * @example 74 | * 75 | * var hmac = CryptoJS.HmacSHA384(message, key); 76 | */ 77 | C.HmacSHA384 = SHA512._createHmacHelper(SHA384); 78 | }()); 79 | 80 | 81 | return CryptoJS.SHA384; 82 | 83 | })); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "crypto-js": "^4.1.1" 9 | } 10 | }, 11 | "node_modules/crypto-js": { 12 | "version": "4.1.1", 13 | "resolved": "https://registry.npmmirror.com/crypto-js/download/crypto-js-4.1.1.tgz", 14 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 15 | } 16 | }, 17 | "dependencies": { 18 | "crypto-js": { 19 | "version": "4.1.1", 20 | "resolved": "https://registry.npmmirror.com/crypto-js/download/crypto-js-4.1.1.tgz", 21 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "crypto-js": "^4.1.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.6 AES链接加密逆向案例/js3.6.6/test.js: -------------------------------------------------------------------------------- 1 | var CryptoJS = require("crypto-js"); 2 | 3 | function lx(hh) { 4 | var aa = hh.split("/"); 5 | var aaa = aa.length; 6 | var bbb = aa[aaa - 1].split('.'); 7 | var ccc = bbb[0]; 8 | var cccc = bbb[1]; 9 | var r = /^\+?[1-9][0-9]*$/; 10 | 11 | var srcs = CryptoJS.enc.Utf8.parse(ccc); 12 | var s = "qnbyzzwmdgghmcnm"; 13 | var k = CryptoJS.enc.Utf8.parse(s); 14 | var en = CryptoJS.AES.encrypt(srcs, k, { 15 | mode: CryptoJS.mode.ECB, 16 | padding: CryptoJS.pad.Pkcs7 17 | }); 18 | var ddd = en.toString(); 19 | ddd = ddd.replace(/\//g, "^"); 20 | ddd = ddd.substring(0, ddd.length - 2); 21 | var bbbb = ddd + '.' + bbb[1]; 22 | aa[aaa - 1] = bbbb; 23 | var uuu = ''; 24 | for (i = 0; i < aaa; i++) { 25 | uuu += aa[i] + '/' 26 | } 27 | uuu = uuu.substring(0, uuu.length - 1); 28 | return uuu; 29 | } 30 | var hh="http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/970369.jhtml"; 31 | console.log(lx(hh)); -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.6 加密参数还原与模拟/3.6.7 cnvd加速乐分析案例/run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021/12/8 10:58 3 | # @IDE :PyCharm 4 | 5 | import requests 6 | import re 7 | import execjs 8 | 9 | headers = { 10 | # 如果被拦截,把headers补齐 11 | 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36' 12 | 13 | } 14 | 15 | sess = requests.session() 16 | url = 'https://www.cnvd.org.cn/flaw/list.htm' 17 | 18 | def start(): 19 | r =sess.get(url,headers=headers,verify=False) 20 | text = r.text 21 | cookie = re.search('',text).group(1) 31 | hash = re.search('"ha":"(.*?)",',data).group(1) 32 | # get_cookie_2是执行JS,完整代码请下载 33 | cookie = get_cookie_2(data,hash).split(';')[0].split('=') 34 | sess.cookies[cookie[0]] =cookie[1] 35 | 36 | def end(page=1): 37 | data = {'number': '请输入精确编号', 'startDate': '', 'endDate': '', 'field': '', 'order': '', 'numPerPage': '10', 'offset': page*10, 'max': '10'} 38 | r1 = sess.post('https://www.cnvd.org.cn/flaw/list.htm?flag=true',headers=headers,data=data,verify=False) 39 | print(r1.text) 40 | 41 | 42 | def get_cookie_2(data,hash): 43 | node = execjs.get() 44 | path=f'./t_{hash}.js' 45 | with open(path,'r',encoding='utf-8') as f: 46 | ctx = node.compile(f.read()) 47 | funcName = f'go({data})' 48 | pwd = ctx.eval(funcName) 49 | return pwd 50 | 51 | 52 | if __name__ == '__main__': 53 | start() 54 | then() 55 | end() 56 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.7 浏览器环境补充/3.7.0 浏览器环境补充/js-hook.txt: -------------------------------------------------------------------------------- 1 | 2 | ## Hook setInterval 3 | ```js 4 | let _setInterval=setInterval; 5 | setInterval=function(a,b){ 6 | if(a.toString().indexOf("debugger")!=-1){ 7 | return null; 8 | } 9 | _setInterval(a,b); 10 | } 11 | ``` 12 | 13 | ## Hook Cookie Info 14 | ```js 15 | var cookie_cache = document.cookie; 16 | Object.defineProperty(document, 'cookie', { 17 | get: function() { 18 | console.log('Getting cookie'); 19 | return cookie_cache; 20 | }, 21 | set: function(val) { 22 | console.log('Setting cookie', val); 23 | var cookie = val.split(";")[0]; 24 | var ncookie = cookie.split("="); 25 | var flag = false; 26 | var cache = cookie_cache.split("; "); 27 | cache = cache.map(function(a){ 28 | if (a.split("=")[0] === ncookie[0]){ 29 | flag = true; 30 | return cookie; 31 | } 32 | return a; 33 | }) 34 | cookie_cache = cache.join("; "); 35 | if (!flag){ 36 | cookie_cache += cookie + "; "; 37 | } 38 | this._value = val; 39 | return cookie_cache; 40 | }, 41 | }); 42 | ``` 43 | 44 | 45 | ## Hook Json Info 46 | ```js 47 | var my_stringify = JSON.stringify; 48 | JSON.stringify = function (params){ 49 | console.log("json_stringify:", params); 50 | return json_stringify(params); 51 | }; 52 | 53 | var my_parse = JSON.parse; 54 | JSON.parse = function (params){ 55 | console.log("json_parse:", params); 56 | return json_parse(params); 57 | }; 58 | ``` 59 | 60 | ## Hook WebSocket 61 | ```js 62 | WebSocket.prototype.senda = WebSocket.prototype.send; 63 | WebSocket.prototype.send = function (data){ 64 | console.info("Hook WebSocket", data); 65 | return this.senda(data) 66 | } 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.7 浏览器环境补充/3.7.0 浏览器环境补充/readme.md: -------------------------------------------------------------------------------- 1 | 常被检测的环境中,有windows、location、navigate、document、native、canvas等。 2 | 3 | 除了这些属性外,还有针对于自动化的检测、node环境的检测、以及浏览器指纹检测、TLS指纹校验等。 4 | 5 | 所谓的补环境是指在Node环境中去运行浏览器的代码,因为Node环境和浏览器环境是不同的,比如DOM渲染和浏览器内置方法,而经常需要补的内容也是这些。 6 | 7 | 笔者总结的补环境有两种方式,一是在本地Node中扣代码补环境,二是通过驱动开启一个浏览器环境去执行代码,但是需要记得补驱动的特征。 8 | 9 | 不过最优的方法还是在本地补,所以这里简单分享一些补的方法和代码。 10 | 11 | --- 12 | 13 | 3.7.0 中给出的几个文件是笔者准备的一些工具类,笔者自己写的和收集于网络的都贴上来。 14 | 15 | 由于这块内容不太好总结,环境并不是通用的。用文字来描述的话,要么贴已经补过的代码,要么贴一键hook的脚本。 16 | 17 | 虽然每次补之前直接把各种环境堆上去碰运气也是一种方法,但是碰到独特的检测点等于直接从0开始。 18 | 19 | 毕竟刚开始都是打着debugger缺啥补啥,大家先掌握那些基本的语法。 20 | 21 | 22 | --- 23 | 24 | 所以先知道怎么补之后,再来决定用什么方法获取对象的属性或方法。 25 | 26 | ```js 27 | document = { 28 | createElement:function(x){ 29 | return {} 30 | } 31 | } 32 | 33 | var _getXxx = { 34 | toString: function() { 35 | return "" 36 | } 37 | }; 38 | 39 | _getXxx.__proto__.getA = function getA(x) { 40 | return {} 41 | }; 42 | ``` 43 | 44 | 45 | 比如给document补一些方法 46 | 47 | ```js 48 | var document = { 49 | createEvent: function createEvent() {}, 50 | addEventListener: function addEventListener(x) {}, 51 | createElement: function createElement(x) { 52 | if(x===""){} else {} 53 | } 54 | }; 55 | ``` 56 | 57 | 比如给 getLX_method 对象增加方法。 58 | ```js 59 | var getLX_method = {}; 60 | getLX_method.__proto__.getExtension = function getExtension(x) { 61 | return {} 62 | } 63 | getLX_method.__proto__.getParameter = function getParameter(x) { 64 | return "" 65 | } 66 | ``` 67 | 68 | 比如补 canvas 的话,我们只需要看它调用的方法和返回的结果,然后去一次性绘制图片,取出base64值放到toDataURL()中即可。 69 | 70 | ```js 71 | document = { 72 | createElement: function createElement(x) { 73 | return canvas 74 | } 75 | }; 76 | 77 | canvas = { 78 | toDataURL: function toDataURL() { 79 | return "data:image/png;base64,i.....ggg==" 80 | }, 81 | getContext: function getContext(x) { 82 | if (x === "xxx") { 83 | return 84 | } else { 85 | return CanvasContext 86 | } 87 | } 88 | }; 89 | 90 | CanvasContext = { 91 | arc: function arc() {}, 92 | stroke: function stroke() {}, 93 | fillText: function fillText() {}, 94 | toString: function() { 95 | return "[object]" 96 | } 97 | }; 98 | 99 | canvas[Symbol.toStringTag] = "HTMLCanvasElement"; 100 | ``` 101 | 102 | 重写String的查找方法 103 | ```js 104 | var _indexOf = String.prototype.indexOf; 105 | String.prototype.indexOf = function (searchValue, fromIndex) { 106 | if (searchValue == 'lx') { 107 | return-1; 108 | } 109 | return _indexOf.apply(this, [searchValue, fromIndex]); 110 | } 111 | ``` 112 | 113 | 重写toString方法: 114 | ```js 115 | var newString = Function.prototype.toString; 116 | Function.prototype.toString = function () { 117 | if (this == Window || this == Location || this == Function.prototype.toString) { 118 | return"function Window() { [native code] }"; 119 | } 120 | return newString.apply(this); 121 | }; 122 | ``` 123 | 124 | 125 | 126 | 再给大家分享一些补环境经验: 127 | 128 | 1、检测环境记心里,指纹信息重获取。 129 | 130 | 2、dom操作记一记,Jsdom多练习。 131 | 132 | 3、不可变对象记得Object.freeze() 133 | 134 | 4、多用debugger巧用proxy 135 | 136 | 5、内置方法防重写 137 | 138 | 6、形成框架方便二次修改 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | --- 147 | 148 | **大家把目录中的Js文件熟悉之后,可以找我领一份JS环境框架** 149 | - 1、文件太多,上传很麻烦 150 | - 2、框架只提供给《爬虫逆向进阶实战》的读者 151 | - 3、微信、QQ、私信找我都可以 152 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.7 浏览器环境补充/3.7.0 浏览器环境补充/t1.js: -------------------------------------------------------------------------------- 1 | // 最基础的 2 | var glb; 3 | var window = global; 4 | window.document = {referrer: ""}; 5 | window.location = { 6 | hash: "", 7 | host: "", 8 | hostname: "", 9 | href: "", 10 | origin: "", 11 | pathname: "/", 12 | port: "", 13 | protocol: "https:", 14 | search: "" 15 | }; 16 | window.navigator = { 17 | appCodeName: "Mozilla", 18 | appName: "Netscape", 19 | appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36", 20 | cookieEnabled: true, 21 | deviceMemory: 8, 22 | doNotTrack: null, 23 | hardwareConcurrency: 4, 24 | language: "zh-CN", 25 | languages: ["zh-CN", "zh"], 26 | maxTouchPoints: 0, 27 | onLine: true, 28 | platform: "Win32", 29 | product: "Gecko", 30 | productSub: "20030107", 31 | userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36", 32 | vendor: "Google Inc.", 33 | vendorSub: "", 34 | }; 35 | 36 | function get_signature(url) { 37 | _signature = ""; 38 | return _signature; 39 | } 40 | 41 | // Js代码位置 42 | url = ''; 43 | console.log(get_signature(url)); 44 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.7 浏览器环境补充/3.7.0 浏览器环境补充/t2.js: -------------------------------------------------------------------------------- 1 | // jsdom 2 | const jsdom = require("jsdom"); 3 | const { JSDOM } = jsdom; 4 | const dom = new JSDOM(`

Hello Lx

`); 5 | window = global; 6 | var document = dom.window.document; 7 | var params = { 8 | location:{ 9 | hash: "", 10 | host: "", 11 | hostname: "", 12 | href: "", 13 | protocol: "", 14 | search: "", 15 | }, 16 | navigator:{ 17 | appCodeName: "Mozilla", 18 | appName: "Netscape", 19 | appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36", 20 | cookieEnabled: true, 21 | deviceMemory: 8, 22 | hardwareConcurrency: 4, 23 | language: "zh-CN", 24 | onLine: true, 25 | platform: "MacIntel", 26 | product: "Gecko", 27 | productSub: "20030107", 28 | userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36", 29 | vendor: "Google Inc." 30 | }, 31 | screen:{ 32 | availHeight: 728, 33 | availLeft: 0, 34 | availTop: 0, 35 | availWidth: 1366, 36 | colorDepth: 24, 37 | height: 768, 38 | pixelDepth: 24, 39 | width: 1366 40 | } 41 | }; 42 | Object.assign(window,params); 43 | 44 | window.document = document; 45 | 46 | // Js代码位置 47 | 48 | function get_signature(url) { 49 | _signature = ""; 50 | return _signature 51 | } 52 | 53 | 54 | console.log(get_signature(url)); 55 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.7 浏览器环境补充/3.7.3 selenium环境模拟/test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from selenium import webdriver 4 | 5 | ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...' 6 | PRO_DIR = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | s1 = """ 9 | 10 | 11 | 12 | 13 | signature-hook 14 | 15 | 16 | 17 | 21 | 22 | """ 23 | 24 | def driver_sig(html_file): 25 | option = webdriver.ChromeOptions() 26 | option.add_argument('headless') 27 | option.add_argument('--no-sandbox') 28 | option.add_argument('--user-agent={}'.format(ua)) 29 | driver = webdriver.Chrome(chrome_options=option) 30 | driver.get('file:///'+ PRO_DIR + html_file) 31 | sig = driver.title 32 | driver.quit() 33 | return sig 34 | 35 | sign_js = ''' 36 | window.navigator = { 37 | userAgent: '' 38 | }; 39 | function get_sign() { 40 | ... //此处省略N行代码 41 | return sign 42 | } 43 | var signature = get_sign(); 44 | document.clear(); 45 | document.write(signature); 46 | ''' 47 | 48 | if __name__ == '__main__': 49 | doc = sign_js.replace("userAgent: ''","userAgent: '{}'".format(ua)) 50 | html_file = 'get_sign.html' 51 | with open(html_file, 'w', encoding='utf-8') as fw: 52 | fw.write(s1 + doc + s2) 53 | sig = driver_sig(html_file) 54 | print(sig) -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.0 加密方法远程调用/readme.md: -------------------------------------------------------------------------------- 1 | 加密方法的远程调用主要是使用了RPC协议,RPC(Remote Procedure Call)是远程调用的意思。RPC的应用十分广泛,比如在分布式中的进程间通讯、微服务中的节点通讯。 2 | 3 | 我们这里使用的rpc其实是实现两个不同进程通信的一种方式,比如在浏览器执行一些方法,将结果返回给本地使用。 4 | 5 | 在JS逆向这块,不论是selenium、cefpython、puppeteer或者其他工具都可以实现。 6 | 7 | 大家可以按照书中的案例进行练习。 8 | 9 | 此文件夹中会更新一些新的案例供大家学习。 10 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.0 加密方法远程调用/头条系web-RPC.md: -------------------------------------------------------------------------------- 1 | 以头条系某短视频(dy)web页面搜索为例,通过RPC的逻辑实现数据采集。 2 | 3 | 目前方案通用于头条系web页面。因为其当前产品大都基于XMLHttpRequest的send方法做一些校验,我们可以自启一个浏览器去完成XMLHttpRequest请求,直接获取返回的responseText。 4 | 5 | 话虽如此,此方案仅是为了配合书中的教程供大家学习,并不代表是最优选择,主要是给大家提供一种思路和方法。 6 | 7 | 代码逻辑很简单,只有50行代码,有不懂的问题可私信或留言。 8 | 9 | 更多精彩内容:[《爬虫逆向进阶实战》](http://t.csdn.cn/mEa64) 10 | 11 | 12 | 13 | --- 14 | 15 | ## 调试分析 16 | 17 | 因受版权影响,我会避开关键词。 18 | 19 | 先找一下其发送逻辑,堆栈第一个点进去。 20 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/cf1f35c4f31d41caa0f475c007b6812f.png) 21 | 22 | 断点后调试,可看到t即是XMLHttpRequest。 23 | 24 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/4e5018337a0449a0a9de97abe79ecc28.png) 25 | 26 | 查看t对象。 27 | 28 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/0036cd99a8e746d7af74c8c5fa5a35ea.png) 29 | 30 | 31 | 在浏览器中调试。 32 | 33 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/32124b7fa3764128900c9b454c8947d2.png) 34 | 35 | 36 | 查看响应。 当前已经返回了responseText和带了签名的responseURL。 37 | 38 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/85026da13f6545a19db303a781b23a49.png) 39 | 40 | 41 | --- 42 | 43 | ## Python实现 44 | 注意:url要换成自己浏览器上的。 45 | ```python 46 | # -*- coding: utf-8 -*- 47 | # @UpdataTime : 2022/6/1 16:00 48 | # @Author : lx 49 | 50 | from selenium import webdriver 51 | 52 | class Browser(): 53 | def __init__(self, **kwargs): 54 | # TODO: update your executablePath 55 | executablePath = r"chromedriver.exe" 56 | self.executablePath = kwargs.get("executablePath",executablePath ) 57 | options = webdriver.ChromeOptions() 58 | self.browser = webdriver.Chrome(executable_path=self.executablePath, chrome_options=options) 59 | self.browser.get('https://www.douyin.com') 60 | 61 | def search_item(self, keyword): 62 | doc = self.browser.execute_script(''' 63 | function queryData(url) { 64 | var p = new Promise(function(resolve,reject) { 65 | var e={ 66 | "url":"https://www.douyin.com/aweme/v1/web/search/item/?device_platform=webapp&aid=6383&channel=channel_pc_web&search_channel=aweme_video_web&sort_type=0&publish_time=0&keyword=%s&search_source=normal_search&query_correct_type=1&is_filter_search=0&from_group_id=&offset=0&count=10&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=102.0.5005.63&browser_online=true&engine_name=Blink&engine_version=102.0.5005.63&os_name=Windows&os_version=10&cpu_core_num=8&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50&webid=7097114192884565534", 67 | "method":"GET" 68 | }; 69 | var h = new XMLHttpRequest; 70 | h.open(e.method, e.url, true); 71 | h.setRequestHeader("accept","application/json, text/plain, */*"); 72 | h.setRequestHeader("salute-by","lx"); 73 | h.setRequestHeader("content-type","application/json;charset=UTF-8"); 74 | h.onreadystatechange =function() { 75 | if(h.readyState === 4 && h.status ===200) { 76 | resolve(h.responseText); 77 | } else {} 78 | }; 79 | h.send(null); 80 | }); 81 | return p; 82 | } 83 | var p1 = queryData('lx'); 84 | res = Promise.all([p1]).then(function(result){ 85 | return result 86 | }) 87 | return res 88 | ''' % (keyword)) 89 | return doc[0] 90 | 91 | def close(self): 92 | self.browser.close() 93 | self.browser.quit() 94 | 95 | 96 | browser = Browser() 97 | # 注意:如果没打印内容,把url换成自己浏览器上的。还不行就在控制台上执行js看是否有误。 98 | # 版本 102.0.5005.63(正式版本) (64 位) 99 | print(browser.search_item('爬虫逆向进阶实战')) 100 | print(browser.search_item('RPC')) 101 | print(browser.search_item('案例')) 102 | print(browser.search_item('教程')) 103 | print(browser.search_item('公众号')) 104 | print(browser.search_item('pythonlx')) 105 | browser.close() 106 | ``` 107 | 108 | --- 109 | 110 | ## 执行结果 111 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f7602589b1334435a6c0b2a7df2d502c.png) 112 | 113 | 114 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.1 微博登陆参数RPC/client.js: -------------------------------------------------------------------------------- 1 | !function(){ 2 | if (window.flagLX){} 3 | else{ 4 | window.weiboLx = makeRequest; 5 | var ws = new WebSocket("ws://127.0.0.1:9999"); 6 | window.flagLX =true; 7 | ws.open = function(evt){}; 8 | ws.onmessage = function(evt){ 9 | var lx = evt.data; 10 | var result = lx.split(","); 11 | var res = window.weiboLx(result[0],result[1],7,false); 12 | ws.send(JSON.stringify(res)); 13 | }} 14 | }(); 15 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.1 微博登陆参数RPC/server.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import time 4 | 5 | async def check_permit(websocket): 6 | # 账号列表 7 | for send_text in [ 8 | '11111111111,111', 9 | '11111111112,112', 10 | '11111111113,113', 11 | '11111111114,114' 12 | ]: 13 | await websocket.send(send_text) 14 | return True 15 | 16 | async def recv_msg(websocket): 17 | while 1: 18 | recv_text = await websocket.recv() 19 | print(recv_text) 20 | 21 | async def main_logic(websocket, path): 22 | await check_permit(websocket) 23 | await recv_msg(websocket) 24 | 25 | start_server = websockets.serve(main_logic, '127.0.0.1', 9999) 26 | asyncio.get_event_loop().run_until_complete(start_server) 27 | asyncio.get_event_loop().run_forever() 28 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.2 抖音直播数据RPC/2022-05-25更新.md: -------------------------------------------------------------------------------- 1 | ## update: 2022-05-25 2 | 3 | 本文内容是继之前失效文章的补充。 RPC的方法大家应该都从书中掌握了。 (虽然官网更新了,但是没有掌握的还可以看一下视频) 4 | 5 | 6 | 所以长话短说,本篇的内容是找新版本弹幕wss协议的RPC的入口点,并进行RPC调用。 7 | 8 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/83e6658b6f3c4957ab6705a04c9925d3.png) 9 | 10 | 11 | 12 | --- 13 | 14 | ## 定位 15 | 16 | 在JS中,websocket 的 new WebSocket() 是固定的语法,可以用做我们定位的关键词。 17 | 18 | webSocket.send 用于向服务器发送数据 19 | webSocket.onopen 用于指定连接成功后的回调函数 20 | webSocket.onmessage 用于指定收到服务器数据后的回调函数 21 | webSocket.onclose 用于指定连接关闭后的回调函数 22 | websocket.binaryType = 'arraybuffer'; 用来表示通用的、固定长度的原始二进制数据缓冲区 23 | 24 | 25 | --- 26 | 27 | RPC最重要的是找到入口点。一般消息处理函数为onmessage 或者 addEventListener("message") 28 | 29 | ```js 30 | ws.onmessage = function(event) { 31 | var data = event.data; 32 | }; 33 | 34 | ws.addEventListener("message", function(event) { 35 | var data = event.data; 36 | }); 37 | ``` 38 | 39 | 全局检索一下,找到this.client.addEventListener("message" 40 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/a3f452a527644875981a38e7a79197e0.png) 41 | 断点往下走,进入bindClientMessage 42 | 43 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/ea1cbd83f5704f84b422f8e3ad07a232.png) 44 | 45 | o = n.Response.deserializeBinary 46 | 47 | 顾名思义,o是响应内容反序列化的二进制,控制台打印下,可以看到关键词WebcastSocialMessage。 48 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/58da43dd97a04e09a3370265841556a0.png) 49 | 50 | 继续往下看代码,if ("msg" === t.getPayloadType() , (()=>o.toObject())) 51 | 52 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/c40cdbcb8d684fb8a378d0e6ef0af1f2.png) 53 | 现在已经能看到一些明文信息了,但是payload的内容似乎经过base64。 54 | 55 | atob一下试试,可以看出这是protobuf序列化后的数据。 56 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/76d33472eee941c18e8e31f6cf758f75.png) 57 | 58 | 所以打好断点继续调试。 59 | 60 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/0f7ca8f1d8aa4760ab2a4c8d272156c9.png) 61 | 62 | 走到runAllEvents中,发现了protobuf反序列化后的数据。 63 | 64 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/57f3ede5764f45659b8080f4ae806e1c.png) 65 | 66 | 67 | 68 | 69 | --- 70 | 71 | 找到位置后,还是用之前的RPC方法即可。 72 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f76f8390c3f44f20b6243b9c48670f52.png) 73 | 74 | 把数据发送到本地。 75 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/42ba93680a6a4b858c44a6c863e83631.png) 76 | 77 | 78 | --- 79 | 80 | ## RPC调用 81 | 82 | 通过wss协议发数据。 83 | 84 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/22b385b8520a4168ae59c29972c45d13.png) 85 | 86 | 把发数据的代码加上,然后在本地起一个服务端。 87 | 88 | 89 | ```python 90 | import asyncio 91 | import websockets 92 | 93 | async def check_permit(websocket): 94 | send_text = 'lx' 95 | await websocket.send(send_text) 96 | return True 97 | 98 | 99 | async def recv_msg(websocket): 100 | while 1: 101 | recv_text = await websocket.recv() 102 | print(recv_text) 103 | 104 | 105 | async def main_logic(websocket, path): 106 | await check_permit(websocket) 107 | await recv_msg(websocket) 108 | 109 | 110 | start_server = websockets.serve(main_logic, '127.0.0.1', 9999) 111 | asyncio.get_event_loop().run_until_complete(start_server) 112 | asyncio.get_event_loop().run_forever() 113 | ``` 114 | 115 | 116 | 117 | 118 | --- 119 | 120 | 运行测试,大功告成! 121 | 122 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/70be2ffec3eb46bd81246109443f207f.png) 123 | 124 | 125 | 只要大家只要掌握到方法,无论网站怎么改都能从容面对。 126 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.2 抖音直播数据RPC/client.js: -------------------------------------------------------------------------------- 1 | // 这是当前新版的 2 | window.dataLx = s.toObject(); 3 | !function(){ 4 | var res = window.dataLx; 5 | if (window.flagLX){ 6 | window.wsLX.send(JSON.stringify(res)); 7 | } 8 | else{ 9 | var ws = new WebSocket("ws://127.0.0.1:9999"); 10 | window.wsLX = ws; 11 | window.flagLX = true; 12 | ws.open = function(evt){}; 13 | ws.onmessage = function(evt){ 14 | ws.send(JSON.stringify(res)); 15 | } 16 | } 17 | }(); 18 | 19 | 20 | 21 | // 这是视频中老版本的 22 | window.dataLx = r; 23 | !function(){ 24 | var res = window.dataLx; 25 | if (window.flagLX){ 26 | window.wsLX.send(JSON.stringify(res)); 27 | } 28 | else{ 29 | var ws = new WebSocket("ws://127.0.0.1:9999"); 30 | window.wsLX = ws; 31 | window.flagLX =true; 32 | ws.open = function(evt){}; 33 | ws.onmessage = function(evt){ 34 | ws.send(JSON.stringify(res)); 35 | }} 36 | }(); 37 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.2 抖音直播数据RPC/readme.md: -------------------------------------------------------------------------------- 1 | 2 | 1、更新内容在 2022-05-25更新.md 中 3 | 4 | 2、更新的视频在 https://www.bilibili.com/video/BV1o34y1L7RX/ 5 | -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.2 抖音直播数据RPC/server.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | 4 | async def check_permit(websocket): 5 | send_text = 'lx' 6 | await websocket.send(send_text) 7 | return True 8 | 9 | async def recv_msg(websocket): 10 | while 1: 11 | recv_text = await websocket.recv() 12 | print(recv_text) 13 | 14 | async def main_logic(websocket, path): 15 | await check_permit(websocket) 16 | await recv_msg(websocket) 17 | 18 | start_server = websockets.serve(main_logic, '127.0.0.1', 9999) 19 | asyncio.get_event_loop().run_until_complete(start_server) 20 | asyncio.get_event_loop().run_forever() -------------------------------------------------------------------------------- /第三章:Web Js逆向/3.9 加密方法远程调用/3.9.3 巨量指数签名RPC/readme.md: -------------------------------------------------------------------------------- 1 | (TODO:之前的版本失效了,大家查看最近提交的文件,旧版可作为参考) 2 | 3 | 对RPC来讲,需要找可调用的方法入口,像头条系这种在send后重写url的,可以通过查找xmlhttprequest堆栈来定位。 4 | 5 | 6 | ![image](https://user-images.githubusercontent.com/45314745/171310204-09a84299-544e-4fd2-bc1f-bc6cd61e2284.png) 7 | 8 | 9 | 找到h.onreadystatechange,可知h就是xmlhttprequest对象.在控制台输出可以看到responseURL中生成了带签名的url。 10 | 11 | 12 | ![image](https://user-images.githubusercontent.com/45314745/171310487-323cd907-0105-4946-b9b9-f3f88e00c4f1.png) 13 | 14 | 15 | 所以我们按照书中方法,进行模拟执行即可。 (可以看视频教程有助理解) 16 | 17 | 18 | 代码中需要安装的依赖库: requests、lxpy、selenium、Crypto 19 | 20 | Crypto库下载链接:https://pan.baidu.com/s/1xOg9qKWNsMfmWuT20Gf9FA?pwd=9999 21 | -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.0 书外新增案例/readme.md: -------------------------------------------------------------------------------- 1 | 以文章内容展示,给大家分享在逆向过程中遇到的一些签名和加密参数。 2 | 3 | -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.1 某新闻加密参数分析和还原/frida1.py: -------------------------------------------------------------------------------- 1 | import frida, sys 2 | def on_message(message, data): 3 | print("[%s] => %s" % (message, data)) 4 | 5 | session = frida.get_usb_device().attach('cn.dahebao') 6 | 7 | jscode_hook = """ 8 | Java.perform( 9 | function(){ 10 | console.log("1. start hook"); 11 | var ba = Java.use("com.dingduan.lib_network.interceptor.CommonParamInterceptor").$new(); 12 | if (ba != undefined) { 13 | console.log("2. find class"); 14 | ba.md5.implementation = function (a1) { 15 | console.log("3. find function"); 16 | console.log(a1); 17 | var res = ba.md5(a1); 18 | console.log("计算Sign:" + res); 19 | return res; 20 | } 21 | } 22 | } 23 | ) 24 | """ 25 | 26 | script = session.create_script(jscode_hook) 27 | script.on('message', on_message) 28 | script.load() 29 | sys.stdin.read() -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.2 某瓣签名Frida还原/frida1.py: -------------------------------------------------------------------------------- 1 | import frida, sys 2 | 3 | def on_message(message, data): 4 | if message['type'] == 'send': 5 | print("[*] {0}".format(message['payload'])) 6 | else: 7 | print(message) 8 | 9 | jscode_hook = """ 10 | Java.perform( 11 | function(){ 12 | console.log("1. start hook"); 13 | var ba = Java.use("com.douban.frodo.utils.crypto.HMACHash1"); 14 | if (ba != undefined) { 15 | console.log("2. find class"); 16 | ba.a.overload('java.lang.String', 'java.lang.String').implementation = function (a1,a2) { 17 | console.log("3. find function"); 18 | console.log(a1); 19 | console.log(a2); 20 | var res = ba.a(a1,a2); 21 | console.log("计算result:" + res); 22 | return res; 23 | } 24 | } 25 | } 26 | ) 27 | """ 28 | 29 | process = frida.get_usb_device().attach('com.douban.frodo') 30 | script = process.create_script(jscode_hook) 31 | script.on('message', on_message) 32 | print('[*] Hook Start Running') 33 | script.load() 34 | sys.stdin.read() -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.2 某瓣签名Frida还原/run.py: -------------------------------------------------------------------------------- 1 | import requests,time 2 | from urllib.parse import quote,urlparse 3 | import hashlib,base64,hmac 4 | 5 | headers = {"User-Agent": "api-client/1 com.douban.frodo/7.18.0(230) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi udid/bba04d59bdf9c91ef59ff80573c4480c1a661a70 platform/mobile nd/1"} 6 | 7 | def bd_request(): 8 | url = "" 9 | ts = str(round(time.time())) 10 | params ={ 11 | 'count':'30', 12 | 'tag':'追剧', 13 | 'os_rom':'miui6', 14 | 'apikey':'0dad551ec0f84ed02907ff5c42e8ec70', 15 | 'channel':'Baidu_Market', 16 | 'udid':'bba04d59bdf9c91ef59ff80573c4480c1a661a70', 17 | 'timezone':'Asia/Shanghai', 18 | "_sig": get_sig(url=url, ts=ts), 19 | "_ts": ts, 20 | } 21 | data = requests.get(url, params=params, headers=headers).text 22 | return data 23 | 24 | def get_sig(url,ts): 25 | urlpath = urlparse(url).path 26 | sign = '&'.join(['GET', quote(urlpath,safe=''), ts]) 27 | sig = hmac.new("bf7dddc7c9cfe6f7".encode(), sign.encode(), hashlib.sha1).digest() 28 | _sig = base64.b64encode(sig).decode() 29 | return _sig 30 | 31 | print(bd_request()) -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.4 某图参数Frida+Flask RPC/run.py: -------------------------------------------------------------------------------- 1 | import frida 2 | from flask import Flask, jsonify, request 3 | 4 | app = Flask(__name__) 5 | 6 | def on_message(message, data): 7 | print("[%s] => %s" % (message, data)) 8 | 9 | 10 | def start_hook(): 11 | session = frida.get_usb_device().attach('com.mt.mtxx.mtxx') 12 | js_code = ''' 13 | rpc.exports = { 14 | "a": function (kw) { 15 | var ret = {}; 16 | Java.perform(function(){ 17 | var SigEntity = Java.use("com.meitu.secret.SigEntity"); 18 | var BaseApplication = Java.use('com.meitu.library.application.BaseApplication'); 19 | var str1 = "search/feeds.json"; 20 | var str3 = "6184556633574670337"; 21 | var content = BaseApplication.getApplication(); 22 | var result = SigEntity.generatorSig(str1, kw, str3, content); 23 | ret["result"]=result.sig.value; 24 | console.log("[*] Sig:",result.sig.value); 25 | } 26 | ) 27 | return ret; 28 | } 29 | } 30 | ''' 31 | script = session.create_script(js_code) 32 | script.on('message', on_message) 33 | script.load() 34 | return script 35 | 36 | 37 | @app.route("/hook") 38 | def search(): 39 | kw = request.args.get("kw") 40 | # 因参数过长,此处代码中的params不完整 41 | params = ['1639127762948', 'CMCC', 'GMT+8',kw, ...] 42 | result = start_hook().exports.a(params) 43 | return jsonify({'result':result}) 44 | 45 | 46 | if __name__ == '__main__': 47 | app.run() -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.5 某东加密参数Unidbg生成/lxServer.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lixi5338619/lxBook/07d632eef3f7eab7258e83d22c9d9afa913c27d3/第九章:安卓逆向案例/9.5 某东加密参数Unidbg生成/lxServer.zip -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.5 某东加密参数Unidbg生成/readme.md: -------------------------------------------------------------------------------- 1 | **案例 9.3也在其中** 2 | 3 | **lxServer**: 是书中对某东APP的sign参数分析和通过Unidbg模拟调用. 4 | 5 | **unidbg-jd-sign-11**:新增的,是对11版本的调用。 6 | 7 | 8 | - 运行前准备好java环境以及maven 9 | - 按照readme中的说明修改路径和测试 10 | -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.5 某东加密参数Unidbg生成/unidbg-jd-sign-11.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lixi5338619/lxBook/07d632eef3f7eab7258e83d22c9d9afa913c27d3/第九章:安卓逆向案例/9.5 某东加密参数Unidbg生成/unidbg-jd-sign-11.zip -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.6 某资讯加固脱壳和参数分析/hook.py: -------------------------------------------------------------------------------- 1 | import frida, sys 2 | def on_message(message, data): 3 | print("[%s] => %s" % (message, data)) 4 | 5 | session = frida.get_usb_device().attach('com.hipu.yidian') 6 | 7 | jscode_hook = """ 8 | Java.perform( 9 | function(){ 10 | console.log("1. start hook"); 11 | var ba = Java.use("com.yidian.news.util.sign.SignUtil"); 12 | if (ba != undefined) { 13 | console.log("2. find class"); 14 | ba.signInternal.implementation = function (a1,a2) { 15 | console.log("3. find function"); 16 | console.log(a1); 17 | console.log(a2); 18 | var res = ba.signInternal(a1,a2); 19 | console.log("计算Sign:" + res); 20 | return res; 21 | } 22 | } 23 | } 24 | ) 25 | """ 26 | 27 | script = session.create_script(jscode_hook) 28 | script.on('message', on_message) 29 | script.load() 30 | sys.stdin.read() -------------------------------------------------------------------------------- /第九章:安卓逆向案例/9.6 某资讯加固脱壳和参数分析/rpc.py: -------------------------------------------------------------------------------- 1 | import frida 2 | 3 | 4 | def on_message(message, data): 5 | print("[%s] => %s" % (message, data)) 6 | 7 | 8 | def start_hook(): 9 | session = frida.get_usb_device().attach('com.hipu.yidian') 10 | print("[*] start hook") 11 | 12 | js_code = ''' 13 | rpc.exports = { 14 | "a": function (params) { 15 | var ret = {}; 16 | Java.perform(function(){ 17 | console.log("1. start hook"); 18 | var ba = Java.use("com.yidian.news.util.sign.SignUtil"); 19 | 20 | var current_application = Java.use('android.app.ActivityThread').currentApplication(); 21 | var a1 = current_application.getApplicationContext(); 22 | 23 | if (ba != undefined) { 24 | console.log("2. find class"); 25 | var a2 = params; 26 | var res = ba.signInternal(a1,a2); 27 | console.log("计算Sign:" + res); 28 | console.log(res) 29 | ret["result"]=res; 30 | console.log(ret) 31 | } 32 | } 33 | ) 34 | return ret; 35 | } 36 | } 37 | ''' 38 | script = session.create_script(js_code) 39 | script.on('message', on_message) 40 | script.load() 41 | return script 42 | 43 | 44 | import time 45 | 46 | stimec = str(int(time.time() * 1000)) 47 | params = f'yidian6.0.4.41kbylz0sj_{stimec}_207030300' 48 | result = start_hook().exports.a(params) 49 | print(result) -------------------------------------------------------------------------------- /第八章:抓包技巧汇总/heytap软件商店抓包.md: -------------------------------------------------------------------------------- 1 | 本文内容是 heytap软件商店抓包案例。 2 | 3 | --- 4 | 5 | 用常规的http/https工具,比如charles、fiddler去抓包时,无法正常对heytapmobi进行抓包。 6 | 7 | 会提示客户端SSL握手失败,Received fatal alert: certificate_unknown 8 | 9 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/5f2fa7e1e3104de5b9d5e88adfa9e107.png) 10 | 11 | 12 | 本以为是双向认证的原因,但是并没找到有效的客户端证书,所以开始通过其他工具进行抓包。 13 | 14 | 拿httpCanary试过,并没有拦截到。 15 | 16 | 经过一番测试发现使用NetKeeper可解决问题。 17 | 18 | --- 19 | 20 | **NetKeeper抓包精灵** 不需要ROOT,通过抓包的结果还能抓取音频以及视频。 21 | 22 | 23 | 设置一下过滤条件,方便查看数据包。 24 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/4c6080a4ddc3429b9010ac78b0a3006d.png?) 25 | 26 | 触发评论请求后,成功看到数据包。 27 | 28 | https://api-cn.store.heytapmobi.com:443/common/v1/comment/list?appId=3700438&size=10&start=40&token=-1&type=bad 29 | 30 | **Type类型:** hot、good、middle、bad 31 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/3ea8c6729c874963aa36a60b8f171744.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6ICD5Y-k5a2m5a62bHg=,size_20,color_FFFFFF,t_70,g_se,x_16) 32 | 33 | 可以发现有加密参数sign。 34 | 35 | 抓到包后,后面的操作就不再多说了,大家自行研究。 36 | 37 | --- 38 | 39 | 40 | 我在源码中也直接搜索过comment相关api,通过hook一些类也能拿到数据接口。 41 | 42 | 43 | -------------------------------------------------------------------------------- /第八章:抓包技巧汇总/readme.md: -------------------------------------------------------------------------------- 1 | 对应第八章《八:抓包技巧汇总》 2 | 3 | 大家先把书中概念性的东西了解一下 4 | 5 | 书中出现的内容就不重复贴了,这里加一些案例和知识点,持续更新 6 | -------------------------------------------------------------------------------- /第八章:抓包技巧汇总/夜神安卓7导入charles证书.md: -------------------------------------------------------------------------------- 1 | 夜神安卓7系统的charles证书导入。由于用户安装的外部证书不被信任,所以需要把SSL证书安装到安卓系统证书目录里。 2 | 3 | --- 4 | 5 | ## 一:下载证书 6 | 开启本地代理,在浏览器输入 chls.pro/ssl 下载证书到本地。 7 | 8 | 把证书原名charles-proxy-ssl-proxying-certificate.pem 先修改为 charles.pem 9 | 10 | --- 11 | 12 | ## 二:打印证书 13 | 14 | 通过openssl输出证书内容,自行安装openssl 15 | 16 | 下载地址:[https://slproweb.com/products/Win32OpenSSL.html](https://slproweb.com/products/Win32OpenSSL.html) 17 | 18 | 安装后通过 openssl command 打开command。 19 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/d4abf24ca70044fb9e277ae9da79b83b.png) 20 | 21 | 输入 `openssl x509 -inform PEM -subject_hash_old -in charles.pem` 22 | 23 | 执行后打印的结果中,第一行的90e59ded复制一下。 24 | 25 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f5f9c2e095fc49519ded35f9e4201ffd.png) 26 | 27 | 此处把证书名修改为 90e59ded.0 。 注意格式,你修改为 xxx.0 28 | 29 | 30 | --- 31 | 32 | ## 三:导入设备 33 | 34 | 不管啥方法,把证书传进设备的 /sdcard/ 文件下。 35 | 36 | 可以通过adb push,命令: `adb push xxx.0 /sdcard/` 37 | 38 | 然后进入adb shell中,adb shell 连接手机,不是root的通过命令 su 切换下,然后cd到 /sdcard/中。 39 | 40 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/c3163457f4904b40a1c7e68bc62f6a0f.png) 41 | 42 | 没问题的话将证书移动到 /system/etc/security/cacerts/ 路径下。 43 | 命令: `mv xxx.0 /system/etc/security/cacerts/` 44 | 45 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/9e127a9157be41ae861fad05223fd2c7.png) 46 | 47 | 有可能会执行失败,说只读之类的Read-only file system 48 | 可以先执行命令:`mount -o rw,remount /system` 49 | 或者执行命令:`mount -o rw,remount /` 50 | 然后再mv移动。 51 | 52 | 53 | --- 54 | 55 | 56 | ## 四:证书授权 57 | 58 | 给证书权限的执行命令:`chmod 777 /system/etc/security/cacerts/xxx.0` 59 | 60 | 重启手机:`reboot` 61 | 62 | 证书导入完成,配置下wifi代理就可以正常抓包了。 63 | 64 | -------------------------------------------------------------------------------- /第八章:抓包技巧汇总/某物app抓包.md: -------------------------------------------------------------------------------- 1 | 2 | ## 环境准备 3 | 模拟器夜神安卓7版本。配合charles的抓包步骤:[http://t.csdn.cn/lpNaV](http://t.csdn.cn/lpNaV) 4 | 5 | 模拟器需要是64位的,在商店下载推荐版本时会提示更新。 6 | 7 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/3339e3d37e9147399616e08c19e3d8fe.png) 8 | 9 | 安装后,创建64位的模拟器,从商店下载app 10 | 11 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/30fbb6bb2dfd474b9661eb8e7fcf0d0f.png) 12 | 13 | 打开后再按要求更新一下,即可成功进入APP。 14 | 15 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/29547b9260d646e6880c39b2740712a1.png) 16 | 更新后的版本:4.95.1.10 17 | 18 | 19 | --- 20 | 21 | 22 | ## No_PROXY抓包 23 | 24 | 发现搜索后没抓到有用的数据包,但是有其他包,可能这个APP采用了 NO_PROXY 的方式拒绝代理。 25 | 26 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/bb59ee050f02483f94d3e16c185a14ef.png) 27 | 28 | 那我们可以采用VPN转发的方式进行测试,如果还不行就需要分析源码进行Hook 或者更换其他类型的抓包工具。 29 | 30 | 我用 drony 转发并没有成功,所以来分析源码。 31 | 32 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e772c61b28545258f11f51649c2f734.png) 33 | com.shizhuang.duapp.common.net.DuHttpConfig.a 34 | 35 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/603a420ca40743efaf13674237315a99.png) 36 | 37 | 在本地进行hook: 38 | ```python 39 | import frida, sys 40 | 41 | def on_message(message, data): 42 | print("[%s] => %s" % (message, data)) 43 | 44 | # dewu 4.95.1.10 45 | session = frida.get_remote_device().attach('com.shizhuang.duapp') 46 | 47 | # okhttp3拦截器 48 | js_code2 = """ 49 | Java.perform(function(){ 50 | console.log("1 start hook"); 51 | var ba = Java.use('okhttp3.internal.http.RealInterceptorChain'); 52 | if (ba){ 53 | console.log("2 find class"); 54 | ba.proceed.overload('okhttp3.Request').implementation = function(a1){ 55 | console.log("3 find method"); 56 | console.log(a1); 57 | return ba.proceed(a1) 58 | } 59 | } 60 | }) 61 | """ 62 | 63 | js_code1 = """ 64 | Java.perform(function(){ 65 | console.log("1 start hook"); 66 | var ba = Java.use('com.shizhuang.duapp.common.helper.net.interceptor.HttpRequestInterceptor').$new(); 67 | if (ba){ 68 | console.log("2 find class"); 69 | ba.intercept.implementation = function(a1){ 70 | console.log("3 find method"); 71 | console.log(a1); 72 | return ba.intercept(a1) 73 | } 74 | } 75 | }) 76 | """ 77 | 78 | 79 | 80 | script = session.create_script(js_code1) 81 | script.on('message', on_message) 82 | script.load() 83 | sys.stdin.read() 84 | ``` 85 | 86 | 找到 com.shizhuang.duapp.modules.mall_search.api.ProductService.searchProductNew() 87 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/106d31369bae4d949e23e3adb85cc39b.png) 88 | 89 | -------------------------------------------------------------------------------- /第六章:安卓逆向/Unidbg/xgorgon.java: -------------------------------------------------------------------------------- 1 | package com.douyin; 2 | import com.github.unidbg.*; 3 | import com.github.unidbg.linux.android.AndroidARMEmulator; 4 | import com.github.unidbg.linux.android.AndroidEmulatorBuilder; 5 | import com.github.unidbg.linux.android.AndroidResolver; 6 | import com.github.unidbg.linux.android.dvm.*; 7 | import com.github.unidbg.memory.Memory; 8 | import com.github.unidbg.memory.MemoryBlock; 9 | import com.github.unidbg.linux.android.dvm.array.ByteArray; 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | public class xgorgon extends AbstractJni { 14 | 15 | private static LibraryResolver createLibraryResolver() { 16 | return new AndroidResolver(23); 17 | } 18 | 19 | private static AndroidEmulator createARMEmulator() { 20 | AndroidEmulator emulator = AndroidEmulatorBuilder.for32Bit().build(); 21 | return emulator; 22 | } 23 | 24 | private final AndroidEmulator emulator; 25 | private final VM vm; 26 | private final DvmClass Native; 27 | 28 | private xgorgon() { 29 | emulator = createARMEmulator(); 30 | final Memory memory = emulator.getMemory(); memory.setLibraryResolver(createLibraryResolver()); 31 | 32 | vm = emulator.createDalvikVM(new File("com.sun.jna")); 33 | vm.setJni(this); 34 | vm.setVerbose(true); // 自行修改文件路径 35 | DalvikModule dm = vm.loadLibrary(new File("C:\\Users\\feiyi\\Desktop\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\douyin\\libcms.so"), false); 36 | dm.callJNI_OnLoad(emulator); 37 | 38 | Native = vm.resolveClass("com/ss/sys/ces/a"); 39 | } 40 | private void destroy() throws IOException { 41 | emulator.close(); 42 | System.out.println("destroy"); } 43 | public static void main(String[] args) throws Exception { 44 | xgorgon test = new xgorgon(); 45 | test.test(); 46 | test.destroy(); 47 | } 48 | 49 | public static String xuzi1(byte[] bArr) { 50 | if (bArr == null) { 51 | return null; 52 | } 53 | char[] charArray = "0123456789abcdef".toCharArray(); 54 | char[] cArr = new char[(bArr.length * 2)]; 55 | for (int i = 0; i < bArr.length; i++) { 56 | int b2 = bArr[i] & 255; 57 | int i2 = i * 2; 58 | cArr[i2] = charArray[b2 >>> 4]; 59 | cArr[i2 + 1] = charArray[b2 & 15]; 60 | } 61 | return new String(cArr); } 62 | 63 | private void test() { 64 | String methodSign = "leviathan(II[B)[B"; 65 | byte[] data = "这里是url经过处理后的data".getBytes(); 66 | int time = (int) (System.currentTimeMillis() / 1000); 67 | 68 | Native.callStaticJniMethod(emulator, methodSign, -1,time,new ByteArray(vm,data)); 69 | 70 | Object ret = Native.callStaticJniMethodObject(emulator, methodSign, -1,time,new ByteArray(vm,data)); 71 | 72 | System.out.println("callObject执行结果:"+((DvmObject) ret).getValue()); 73 | byte[] tt = (byte[]) ((DvmObject) ret).getValue(); 74 | System.out.println(new String(tt)); 75 | String s = xuzi1(tt); System.out.println(s); 76 | } 77 | } -------------------------------------------------------------------------------- /第六章:安卓逆向/抖音/libcms.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lixi5338619/lxBook/07d632eef3f7eab7258e83d22c9d9afa913c27d3/第六章:安卓逆向/抖音/libcms.so -------------------------------------------------------------------------------- /第十一章:反爬虫补充/11.1 css动态字体反爬/选哪儿网动态字体.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import requests 4 | import re 5 | 6 | headers = { 7 | 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36', 8 | 'origin': 'https://www.xuannaer.com', 9 | 'referer': 'https://www.xuannaer.com/', 10 | 'sec-fetch-dest': 'font', 11 | 'sec-fetch-mode': 'cors', 12 | 'sec-fetch-site': 'same-site' 13 | } 14 | 15 | doc = requests.get('https://www.xuannaer.com/zhaopaigua',headers=headers).text 16 | 17 | # 这个链接会更新,注意下 18 | woff = re.findall('https://img2.xuanzhi.com/static/new/fonts/\w{16}/font.woff\?\d{11}',doc,re.S)[0] 19 | 20 | print(woff) 21 | woff_bytes = requests.get(woff,headers=headers,allow_redirects=True) 22 | with open('zhaopaigua.woff','wb') as f: 23 | f.write(woff_bytes.content) 24 | 25 | from fontTools.ttLib import TTFont 26 | font = TTFont('zhaopaigua.woff') 27 | font.saveXML('zhaopaigua.xml') 28 | 29 | f = open('zhaopaigua.xml','r',encoding='utf-8') 30 | document = f.read() 31 | f.close() 32 | cmap = re.findall('',document,re.S) 33 | item = {} 34 | for node in cmap: 35 | item[node[1]] = chr(eval(node[0])) 36 | 37 | GlyphID = re.findall('',document,re.S)[1:] 38 | 39 | # 只要数字 取0-9 前10个元素 40 | result = {} 41 | for li in GlyphID[:10]: 42 | num = int(li[0])-1 # 正确的数字 43 | result[item[li[1]]]=num 44 | print(result) -------------------------------------------------------------------------------- /第十一章:反爬虫补充/11.2 tls指纹识别/aiohttp_ja3.py: -------------------------------------------------------------------------------- 1 | import random 2 | import ssl 3 | # aiohttp 修改 ja3 4 | 5 | class DESAdapter(): 6 | def __init__(self): 7 | self.ORIGIN_CIPHERS = ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES') 8 | 9 | def __call__(self): 10 | CIPHERS = self.ORIGIN_CIPHERS.split(':') 11 | random.shuffle(CIPHERS) 12 | CIPHERS = ':'.join(CIPHERS) 13 | CIPHERS = CIPHERS + ':!aNULL:!eNULL:!MD5' 14 | context = ssl.create_default_context() 15 | context.set_ciphers(CIPHERS) 16 | return context 17 | 18 | import aiohttp,asyncio 19 | async def main(): 20 | async with aiohttp.ClientSession() as session: 21 | for req in range(5): 22 | async with session.get("https://ja3er.com/json",ssl=Adapter()) as res: 23 | data = await res.json() 24 | print(data) 25 | 26 | if __name__ == '__main__': 27 | Adapter = DESAdapter() 28 | m = asyncio.get_event_loop() 29 | m.run_until_complete(main()) -------------------------------------------------------------------------------- /第十一章:反爬虫补充/11.2 tls指纹识别/requests_jar3.py: -------------------------------------------------------------------------------- 1 | from requests.adapters import HTTPAdapter 2 | from urllib3.util.ssl_ import create_urllib3_context 3 | import random,requests 4 | 5 | class DESAdapter(HTTPAdapter): 6 | def __init__(self, *args, **kwargs): 7 | self.CIPHERS = self.get_ciphers() 8 | super().__init__(*args, **kwargs) 9 | 10 | def get_ciphers(self): 11 | ORIGIN_CIPHERS = ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' 12 | 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES') 13 | CIPHERS = ORIGIN_CIPHERS.split(':') 14 | random.shuffle(CIPHERS) 15 | CIPHERS = ':'.join(CIPHERS) 16 | CIPHERS = CIPHERS + ':!aNULL:!eNULL:!MD5' 17 | return CIPHERS 18 | 19 | def init_poolmanager(self, *args, **kwargs): 20 | context = create_urllib3_context(ciphers=self.CIPHERS) 21 | kwargs['ssl_context'] = context 22 | return super(DESAdapter, self).init_poolmanager(*args, **kwargs) 23 | 24 | 25 | if __name__ == '__main__': 26 | sess = requests.Session() 27 | while 1: 28 | sess.mount('https://ja3er.com', DESAdapter()) 29 | res = sess.get('https://ja3er.com/json').json() 30 | print(res) -------------------------------------------------------------------------------- /第十一章:反爬虫补充/11.2 tls指纹识别/scrapy_Ja3_download.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Optional 3 | import random 4 | import ssl 5 | from twisted.internet.defer import Deferred 6 | from scrapy import signals 7 | from scrapy.http import Request, Response, Headers 8 | from scrapy.spiders import Spider 9 | from scrapy.settings import Settings 10 | from scrapy.crawler import Crawler 11 | from scrapy.utils.defer import deferred_from_coro, deferred_f_from_coro_f 12 | from scrapy.responsetypes import responsetypes 13 | from scrapy.core.downloader.handlers.http import HTTPDownloadHandler 14 | import aiohttp 15 | 16 | 17 | ORIGIN_CIPHERS = ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' 18 | 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES') 19 | 20 | 21 | class SSLFactory: 22 | def __init__(self): 23 | self.ciphers = ORIGIN_CIPHERS.split(":") 24 | 25 | def __call__(self) -> ssl.SSLContext: 26 | random.shuffle(self.ciphers) 27 | ciphers = ":".join(self.ciphers) 28 | ciphers = ciphers + ":!aNULL:!eNULL:!MD5" 29 | 30 | context = ssl.create_default_context() 31 | context.set_ciphers(ciphers) 32 | return context 33 | 34 | 35 | sslgen = SSLFactory() 36 | 37 | 38 | class Ja3DownloadHandler(HTTPDownloadHandler): 39 | def __init__(self, settings: Settings, crawler: Optional[Crawler] = None): 40 | super().__init__(settings, crawler) 41 | #super().__init__(settings) 42 | self.client = None 43 | crawler.signals.connect(self._engine_started, signals.engine_started) 44 | 45 | @deferred_f_from_coro_f 46 | async def _engine_started(self, signal, sender): 47 | client = aiohttp.ClientSession() 48 | self.client = await client.__aenter__() 49 | 50 | def download_request(self, request: Request, spider: Spider) -> Deferred: 51 | if request.meta.get("ja3"): # 注意大小写 52 | return deferred_from_coro(self._download_request(request, spider)) 53 | return super().download_request(request, spider) # 普通下载 54 | 55 | async def _download_request(self, request: Request, spider: Spider) -> Response: 56 | """aiohttp下载逻辑""" 57 | async with self.client.request(request.method, 58 | request.url, 59 | data=request.body, 60 | headers=request.headers.to_unicode_dict(), 61 | cookies=request.cookies, 62 | ssl=sslgen()) as response: 63 | headers = Headers(response.headers) 64 | body = await response.read() 65 | respcls = responsetypes.from_args(headers=headers, 66 | url=str(response.url), 67 | body=body) 68 | return respcls(url=str(response.url), 69 | status=response.status, 70 | headers=headers, 71 | body=body, 72 | flags=["ja3"], 73 | request=request, 74 | protocol=response.version) 75 | 76 | @deferred_f_from_coro_f 77 | async def close(self): 78 | await self.client.__aexit__(None, None, None) 79 | await super().close() 80 | -------------------------------------------------------------------------------- /第十一章:反爬虫补充/11.3 http2/scrapy_http2_download.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Optional 3 | 4 | from twisted.internet.defer import Deferred 5 | 6 | from scrapy import signals 7 | from scrapy.http import Request, Response, Headers 8 | from scrapy.spiders import Spider 9 | from scrapy.settings import Settings 10 | from scrapy.crawler import Crawler 11 | from scrapy.utils.defer import deferred_from_coro, deferred_f_from_coro_f 12 | from scrapy.responsetypes import responsetypes 13 | from scrapy.core.downloader.handlers.http import HTTPDownloadHandler 14 | import httpx 15 | 16 | 17 | class HttpxDownloadHandler(HTTPDownloadHandler): 18 | def __init__(self, settings: Settings, crawler: Optional[Crawler] = None): 19 | super().__init__(settings, crawler) 20 | self.client = None 21 | crawler.signals.connect(self._engine_started, signals.engine_started) 22 | 23 | @deferred_f_from_coro_f 24 | async def _engine_started(self, signal, sender): 25 | client = httpx.AsyncClient(http2=True) 26 | self.client = await client.__aenter__() 27 | 28 | def download_request(self, request: Request, spider: Spider) -> Deferred: 29 | if request.meta.get("h2"): 30 | return deferred_from_coro(self._download_request(request, spider)) 31 | return super().download_request(request, spider) # 普通下载 32 | 33 | async def _download_request(self, request: Request, spider: Spider) -> Response: 34 | """httpx下载逻辑""" 35 | response = await self.client.request(request.method, 36 | request.url, 37 | content=request.body, 38 | headers=request.headers.to_unicode_dict(), 39 | cookies=request.cookies) 40 | headers = Headers(response.headers) 41 | respcls = responsetypes.from_args(headers=headers, 42 | url=response.url, 43 | body=response.content) 44 | return respcls(url=str(response.url), 45 | status=response.status_code, 46 | headers=headers, 47 | body=response.content, 48 | flags=["httpx"], 49 | request=request, 50 | protocol=response.http_version) 51 | 52 | @deferred_f_from_coro_f 53 | async def close(self): 54 | await self.client.__aexit__() 55 | await super().close() -------------------------------------------------------------------------------- /第十章:验证码识别技术/10.2.1.1 邮箱滑块验证码.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from io import BytesIO 3 | import cv2,numpy as np, random, requests, time 4 | from selenium.webdriver import ActionChains 5 | import warnings 6 | from PIL import Image 7 | warnings.filterwarnings("ignore",category=DeprecationWarning) 8 | 9 | '''识别缺口位置、计算偏移值''' 10 | class SlideCrack(object): 11 | def __init__(self, gap, bg, out=None): 12 | """ 13 | init code 14 | :param gap: 缺口图片 15 | :param bg: 背景图片 16 | :param out: 输出图片 17 | """ 18 | self.gap = gap 19 | self.bg = bg 20 | self.out = out 21 | 22 | @staticmethod 23 | def clear_white(img): 24 | # 清除图片的空白区域,这里主要清除滑块的空白 25 | img = cv2.imdecode(np.frombuffer(img,np.uint8), cv2.IMREAD_UNCHANGED) 26 | rows, cols, channel = img.shape 27 | min_x = 255 28 | min_y = 255 29 | max_x = 0 30 | max_y = 0 31 | for x in range(1, rows): 32 | for y in range(1, cols): 33 | t = set(img[x, y]) 34 | if len(t) >= 2: 35 | if x <= min_x: 36 | min_x = x 37 | elif x >= max_x: 38 | max_x = x 39 | 40 | if y <= min_y: 41 | min_y = y 42 | elif y >= max_y: 43 | max_y = y 44 | img1 = img[min_x:max_x, min_y: max_y] 45 | return img1 46 | 47 | def template_match(self, tpl, target): 48 | th, tw = tpl.shape[:2] 49 | result = cv2.matchTemplate(target, tpl, cv2.TM_CCOEFF_NORMED) 50 | # 寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置 51 | min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) 52 | tl = max_loc 53 | # br = (tl[0] + tw, tl[1] + th) 54 | # 绘制矩形边框,将匹配区域标注出来 55 | # target:目标图像 56 | # tl:矩形定点 57 | # br:矩形的宽高 58 | # (0,0,255):矩形边框颜色 59 | # 1:矩形边框大小 60 | # cv2.rectangle(target, tl, br, (0, 0, 255), 2) 61 | # if self.out: 62 | # cv2.imwrite(self.out, target) 63 | return tl[0] 64 | 65 | @staticmethod 66 | def image_edge_detection(img): 67 | edges = cv2.Canny(img, 100, 200) 68 | return edges 69 | 70 | def discern(self): 71 | img1 = self.clear_white(self.gap) 72 | img1 = cv2.cvtColor(img1, cv2.COLOR_RGB2GRAY) 73 | slide = self.image_edge_detection(img1) 74 | back = cv2.cvtColor(np.asarray(Image.open(BytesIO(self.bg))), cv2.COLOR_RGB2GRAY) 75 | back = self.image_edge_detection(back) 76 | slide_pic = cv2.cvtColor(slide, cv2.COLOR_GRAY2RGB) 77 | back_pic = cv2.cvtColor(back, cv2.COLOR_GRAY2RGB) 78 | x = self.template_match(slide_pic, back_pic) # 滑块偏移值 79 | # 输出横坐标, 即 滑块在图片上的位置 80 | return x 81 | 82 | 83 | def get_distance(gap, bg): 84 | """ 85 | 计算滑动距离 86 | """ 87 | with open(gap,'rb') as f: 88 | gap = f.read() 89 | with open(bg,'rb') as f: 90 | bg = f.read() 91 | sc = SlideCrack(gap, bg, "image/r.png") 92 | distance = int(sc.discern() // 2.4) 93 | return distance 94 | 95 | 96 | '''模拟验证行为''' 97 | def main(): 98 | driver = webdriver.Chrome(executable_path=r'chromedriver.exe') 99 | driver.get('https://mail.qq.com') 100 | time.sleep(1) 101 | driver.switch_to.frame("login_frame") 102 | time.sleep(1) 103 | driver.find_element_by_id('switcher_plogin').click() 104 | 105 | username = '1234567{}@qq.com'.format(random.randint(0, 99)) 106 | driver.find_element_by_id("u").send_keys(username) 107 | driver.find_element_by_id("p").send_keys('wwwwwwwww') 108 | time.sleep(1) 109 | driver.find_element_by_id("login_button").click() 110 | time.sleep(1) 111 | 112 | driver.switch_to.frame(driver.find_element_by_id('tcaptcha_iframe')) 113 | time.sleep(1) 114 | src_big = driver.find_element_by_id('slideBg').get_attribute('src') 115 | time.sleep(1) 116 | src_small = driver.find_element_by_id('slideBlock').get_attribute('src') 117 | img_big = requests.get(src_big).content 118 | img_small = requests.get(src_small).content 119 | with open('image/yanzhengtu.jpg', 'wb') as f: 120 | f.write(img_big) 121 | with open('image/huakuai.png', 'wb') as f: 122 | f.write(img_small) 123 | 124 | y = get_distance('image/huakuai.png','image/yanzhengtu.jpg') 125 | time.sleep(2) 126 | huakuai = driver.find_element_by_id('tcaptcha_drag_thumb') 127 | action = ActionChains(driver) 128 | action.click_and_hold(huakuai).perform() 129 | action.move_by_offset(y-30, 0).perform() 130 | action.release(on_element=huakuai).perform() 131 | time.sleep(3) 132 | driver.close() 133 | 134 | if __name__ == '__main__': 135 | main() 136 | -------------------------------------------------------------------------------- /第四章:自动化工具的应用/4.2.4 Pyppeteer拦截器/test.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | const puppeteer = require("puppeteer-extra"); 3 | const StealthPlugin = require("puppeteer-extra-plugin-stealth"); 4 | puppeteer.use(StealthPlugin()); 5 | 6 | var apage; 7 | var message_js = "js content"; 8 | puppeteer.launch({ 9 | userDataDir: "data", 10 | ignoreDefaultArgs: ["--enable-automation"], 11 | headless: false, 12 | }).then(browser => { 13 | browser.newPage().then((page) => { 14 | apage = page; 15 | page.setRequestInterception(true).then(() => { 16 | page.on('request',async (req) => { 17 | if (req.url().indexOf("https://www.lx.js") != -1) { 18 | req.respond({ 19 | status:200, 20 | headers:{}, 21 | body:message_js 22 | }) 23 | } 24 | else if (req.url().indexOf("https://www.lx.png") != -1) { 25 | req.abort() 26 | } 27 | page.on('response', async response => { 28 | if (response.url().indexOf("https://www.lx.json/") != -1) { 29 | let message = response.text(); 30 | message.then(res =>{ 31 | console.log(res) 32 | }) 33 | .catch(err =>{ 34 | console.log(err) 35 | }) 36 | } 37 | }) 38 | req.continue(); 39 | }); 40 | }) 41 | page.goto("https://www.lx.com") 42 | 43 | }, {waitUntil: "networkidle0"}).then(() => { 44 | }) 45 | }); -------------------------------------------------------------------------------- /第四章:自动化工具的应用/4.2.4 Pyppeteer拦截器/test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pyppeteer import launch 3 | 4 | async def main(): 5 | browser = await launch() 6 | page = await browser.newPage() 7 | # 设置 request 拦截器 8 | await page.setRequestInterception(True) 9 | page.on('request', lambda req: asyncio.ensure_future(intercept_request(req))) 10 | # 设置 response 拦截器 11 | page.on('response', lambda rep: asyncio.ensure_future(intercept_response(rep))) 12 | await page.goto('https://www.baidu.com') 13 | await browser.close() 14 | 15 | # 请求拦截器 16 | async def intercept_request(Request): 17 | if Request.url=='': 18 | # 跳转url 19 | await Request.continue_({"url": ""}) 20 | elif Request.url =='': 21 | # 停止请求 22 | await Request.abort() 23 | elif Request.url =='': 24 | # 用给定的响应内容完成请求 25 | await Request.respond({"status":"","body":""}) 26 | else: 27 | # 保持请求 28 | await Request.continue_() 29 | 30 | # 响应拦截器 31 | async def intercept_response(Response): 32 | print(Response.url) 33 | if Response.url=="https://www.baidu.com": 34 | response = await Response.text() 35 | ... 36 | 37 | asyncio.get_event_loop().run_until_complete(main()) -------------------------------------------------------------------------------- /第四章:自动化工具的应用/4.7.3 autojs指数查询案例/test.js: -------------------------------------------------------------------------------- 1 | app.launchApp('微信'); 2 | 3 | //跳转到首页 4 | function goToHomePage(){ 5 | let k = 0; 6 | while(k < 30){ 7 | k = k + 1; 8 | if(text("微信").depth(10).exists()){ 9 | toast('到首页'); 10 | break; 11 | }else{ 12 | back(); 13 | sleep(1000*0.2); 14 | } 15 | } 16 | } 17 | 18 | //跳转到指数页面 19 | function goToZhiShuPage(){ 20 | var it = className("android.widget.ImageView").depth(17).findOne(); 21 | var b = it.bounds(); 22 | click(b.centerX(), b.centerY()); 23 | sleep(1500); 24 | 25 | //跳转到 微信指数 小程序 26 | id('m7').findOne().setText('微信指数'); 27 | sleep(1000); 28 | 29 | var wxzs = text('微信指数').depth(14).findOne(); 30 | var bb = wxzs.bounds(); 31 | click(bb.centerX(), bb.centerY()); 32 | } 33 | 34 | //根据关键字查询 35 | function getZhiShuForKey(tkey){ 36 | sleep(1000*1.5); 37 | var wxzs = text('搜索').findOne(); 38 | var bb = wxzs.bounds(); 39 | click(bb.centerX(), bb.centerY()); 40 | 41 | className('android.widget.EditText').findOne().setText(tkey); 42 | sleep(1000); 43 | 44 | //点击键盘上面的'搜索'按钮 45 | click(device.width - 16, device.height - 16); 46 | sleep(1000*2); 47 | 48 | //获取关键字和指数 49 | let findData = {keyword:tkey, //关键字 50 | dataKey:'', //时间戳,显示在界面上的时间 51 | zhishu:'' //指数值 52 | }; 53 | 54 | try { 55 | findData.dataKey = className('android.view.View').findOne().text(); 56 | } catch (error) { 57 | toast(error); 58 | exit(); 59 | } 60 | toast('日期' + findData.dataKey + ', 指数' + findData.zhishu); 61 | } 62 | 63 | // “微信指数” 64 | if(1){ 65 | goToHomePage(); 66 | goToZhiShuPage(); 67 | getZhiShuForKey('Lx'); 68 | sleep(1000); 69 | } -------------------------------------------------------------------------------- /附录(一些经验)/readme.md: -------------------------------------------------------------------------------- 1 | 我觉得附录才是对大家帮助最大的东西,可惜出版社开源节流,没有印在书中。 2 | 3 | 因为这里面是我自身经验的总结,比如检索技巧和面试之谈。 4 | 5 | 如果你看到了这里,一定不要错过! 6 | 7 | -------------------------------------------------------------------------------- /附录(一些经验)/检索技巧.md: -------------------------------------------------------------------------------- 1 | ## 爬虫工程师应该掌握的检索技巧 2 | 3 | 一般来说我们要做的事情前人已经完成过,其实不需要自己动手去逆向,找到前人的代码合理更改能极大程度地提高工作效率,我分享一下互联网中的信息检索技巧。 4 | 5 | ![image](https://user-images.githubusercontent.com/45314745/171538214-e80b534a-bbaa-4c4a-a1cd-3cf47231b8c2.png) 6 | 7 | 我在搜代码的时候,一般先会搜项目名,比如xx爬虫,找不到的话就会根据加密参数的名称、接口的关键词到Code中去搜。 8 | 9 | 比如某个参数叫做x-gorgon,就可以在Code中搜索关键词x-gorgon或者xgorgon,然后选择你想看的编程语言,再Sort中选择Recently indexed,按最近更新来检索。 10 | 11 | 比如接口的链接中有一个关键词UserByScreenName,那就搜索/UserByScreenName,然后选择语言和排序。 12 | 13 | ![image](https://user-images.githubusercontent.com/45314745/171538280-430613f5-8e2c-498e-b9e0-60cef7c76c7e.png) 14 | 15 | 这样检索大概率能找到前人已经完成的代码,你在此基础上开发能节省非常多的时间,同时多阅读别人的代码能让自己的编码技术提升得很快。 16 | 17 | 当然也不是要大家无脑检索,花十多分钟找不到的话就自己动手开发吧。 18 | 19 | 另外也不要过度依赖开源库,尽管能快速完成任务很好,但是找到了代码后自己跟着流程复现一遍才是最重要的。 20 | 21 | 22 | ## Csdn的检索技巧 23 | Csdn也是一个非常庞大的代码平台,我们需要合理的检索才能找到自己想要的东西。 24 | 25 | 现在搜索框中有高级检索,可以筛选搜索类型和发布时间,这样能有效检索。 26 | 27 | ![image](https://user-images.githubusercontent.com/45314745/171538392-b4abb724-f416-410f-9af0-4e9917ee875b.png) 28 | 29 | ## 经验分享 30 | 31 | 百度、谷歌、微信搜一搜这种搜索引擎的技巧就不多说了,相信大家使用的也非常熟练。 32 | 33 | 不过有时候我们检索数据并不是为了找代码,而是找数据源。 34 | 35 | 我在近两年遇到的某些数据采集需求,并没有明确的采集目标,数据源需要自己去检索。 36 | 37 | 比如说找多少年的文献数据、找某国家某地区的企业数据、找一些高端人才数据。 38 | 39 | 这些内容没有一个明确的数据聚合网站,只能通过搜索引擎去查资源。你需要熟练使用搜索引擎,细心观察,比如可能在小众站点或者暗网中。 40 | 41 | 这其实已经脱离了爬虫工程师的业务范畴,但是一个合格的开发者也应该具备全网检索的能力,所以大家平时要注重积累,最好是搭建一个自己的备忘录,或者把一些东西记录到博客中。 42 | -------------------------------------------------------------------------------- /附录(一些经验)/面试之谈.md: -------------------------------------------------------------------------------- 1 | ## 总结了一些面试经验分享给大家 2 | 3 | 其实我本身并没有换几次工作,但是参加的面试次数可以称得上是不胜枚举,因为只想找一个自己喜欢的工作环境和生活节奏,所以会给自己获取更多的选择。 4 | 5 | 建议大家每年参与几次面试或者旁听他人面试,可以看一看最新的行业发展和岗位需求,以致于更好的提升自己。 6 | 7 | 8 | ### 面试前你需要提前准备好: 9 | - 一份赏心悦目的简历。 10 | - 一个内容丰富的博客。 11 | - 一些体现技术的项目。 12 | - 一段清晰的自我介绍。 13 | 14 | 15 | 16 | ```diff 17 | + 现在技术类职位需求很多,但是求职者的数量更多,记得当年一个HR让我把简历发私人邮箱,他说一天内收到的简历超过了某直聘的上限。 18 | - 如何让HR筛选到你的简历多看两眼是什么重要的,所以简历结构需要清晰明了,能让人一眼就确定是符合岗位需求的人。 19 | 20 | + 个人博客是需要花时间运营的,但也并不是每次都需要长篇大论,只要能记录下来自己的经验或者技术点就好,更多的用途是让自己在以后的复盘中可以快速总结。 21 | - 当然高质量的个人博客能给面试加更多分,毕竟简历内容有限,技术的知识点也是有限的,当面试官根据你的博客提问时,想必你能回答的更加得心应手。 22 | 23 | + 之前做过的项目一定要整理清晰,当被问到如何做某项目时,最好能把项目架构、项目开发难点、问题解决方法等流畅地讲出来。 24 | 比如说到采集某APP的数据时,通过逆向分析解决了某些加密或者某种风控。 25 | 在逆向的过程中对该APP的架构也进行了分析,发现该APP采用okhttp请求框架,ffmpeg音视频处理库、robust热更新、bspatch增量更新、某些sdk、某种消息传输协议等。 26 | 这些开源库或者框架我们并不需要去掌握多少,大致知道是什么用途的就可以,这样的项目描述无疑能给面试官增加N点好感度。 27 | 28 | + 一段清晰的自我介绍是很重要的,自我介绍能快速体现出一个人的个人能力和素质水平,而第一印象一旦形成就很难改变。 29 | 所以我们需要简洁明了地介绍自己的履历和最近负责的项目,当然可以提前准备好稿子,在面试前多背两次。 30 | ``` 31 | --------------------------------------------------------------------------------