98js逆向笔记
1、工具
JS加解密
https://www.bejson.com/enc/eval_package/
Ob混淆
https://github.com/Tsaiboss/decodeObfuscator
AST 混淆还原框架
https://github.com/sml2h3/ast_tools
Chrome 插件
https://github.com/cilame/v_jstools
jsjiami v6 专用解密工具
https://github.com/NXY666/JsjiamiV6-Decryptor
谷歌验证码绕过插件
Noptcha
2、JS逆向基础
JS基础
# JS对象--------------------------------------------------
function Person(name, age) {
this.name = name;
this.age = age;
}
let obj = new Person("zs", 18);
console.log(obj.name);
console.log(obj.age);
# 函数参数------------------------------------------------
function add(a, b){
console.log(arguments[0]);
console.log(arguments[1]);
return a + b;
}
let res = add(1, 5);
console.log(res);
# 函数作用域---------------------------------------------------
let a = 1;
function test(){
a = 5;
console.log(a);
}
test();
console.log(a);
# 字符串-----------------------------------------------------
let name = "zs";
console.log(name);
console.log(\u006e\u0061\u006d\u0065);
# base64------------------------------------------------
// str -> base64
let buffer = Buffer.from("i love you china");
let base64Str = buffer.toString("base64");
console.log(base64Str); // aSBsb3ZlIHlvdSBjaGluYQ==
// base64 -> str
let str = Buffer.from("aSBsb3ZlIHlvdSBjaGluYQ==", "base64").toString();
console.log(str);
JS面向对象编程
js中一切皆对象
构造函数对象都有一个属性prototypejs中, 只要是对象, 都有一个属性__proto_
# JS面向对象--------------------------------------------------
function Person(name, age){
this.name=name;
this.age=age;
/*this.show=function(){
console.log("p实例:my name is " + this.name);
}*/
}
Person.prototype.show = function(){
console.log("原型对象:my name is " + this.name);
}
let p =new Person("zs", 18);
p.show();
# 继承链------------------------------------------------------------
function Person(name, age) {
this.name=name;
this.age=age;
/*this.say=function(){
console.log("实例my name is " + this.name);
}*/
}
/*Person.prototype.say=function () {
console.log("my name is " + this.name);
}
*/
Object.prototype.say=function () {
console.log("my name is " + this.name);
}
let p = new Person("zs", 18);
p.say();
数据劫持
# 数据代理
let o1 = {
num: 1
}
let o2 = {}
Object.defineProperty(o2, 'num', {
get(){
return o1.num
},
set(val){
o1.num = val
}
})
ES6语法
# 模块的导入和导出
module.exports = {
}
// 导入
const m = require("moduleName");
let与var变量的区别
let变量不可重复声明
let变量有块级作用域,var没有
var会变量提升,let不存在变量提升
模板字符串
str = my name is ${p.name}
箭头函数
f = () ->{
}
可变形参
function f(...params) {
console.log(params.length)
}
3、常见加解密算法
Base64编解码
# 浏览器-----------------------------------------
// 编码
window.btoa("gmbjzg") // "Z21ianpn"
// 解码
window.atob("Z21ianpn") // "gmbjzg"
哈希散列MD5
# Node-----------------------------------------
// 导库
const crypto = require('crypto');
// md5加密算法
var obj = crypto.createHash('md5');
obj.update('123456');
var str = obj.digest('hex');
console.log(str);
对称加密算法AES/DES
什么叫对称加密算法呢?对称加密算法就是使用密钥对明文进行加密,使用加密的密钥对密文进行解密。对称加密算法是可逆的。也就是说,如果我们得到一段密文数据,如果能够获取到加密的密钥,那么我们就能对密文数据进行解密。
# AES-ECB---------------------------------------
const CryptoJs = require('crypto-js');
// AES加密
let password = "123456";
let key = "1234567890abcdef"
cfg = {
mode: CryptoJs.mode.ECB,
padding: CryptoJs.pad.Pkcs7
}
let encPwd = CryptoJs.AES.encrypt(password, key, cfg).toString()
console.log(encPwd) // U2FsdGVkX1+meKI+IXd44qgc50bKb2rDbN91OutwBWs=
// AES解密
encPwd = "U2FsdGVkX1+meKI+IXd44qgc50bKb2rDbN91OutwBWs="
decPwd = CryptoJs.AES.decrypt(encPwd, key, cfg).toString(CryptoJs.enc.Utf8) // 指定解码方式
console.log(decPwd) // 123456
一共需要传递三个参数,分别是password | key 以及 cfg配置模式
mode模式常用的有 CBC | ECB两种
这两种模式的区别在于是否需要配置iv向量
使用CBC模式进行加密示例代码如下:
# AES-CBC-----------------------------------------------------
const CryptoJs = require('crypto-js');
let password = CryptoJs.enc.Utf8.parse("123456") // 指定以什么编码方式解析明文
let key = CryptoJs.enc.Utf8.parse("1234567890abcdef")
let iv = CryptoJs.enc.Utf8.parse("123456") // 需指定初始向量
cfg = {
mode: CryptoJs.mode.CBC,
padding: CryptoJs.pad.Pkcs7,
iv: iv
}
let encPwd = CryptoJs.AES.encrypt(password, key, cfg).toString(CryptoJs.enc.Utf8)
console.log(encPwd)
非对称加密算法RSA
公钥和私钥需要生成,这里我们使用在线生成方式
加解密网址:http://web.chacuo.net/netrsakeypair/
非对称加密算法有公钥和私钥,公钥加密,私钥解密
RSA--------------------------------------
const JSEncrypt = require("jsencrypt");
window = global
publickey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnX3j/luQG7JnPFIFJKEvPC/tFtv14XIzT7FQXYpKsOt2t4uLh6hZa5H5WcEinF46nc91UbrS5UA9Fnnm+Ev20pwUEPVu4On47am6vJOsq8oqQoZDsMu6VGZIzKIm8vDylO6I2xrTaXY2G3hdiRKF7988tA4oYsFOTZ/yG/BOlNwIDAQAB'
// 加密
let jse = new JSEncrypt()
jse.setPublicKey(publickey)
var encStr = jse.encrypt('123456')
console.log("加密数据: " + encStr)
// 解密
privatekey = 'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKdfeP+W5Absmc8UgUkoS88L+0W2/XhcjNPsVBdikqw63a3i4uHqFlrkflZwSKcXjqdz3VRutLlQD0Weeb4S/bSnBQQ9W7g6fjtqbq8k6yryipChkOwy7pUZkjMoiby8PKU7ojbGtNpdjYbeF2JEoXv3zy0DihiwU5Nn/Ib8E6U3AgMBAAECgYA6KW0stEytM08HrQJ4X65oVquMwFg4mUC+7CMUtUZu303lfTCGfQgjsb9NXluA5SjHe/Xvv0DCHNYRxU5dBNBwhIXaRLy6zLKKKp/0gOn1C3dFY/MQOVoEpJ8uxUQh9Kf37F5J9gT64JNooKTTNydqTcmfIhG/u3WFiTVjfW5sEQJBANmvAgUneA6eEC6LwhX9gxdp0T2S+hop19zAm4ErHQld47TlSAxVgwArQG4oJ5J2OWlIT4vzuO1OJOaCj4wZYXMCQQDE1W0uZA1YtjVK7OUuD3f/rgNzolpc0XEEZDPxKsoirEFgW/cFNCTJKIdGK2RgnthLWiN01a9bL6+sF2vLO2wtAkAd0h3Cuv91cS3iUn8KKCqXQIXLm6DriKPrt+8VqORXbidNlsNh/SzvDv3KmXGiXNPMmn1bPM4upC/l7CjiFnAFAkBnmu+dO4zK5R2oEomPdRT0v+OROiPWN2gFp7iveJZtKb4/uiiL1KaIO4z4ol5zfSjcgNWo6dEjbjZJnwpeLykBAkAY/tYLGyrHe0isoZL2xXPlrvde2tbKcbzMrheH1wuqEMX0o0+uHCyFgn2rAzMcfUlntb9iZLLJDkJ+bFET1j3l'
jse.setPrivateKey(privatekey)
var Str = jse.decrypt(encStr)
console.log("解密数据: " + Str)
4、python执行js代码
# 安装第三方库
pip install PyExecJS2
# 编译调用执行
ctx = execjs.compile(open("./xxx.js", "r", encoding="utf-8").read())
datas = ctx.call("mainc", response.text) # 参数一:调用的函数 参数二:实参
5、Js逆向专题
运行js代码的三种 node v8引擎 浏览器
浏览器调试
打开调试面板的几种方式
- F12
- 快捷键 Ctrl+Shift+I
- 鼠标右键检查
- 浏览器右上角三个小点点 => 更多工具 => 开发者工具
- 新打开标签页 => 输入网址 => 回车
无限Debugger
-
断点处添加条件 Add conditional breakpoint / Never pause Here
-
ReRes/Chrome的Overrides,替换js文件
-
代码注入
# HOOK代码------------------------------------------
Function.prototype.constructor_ = Function.prototype.constructor;
Function.prototype.constructor = function (arg) {
if(arg == "debugger") {
return function (){};
}
return Function.prototype.constructor_(arg);
}
浏览器插件编写
示例
# manifest.json---------------------------------------------
{
"name": "shijia", // 插件名称
"version": "1.0", // 插件版本
"description": "无限debugger",
"manifest_version": 3,
"content_scripts": [{
"matches": ["<all_urls>"], // 匹配url地址
"js": ["debugger.js"],
"all_frames": true,
"permissions": ["tabs"],
"run_at": "document_start" // 代码注入的时间
}]
}
# debugger.js---------------------------------------------------
console.log("注入js文件")
代理
obj = new Proxy(global, {
set(obj, prop, value) {
console.log(obj, prop, value);
return Reflect.set(...arguments);
},
get: function (target, property, receiver) {
console.log(target, property, receiver);
return target[property];
}
});
6、JS混淆原理
异或运算
// 原理: a ^ b = c => c ^ b = a
let a = 1
let b = 1
let c = a ^ b
console.log(c) // 0
console.log(c ^ b) // 1
对象属性访问
arr = ["getFullYear"]
year = new Date()[arr[0]]()
console.log(year)
乱序数组
// 没有乱序的数组
arr = ["getFullYear", "getMonth", "getDay"]
// 打乱数组
function upset(arr, num){
for(let i=0; i<num; i++){
arr.unshift(arr.pop())
}
console.log(arr)
}
// 还原数组
function recover(arr, num){
for(let i=0; i<num; i++){
arr.push(arr.shift())
}
console.log(arr)
}
垃圾代码
// 垃圾代码
function add1(n1, n2){
return add2(n1, n2);
}
function add2(n1, n2){
return add3(n1, n2)
}
function add3(n1, n2){
return n1 + n2;
}
let a = 1;
let b = 2;
let c = add1(a, b)
console.log(c);
逗号表达式
// 原来代码
function sum(n1){
a = n1 + 1;
b = a + 2;
c = b + 3;
d = c + 4;
e = d + 5;
return e;
}
// 混淆后的代码
function sum(n1){
return a = n1 + 1,
b = a + 2,
c = b + 3,
d = c + 4,
e = d + 5
}
console.log(sum(0))
7、JS常见混淆
AAEncode & JJEncode
解决思路:复制混淆后的代码在sublime编辑器中打开,删除最后一个括号里的内容以及括号
JSFuck
解决思路:复制混淆后的代码在Sublime
编辑器打开,匹配最后一个括号)
内的代码,复制括号中的代码在浏览器中输出
8、补浏览器环境
JS三种执行环境
●浏览器
●V8
●Node
沙箱环境
https://github.com/patriksimek/vm2
V8
示例代码
var fs = require('fs');
const {VM} = require('vm2');
const vm = new VM();
var data = fs.readFileSync('./src/code.js', 'utf8')
vm.run(data)
调试代码
const {VM, VMScript} = require('vm2');
const fs = require('fs');
// 运行的code代码
const file = `${__dirname}/code.js`;
// 需要补的window环境
const windowfile = `${__dirname}/window.js`;
const vm = new VM();
const script = new VMScript(fs.readFileSync(windowfile)+fs.readFileSync(file), "我正在调试的代码");
vm.run(script);
Node
navigator = {
userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
};
const descriptor1 = Object.getOwnPropertyDescriptor(navigator, 'userAgent');
console.log(descriptor1);
9、JS逆向经验
分析步骤
(1)抓包分析
(2)定位加密位置
(3)扣取JS代码
(4)浏览器还原
(5)Node还原
搜索关键字
笔记参考视频https://space.bilibili.com/409871996
本文来自博客园,作者:__username,转载请注明原文链接:https://www.cnblogs.com/code3/p/17205062.html