网页逆向-4代瑞数逆向实战

扣某数4代代码

点击检查,会进入一个断点,这个断点是不用去掉的,根据断点的函数堆栈往上找,可以找到一段vm代码

这里的几千行vm代码,都是通过eval()函数直接执行得到的,然后把执行返回的结果丢到vm里去

一般瑞数的代码,变量几乎都是$_这种方式命名的

vm代码的加载,是通过一个链接变量配合js的自执行函数来得到的

这里的$_ts就是链接,可以在全局下一个事件监听断点中的script脚本断点,在每一个js脚本执行之前断下来,这样可以找到所有加载的js脚本,从而定位到js的入口,当第一次断下来的时候,点击执行,会定位到很多js脚本

一直找到带有$_ts特征的js脚本

把这个js代码放到input.js里,然后继续运行,会跳转到index.html里的一段自执行函数里,把自执行函数也放到input.js里

这里自执行函数的上面的src属性就是链接

1
<script type="text/javascript" charset="iso-8859-1" src="http://www.fangdi.com.cn/4QbVtADbnLVIc/d.FxJzG50F.dfe1675.js" r='m'></script>

如果要处理编码的话,要用这里来的charset属性值的编码

自执行函数和上面的js脚本配合起来就生成了eval()代码,要补的就是这个input.js

补DOM环境

先跑起来,看日志,先把proxy打开,看访问了哪些属性,找到如下报错

1
{func|apply:[document.getElementsByTagName] -> type:[[object HTMLCollection]] ->args:[meta]} 

因为我们没有创建meta标签,所以当调用这个方法的时候,获取不到返回值,需要在userVar.js里添加标签

浏览器创建meta标签,查看__proto__属性,看原型是什么,运行脱环境脚本把原型脱下来,这里原型是HTMLMetaElement

1
2
3
4
!function () {
// 创建meta标签
let a=document.createElement('meta')
}()

运行完之后又有新的报错

1
2
3
4
5
{obj|get:[Document_getElementsByTagName_meta] -> prop:[0] -> type:[[object HTMLMetaElement]]} 
{obj|get:[Document_createElement_meta_1] -> prop:[Symbol(proto)] -> result:[undefined]}
{obj|get:[Document_createElement_meta_1] -> prop:[content] -> result:[getProtoAttribute->content未定义]}
{obj|get:[Document_createElement_meta_1] -> prop:[Symbol(proto)] -> result:[undefined]}
{obj|get:[Document_createElement_meta_1] -> prop:[parentNode] -> result:[getProtoAttribute->parentNode未定义]}

报错是尝试获取meta标签的content属性,获取不到,就需要通过setProtoAttribute()去设置属性

content的值是什么呢?在首页的js里

1
<meta content="{qqqqqqqqqKnjVHivS8bGRiTVhOcq6Maaqc80|[wLVH7fsVmJwJZVo.xiGD7rIcs4gdC06cDeVr6ktNmFEeLAhcpXmqnl64pexk4GD.qFLNNOd4rIWIbSdlrQgIbktwxBWcgsjQEZek0uB1sQGHnunKHiEHycuQEw3BCqMvoIVmZqtHiz96GS6tmeqA6Gosoe7QTaoFs3329O1qH_eo9pDdmEfrPV6Zx_7GBO8HmtE_bu5zs33uNSjPo40r_rU_Wt0Z.koPpNy3Bpcdp5Tr_YP6r8V1Zsc3Hx7ST0F2KEqAGlIAAwx3GGU1ohTm0fiKmhLbvl6MrhLa2lc3ihekSGMiE3gO9licq.0M900hb5iuoBOwLP6l4096r0qqqqqqqqqlFSrlkKlVDdfe167qJ1701684316574hFGza5HAY3CNl3650qq!x7z,aac,amr,asm,avi,bak,bat,bmp,bin,c,cab,css,csv,com,cpp,dat,dll,doc,dot,docx,exe,eot,fla,flc,fon,fot,font,gdb,gif,gz,gho,hlp,hpp,htc,ico,ini,inf,ins,iso,js,jar,jpg,jpeg,json,java,lib,log,mid,mp4,mpa,m4a,mp3,mpg,mkv,mod,mov,mim,mpp,msi,mpeg,obj,ocx,ogg,olb,ole,otf,py,pyc,pas,pgm,ppm,pps,ppt,pdf,pptx,png,pic,pli,psd,qif,qtx,ra,rm,ram,rmvb,reg,res,rtf,rar,so,sbl,sfx,swa,swf,svg,sys,tar,taz,tif,tiff,torrent,txt,ttf,vsd,vss,vsw,vxd,woff,woff2,wmv,wma,wav,wps,xbm,xpm,xls,xlsx,xsl,xml,z,zip,apk,plist,ipaqqqqqqqiiFEH9qcMcNQt9A{UifVEhaZ2qofEQYe6svziQ2YnKPaqhqZ2YlmW1mTks9SEc9eKUTpCp2pHs0fwsTLRqrGkm0fh1TNsA2ZIKYJRKTZQssmgrvZrIaJeqoLrIam_rvaxwAN5qk162qr1qqqqqqqqqlmZlgm26649 0wR7HvJ6IsUC410DntKRngA;QyqA82EGtIB6ePNEeYo9NG;iEm6gdSTTpYiqU10OlvsnG;yMG8gk5okQ97gP4eb.IadA;T8F36FaS9AtR4sXBkRr0iG;RTlM3IYjAzboXbIiNSIFRA;t7_svh3Kc3.VU9jOjAJgdq;.8D9Zx78FrKF.Zn4xbfmIG;IMhCM7gXESIqShs5TNMo9A;pvBPF7OtrK6trS5vZYizwa;9qxqLXuEeDQeAlNfAL_l.A;VNeyFcNDtQZhV2sfCxyHqA;kT4JL2WRSOhvUIEcOjSrva;LpFhLGWYI8eFx_X999MLEq;NqssQaVItFB0TevtNxJrkG;AI3RN3R7lP0BBnYsoCO5KG;xrYRhwM6FYW7zCsPL.iecq;0kOXzZzt1eXLrlPo.QQ4xG;ApKNqLIRoybF5rIxSnabBG;hfgZrtz_KscdFC6a3f1wKA;Y53wfMWCgfXKRrRD8hGqNit1074790472r0Vl67PscJJxVVvs1rJxMqf">

