JS逆向学习笔记 - 持续更新中

JS逆向学习笔记

寻找深圳爬虫工做,微信:cjh-18888javascript

文章目录

一. JS Hook

1. JS HOOK 原理和做用

原理:替换原来的方法. (好像写了句废话)

function test(aa,bb){ 
    cc = aa + bb;
    return cc;
}

Hook代码:css

var _test = test;  // 拿到test
test = function(x,y){ 
    console.log(x,y); //输出拿到的参数
    var retval = _test(x,y); // retval 是原来的计算结果
    console.log( retval)
    return retval + 1 // 修改返回结果
}

此时从新调用test, 结果比正常值多了1html

做用: 能够去Hook一些内置的函数, 例如Debugger, setInterval,JSON.stringify等等

//Hook setInterval 
var _setInterval = setInterval;
setInterval = function(a,b){ 
    console.log(a + '',b)
    return 'setInterval is Kill'
}

//Hook JSON.stringify
stringify = JSON.stringify;
JSON.stringify = function(a){ 
console.log('Hook JSON.stringify ->' + stringify(a))
return stringify(a)
}

2.JSHook 检测与过检测

原理: 其实就是检测代码是否和原来的相等.

案例代码:前端

function test(aa,bb){ var cc = aa + bb;return cc;}

function checkTest(func){ 
    test + '' == 'function test(aa,bb){var cc = aa + bb;return cc;}'?console.log('func未被修改'):console.log('func被修改了')
}

此时咱们能够把hook代码置入到浏览器java

//控制台置入的代码
var _test = test; 
test = function(x,y){ 
    console.log(x,y); 
    var retval = _test(x,y); 
    console.log( retval)
    return retval 
}

绕过手段: 修改Function的toString方法.

Function.prototype.toString=function(){ 
	return "function test(x,y){z=x+y;return z;}";
}

3.JS过反调试

反调试代码:jquery

