逆向系列
Chrome开发者工具介绍
相关信息
Elements(元素面板) Console(控制台面板) Sources(源代码面板) Network (网络面板) Performance(性能面板) Memory (内存面板) Application(应用面板) Security (安全面板) Audits(审核面板)
- Elements(元素面板)∶使用“元素”面板可以通过自由操纵DOM和cSS来重演您网站的布局和设计。
- Console(控制台面板)﹔在开发期间,可以使用控制台面板记录诊断信息,或者使用它作为shell,在页面上与JavaScript交互
- Sources(源代码面板)﹔在源代码面板中设置断点来调试JavaScript,或者通过Workspaces (工作区)连接本地文件来使用开发者工具的实时编辑器
- Network(网络面板):从发起网页页面请求Request后得到的各个请求资源信息(包括状态.资源类型、大小、所用时间等),并可以根据这个进行网络性能优化
Network面板使用
请求排序默认以升序排序(最先请求的在最上面) 1.Controls(控制器):使用这些选项可以控制Network面板的外观和功能 2.Filters(过滤器):使用这些选项可以控制在 Requests Table中显示哪些资源。提示:按住Cmd(Mac)或ctrl (Windows/Linux)并点击过滤器可以同时选择多个过滤器 3.Overview(概览):此图表显示了资源检索时间的时间线。如果您看到多条竖线堆叠在一起,则说明这些资源被同时检索 4.Requests Table(请求列表):此表格列出了检索的每一个资源。默认情况下,此表格按时间顺序排序,最早的资源在顶部。点击资源的名称可以显示更多信息。提示:右键点击Timeline 以外的任何一个表格标题可以添加或移除信息列 5.Summary(概要)﹔此窗格可以一目了然地告诉您请求总数、传输的数据量和加载时间
过滤器
domain:仅显示来自指定域的资源。您可以使用通配符(()来包括多个域。例如,.com显示以.com结尾的所有域名中的资源。DevTools会在自动完成下拉菜单中自动填充它遇到的所有域。 has-response-header:显示包含指定HTTP响应头信息的资源。DevTools会在自动完成下拉菜单中自动填充它遇到的所有响应头。 is:通过is:running找出WebSocket请求。 larger-than(大于):显示大于指定大小的资源(以字节为单位)。设置值10o0等效于设置值1k。 method(方法):显示通过指定的HTTP方法类型检索的资源。DevTools使用它遇到的所有HTTP方法填充下拉列表。 mime-type (mime类型:显示指定MIME类型的资源。DevTools使用它遇到的所有MIME类型填充下拉列表。 mixed-content(混合内容:显示所有混合内容资源( mixed-content:all)或仅显示当前显示的内容( mixed-content:displayed) 。 scheme (协议):显示通过不受保护的HTTP (scheme:http)或受保护的HTTPS(scheme:https)检索的资源。 set-cookie-domain (cookie域)﹔显示具有Set-Cookie头,并且其Domain属性与指定值匹配的资源。DevTools会在自动完成下拉菜单中自动填充它遇到的所有cookie域。 set-cookie-name (cookie名):显示具有set-Cookie头,并且名称与指定值匹配的资源。DevTools会在自动完成下拉菜单中自动填充它遇到的所有Cookie名。 set-cookie-value (cookie值):显示具有set-Cookie头,并且值与指定值匹配的资源。DevTools会在自动完成下拉菜单中自动填充它遇到的所有cookie值。 status-code(状态码)﹔仅显示其HTTP状态代码与指定代码匹配的资源。DevTools会在自动完成下拉菜单中自动填充它遇到的所有状态码。
快速编写爬虫代码
从浏览器请求中复制出
curl 'https://www.twincn.com/item.aspx?no=00713302' \
-H 'authority: www.twincn.com' \
-H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
-H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
-H 'cache-control: max-age=0' \
-H 'cookie: _gid=GA1.2.1033410551.1670461962; ASP.NET_SessionId=ljnhq023rpue0tgy3eqixoia; _ga_JJ8ZENTE7M=GS1.1.1670481408.38.1.1670483660.0.0.0; _gat_gtag_UA_15352051_32=1; _ga=GA1.1.1558928701.1662084209' \
-H 'sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: document' \
-H 'sec-fetch-mode: navigate' \
-H 'sec-fetch-site: none' \
-H 'sec-fetch-user: ?1' \
-H 'upgrade-insecure-requests: 1' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.42' \
--compressed
复制到postman后,继续 自动将参数填好了,然后发送请求,就可以得到正确response
导出 postman中的请求 选择格式
然后复制出去
import requests
url = "https://www.twincn.com/item.aspx?no=00713302"
payload={}
headers = {
'authority': 'www.twincn.com',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'max-age=0',
'cookie': '_gid=GA1.2.1033410551.1670461962; ASP.NET_SessionId=ljnhq023rpue0tgy3eqixoia; _ga_JJ8ZENTE7M=GS1.1.1670481408.38.1.1670483660.0.0.0; _gat_gtag_UA_15352051_32=1; _ga=GA1.1.1558928701.1662084209',
'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.42'
}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
Source面板使用
看下一篇文章(找到加密位置) 找到加密位置
在 Source面板中找到Overrides子面板。 点击Select folder for Overrides 选择一个目录,作为文件的存储目录,同时确保打开了Enable Local Overrides。 在Network面板,选中一个请求右键,点击Save for overrides。 DevTools会在本地创建一个与请求内容相同的文件,并在 Source面板中打开,编辑完刷新页面以查看效果。 调试面板
Watch -变量监听 Call Stack一断点的调用栈列表Scope-断点所在作用域的内容 Breakpoints-断点列表 XHR/fetch Breakpoints-请求断点列表 DOM Breakpoints - Dom断点列表 Event Listener Breakpoints一可断点的事件监听列表
Console面板使用
console.info -向控制台输出提示信息
console.error -向控制台输出错误信息
console.warn -向控制台输出警示信息
console.assert -断言
Console.table # 有助于查看结构化的数据(json)
Copy(...)
console 中的$
Console 中使用xpath查询DOM $x()

常用工具介绍
Charles
HTTP抓包工具 支持 Windows、Linux、Mac 功能介绍
- 截获请求
- 过滤请求
- 重发请求
- 设置断点
- 端口转发
- 反向代理
配置证书详见 引用文章中的charles安装和使用 抓包工具和PyExeJs模块
重发请求

编辑请求

设置断点
模拟网速
反向代理

Toggle JavaScript
一个Chrome插件 开启和关闭JavaScript功能
EditThisCookie
一个Chrome插件 https://www.editthiscookie.com/ 方便修改Cookies的浏览器插件 功能介绍
- 开发者工具编辑
- 管理Cookies
- 导出Cookies
- 导入Cookies
无限 debugger
作用
反调试:阻止我们调试和分析目标代码的运行逻辑
实现
debugger关键字的应用:
- Function/eval "debugger"
- function debugger
*解决方案
- 禁用所有断点
- 禁用某处断点
- 条件断点
- 中间人工具替换特征字符串
- reres(浏览器插件)替换本地修改过的文件
- 重写关键函数(使用较多)
相关信息
先断点在关键函数声明之后,然后在Console重写 function start_debugger(){}
- 下列第一行代码如果返回 True,就可以使用第二行
(function(){}).constructor === Function //true
Function.prototype.constructor = function(){} //f(){}
快速定位关键点
快速定位之搜索
- 中间人的全局搜索
- *开发者工具的全局搜索

快速定位之断点
- XHR
- DOM
- EVENT
- 自定义
快速定位之hook
- json
- cookie
- window attr
- eval/Function
- websocket
- and so on
快速定位之分析
- Elements Event Listeners
- Network type initator
- Console Log XMLHttpRequests
代码混淆原理
为什么要进行JavaScript加密或混淆
- JavaScript 代码运行于客户端
- JavaScript代码是公开透明的
如何对JavaScript进行保护
- 代码压缩: 去除空格、换行等
- 代码加密: eval、emscripten、WebAssembly等
- 代码混淆: 变量混淆、常量混淆、控制流扁平化、调试保护等
JavaScript加密实现
eval 加密
JavaScript 代码->eval方法的字符串参数
Emscripten
通过 Emscripten 编译器 将C或者C++代码编译成JavaScript(不是普通的JS代码,叫做asm.js) 核心:C/C++ 编译:Emscripten 结果:asm.js 调用:JavaScript
WebAssembly
核心:C/C++ 结果:wasm 文件 调用:JavaScript
JavaScript混淆技术
- 变量混淆
- 反调试
- 字符串混淆
- 多态变异
- 属性加密
- 锁定域名
- 控制流平坦化
- 反格式化
- 僵尸代码注入
- 特殊编码
- 代码压缩
开源项目
UglifyJs: [https://github.com/mishoo/Uglify]S](https://github.com/mishoo/Uglify]S2terser:) terser: https://github.com/terser/terser javascript-obfuscator: https://github.com/javascript-obfuscator/javascript-obfuscator jsfuck: https://github.com/aemkei/jsfuck AAEncode: https://github.com/bprayudha/jquery.aaencode JJEncode: https://github.com/ay86/jEncrypt
商业服务
https://javascriptobfuscator.com/https://jscrambler.com/http://stunnix.com/
JavaScript混淆实现

变量名混淆
普通混淆
16进制混淆
字符串混淆
自我保护
若格式化后运行,会直接将浏览器卡死
控制流平坦化
逻辑处理块统一加上前驱逻辑块,提高逻辑流程复杂度。
僵尸代码注入
僵尸代码:不会被执行的代码或对上下文没有任何影响的代码 注入之后可以对现有的JavaScript 代码的阅读形成干扰。
对象键名替换
对Object对象键名替换
禁用控制台输出
调试保护
- 无限Debug
- 定时Debug
- debugger关键字

域名锁定
- 只允许在特定于名下运行
- 降低被模拟风险

JSFuck

AAEncode

JJEncode

处理常见代码混淆操作的方法
加密分析流程总结
- 查看关键包-分析哪些参数是加密的
- 搜索参数
参数名= / 参数名 = / 参数名: / 参数名 : 参数名 2.1 查看网络面板的 Initiator(发起) 2.2 xhr 断点调试 2.3 hook相关逻辑
- 分析加密
- 补全加密逻辑
样例一总结
将JavaScript代码转换成颜文字网络表情的编码以达到混淆的目的 原理:这类混淆通常都是使用构造函数将字符串作为代码运行 例如:
const sum = new Function('a', 'b', 'return a+ b');
console.log(sum(2,6));
解决方法: 1.直接将混淆后的代码粘贴至控制台通过右下角VM查看源代码 2.删除代码结尾的 "('_');" 替换为“toString()”或将修改后的代码粘贴至控制台运行。
样例二
这类混淆的原理都是通过(O)["constructor"])"constructor"()来执行代码的,上面这一串代码等价于Function(code)() Function构造函数创建了一个新的Function对象,我们直接调用构造函数就可以动态创建函数了。 就像下面这段代码
const sum = new Function('a" , "b" , 'return a+b');
console.log(sum(2,6));
传递给Funtion的所有参数按照传递顺序被视为函数的形参,最后一个函数传入函数体,调用执行的时候就会创建一个计算两数之和的函数了。 总结 将JavaScript代码转换仅由符号组成的代码以达到混淆的目的原理:这类混淆通常都是使用构造函数将字符串作为代码运行转换流程大致如下: 解决方法: 1.直接将混淆后的代码粘贴至控制台通过VM查看源代码 2.删除代码结尾的“()”替换为“toString()”或将修改后的代码粘贴至控制台运行。
样例三总结
将JavaScript代码转换成只有6种字符([
,]
,(
,)
,!
,+
)的编码,以达到混淆的目的 例如: '0'∵'[+[]]’ '1':'[+!+[]]' '2':'[!+[]+!+[]] 'a':'(false+"")[1]' 'b':'(Function("return{}")()+"")[2]' 'c'😢[]["filter"]+"")[3]' 解决方法: 1.直接将混淆后的代码粘贴至控制台通过VM查看源代码 2.1删除代码结尾的“()”替换为“toString()”或将修改后的代码粘贴至控制台运行。 2.2将代码结尾最后一对的“()”包含的代码抽离出来单独运行
样例四总结
样例四其实这谈不上是加密,只能算是一种编码(Encode)或者也可以称为是一种打包(packer),类似于base64这样的编码,都是可以以一定方式还原的。 可以看到这个样例的开头是eval,在 js 中eval中接受的参数是包含有效JavaScript 代码的字符串。 这个字符串将由JavaScript分析器进行分析和执行。所以我们可以断定Function(p,a,c,k,e,r)或者是function(p,a,c,k,e,d)返回的就是解密好的js源代码,然后传递给eval。 所以我们只需要将eval更改为 alert / document.write / console.log即可解密。
样例五总结
样例五可以说是一类混淆程度较为严重的例子。 通过将原始的js 代码经过一系列的标识符混淆、死代码注入、防调试注入等操作达到代码保护的目的。 这类代码通过调试还原也可以达到破解的目的,但是会耗费大量的精力。我们面对这类混淆的解决方法: 0.熟悉这类混淆通用的混淆代码 1.找到代码的入口 2.将代码主体扣取出来 3.去除无用的代码与环境监测代码(埋雷),补全缺失的代码 4.运行处理后的代码 课后参考样例: https://mp.weixin.qq.com/s/6GURR9mZzlt9rbiLCje7UA