那么parentNode的报错怎么处理?

1
{obj|get:[Document_createElement_meta_1] -> prop:[parentNode] -> result:[getProtoAttribute->parentNode未定义]} 

在浏览器中运行document.getElementByTagName("meta"),返回的是一个meta列表,找到对应的meta标签,然后输出这个标签的parentNode属性,发现就是head标签,创建head标签,把parentNode属性指过去

其他的DOM元素参照此种方法

1
2
3
4
5
6
7
8
!function () {
let head = document.createElement('head')
let meta=document.createElement('meta')
sandBox.setProtoAttribute.call(meta,'content','{qqqqq!x7z,aac,amr,asm,avi,bak,bat,bmp,bin,c,cab,css,csv,com,cpp,dat,dll,doc,dot,docx,exe,eot,fla,flc,fon,fot,font,gdb,gif,gz,gho,hlp,hpp,htc,ico,ini,inf,ins,iso,js,jar,jpg,jpeg,json,java,lib,log,mid,mp4,mpa,m4a,mp3,mpg,mkv,mod,mov,mim,mpp,msi,mpeg,obj,ocx,ogg,olb,ole,otf,py,pyc,pas,pgm,ppm,pps,ppt,pdf,pptx,png,pic,pli,psd,qif,qtx,ra,rm,ram,rmvb,reg,res,rtf,rar,so,sbl,sfx,swa,swf,svg,sys,tar,taz,tif,tiff,torrent,txt,ttf,vsd,vss,vsw,vxd,woff,woff2,wmv,wma,wav,wps,xbm,xpm,xls,xlsx,xsl,xml,z,zip,apk,plist,ipaqqqqqqqqqqqQExlYNrKGltA1NrFblFWYNfiXmFLU|[Ue3bgsCmH3f9LDPgkM9Byu_sRX0KPb1zWjAL.SB3HJVV0cXSMyrNzC1tMjmjvnOQtJ7qTpFthxaCnOFUhxZCuSBWk_q.7rHkcNyjGrtkRxZbbfUmmMWR1uNzJCVyFprAc0AsASA5hD3fWGzyqKlDFpwPcKGtRfwTKTQuA1xptUfpAqw7q22UY27zWUGCMnm3qvW71c2ZKC92YGSpcVaUoaWhx2gjU1QwVDeisGRiVpNUDlyllSAloPRxt9Gvr2rns2lDVTlrYaTiVSWrcbSsrAYoqb9asYz0lG7bxT3NhGfYwSreJuZThTVumqgJE27{W3DR1UmYYKG7WqrqEkaL1ccAWmqZqUYNmVV7ilS3FrSVVDfx3kpGFipqxlSN1AYplKPEAmGlpmiZJc80HpCYCVvm8ITxuKvAqqKt3vbVHzj9km6gN9fozlVaAr1qqr0k162l4096qhjUsrECFvJvEqqqqqqqqqqqqqqqq 0wR7HvJ6IsUC410DntKRngA;QyqA82EGtIB6ePNEeYo9NG;iEm6gdSTTpYiqU10OlvsnG;yMG8gk5okQ97gP4eb.IadA;T8F36FaS9AtR4sXBkRr0iG;RTlM3IYjAzboXbIiNSIFRA;t7_svh3Kc3.VU9jOjAJgdq;.8D9Zx78FrKF.Zn4xbfmIG;IMhCM7gXESIqShs5TNMo9A;pvBPF7OtrK6trS5vZYizwa;9qxqLXuEeDQeAlNfAL_l.A;VNeyFcNDtQZhV2sfCxyHqA;kT4JL2WRSOhvUIEcOjSrva;LpFhLGWYI8eFx_X999MLEq;NqssQaVItFB0TevtNxJrkG;AI3RN3R7lP0BBnYsoCO5KG;xrYRhwM6FYW7zCsPL.iecq;0kOXzZzt1eXLrlPo.QQ4xG;ApKNqLIRoybF5rIxSnabBG;hfgZrtz_KscdFC6a3f1wKA;qm26649Ddfe167lDXlTkHUVqlw7TRVE9VsQSzdm6AUIrRZlv01qr0l3650t1074790472hBz59hckrz4_Y_5oAAjnMlZF4gEHgvVyqXJ1699535420627\n')
sandBox.setProtoAttribute.call(meta,'parentNode',head)
let script = document.createElement('script')
sandBox.setProtoAttribute.call(script,'parentNode',head)
}()

重新运行,发现报错信息

1
{Node_removeChild->[object HTMLMetaElement] 具体内容未实现} 

这个函数是没有实现的,其实瑞数在执行这个函数的目的是在获取了metacontent属性和script标签的自执行函数之后,就会把这两个标签移除,但是这个函数不是监测点,其实不去补也行

继续看日志,里面有没有获取到的result是undefined的,发现locationnavigator没有获取到

1
{obj|get:[location] -> prop:[navigator] -> result:[undefined]} 

补的话也很容易

1
location.navigator = navigator