var fuck=["\u0068\u0065\u006e\u0076\u0061\u0074","\u005f\u006b\u0070\u006f\u0076\u0074\u0071\u005f\u0076\u006b\u0074","\u0066\u006b\u005f\u0071\u0069\u0061\u0070\u0076","\u0068\u0071\u0070\u005f\u0076\u0065\u006b\u0070\u0022\u006f\u0076\u0074\u0056\u006b\u0051\u0065\u0070\u0076\u003a\u002a\u006f\u0076\u0074\u0025\u0077\u0068\u006b\u0074\u002a\u0078\u005d\u0074\u0022\u0065\u0039\u0032\u002e\u005d\u0074\u0074\u0039\u0057\u0059\u0037\u0065\u003e\u006f\u0076\u0074\u0030\u006e\u0061\u0070\u0063\u0076\u006a\u0037\u0065\u0027\u0027\u0025\u0077\u0078\u005d\u0074\u0022\u005d\u0039\u006f\u0076\u0074\u0030\u005f\u006a\u005d\u0074\u003f\u006b\u0066\u0061\u003d\u0076\u002a\u0065\u0025\u0037\u005d\u0074\u0074\u0030\u0072\u0071\u006f\u006a\u002a\u005d\u0021\u0034\u003b\u005d\u0029\u0036\u003c\u005d\u0027\u0034\u0025\u0037\u0079\u0074\u0061\u0076\u0071\u0074\u0070\u0022\u0070\u0061\u0073\u0022\u0051\u0065\u0070\u0076\u003a\u003d\u0074\u0074\u005d\u0075\u002a\u005d\u0074\u0074\u0025\u0037\u0079","\u0076\u006b\u004f\u0076\u0074\u0065\u0070\u0063","\u0074\u0061\u0076\u0071\u0074\u0070\u0022\u0076\u006a\u0065\u006f","\u0068\u0071\u005f\u0067\u0055\u006b\u0071","\u0070\u0067\u005b\u0059\u0078\u0061\u0067\u0072","\u006c\u0076\u005d\u006a","\u0066\u0061\u0059\u0072\u005b\u006c\u005d\u0072\u005f\u0065\u0059\u0067","\u0066\u0066\u0066","\u005b\u0067\u0072\u006b\u0067\u0070\u005d\u0032\u0070\u0067\u005f\u002c\u0026\u005d\u0076\u0076\u0026\u0021","\u006a\u0059\u0068\u0069\u005b\u005b\u0059\u0078\u002f","\u006c\u0069\u0074\u0057\u007a\u005d\u0063\u0074\u0026\u0067\u0059\u007a\u003d\u0074\u007a\u0059\u0078\u007c\u0055\u0072\u002e\u001d\u0026\u006f\u0026\u004f\u0074\u0055\u007a\u005d\u007c\u0059\u0026\u0057\u0063\u006a\u0059\u0051\u0026\u0071","\u006f\u0061\u0076\u0045\u0070\u0076\u0061\u0074\u0078\u005d\u006e\u002a\u0068\u0071\u005f\u0067\u002d\u0057\u0027\u0057\u0059\u0059\u0057\u006f\u0056\u006b\u004f\u002a\u0068\u0071\u005f\u0067\u0057\u0038\u0059\u0025\u0059\u002a\u0068\u0071\u005f\u0067\u002d\u0057\u0027\u0057\u0059\u0059\u0057\u006f\u0056\u006b\u004f\u002a\u0068\u0071\u005f\u0067\u0057\u0038\u0059\u0025\u0059\u002a\u0068\u0071\u005f\u0067\u002d\u0057\u0027\u0057\u0059\u0059\u0057\u006f\u0056\u006b\u004f\u002a\u0068\u0071\u005f\u0067\u0057\u0038\u0059\u0025\u0059\u002a\u0068\u0071\u005f\u0067\u0057\u002d\u0034\u0059\u0025\u0025\u0025\u002e\u002d\u0032\u0032\u0032\u0025","\u006a\u006d\u005b\u0063\u0029\u0053\u0023\u0053\u0055\u0055\u0053\u006b\u0058\u0067\u004b\u002c\u006b\u0058\u0067\u004b\u002c\u006a\u006d\u005b\u0063\u0053\u0031\u0055\u0021\u0021\u0055\u0035\u0035\u002b\u0037\u005b\u0067\u0072\u006b\u0067\u0070\u005d\u0032\u0070\u0067\u005f\u002c\u0026\u0067\u006e\u0066\u0063\u003e\u002f\u0038\u002f\u002f\u002f\u003a\u0026\u0023\u006a\u006d\u005b\u0063\u0029\u0053\u0023\u0053\u0055\u0055\u0053\u006b\u0058\u0067\u004b\u002c\u006b\u0058\u0067\u004b\u002c\u006a\u006d\u005b\u0063\u0053\u0031\u0055\u0021\u0021\u0055\u0023\u0026\u002f\u0038\u0026\u0021\u003e\u005b\u0067\u0072\u006b\u0067\u0070\u005d\u0032\u0070\u0067\u005f\u002c\u0026\u005d\u0076\u0076\u0026\u0021"],bianchengmao=-1,fff=-1;var fuck1=[1,2,3,4];console.log("过了检测就会给你正确答案哦!");function Uint8ToStr_(arr){ for(var i=0,str="";i<arr.length;i++){ var a=arr[i];str+=String.fromCharCode(a)}return str}function strToUint8_(str){ for(var i=0,arr=[];i<str.length;i++){ var a=str.charCodeAt(i);arr.push(a)}return new Uint8Array(arr)}function strToUint8(str){ for(var i=0,arr=[];i<str.length;i++){ var a=str.charCodeAt(i);arr.push(a%2?a-4:a+2)}return new Uint8Array(arr)}function Uint8ToStr(arr){ for(var i=0,str="";i<arr.length;i++){ var a=arr[i];str+=String.fromCharCode(a%2?a+4:a-2)}return str}function sToS(x){ return Uint8ToStr(strToUint8_(x))}fuck1[!+[]+!+[]]=[][sToS(fuck[0])][sToS(fuck[1])];fuck1[+[]]=fuck1[!+[]+!+[]](sToS(fuck[5]))(),fuck1[+[]][sToS(fuck[6])]=sToS;fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();fuck1[+[]][fuck1[+[]]["fuckYou"](fuck1[+[]]["fuckYou"](fuck[7]))][fuck1[+[]]["fuckYou"](fuck1[+[]]["fuckYou"](fuck[8]))]==fuck1[+[]]["fuckYou"](fuck1[+[]]["fuckYou"](fuck[9]))?fuck1[+[]][(![]+[])[+[]]+(![]+[])[+[]]+(![]+[])[+[]]]=1:fuck1[+[]][[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+[]]+(![]+[])[+[]]+(![]+[])[+[]])()]=0;fuck1[+[]][(![]+[])[+[]]+(![]+[])[+[]]+(![]+[])[+[]]]==+!+[]?fuck1[+[]][sToS(sToS(fuck[9]))]=1:fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();setInterval+""==fuck1[+[]][sToS(fuck[6])](fuck1[+[]][sToS(fuck[6])](fuck1[+[]][sToS(fuck[6])](fuck[13])))?fuck1[+[]][sToS(sToS(fuck[9]))]=fuck1[+[]][sToS(sToS(fuck[9]))]+2:fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();fuck1[!+[]+!+[]](fuck1[+[]][sToS(fuck[6])](fuck1[+[]][sToS(fuck[6])](fuck[15])))(+!+[])+(+!+[]);

咱们直接看到这块webpack

fuck1[!+[] + !+[]] = [][sToS(fuck[0])][sToS(fuck[1])];
// fuck[2] = Function;
fuck1[+[]] = fuck1[!+[] + !+[]](sToS(fuck[5]))(),
// fuck[0] = Function('return this')()
fuck1[+[]][sToS(fuck[6])] = sToS;
fuck1[!+[] + !+[]](fuck1[+[]][sToS(fuck[6])](fuck[14]))();
// fuck1[2]("setInterval(debugger;,1000)")()

经过上面的代码解析, 咱们能够看到,debugger其实就是经过setInterval方法来调用的. 那么咱们其实能够写这个过调试代码.git

4. JSHook 对象属性

Hook对象属性须要使用到github

Object.defineProperty() //方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 | 这个经常使用一些.
Object.defineProperties() // 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。

1. Object.defindPropety()

下面这个是定义了一个案例. 咱们使用Object.defindProperty()来修改属性对象的setweb

var obj = { 
    'name': function(){ 
        return 'xiaopang';
    }
}

而后咱们编写Hook代码.

Object.defineProperty(obj,'name',{ 
    'set':function(x){ 
        console.log(x)
        return x;
    }
})

