├── .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 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
--------------------------------------------------------------------------------
/第三章: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 | 
21 |
22 | 断点后调试,可看到t即是XMLHttpRequest。
23 |
24 | 
25 |
26 | 查看t对象。
27 |
28 | 
29 |
30 |
31 | 在浏览器中调试。
32 |
33 | 
34 |
35 |
36 | 查看响应。 当前已经返回了responseText和带了签名的responseURL。
37 |
38 | 
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 | 
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 | 
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 | 
41 | 断点往下走,进入bindClientMessage
42 |
43 | 
44 |
45 | o = n.Response.deserializeBinary
46 |
47 | 顾名思义,o是响应内容反序列化的二进制,控制台打印下,可以看到关键词WebcastSocialMessage。
48 | 
49 |
50 | 继续往下看代码,if ("msg" === t.getPayloadType() , (()=>o.toObject()))
51 |
52 | 
53 | 现在已经能看到一些明文信息了,但是payload的内容似乎经过base64。
54 |
55 | atob一下试试,可以看出这是protobuf序列化后的数据。
56 | 
57 |
58 | 所以打好断点继续调试。
59 |
60 | 
61 |
62 | 走到runAllEvents中,发现了protobuf反序列化后的数据。
63 |
64 | 
65 |
66 |
67 |
68 |
69 | ---
70 |
71 | 找到位置后,还是用之前的RPC方法即可。
72 | 
73 |
74 | 把数据发送到本地。
75 | 
76 |
77 |
78 | ---
79 |
80 | ## RPC调用
81 |
82 | 通过wss协议发数据。
83 |
84 | 
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 | 
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 | 
7 |
8 |
9 | 找到h.onreadystatechange,可知h就是xmlhttprequest对象.在控制台输出可以看到responseURL中生成了带签名的url。
10 |
11 |
12 | 
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 | 
10 |
11 |
12 | 本以为是双向认证的原因,但是并没找到有效的客户端证书,所以开始通过其他工具进行抓包。
13 |
14 | 拿httpCanary试过,并没有拦截到。
15 |
16 | 经过一番测试发现使用NetKeeper可解决问题。
17 |
18 | ---
19 |
20 | **NetKeeper抓包精灵** 不需要ROOT,通过抓包的结果还能抓取音频以及视频。
21 |
22 |
23 | 设置一下过滤条件,方便查看数据包。
24 | 
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 | 
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 | 
20 |
21 | 输入 `openssl x509 -inform PEM -subject_hash_old -in charles.pem`
22 |
23 | 执行后打印的结果中,第一行的90e59ded复制一下。
24 |
25 | 
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 | 
41 |
42 | 没问题的话将证书移动到 /system/etc/security/cacerts/ 路径下。
43 | 命令: `mv xxx.0 /system/etc/security/cacerts/`
44 |
45 | 
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 | 
8 |
9 | 安装后,创建64位的模拟器,从商店下载app
10 |
11 | 
12 |
13 | 打开后再按要求更新一下,即可成功进入APP。
14 |
15 | 
16 | 更新后的版本:4.95.1.10
17 |
18 |
19 | ---
20 |
21 |
22 | ## No_PROXY抓包
23 |
24 | 发现搜索后没抓到有用的数据包,但是有其他包,可能这个APP采用了 NO_PROXY 的方式拒绝代理。
25 |
26 | 
27 |
28 | 那我们可以采用VPN转发的方式进行测试,如果还不行就需要分析源码进行Hook 或者更换其他类型的抓包工具。
29 |
30 | 我用 drony 转发并没有成功,所以来分析源码。
31 |
32 | 
33 | com.shizhuang.duapp.common.net.DuHttpConfig.a
34 |
35 | 
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 | 
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 | 
6 |
7 | 我在搜代码的时候,一般先会搜项目名,比如xx爬虫,找不到的话就会根据加密参数的名称、接口的关键词到Code中去搜。
8 |
9 | 比如某个参数叫做x-gorgon,就可以在Code中搜索关键词x-gorgon或者xgorgon,然后选择你想看的编程语言,再Sort中选择Recently indexed,按最近更新来检索。
10 |
11 | 比如接口的链接中有一个关键词UserByScreenName,那就搜索/UserByScreenName,然后选择语言和排序。
12 |
13 | 
14 |
15 | 这样检索大概率能找到前人已经完成的代码,你在此基础上开发能节省非常多的时间,同时多阅读别人的代码能让自己的编码技术提升得很快。
16 |
17 | 当然也不是要大家无脑检索,花十多分钟找不到的话就自己动手开发吧。
18 |
19 | 另外也不要过度依赖开源库,尽管能快速完成任务很好,但是找到了代码后自己跟着流程复现一遍才是最重要的。
20 |
21 |
22 | ## Csdn的检索技巧
23 | Csdn也是一个非常庞大的代码平台,我们需要合理的检索才能找到自己想要的东西。
24 |
25 | 现在搜索框中有高级检索,可以筛选搜索类型和发布时间,这样能有效检索。
26 |
27 | 
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 |
--------------------------------------------------------------------------------