后面还有尝试获取location的一堆属性,没获取到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{obj|get:[location] -> prop:[navigator] -> result:[undefined]} 
{obj|get:[location] -> prop:[callPhantom] -> result:[undefined]}
{obj|get:[location] -> prop:[_phantom] -> result:[undefined]}
{obj|get:[location] -> prop:[$hook$] -> result:[undefined]}
{obj|get:[location] -> prop:[$$logger] -> result:[undefined]}
{obj|get:[location] -> prop:[$$lsp] -> result:[undefined]}
{obj|get:[location] -> prop:[$$lsrb] -> result:[undefined]}
{obj|get:[location] -> prop:[$hdx$] -> result:[undefined]}
{obj|get:[location] -> prop:[$readyCodeAlreadyExecutedInThisFrame] -> result:[undefined]}
{obj|get:[location] -> prop:[$sdx$] -> result:[undefined]}
{obj|get:[location] -> prop:[$uie$] -> result:[undefined]}
{obj|get:[location] -> prop:[netsparker] -> result:[undefined]}
{obj|get:[location] -> prop:[__ns] -> result:[undefined]}
{obj|get:[location] -> prop:[__nsAppendText] -> result:[undefined]}
{obj|get:[location] -> prop:[eoWebBrowser] -> result:[undefined]}
{obj|get:[location] -> prop:[hp_identifier] -> result:[undefined]}
{obj|get:[location] -> prop:[appScanClick] -> result:[undefined]}
{obj|get:[location] -> prop:[appScanFocusOut] -> result:[undefined]}
{obj|get:[location] -> prop:[appScanKeyDown] -> result:[undefined]}
{obj|get:[location] -> prop:[appScanKeyUp] -> result:[undefined]}

补的话,在浏览器中copy下location的实例,丢到userVar.js里就行了

1
2
3
4
5
6
7
8
9
10
11
12
location = {
"ancestorOrigins": {},
"href": "chrome://new-tab-page/",
"origin": "chrome://new-tab-page",
"protocol": "chrome:",
"host": "new-tab-page",
"hostname": "new-tab-page",
"port": "",
"pathname": "/",
"search": "",
"hash": ""
}

后面把proxy关掉,这样可以看函数缺失,就是哪些函数没有实现