Hook注意点:

  • 对象须要建立之后方可Hook
  • 通常都是Hook全局对象.
  • 不仅是能够Hook自定义,咱们还能够Hook系统的对象属性, document.cookie

二. Chrome拓展(Chrome Extension)开发

1. 基本介绍

部分资料引用于 https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html

1. 什么是Chrome插件?

​ 严格来说,咱们正在说的东西应该叫Chrome扩展(Chrome Extension),真正意义上的Chrome插件是更底层的浏览器功能扩展,可能须要对浏览器源码有必定掌握才有能力去开发。鉴于Chrome插件的叫法已经习惯,本文也所有采用这种叫法,但读者需深知本文所描述的Chrome插件实际上指的是Chrome扩展。

​ Chrome插件是一个用Web技术开发、用来加强浏览器功能的软件,它其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包.

​ 另外,其实不仅是前端技术,Chrome插件还能够配合C++编写的dll动态连接库实现一些更底层的功能(NPAPI),好比全屏幕截图。因为安全缘由,Chrome浏览器42以上版本已经陆续再也不支持NPAPI插件,取而代之的是更安全的PPAPI。

2. 学习Chrome插件开发的意义

加强浏览器功能, 实现属于本身的“定制版”浏览器。而后再本文中,咱们则是须要经过学习Chrome浏览器插件的开发,来实现 JSHOOK 代码的注入.

Chrome提供了很是多实用的API,包括但不限于:

  • 书签控制
  • 下载控制
  • 窗口控制
  • 标签控制
  • 网络请求控制,各种事件坚挺
  • 自定义原生菜单
  • 完善的通信机制
  • 等等

3. 为何是Chrome插件,而不是firefox插件?

  1. Chrome的市场占有率更高.
  2. 开发简单
  3. 应用场景更普遍,Firefox插件只能运行在Firefox上,而Chrome除了Chrome浏览器以外,还能够运行在全部webkit内核的国产浏览器,好比360极速浏览器、360安全浏览器、搜狗浏览器、QQ浏览器等等;
  4. 除此以外,Firefox浏览器也对Chrome插件的运行提供了必定的支持;

2. 文件结构

Chrome插件没有严格的项目结构要求,只要保证本目录有一个manifast.json便可.也不须要专门的IDE,普通的web开发工具便可。

从右上角菜单->更多工具->扩展程序能够进入 插件管理页面,也能够直接在地址栏输入 chrome://extensions 访问。

勾选开发者模式便可以文件夹的形式直接加载插件,不然只能安装.crx格式的文件。Chrome要求插件必须从它的Chrome应用商店安装,其它任何网站下载的都没法直接安装,因此,其实咱们能够把crx文件解压,而后经过开发者模式直接加载。

开发中,代码有任何改动都必须从新加载插件,只须要在插件管理页按下Ctrl+R便可,以防万一最好还把页面刷新一下。

1. manifest.json

这是一个Chrome插件最重要也是必不可少的文件,用来配置全部和插件相关的配置,必须放在根目录。其中,manifest_versionnameversion3个是必不可少的,descriptionicons是推荐的。

{ 
	// 清单文件的版本,这个必须写,并且必须是2
	"manifest_version": 2,
	// 插件的名称
	"name": "demo",
	// 插件的版本
	"version": "1.0.0",
	// 插件描述
	"description": "简单的Chrome扩展demo",
	// 图标,通常偷懒所有用一个尺寸的也没问题
	"icons":
	{ 
		"16": "img/icon.png",
		"48": "img/icon.png",
		"128": "img/icon.png"
	},
	// 会一直常驻的后台JS或后台页面
	"background":
	{ 
		// 2种指定方式,若是指定JS,那么会自动生成一个背景页
		"page": "background.html"
		//"scripts": ["js/background.js"]
	},
	// 浏览器右上角图标设置,browser_action、page_action、app必须三选一
	"browser_action": 
	{ 
		"default_icon": "img/icon.png",
		// 图标悬停时的标题,可选
		"default_title": "这是一个示例Chrome插件",
		"default_popup": "popup.html"
	},
	// 当某些特定页面打开才显示的图标
	/*"page_action": { "default_icon": "img/icon.png", "default_title": "我是pageAction", "default_popup": "popup.html" },*/
	// 须要直接注入页面的JS
	"content_scripts": 
	[
		{ 
			//"matches": ["http://*/*", "https://*/*"],
			// "<all_urls>" 表示匹配全部地址
			"matches": ["<all_urls>"],
			// 多个JS按顺序注入
			"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
			// JS的注入能够随便一点,可是CSS的注意就要千万当心了,由于一不当心就可能影响全局样式
			"css": ["css/custom.css"],
			// 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
			"run_at": "document_start"
		},
		// 这里仅仅是为了演示content-script能够配置多个规则
		{ 
			"matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
			"js": ["js/show-image-content-size.js"]
		}
	],
	// 权限申请
	"permissions":
	[
		"contextMenus", // 右键菜单
		"tabs", // 标签
		"notifications", // 通知
		"webRequest", // web请求
		"webRequestBlocking",
		"storage", // 插件本地存储
		"http://*/*", // 能够经过executeScript或者insertCSS访问的网站
		"https://*/*" // 能够经过executeScript或者insertCSS访问的网站
	],
	// 普通页面可以直接访问的插件资源列表,若是不设置是没法直接访问的
	"web_accessible_resources": ["js/inject.js"],
	// 插件主页,这个很重要,不要浪费了这个免费广告位
	"homepage_url": "https://www.baidu.com",
	// 覆盖浏览器默认页面
	"chrome_url_overrides":
	{ 
		// 覆盖浏览器默认的新标签页
		"newtab": "newtab.html"
	},
	// Chrome40之前的插件配置页写法
	"options_page": "options.html",
	// Chrome40之后的插件配置页写法,若是2个都写,新版Chrome只认后面这一个
	"options_ui":
	{ 
		"page": "options.html",
		// 添加一些默认的样式,推荐使用
		"chrome_style": true
	},
	// 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字
	"omnibox": {  "keyword" : "go" },
	// 默认语言
	"default_locale": "zh_CN",
	// devtools页面入口,注意只能指向一个HTML文件,不能是JS文件
	"devtools_page": "devtools.html"
}

2. content-scripts

所谓content-scripts,其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还能够包括css的),借助content-scripts咱们能够实现经过配置的方式轻松向指定页面注入JS和CSS(若是须要动态注入,能够参考下文),最多见的好比:广告屏蔽、页面CSS定制,等等。

{ 
	// 须要直接注入页面的JS
	"content_scripts": 
	[
		{ 
			//"matches": ["http://*/*", "https://*/*"],
			// "<all_urls>" 表示匹配全部地址
			"matches": ["<all_urls>"],
			// 多个JS按顺序注入
			"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
			// JS的注入能够随便一点,可是CSS的注意就要千万当心了,由于一不当心就可能影响全局样式
			"css": ["css/custom.css"],
			// 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
			"run_at": "document_start"
		}
	],
}

特别注意,若是没有主动指定run_atdocument_start(默认为document_idle),下面这种代码是不会生效的:

document.addEventListener('DOMContentLoaded', function()
{ 
	console.log('我被执行了!');
});

content-scripts和原始页面共享DOM,可是不共享JS,如要访问页面JS(例如某个JS变量),只能经过injected js来实现。content-scripts不能访问绝大部分chrome.xxx.api,除了下面这4种:

  • chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
  • chrome.i18n
  • chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
  • chrome.storage

其实看到这里不要悲观,这些API绝大部分时候都够用了,非要调用其它API的话,你还能够经过通讯来实现让background来帮你调用(关于通讯,后文有详细介绍)。

好了,Chrome插件给咱们提供了这么强大的JS注入功能,剩下的就是发挥你的想象力去玩弄浏览器了。

3.background

后台(姑且这么翻译吧),是一个常驻的页面,它的生命周期是插件中全部类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,因此一般把须要一直运行的、启动就运行的、全局的代码放在background里面。

background的权限很是高,几乎能够调用全部的Chrome扩展API(除了devtools),并且它能够无限制跨域,也就是能够跨域访问任何网站而无须要求对方设置CORS

通过测试,其实不止是background,全部的直接经过chrome-extension://id/xx.html这种方式打开的网页均可以无限制跨域

配置中,background能够经过page指定一张网页,也能够经过scripts直接指定一个JS,Chrome会自动为这个JS生成一个默认的网页:

{ 
	// 会一直常驻的后台JS或后台页面
	"background":
	{ 
		// 2种指定方式,若是指定JS,那么会自动生成一个背景页
		"page": "background.html"
		//"scripts": ["js/background.js"]
	},
}

须要特别说明的是,虽然你能够经过chrome-extension://xxx/background.html直接打开后台页,可是你打开的后台页和真正一直在后台运行的那个页面不是同一个,换句话说,你能够打开无数个background.html,可是真正在后台常驻的只有一个,并且这个你永远看不到它的界面,只能调试它的代码。

4. event-pages

这里顺带介绍一下event-pages,它是一个什么东西呢?鉴于background生命周期太长,长时间挂载后台可能会影响性能,因此Google又弄一个event-pages,在配置文件上,它与background的惟一区别就是多了一个persistent参数:

{ 
	"background":
	{ 
		"scripts": ["event-page.js"],
		"persistent": false
	},
}

它的生命周期是:在被须要时加载,在空闲时被关闭,什么叫被须要时呢?好比第一次安装、插件更新、有content-script向它发送消息,等等。

除了配置文件的变化,代码上也有一些细微变化,我的这个简单了解一下就好了,通常状况下background也不会很消耗性能的。

5. popup

popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就当即关闭,通常用来作一些临时性的交互。

博客园网摘插件popup效果

popup能够包含任意你想要的HTML内容,而且会自适应大小。能够经过default_popup字段来指定popup页面,也能够调用setPopup()方法。

配置方式:

{ 
	"browser_action":
	{ 
		"default_icon": "img/icon.png",
		// 图标悬停时的标题,可选
		"default_title": "这是一个示例Chrome插件",
		"default_popup": "popup.html"
	}
}

img

须要特别注意的是,因为单击图标打开popup,焦点离开又当即关闭,因此popup页面的生命周期通常很短,须要长时间运行的代码千万不要写在popup里面。

在权限上,它和background很是相似,它们之间最大的不一样是生命周期的不一样,popup中能够直接经过chrome.extension.getBackgroundPage()获取background的window对象。

6. injected-script

这里的injected-script是我给它取的,指的是经过DOM操做的方式向页面注入的一种JS。为何要把这种JS单独拿出来讨论呢?又或者说为何须要经过这种方式注入JS呢?