1
2
3
4
5
6
7
{EventTarget_addEventListener | 异步事件:load} 
{Node_removeChild->[object HTMLMetaElement] 具体内容未实现}
{EventTarget_addEventListener | 异步事件:unload}
{Node_removeChild->[object HTMLScriptElement] 具体内容未实现}
{tools|dispatch -> 环境缺失:Navigator_languages_get ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:Document_documentElement_get ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:Navigator_webkitPersistentStorage_get ->错误:Cannot read properties of undefined (reading 'apply')

先看Navigator_webkitPersistentStorage_get,浏览器运行navigator.webkitPersistentStorage

1
2
3
4
5
6
DeprecatedStorageQuota {}
[[Prototype]]: DeprecatedStorageQuota
queryUsageAndQuota: ƒ queryUsageAndQuota()
requestQuota: ƒ requestQuota()
Symbol(Symbol.toStringTag): "DeprecatedStorageQuota"
[[Prototype]]: Object

浏览器执行getEnvCode(DeprecatedStorageQuota),发现报错了,因为这个类在浏览器中就不存在,所以直接模仿其他类的代码构造一个

然后运行Object.getOwnPropertyDescriptors(navigator.webkitPersistentStorage),浏览器直接返回空,所以在实现这个函数的时候直接返回空就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//DeprecatedStorageQuota
DeprecatedStorageQuota=function DeprecatedStorageQuota() {

}
//删除构造函数
delete DeprecatedStorageQuota.prototype.DeprecatedStorageQuota

//命名
sandBox.safeProto(DeprecatedStorageQuota,"DeprecatedStorageQuota")

// 因为原型上直接就是Object了,所以不用补原型链
// 原型上还有俩方法,可以补也可以不补
sandBox.defineProperty(DeprecatedStorageQuota.prototype,"queryUsageAndQuota",{
configurable:false,
enumerable: false,
writable:false,
value:function queryUsageAndQuota() {

}
})
sandBox.defineProperty(DeprecatedStorageQuota.prototype,"requestQuota",{
configurable:false,
enumerable: false,
writable:false,
value:function requestQuota() {

}
})
1
2
3
4
5
6
sandBox.envFuncs.Navigator_webkitPersistentStorage_get=function () {
let _DeprecatedStorageQuota={}
Object.setPrototypeOf(_DeprecatedStorageQuota,DeprecatedStorageQuota.prototype)
_DeprecatedStorageQuota=sandBox.proxy(_DeprecatedStorageQuota,'DeprecatedStorageQuota')
return _DeprecatedStorageQuota;
}

继续实现Document_documentElement_get,这个函数返回的是当前页面的document,但是在补的时候不需要返回这么复杂,意思一下就行

1
2
3
sandBox.envFuncs.Document_documentElement_get=function () {
return '<html></html>'
}

同样的方法补Navigator_languages_get

1
2
3
sandBox.envFuncs.Navigator_languages_get=function () {
return ['zh-CN', 'en', 'en-GB', 'en-US']
}

然后把proxy打开,重新运行看日志

1
{tools|dispatch -> 环境缺失:window_setInterval ->错误:Cannot read properties of undefined (reading 'apply') 

首先把对应的属性加到GlobalThis.js里,这个是window的模拟代码,因为window的属性太多了,需要哪个才往里面加哪个

1
2
3
sandBox.defineProperty(window,"setInterval",{configurable:true,enumerable:true,writable:true,value:function setInterval() {
return sandBox.dispatch("window_setInterval",this,arguments)
}})

然后开始实现window_setInterval,同时实现window_clearInterval

toolFuncs.js里定义存储事件的列表和事件ID

1
2
sandBox.asyncCode.setInterval= []
sandBox.asyncCode.intervalID=-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
sandBox.envFuncs.window_setInterval=function () {
sandBox.asyncCode.intervalID+=1
let func=arguments[0]
let delay=arguments[1]
let args=[]
for(let i=2;i<arguments.length;i++){
args.push(arguments[i])
}
let type='func'
if(typeof func==='function'){
type='func'
}else{
type='func_str'
}
let event={
'this':this,
'type':type,
'func':func,
'delay':delay,
'args':args,
'intervalID':sandBox.asyncCode.intervalID
}
sandBox.asyncCode.setInterval.push(event)
sandBox.asyncCode.setInterval.sort(function (a,b) {
return a.delay-b.delay
})
return sandBox.asyncCode.intervalID
}
sandBox.envFuncs.window_clearInterval=function (intervalID){
for(let i=0;i<sandBox.asyncCode.setInterval.length;i++){
let event=sandBox.asyncCode.setInterval[i]
if(event['intervalID']===intervalID){
sandBox.asyncCode.setInterval.splice(i, 1);
}
}
}

然后在asyncCode.js中实现取出事件、执行事件的代码逻辑

1
2
3
4
5
6
7
8
9
10
11
if(sandBox.asyncCode.setInterval!==[]){
let intervalFunc=sandBox.asyncCode.setInterval
for(let i=0;i<intervalFunc.length;i++){
let event=intervalFunc[i]
if(event['type']==='func'){
event['func'].apply(event['this'],event['args'])
}else{
eval(event['func'])
}
}
}

补异步环境

1
{tools|dispatch -> 环境缺失:Navigator_getBattery ->错误:Cannot read properties of undefined (reading 'apply') 

浏览器运行navigator.getBattery(),返回Promise对象,Promise一般是和.then搭配起来,达到异步执行代码的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//then的参数battery就是getBattery返回的对象
navigator.getBattery().then(function (battery) {
console.log(battery)
})
// 浏览器运行上面的代码,会执行then里定义的函数,执行结果
// 这里就是把参数给打印出来

BatteryManager {charging: true, chargingTime: 0, dischargingTime: Infinity, level: 1, onchargingchange: null, …}
charging: true
chargingTime: 0
dischargingTime: Infinity
level: 1
onchargingchange: null
onchargingtimechange: null
ondischargingtimechange: null
onlevelchange: null
[[Prototype]]: BatteryManager

首先解释一下这个navigator.getBattery()是干什么的,它的作用就是获取电池信息,并且它的回调函数的参数是BatteryManager对象,一定是这个对象,不会是别的

1
2
3
4
navigator.getBattery().then(function (abc) {
console.log(abc); // abc 就是 BatteryManager
// 不管参数叫什么,a,aa,foo,abc,都会被浏览器当作BatteryManager对象
});

所以在模拟的时候,回调参数的参数一定要构造成BatteryManager对象,然后补的话和补setTimeout差不多思路,都是把Promise取出来,挨个去执行一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sandBox.envFuncs.Navigator_getBattery=function () {
// 脱环境脚本把BatteryManager脱出来丢到envs里BatteryManager.js里
let batteryManager={}
Object.setPrototypeOf(batteryManager,BatteryManager.prototype)
batteryManager=sandBox.proxy(batteryManager,'BatteryManager')
let obj={
'then':function (callback) {
let _callback=callback;
callback=function () {
return _callback(batteryManager)
}
sandBox.asyncCode.Promises.push(callback)
}
}
return obj
}

toolFuncs.js里定义全局存放Promise对象的列表

1
sandBox.asyncCode.Promises=[]

这里简单解释一下怎么触发的这个逻辑?

当执行如下代码

1
2
3
navigator.getBattery().then(function (battery) {
console.log(battery)
})

调用的是上面的Navigator_getBattery,返回了obj对象,这个obj对象里定义了then属性,所以代码调用的navigator.getBattery().then实际上触发的是Navigator_getBatteryobj对象的then属性的属性值定义的函数,在这个函数里获取到了navigator.getBattery().then(...)传入的参数,也就是回调函数,把回调函数添加到全局列表中,等待依次执行

在asyncCode.js中依次执行Promise的异步回调函数

1
2
3
4
5
if(sandBox.asyncCode.Promises!==[]){
for(let i=0;i<sandBox.asyncCode.Promises.length;i++){
sandBox.asyncCode.Promises[i]()
}
}

但是运行main.js的时候直接报错了

1
can't read properties of undefined(reading 'level')

获取不到level属性,结合BatteryManager.js里的属性,需要实现一下这个函数

1
2
3
sandBox.defineProperty(BatteryManager.prototype,"level",{configurable:true,enumerable:true,get:function () {
return sandBox.dispatch("BatteryManager_level_get",this,arguments,undefined)
}

其他的方法也可以一并设置一下,返回值参照浏览器的属性值

1
2
3
4
5
6
7
8
9
10
11
12
sandBox.envFuncs.BatteryManager_level_get=function () {
return 0.9
}
sandBox.envFuncs.BatteryManager_charging_get=function () {
return true
}
sandBox.envFuncs.BatteryManager_chargingTime_get=function () {
return Infinity
}
sandBox.envFuncs.BatteryManager_dischargingTime_get=function (){
return Infinity
}

注意有个报错信息是获取navigatorbattery属性,返回了undefined,这个不用管,因为浏览器执行navigator.battery返回的也是undefined,和浏览器保持一致就行了

补cookie生成环境

报错:未实现Document_createElement_form,在envFuncs.js里实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//创建标签
sandBox.envFuncs.Document_createElement=function (tagName,options) {
let tag={};
switch (tagName) {
case "div":
Object.setPrototypeOf(tag,HTMLDivElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
//...
case "form":
Object.setPrototypeOf(tag,HTMLFormElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
default:
console.log(`未实现Document_createElement_${tagName}`)
break;
}
sandBox.tags.push(tag)
return tag;
}

然后脱环境脚本把HTMLFormElement脱下来,丢到HTMLFormElement.js里

然后处理报错

1
{tools|dispatch -> 环境缺失:Element_getAttribute ->错误:Cannot read properties of undefined (reading 'apply') 

getAttribute() 是一个 DOM 方法,用于获取 HTML 元素上的指定属性值(作为字符串),函数传入一个参数,然后把参数作为属性,尝试在当前DOM上访问该属性的属性值

比如

1
2
3
4
5
6
7
8
9
<div id="myDiv" class="box" data-info="hello"></div>
//
const div = document.getElementById("myDiv");

div.getAttribute("class"); // 返回 "box"
div.getAttribute("data-info"); // 返回 "hello"
div.getAttribute("id"); // 返回 "myDiv"
div.getAttribute("style"); // 没有设置,返回 null
div.getAttribute("Class"); // 注意大小写,返回 null

问题是,代码访问了哪些标签的哪些属性呢?首先要拿到属性,才能针对性的返回属性值,所以先写一个log输出,看访问了哪些属性

1
2
3
sandBox.envFuncs.Element_getAttribute=function (name) {
console.log(`{Element_getAttribute| get name ${name}}`)
}

然后运行看日志,发现访问了r属性,查看html源码,找到了这个标签

1
<script type="text/javascript" r="m">

然后新增一个script标签,在script标签里添加上r属性,属性值是m

1
2
3
let script=document.createElement('script');
sandBox.setProtoAttribute.call(script,'parentNode',head)
sandBox.setProtoAttribute.call(script,'r','m')

后面就是在模拟的函数中返回属性值

1
2
3
4
sandBox.envFuncs.Element_getAttribute=function (name) {
console.log(`{Element_getAttribute| get name ${name}}`)
return sandBox.getProtoAttribute.call(this,name)
}

重新运行,报错

1
can't read properties of undefined(reading 'removeChild')

查看日志,看是在哪里发生的属性访问导致报错

1
{tools|dispatch -> 环境缺失:Node_parentElement_get ->错误:Cannot read properties of undefined (reading 'apply') 

这个函数是获取当前节点的父节点,由于我们在userVar.js里已经设置过标签了,那么获取的节点的父节点大概率也是script标签和meta标签的父节点

1
2
3
4
5
6
7
let head=document.createElement('head');
let meta=document.createElement('meta');
sandBox.setProtoAttribute.call(meta,'content','...')
sandBox.setProtoAttribute.call(meta,'parentNode',head);
let script=document.createElement('script');
sandBox.setProtoAttribute.call(script,'parentNode',head);
sandBox.setProtoAttribute.call(script,'r','m');

那么在实现函数的时候直接返回标签就可以了

1
2
3
sandBox.envFuncs.Node_parentElement_get=function () {
return sandBox.getProtoAttribute.call(this,'parentNode')
}

然后重新运行,没有报错,说明此时DOM已经补的差不多了,剩下的就是globalThis的一些属性了,打开proxy,看里面有哪些日志返回的是undefined

这种是不用补的

1
{obj|get:[window] -> prop:[$_ts] -> result:[undefined]} 

因为这是input.js里的

1
2
3
4
5
6
7
//要补得JS代码
debugger;
$_ts = window['$_ts'];
if (!$_ts)
$_ts = {};
$_ts.scj = [];
$_ts['dfe1675'] = '...'

要补的是这种

1
{obj|get:[window] -> prop:[execScript] -> result:[undefined]} 

怎么补的,在浏览器打开目标网站,在控制台访问execScript,结果报错了,访问不到,说明也不用补

继续处理

1
{obj|get:[location] -> prop:[Symbol(Symbol.toStringTag)] -> result:[undefined]} 

这个的原因是在userVar.js里的location赋值出了问题

1
2
3
4
5
6
7
8
9
10
11
12
13
location.navigator=navigator;
location={
"ancestorOrigins": {},
"href": "http://localhost:63342/sandBox/pages/rs4/index.html?_ijt=i41d0ihh9alv4rd4olmj2co76i&_ij_reload=RELOAD_ON_SAVE",
"origin": "http://localhost:63342",
"protocol": "http:",
"host": "localhost:63342",
"hostname": "localhost",
"port": "63342",
"pathname": "/sandBox/pages/rs4/index.html",
"search": "?_ijt=i41d0ihh9alv4rd4olmj2co76i&_ij_reload=RELOAD_ON_SAVE",
"hash": ""
}

因为在Location.js里设置过了location,并完成了原型链的模拟

1
2
//location环境
location={}

而userVar.js在output.js的位置位于Location.js的下面,导致userVar.js把Location.js里设置的模拟代码给覆盖了

所以应该改成这样

1
2
3
4
5
6
7
8
9
10
location.ancestorOrigins={};
location.href="http://localhost:63342/sandBox/pages/rs4/index.html?_ijt=afksjb8r2vke54ojhq43mn6ogq&_ij_reload=RELOAD_ON_SAVE";
location.origin="http://localhost:63342";
location.protocol="http:";
location.host="localhost:63342";
location.hostname="localhost";
location.port= "63342";
location.pathname="/sandBox/pages/rs4/index.html";
location.search="?_ijt=afksjb8r2vke54ojhq43mn6ogq&_ij_reload=RELOAD_ON_SAVE"
location.hash=""

重新运行,直接报错了

1
can't read properties of undefined(reading 'userAgent')

关掉代理,重新运行,看日志,哪些方法没有实现

1
2
3
4
5
6
7
8
{tools|dispatch -> 环境缺失:location_href_set ->错误:Cannot read properties of undefined (reading 'apply') 
{tools|dispatch -> 环境缺失:location_protocol_set ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:location_host_set ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:location_hostname_set ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:location_port_set ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:location_pathname_set ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:location_search_set ->错误:Cannot read properties of undefined (reading 'apply')
{tools|dispatch -> 环境缺失:location_hash_set ->错误:Cannot read properties of undefined (reading 'apply')

报错的原因是因为location对象在Location.js里被保护过了,有的属性直接用location.href='xxx'的方式设置不上去,需要在envFuncs.js里实现对应函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//location
!function () {
sandBox.envFuncs.location_href_set=function (value) {
return sandBox.setProtoAttribute.call(this,'href',value)
}
sandBox.envFuncs.location_href_get=function () {
return sandBox.getProtoAttribute.call(this,'href')
}
sandBox.envFuncs.location_protocol_set=function (value) {
return sandBox.setProtoAttribute.call(this,'protocol',value)
}
sandBox.envFuncs.location_protocol_get=function () {
return sandBox.getProtoAttribute.call(this,'protocol')
}
sandBox.envFuncs.location_host_set=function (value) {
return sandBox.setProtoAttribute.call(this,'host',value)
}
sandBox.envFuncs.location_host_get=function () {
return sandBox.getProtoAttribute.call(this,'host')
}
sandBox.envFuncs.location_hostname_set=function (value) {
return sandBox.setProtoAttribute.call(this,'hostname',value)
}
sandBox.envFuncs.location_hostname_get=function () {
return sandBox.getProtoAttribute.call(this,'hostname')
}
sandBox.envFuncs.location_port_set=function (value) {
return sandBox.setProtoAttribute.call(this,'port',value)
}
sandBox.envFuncs.location_port_get=function () {
return sandBox.getProtoAttribute.call(this,'port')
}
sandBox.envFuncs.location_pathname_set=function (value) {
return sandBox.setProtoAttribute.call(this,'pathname',value)
}
sandBox.envFuncs.location_pathname_get=function () {
return sandBox.getProtoAttribute.call(this,'pathname')
}
sandBox.envFuncs.location_search_set=function (value) {
return sandBox.setProtoAttribute.call(this,'search',value)
}
sandBox.envFuncs.location_search_get=function () {
return sandBox.getProtoAttribute.call(this,'search')
}
sandBox.envFuncs.location_hash_set=function (value) {
return sandBox.setProtoAttribute.call(this,'hash',value)
}
sandBox.envFuncs.location_hash_get=function () {
return sandBox.getProtoAttribute.call(this,'hash')
}
}()

然后运行,正常不报错

继续看日志

1
{obj|get:[window] -> prop:[DOMParser] -> type:[undefined]} 

浏览器执行DOMParser,是有返回的,所以需要补

然后把对应的属性给放到globalThis.js里

1
2
3
sandBox.defineProperty(window,"DOMParser",{configurable:true,enumerable:false,writable:true,value:function DOMParser() {
return sandBox.dispatch("window_DOMParser",this,arguments)
}})

然后重新运行,日志里获取到返回值了

继续看日志

1
2
{obj|get:[window] -> prop:[msCrypto] -> result:[undefined]} 
{obj|has:[window] -> prop:[ActiveXObject] -> has:[undefined]}

浏览器运行ActiveXObjectmsCrypto都报错了,不用管

1
{obj|get:[window] -> prop:[name] -> result:[undefined]} 

可以在globalThis里补一下,因为globalThis.js里给window模拟了name属性,在get属性值里return一个就行了

1
2
3
4
5
6
7
8
9
sandBox.defineProperty(window,'name',{
configurable:true,
enumerable:true,
get:function () {
return ''
},
set:function () {
}
})

重新运行继续看日志

1
{obj|get:[window] -> prop:[globalStorage] -> result:[undefined]} 

浏览器运行globalStorage,报错,所以不用补

继续看日志

1
{obj|get:[window] -> prop:[indexedDB] -> type:[[undefined]]} 

浏览器运行indexedDB,返回的是IDBFactory

先把indexdDB属性添加到globalThis.js里

1
2
3
sandBox.defineProperty(window,"indexedDB",{configurable:true,enumerable:true,get:function () {
return sandBox.dispatch("window_indexedDB_get",this,arguments)
},set:undefined})

然后实现window_indexedDB_get方法,该方法返回一个IDBFactory对象

1
2
3
4
5
6
sandBox.envFuncs.window_indexedDB_get=function () {
let db={};
Object.setPrototypeOf(db,IDBFactory.prototype)
db=sandBox.proxy(db,'IDBFactory')
return db
}

脱环境脚本把IDBFactory脱下来丢到IDBFactory.js里,重新运行,indexDB的获取不再报错

继续看日志

1
{obj|get:[navigator] -> prop:[standalone] -> result:[undefined]} 

浏览器执行navigator.standalone,返回undefined,所以不要补

继续看日志

1
{obj|get:[navigator] -> prop:[chrome] -> result:[undefined]} 

浏览器执行chrome,返回

1
2
3
4
5
6
7
{loadTimes: ƒ, csi: ƒ}
app: (...)
csi: ƒ ()
loadTimes: ƒ ()
get app: ƒ ()
set app: ƒ ()
[[Prototype]]: Object

在globalThis.js里添加对应属性

1
sandBox.defineProperty(window,"chrome",{configurable:false,enumerable:true,writable:true,value:{}})

不需要补其他的了,因为浏览器执行chrome.runtimechrome.webstore都返回undefined

1
2
3
{obj|get:[window.chrome] -> prop:[runtime] -> result:[undefined]} 
{obj|get:[window] -> prop:[chrome] -> type:[[object Object]]}
{obj|get:[window.chrome] -> prop:[webstore] -> result:[undefined]}

继续看日志

1
{obj|get:[window] -> prop:[webkitRequestFileSystem] -> type:[[undefined]]} 

添加对应的属性到globalThis.js里

1
2
3
sandBox.defineProperty(window,"webkitRequestFileSystem",{configurable:true,enumerable:true,writable:true,value:function webkitRequestFileSystem() {
return sandBox.dispatch("window_webkitRequestFileSystem",this,arguments)
}})

然后运行main.js,查看日志

1
{tools|dispatch -> 环境缺失:window_webkitRequestFileSystem ->错误:Cannot read properties of undefined (reading 'apply') 

补该函数,不用添加返回值,因为浏览器执行webkitRequestFileSystem(1,2,{}),返回的就是undefined

1
2
3
sandBox.envFuncs.window_webkitRequestFileSystem=function () {

}

继续看日志

1
{tools|dispatch -> 环境缺失:IDBFactory_open ->错误:Cannot read properties of undefined (reading 'apply') 

浏览器执行下window.indexedDB.open("toDoList",4);,返回如下

1
2
3
4
5
6
7
8
9
10
11
IDBOpenDBRequest {onblocked: null, onupgradeneeded: null, source: null, transaction: null, readyState: 'pending', …}
error: null
onblocked: null
onerror: null
onsuccess: null
onupgradeneeded: null
readyState: "done"
result: IDBDatabase {name: 'toDoList', version: 4, objectStoreNames: DOMStringList, onabort: null, onclose: null, …}
source: null
transaction: null
[[Prototype]]: IDBOpenDBRequest

脱环境脚本把IDBOpenDBRequest和其原型链给脱下来,然后再补方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
sandBox.envFuncs.IDBFactory_open = function (name, options) {
// 构造上面返回的对象
let result = {}
Object.setPrototypeOf(result, IDBDatabase.prototype)
sandBox.setProtoAttribute.call(result, 'name', name.toString())
sandBox.setProtoAttribute.call(result, 'version', 1)
sandBox.setProtoAttribute.call(result, 'onabort', null)
sandBox.setProtoAttribute.call(result, 'onclose', null)
sandBox.setProtoAttribute.call(result, 'onerror', null)
sandBox.setProtoAttribute.call(result, 'onversionchange', null)
//DOMStringList
let domList = {}
Object.setPrototypeOf(domList, DOMStringList.prototype)
sandBox.setProtoAttribute.call(domList, 'length', 0)
domList = sandBox.proxy(domList, 'DOMStringList')
sandBox.setProtoAttribute.call(result, 'objectStoreNames', domList)

result = sandBox.proxy(result, 'IDBDatabase')

let idb = {}
Object.setPrototypeOf(idb, IDBOpenDBRequest.prototype)
sandBox.setProtoAttribute.call(idb, 'result', result)
sandBox.setProtoAttribute.call(idb, 'readyState', 'done')
sandBox.setProtoAttribute.call(idb, 'error', null)
sandBox.setProtoAttribute.call(idb, 'onblocked', null)
sandBox.setProtoAttribute.call(idb, 'onerror', null)
sandBox.setProtoAttribute.call(idb, 'onsuccess', null)
sandBox.setProtoAttribute.call(idb, 'onupgradeneeded', null)
sandBox.setProtoAttribute.call(idb, 'source', null)
sandBox.setProtoAttribute.call(idb, 'transaction', null)
idb = sandBox.proxy(idb, 'IDBOpenDBRequest')
return idb
}

再运行,会报一堆和上面IDBOpenDBRequestDOMStringList有关的getset方法,实现就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
sandBox.envFuncs.IDBDatabase_name_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'name', value)
}
sandBox.envFuncs.IDBDatabase_name_get = function () {
return sandBox.getProtoAttribute.call(this, 'name')
}
sandBox.envFuncs.IDBDatabase_objectStoreNames_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'objectStoreNames', value)
}
sandBox.envFuncs.IDBDatabase_objectStoreNames_get = function () {
return sandBox.getProtoAttribute.call(this, 'objectStoreNames')
}
sandBox.envFuncs.IDBDatabase_onabort_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onabort', value)
}
sandBox.envFuncs.IDBDatabase_onabort_get = function () {
return sandBox.getProtoAttribute.call(this, 'onabort')
}
sandBox.envFuncs.IDBDatabase_onclose_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onclose', value)
}
sandBox.envFuncs.IDBDatabase_onclose_get = function () {
return sandBox.getProtoAttribute.call(this, 'onclose')
}
sandBox.envFuncs.IDBDatabase_onerror_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onerror', value)
}
sandBox.envFuncs.IDBDatabase_onerror_get = function () {
return sandBox.getProtoAttribute.call(this, 'onerror')
}
sandBox.envFuncs.IDBDatabase_onversionchange_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onversionchange', value)
}
sandBox.envFuncs.IDBDatabase_onversionchange_get = function () {
return sandBox.getProtoAttribute.call(this, 'onversionchange')
}
sandBox.envFuncs.IDBDatabase_version_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'version', value)
}
sandBox.envFuncs.IDBDatabase_version_get = function () {
return sandBox.getProtoAttribute.call(this, 'version')
}
sandBox.envFuncs.IDBRequest_error_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'error', value)
}
sandBox.envFuncs.IDBRequest_error_get = function () {
return sandBox.getProtoAttribute.call(this, 'error')
}
sandBox.envFuncs.IDBOpenDBRequest_onblocked_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onblocked', value)
}
sandBox.envFuncs.IDBOpenDBRequest_onblocked_get = function () {
return sandBox.getProtoAttribute.call(this, 'onblocked')
}
sandBox.envFuncs.IDBRequest_readyState_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'readyState', value)
}
sandBox.envFuncs.IDBRequest_readyState_get = function () {
return sandBox.getProtoAttribute.call(this, 'readyState')
}
sandBox.envFuncs.IDBRequest_result_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'result', value)
}
sandBox.envFuncs.IDBRequest_result_get = function () {
return sandBox.getProtoAttribute.call(this, 'result')
}
sandBox.envFuncs.IDBRequest_source_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'source', value)
}
sandBox.envFuncs.IDBRequest_source_get = function () {
return sandBox.getProtoAttribute.call(this, 'source')
}
sandBox.envFuncs.IDBRequest_transaction_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'transaction', value)
}
sandBox.envFuncs.IDBRequest_transaction_get = function () {
return sandBox.getProtoAttribute.call(this, 'transaction')
}
sandBox.envFuncs.IDBRequest_onsuccess_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onsuccess', value)
}
sandBox.envFuncs.IDBRequest_onsuccess_get = function () {
return sandBox.getProtoAttribute.call(this, 'onsuccess')
}
sandBox.envFuncs.IDBRequest_onerror_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onerror', value)
}
sandBox.envFuncs.IDBRequest_onerror_get = function () {
return sandBox.getProtoAttribute.call(this, 'onerror')
}
sandBox.envFuncs.IDBOpenDBRequest_onupgradeneeded_set = function (value) {
return sandBox.setProtoAttribute.call(this, 'onupgradeneeded', value)
}
sandBox.envFuncs.IDBOpenDBRequest_onupgradeneeded_get = function () {
return sandBox.getProtoAttribute.call(this, 'onupgradeneeded')
}