这是由于content-script有一个很大的“缺陷”,也就是没法访问页面中的JS,虽然它能够操做DOM,可是DOM却不能调用它,也就是没法在DOM中经过绑定事件的方式调用content-script中的代码(包括直接写onclickaddEventListener2种方式都不行),可是,“在页面上添加一个按钮并调用插件的扩展API”是一个很常见的需求,那该怎么办呢?其实这就是本小节要讲的。

content-script中经过DOM方式向页面注入inject-script代码示例:

// 向页面注入JS
function injectCustomJs(jsPath)
{ 
	jsPath = jsPath || 'js/inject.js';
	var temp = document.createElement('script');
	temp.setAttribute('type', 'text/javascript');
	// 得到的地址相似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js
	temp.src = chrome.extension.getURL(jsPath);
	temp.onload = function()
	{ 
		// 放在页面很差看,执行完后移除掉
		this.parentNode.removeChild(this);
	};
	document.head.appendChild(temp);
}

你觉得这样就好了?执行一下你会看到以下报错:

Denying load of chrome-extension://efbllncjkjiijkppagepehoekjojdclc/js/inject.js. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.

意思就是你想要在web中直接访问插件中的资源的话必须显示声明才行,配置文件中增长以下:

{ 
	// 普通页面可以直接访问的插件资源列表,若是不设置是没法直接访问的
	"web_accessible_resources": ["js/inject.js"],
}

7.更多

更多关于Chrome Extension的开发请看博客https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html

3.实战 - Js自动注入Hook代码

在2.2中,介绍了许许多多的Chrome的页面各个做用. 接下来进行实战.

1. manifest.json

因为咱们是须要在网页打开的时候, 立马将咱们的代码注入进去,这样才能毫无遗漏的把一些操做给Hook出来。所以,咱们的manifest.json文件应该以下配置

{ 
	"manifest_version": 2,
	"name": "小胖JS自动注入插件",
	"version": "1.0",
	"description": "小胖JS自动注入插件,QQ2625112940",
	"author": "xiaopang",
	"icons":
	{ 
		"16":"ico.png",
		"48": "icon.png",
		"128": "icon.png"
	},
	"browser_action": 
	{ 
		"default_icon": "icon.png",
		"default_popup": "popup.html"
	},
	"content_scripts": 
	[
		{ 
			"matches": ["<all_urls>"],
			"js": ["content-script.js"],
			"run_at": "document_start",
			"all_frames": true
		}
	],
	"permissions":
	[
        "<all_urls>",
        "webRequest",
        "webRequestBlocking",
        "tabs",
        "http://*/*",
        "https://*/*",
        "contextMenus",
        "cookies",
        "unlimitedStorage",
        "notifications",
        "storage",
        "clipboardWrite"
    ]
}

2. content_script.js

那么在浏览器中, 咱们应该要如何载入js文件呢?能够参照下面代码

(function() { 
	var spt = document.createElement('script');
	
	
	spt.innerHTML = ` // ---- Cookie 监听 var cookie_cache = document.cookie; // 获取到原来的cookie Object.defineProperty(document,'cookie',{ // 获取Cookie时,触发的动做 get: function(){ return cookie_cache; }, //当Cookie被设置的时候,触发的动做 set: function(val){ console.log('Cookies Setting',val); // debugger; var cookie = val.split(';')[0]; var ncookie = cookie.split("="); var flag = false; var cache = cookie_cache.split("; "); cache = cache.map(function(a){ if (a.split("=")[0] === ncookie[0]){ flag = true; return cookie; } return a; }) cookie_cache = cache.join("; "); if (!flag){ cookie_cache += cookie + "; "; } this._value = val; return cookie_cache; } }) // ---- `
	document.documentElement.appendChild(spt);
})();

上面的例子是对Cookie进行监控的代码,暂且忽略功能的实现问题. 就单纯看建立Script的过程, 其实就是下面这点而已.

(function() { 
	var spt = document.createElement('script');
	spt.innerHTML = ` // 业务逻辑代码 `
	document.documentElement.appendChild(spt);
})();

下面再祭出一些注入的业务逻辑代码

//HOOK JSON stringify
    var rstringify = JSON.stringify;
    JSON.stringify = function(a){ 
        console.log("Detect Json.stringify", a);
		//debugger;
        return rstringify(a);
    }

    //HOOK json parse
    //var strparse = JSON.parse
    //JSON.parse = function(b){ 
        //console.log("Detect Json.Parse", b);
        //return strparse(b);
    //}

    //var plugins_cache = window.navigator
    //Object.defineProperty(navigator, 'plugins', { 
    // get: function() { 
    // console.log('Getting plugins');
    // //debugger;
    // return plugins_cache;
    // },
    // set: function(val) { 
    // console.log('获取信息');
    // console.log(val);
    // debugger;
    // },
    //});

	var _eval = eval;
	eval = function(e){ 
		_eval(e.replace("debugger",""));
	}
	eval.toString = _eval.toString;

	var _Function = Function;
	Function = function(e){ 
		_Function(e.replace("debugger",""));
	}
	Function.toString = _Function.toString;

	var _constructor = constructor;
	Function.prototype.constructor = function(s) { 
		if (s == "debugger"){ 
			console.log(s);
			return null;
		}
		return _constructor(s);
	}

三. 调试技巧

1. 快速定位关键代码

  • initiator函数堆栈
  • callstack函数堆栈
  • xhr断点
  • JS HOOK

2. Conditional breakpoints

在代码左边的行号 - > 右键 -> Edit breakpoints -> 而后输入表达式, 结果=true的时候会自动断下

3. Reres拓展插件

笔者使用该插件通常状况:

通常状况下是遇到大文件的时候,会使用Reres插件,或者须要修改代码进行调试的时候

Github地址:https://github.com/annnhan/ReRes

添加规则

点击“添加规则”按钮,输入如下信息,而后保存:

  • If URL match: 一个正则表达式,当请求的URL与之匹配时,规则生效。注意:不要填开头的/和结束的/gi,如/.*/gi请写成.*
  • Response: 映射的响应地址,这个地址会替换掉url中与上面正则匹配的部分。线上地址请以http://开头,本地地址以file:///开头,好比http://cssha.comfile:///D:/a.js

练习网站:

  • https://www.cls.cn/ 登陆的password
  • http://api.51pin.foxconn.com/iRecruitWeb/Recruit/Activity/ActivityParticipate.html?module=2

4. monitor监听方法

5. monitorEvents监听方法

6. watch监听变量

7.控制台实时表达式

四. 实战

1. webpack总体改写方案

其实就是在webpack命名的函数. webpack中,会有须要的

var aaa = n(12);

var bbb = n(45);

咱们对于webpack的网站,我自认为不适合用扣算法,不适合用缺乏补啥的方法,谁能知道n(*)里面还有没有嵌套其余的n呢. 因此我认为用总体改写就是一个好的办法.

总体改写的思路以下:
1. 找到加密位置
2. 查到当前方法实现代码,总体拿下.

相似这种的就拿下.

(window["webpackJsonp"]=window["webpackJsonp"]||[]).push(
"aaa":function(e,t,r){ },
"bbb":functino(e,t,r){ }
)()
3. 找到"n"函数声明位置.

通常相似于这样, 具体如何说我好像无法表达. 也是整个文件拿下通常.