继续看日志

1
{obj|get:[window] -> prop:[fetch] -> type:[undefined]} 

把对应属性添加到globalThis.js里

1
2
3
sandBox.defineProperty(window,"fetch",{configurable:true,enumerable:true,writable:true,value:function fetch() {
return sandBox.dispatch("window_fetch",this,arguments)
}})

后面基本上依葫芦画瓢了,造轮子就行

如果要打印cookie,在output.js里或者在asyncCode.js里加上

1
console.log('cookie:',sandBox.cookieJar)

get参数生成

浏览器访问一下页面,可以看到所有的访问都携带了一个get请求的参数

请求的发送是通过XMLHttpRequest,推测是在这个对象上做了修改,这样每次用这个对象发送请求的时候都可以携带参数

浏览器执行XMLHttpRequest.prototype,发现里面的open属性被重写了

点开这个属性,可以进到源代码里

推测就是在这个函数里对参数进行加密的

注意这个时候不能在这里打断点,因为这是VM的代码,VM123.js 不是服务端返回的固定 JS 文件,而是通过 eval()URL.createObjectURL(new Blob(...)) 等方式动态生成的临时脚本文件,每次页面加载都会变,对 VM1234 打的断点,在下一次刷新后可能变成了 VM6578,原来的断点失效了。