!function(e) { 
    function r(r) { 
        for (var n, a, i = r[0], c = r[1], l = r[2], p = 0, s = []; p < i.length; p++)
            a = i[p],
            Object.prototype.hasOwnProperty.call(o, a) && o[a] && s.push(o[a][0]),
            o[a] = 0;
        for (n in c)
            Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]);
        for (f && f(r); s.length; )
            s.shift()();
        return u.push.apply(u, l || []),
        t()
    }
    function t() { 
        for (var e, r = 0; r < u.length; r++) { 
            for (var t = u[r], n = !0, i = 1; i < t.length; i++) { 
                var c = t[i];
                0 !== o[c] && (n = !1)
            }
            n && (u.splice(r--, 1),
            e = a(a.s = t[0]))
        }
        return e
    }
    var n = { }
      , o = { 
        1: 0
    }
      , u = [];
    function a(r) { 
        if (n[r])
            return n[r].exports;
        var t = n[r] = { 
            i: r,
            l: !1,
            exports: { }
        }
          , o = !0;
        try { 
            e[r].call(t.exports, t, t.exports, a),
            o = !1
        } finally { 
            o && delete n[r]
        }
        return t.l = !0,
        t.exports
    }
    a.e = function(e) { 
        var r = []
          , t = o[e];
        if (0 !== t)
            if (t)
                r.push(t[2]);
            else { 
                var n = new Promise((function(r, n) { 
                    t = o[e] = [r, n]
                }
                ));
                r.push(t[2] = n);
                var u, i = document.createElement("script");
                i.charset = "utf-8",
                i.timeout = 120,
                a.nc && i.setAttribute("nonce", a.nc),
                i.src = function(e) { 
                    return a.p + "static/chunks/" + ({ }[e] || e) + "." + { 
                        53: "6d99d4eacdc1f6ea047f",
                        54: "cbec7184fead9e811bbf"
                    }[e] + ".js"
                }(e);
                var c = new Error;
                u = function(r) { 
                    i.onerror = i.onload = null,
                    clearTimeout(l);
                    var t = o[e];
                    if (0 !== t) { 
                        if (t) { 
                            var n = r && ("load" === r.type ? "missing" : r.type)
                              , u = r && r.target && r.target.src;
                            c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + u + ")",
                            c.name = "ChunkLoadError",
                            c.type = n,
                            c.request = u,
                            t[1](c)
                        }
                        o[e] = void 0
                    }
                }
                ;
                var l = setTimeout((function() { 
                    u({ 
                        type: "timeout",
                        target: i
                    })
                }
                ), 12e4);
                i.onerror = i.onload = u,
                document.head.appendChild(i)
            }
        return Promise.all(r)
    }
    ,
    a.m = e,
    a.c = n,
    a.d = function(e, r, t) { 
        a.o(e, r) || Object.defineProperty(e, r, { 
            enumerable: !0,
            get: t
        })
    }
    ,
    a.r = function(e) { 
        "undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { 
            value: "Module"
        }),
        Object.defineProperty(e, "__esModule", { 
            value: !0
        })
    }
    ,
    a.t = function(e, r) { 
        if (1 & r && (e = a(e)),
        8 & r)
            return e;
        if (4 & r && "object" === typeof e && e && e.__esModule)
            return e;
        var t = Object.create(null);
        if (a.r(t),
        Object.defineProperty(t, "default", { 
            enumerable: !0,
            value: e
        }),
        2 & r && "string" != typeof e)
            for (var n in e)
                a.d(t, n, function(r) { 
                    return e[r]
                }
                .bind(null, n));
        return t
    }
    ,
    a.n = function(e) { 
        var r = e && e.__esModule ? function() { 
            return e.default
        }
        : function() { 
            return e
        }
        ;
        return a.d(r, "a", r),
        r
    }
    ,
    a.o = function(e, r) { 
        return Object.prototype.hasOwnProperty.call(e, r)
    }
    ,
    a.p = "",
    a.oe = function(e) { 
        throw console.error(e),
        e
    }
    ;
    var i = window.webpackJsonp = window.webpackJsonp || []
      , c = i.push.bind(i);
    i.push = r,
    i = i.slice();
    for (var l = 0; l < i.length; l++)
        r(i[l]);
    var f = c;
    t()
}([]);
4. window.n = a;
!function(e) { 
    function r(r) { 
        for (var n, a, i = r[0], c = r[1], l = r[2], p = 0, s = []; p < i.length; p++)
            a = i[p],
            Object.prototype.hasOwnProperty.call(o, a) && o[a] && s.push(o[a][0]),
            o[a] = 0;
        for (n in c)
            Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]);
        for (f && f(r); s.length; )
            s.shift()();
        return u.push.apply(u, l || []),
        t()
    }
    function t() { 
        for (var e, r = 0; r < u.length; r++) { 
            for (var t = u[r], n = !0, i = 1; i < t.length; i++) { 
                var c = t[i];
                0 !== o[c] && (n = !1)
            }
            n && (u.splice(r--, 1),
            e = a(a.s = t[0]))
        }
        return e
    }
    var n = { }
      , o = { 
        1: 0
    }
      , u = [];
    function a(r) { 
        if (n[r])
            return n[r].exports;
        var t = n[r] = { 
            i: r,
            l: !1,
            exports: { }
        }
          , o = !0;
        try { 
            e[r].call(t.exports, t, t.exports, a),
            o = !1
        } finally { 
            o && delete n[r]
        }
        return t.l = !0,
        t.exports
    }
    a.e = function(e) { 
        var r = []
          , t = o[e];
        if (0 !== t)
            if (t)
                r.push(t[2]);
            else { 
                var n = new Promise((function(r, n) { 
                    t = o[e] = [r, n]
                }
                ));
                r.push(t[2] = n);
                var u, i = document.createElement("script");
                i.charset = "utf-8",
                i.timeout = 120,
                a.nc && i.setAttribute("nonce", a.nc),
                i.src = function(e) { 
                    return a.p + "static/chunks/" + ({ }[e] || e) + "." + { 
                        53: "6d99d4eacdc1f6ea047f",
                        54: "cbec7184fead9e811bbf"
                    }[e] + ".js"
                }(e);
                var c = new Error;
                u = function(r) { 
                    i.onerror = i.onload = null,
                    clearTimeout(l);
                    var t = o[e];
                    if (0 !== t) { 
                        if (t) { 
                            var n = r && ("load" === r.type ? "missing" : r.type)
                              , u = r && r.target && r.target.src;
                            c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + u + ")",
                            c.name = "ChunkLoadError",
                            c.type = n,
                            c.request = u,
                            t[1](c)
                        }
                        o[e] = void 0
                    }
                }
                ;
                var l = setTimeout((function() { 
                    u({ 
                        type: "timeout",
                        target: i
                    })
                }
                ), 12e4);
                i.onerror = i.onload = u,
                document.head.appendChild(i)
            }
        return Promise.all(r)
    }
    ,
    a.m = e,
    a.c = n,
    a.d = function(e, r, t) { 
        a.o(e, r) || Object.defineProperty(e, r, { 
            enumerable: !0,
            get: t
        })
    }
    ,
    a.r = function(e) { 
        "undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { 
            value: "Module"
        }),
        Object.defineProperty(e, "__esModule", { 
            value: !0
        })
    }
    ,
    a.t = function(e, r) { 
        if (1 & r && (e = a(e)),
        8 & r)
            return e;
        if (4 & r && "object" === typeof e && e && e.__esModule)
            return e;
        var t = Object.create(null);
        if (a.r(t),
        Object.defineProperty(t, "default", { 
            enumerable: !0,
            value: e
        }),
        2 & r && "string" != typeof e)
            for (var n in e)
                a.d(t, n, function(r) { 
                    return e[r]
                }
                .bind(null, n));
        return t
    }
    ,
    a.n = function(e) { 
        var r = e && e.__esModule ? function() { 
            return e.default
        }
        : function() { 
            return e
        }
        ;
        return a.d(r, "a", r),
        r
    }
    ,
    a.o = function(e, r) { 
        return Object.prototype.hasOwnProperty.call(e, r)
    }
    ,
    a.p = "",
    a.oe = function(e) { 
        throw console.error(e),
        e
    }
    ;
    var i = window.webpackJsonp = window.webpackJsonp || []
      , c = i.push.bind(i);
    i.push = r,
    i = i.slice();
    for (var l = 0; l < i.length; l++)
        r(i[l]);
    var f = c;
    t()
    // 重要**** 
    window.n = a;
    // 重要****
}([]);

2. sojson反调试