首先要断在这个代码里,怎么做呢?还是打上脚本加载的全局断点,直到跟到index那里

搜索ret = ,这是瑞数的特征字符串

在这里断下来,然后步入,就找到了动态生成的VM的代码,搜索字符串arguments[1],因为这是上面函数的字符串,再怎么混淆,这个是不变的,最终定位到这里

在这里打上断点,发现执行_$ju()的时候返回undefined,说明这个函数不重要,执行后面的arguments[1]_$pr(arguments[1]),每次执行_$pr(arguments[1])的时候返回结果都不一样,所以关键函数就是这里的_$pr()函数

步入这个函数

后面要做的就是模拟这个函数,函数做了什么,我们在代码中就要模拟对应的行为,比如在跟代码的时候发现代码中对a标签设置了href属性,由于前面我们已经在asyncCode.js中创建了a标签,所以这里就实现设置href属性的方法就行了

快捷的方式是在代码里直接看变量的属性,哪些属性被设置成什么值了很清楚,在初始化a标签的时候设置上去就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
case "a":
Object.setPrototypeOf(tag,HTMLAnchorElement.prototype)
//初始化a标签内容
tag.namespaceURI='http://www.w3.org/1999/xhtml'
tag.accessKey=''
location.protocol?tag.protocol=location.protocol:tag.protocol='http:'
location.host?tag.baseURI=location.protocol+"//"+location.host:tag.baseURI='http://xjb'
location.host?tag.host=location.host:tag.host=""
location.hostname?tag.hostname=location.hostname:location.hostname=""
location.port?tag.port=location.port:tag.port=""
location.origin?tag.origin=location.origin:tag.origin=""
location.search?tag.search=location.search:tag.search=""
location.pathname?tag.pathname=location.pathname:tag.pathname=""
location.hash?tag.hash=location.hash:tag.hash=""
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
sandBox.envFuncs.HTMLAnchorElement_href_set=function (value) {
if(location.protocol&&location.host){
if(value.startsWith("./")){
value = value.substring(1);
let new_value=location.protocol+'//'+location.host+value
sandBox.setProtoAttribute.call(this,'pathname',value)
return sandBox.setProtoAttribute.call(this,'href',new_value)
}
}
return sandBox.setProtoAttribute.call(this,'href',value)
}
sandBox.envFuncs.HTMLAnchorElement_href_get=function () {
return sandBox.getProtoAttribute.call(this,'href')
}
sandBox.envFuncs.HTMLAnchorElement_protocol_set=function (value) {
return sandBox.setProtoAttribute.call(this,'protocol',value)
}
sandBox.envFuncs.HTMLAnchorElement_protocol_get =function () {
return sandBox.getProtoAttribute.call(this,'protocol')
}
sandBox.envFuncs.HTMLAnchorElement_host_set=function (value) {
return sandBox.setProtoAttribute.call(this,'host',value)
}
sandBox.envFuncs.HTMLAnchorElement_host_get =function () {
return sandBox.getProtoAttribute.call(this,'host')
}
sandBox.envFuncs.HTMLAnchorElement_hostname_set=function (value) {
return sandBox.setProtoAttribute.call(this,'hostname',value)
}
sandBox.envFuncs.HTMLAnchorElement_hostname_get =function () {
return sandBox.getProtoAttribute.call(this,'hostname')
}
sandBox.envFuncs.HTMLAnchorElement_port_set=function (value) {
return sandBox.setProtoAttribute.call(this,'port',value)
}
sandBox.envFuncs.HTMLAnchorElement_port_get =function () {
return sandBox.getProtoAttribute.call(this,'port')
}
sandBox.envFuncs.HTMLAnchorElement_origin_set =function (value) {
return sandBox.setProtoAttribute.call(this,'origin',value)
}
sandBox.envFuncs.HTMLAnchorElement_origin_get =function () {
return sandBox.getProtoAttribute.call(this,'origin')
}
sandBox.envFuncs.HTMLAnchorElement_search_set=function (value) {
return sandBox.setProtoAttribute.call(this,'search',value)
}
sandBox.envFuncs.HTMLAnchorElement_search_get =function () {
return sandBox.getProtoAttribute.call(this,'search')
}
sandBox.envFuncs.HTMLAnchorElement_hash_set=function (value) {
return sandBox.setProtoAttribute.call(this,'hash',value)
}
sandBox.envFuncs.HTMLAnchorElement_hash_get =function () {
return sandBox.getProtoAttribute.call(this,'hash')
}
sandBox.envFuncs.HTMLAnchorElement_hostname_set=function (value) {
return sandBox.setProtoAttribute.call(this,'hostname',value)
}
sandBox.envFuncs.HTMLAnchorElement_hostname_get =function () {
return sandBox.getProtoAttribute.call(this,'hostname')
}
sandBox.envFuncs.HTMLAnchorElement_pathname_set =function (value) {
return sandBox.setProtoAttribute.call(this,'pathname',value)
}
sandBox.envFuncs.HTMLAnchorElement_pathname_get =function () {
return sandBox.getProtoAttribute.call(this,'pathname')
}

在asyncCode.js中添加xmlHTTP请求,参数在浏览器中打断点可以看到

1
2
let xml=new XMLHttpRequest();
console.log(xml.open('GET','./modules/top.jsp'))

然后在日志中可以看到成功发起请求

这里不用再扣代码,因为加密逻辑都在input.js里,我们要做的就是把需要的标签给补齐