案例地址:https://www.sojson.com/beian/

1. 修改setInterval

如何定位不说了, 直接走到关键的地方

window[b('96', 'lInO')](function() {  //b('96', 'lInO') == "setInterval"
    var cf = { 
        'gSHOk': function(cg) { 
            return cg(); //cg()其实就是一个检测debug的函数.
        }
    };
    cf['gSHOk'](en);
}, 0x7d0);

为了避免影响到其余setInterval函数的执行. 这里能够加一点条件.

var _setInterval = setInterval;
setInterval = function(a,b){ 
    console.log(a + '',b)
    if(a.indexOf("gSHOk':function(cg){return cg();}")!= -1){ 
		return 'setInterval is Kill'
	}
    _setInterval(a,b)
}

2. Conditional breakpoints

这里打开调试工具会直接跳到下面这一块,咱们这节对debugger;行号栏目, 右键 add conditional breakpoints, 输入false . 当条件为false的时候,就不会执行此条件.

function eC(eD) { 
    var eE = { 
        'oihUc': b('161', '!9L9'),
        'YSZMe': ep[b('162', 'bhfu')]
    };
    if (ep[b('163', 'QS!f')](b('164', '1$&&'), ep[b('165', 'C5IH')])) { 
        en();
    } else { 
        if (typeof eD === ep[b('166', '18LM')]) { 
            var eG = function() { 
                if (eE[b('167', 'tEyN')] !== eE[b('168', 'Gq^E')]) { 
                    debugger ;
                } else { 
                    so[b('169', 'eOuM')](res[b('16a', 'w2W4')]);
                }
            };
            return ep['bzKJh'](eG);
        } else { 
            if (ep[b('16b', 'IIR5')](ep[b('16c', 'BDu]')]('', eD / eD)[ep[b('16d', '(igu')]], 0x1) || eD % 0x14 === 0x0) { 
                debugger ;
            } else { 
                debugger ;
            }
        }
        ep['RwYcr'](eC, ++eD);
    }
}

3. 函数替换,函数置空

4. Activate breakpoints

直接快捷键. Ctrl + F8

5. 修改debugger

(貌似失效了)

简单点的

Function.prototype.constructor = function(){ }

完善点

Function.prototype.__constructor_back = Function.prototype.constructor;
Function.prototype.constructor = function() { 
    if(arguments && typeof arguments[0]==='string'){ 
        //alert("new function: "+ arguments[0]);
        if("debugger" === arguments[0]){ 
            //arguments[0]="console.log(\"anti debugger\");";
            //arguments[0]=";";
            return
        }
    }
   return Function.prototype.__constructor_back.apply(this,arguments);
}

总结:我的认为,最好用的方法应该是1,2,4了. 操做简单.

3. 某视频反调试案例

http://peng3.com/vip?a=https%3A%2F%2F2.08bk.com%2F%3Furl%3D%0D%0A&url=http%3A%2F%2F1.zhananhome.applinzi.com%2Fwx

//定位到这边.
jdetects.create(function(e) { 
    var a = 0;
    var n = setInterval(function() { 
        if ("on" === e) { 
            setTimeout(function() { 
                if (a === 0) { 
                    a = 1;
                    setTimeout(Base64.decode(code));
                }
            }, 200);
        }
    }, 100);

注入JS代码

var _setInterval = setInterval;
setInterval = function(a,b){ 
    console.log(a + '',b)
    if(a+''.indexOf("setTimeout(Base64.decode(code));")!= -1){ 
		return 'Debugger is Kill'
	}
    _setInterval(a,b)
}

解决这个之后又发现个惊人的操做.在控制台发现Console was cleared

function o() { 
    window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized ? t("on") : (a = "off",
                                                                                                console.log(d),
                                                                                                ("undefined" !== typeof console.clear) && console.clear(),
                                                                                                t(a));
}

修改下o方法

o = function(){ }

或者在上面的setInterval代码加多个条件. 缘由是向上跟踪发现如下代码

var f = setInterval(o, i);
var _setInterval = setInterval;
setInterval = function(a,b){ 
    console.log(a + '',b)
    if( a + ''.indexOf("setTimeout(Base64.decode(code));")!= -1){ 
		return 'Debugger is Kill'
	}
    if (a + ''.indexOf('console.clear')!=-1){ 
        return 'console.clear is Kill'
    }
    _setInterval(a,b)
}

4. 自写算法案例 -1

5. 自写算法案例 -2

6. JS混淆原理(eval和Function)

7. JS混淆原理(数字混淆和字符串混淆)

8. 五秒防火墙fuckjs原理分析改写

9. 流程控制混淆原理(switch)

10. 流程控制混淆原理(逗号运算符)

五.AST入门与实战

1. AST抽象语法树入门

2. Babel组件traverse

3. Babel组件types

4. 用Babel生成一个新函数

5. Babel中节点操做

6. 用Babel给函数加点料

7. 用Babel实现变量名混淆

7. 用Babel实现数组乱序

8. 用Babel实现字符串加密

9. 实现十六进制文本加密

10. 实现unicode加密

11. JS混淆还原(字符串解密)

12. JS混淆还原(去除花指令)

13. JS混淆还原(AST节点调试技巧)

14. switch流程平坦化还原(复原指令顺序)

15. JS混淆实战案例

六. 滑块破解

1. 云片

2. 2980

相关文章
相关标签/搜索