记一次JS逆向学习
🌸 原文链接
在学习和记录之前,先致敬一下大佬的文章,感觉讲的挺好的,挺细致的。
原文链接:十年老兵带你做JS逆向分析
🌸 前置环境
https://www.ssjj.cn/?from=bingsem_ss1_dmt_423400015
,这是本次要进行测试的网站,首先看看怎么样就是做了 JS 加密的:
来到这个页面后我们点击登录,使用账号登录,使用F12
看数据包信息。
可以看到数据包中存在着加密的password
,这里输入的账号和密码是:admin/123456
,至于加密后会对渗透测试有什么影响,这里就不说啦。主要是记录JS
逆向的几个方法。
🌸 JS逆向方法
🌟 第一种方法-根据关键字搜索
尝试在所有的文件中进行查找关键字password
:
然后在所搜的结果中,从后往前(找到加密和不加密的临界点,一般加密算法就是在这个临界点)这里先在最后的两个点打了断点,打了断点可以在旁边看到的:
当再次点击“登录”时,触发了第二个断点:
可以看到password:it(r)
是在u
中定义的:
之后就需要看一下,这个u
还出现在什么地方,或者说这个u
在什么函数中:
最终发现这个u
在function()
中!接下来再设置一个断点到function()
的最后,为什么要设置在function()
最后呢?因为在最后打断点的话,就能看到这个函数的整个调用过程。
一定要确定好位置,JS
代码看起来比较费眼睛。由于这个断点在刚才停下来的断点后面,因此不需要重新点击“登录”,直接往下执行:
然后便可以看到整个函数的执行和参数变化:
这里就发现了u
里面出现了加密的password
,明明r
上面还是123456
,可以看到password: it(r)
,出来就变成了加密的了,所以肯定就是it
做了什么不见得人的事情。
之后将鼠标移动到it(r)
上面,便可以看到这个函数的位置:
然后点击这个蓝色位置,便可以跳转到具体代码:
最终找到了加密的地方,可以看到采用了AES
。接下来就是复现他的加密过程,将这个it
函数复制出来,利用AI
生成能够在浏览器开发者模式运行的代码。
Ai
生成的代码如下:
// 原始加密函数
function it(t) {var e = CryptoJS.enc.Utf8.parse("5yuy675489834ert");return CryptoJS.AES.encrypt(t, e, {iv: e,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.ZeroPadding}).toString()
}// 封装函数以便在UI中使用
function encryptText() {const inputText = document.getElementById('inputText').value;if (!inputText) {alert('请输入要加密的文本');return;}try {const encrypted = it(inputText);document.getElementById('encryptedResult').textContent = encrypted;} catch (error) {console.error('加密错误:', error);alert('加密过程中出现错误: ' + error.message);}
}// 为了方便在控制台测试,将函数暴露到全局
window.it = it;
window.encryptText = encryptText;
接下来点击enter
:
在console
里面输入“it('123456')
”然后回车即可看到结果:
可以确定和之前的登录时,请求包中的加密password
是一样的。
🌟 第二种方法-文件流程断点
点击“网络”->“发起程序(Initiator
)”->“请求调用堆栈(Request call stack
)”
可以看到这里都是一个文件:unilogin.js
,如果碰到了存在很多个文件的时候,就先找login auth unilogin
等这种敏感的文件。这里就可以随便点一个,比如value、ajax
等都可以,这里我就先点击了ajax
,下断点再次点击登录:
这里可以看到右边已经出现了password
被加密了,因此这里已经被加密了,我们应该往前找:
依次往前看,看一下value
:
发现还是加密的,再往前找:
发现在fn
中并没有加密过的参数值,所以这个加密的过程就是在value、fn
中。
最终来到value
,可以看到u
里面就是加密的,因此就可以定位u
,如上截图,然后就看到password: it(r)
,r
还是明文呢,it()
之后password
就是密文了。因此还是将鼠标移动到it
上面,跳转后,便来到了具体的加密代码中:
后续的复现过程如上。
🌟 第三种方法-XHR断点
XHR
就是针对于xmlhttprequest
请求进行断点操作。首先还在网路这个老演员处,查看发起的请求是不是xhr
请求。
首先通过浏览器F12开发者工具中的“网路”面板来查看一个请求是不是XHR(XMLHttpRequest)
,XHR
是Ajax
技术的核心。用于在后台与服务器交换数据。
打开F12开发者面板,还在网路这个老演员处,点击“登录”,查看发起的请求是不是一个XHR
请求:
发现是一个XHR
请求,之后直接点击后面的unilogin.js
文件。
然后便跳转到了如上界面。接下来就把请求的URL
加入到请求XHR
断点中。此时需要注意两点:
-
- 必须是固定的
URL
,也就是说刚才确定的XHR
请求URL
和加入的XHR
断点的URL
保持一致(例如上图中的loginWithAccount
这个URL
)。 - 加入
XHR
断点中的URL
是去除域名和参数后的URL
地址(例如上图的完整url:/v1/user/loginWithAccount
)。
- 必须是固定的
在XHR/fetch Breakpoints
中添加该路径:
再次点击登录,就会断下来:
断下来之后,右边就能看到会出现两个部分:作用域和调用堆栈。
我们通过调用堆栈来看作用域。首先现在断下来的地方在send
:
由于现在看的是堆栈,所以send
就是最后了,我们要往前看,也就是堆栈调用中往下看,源码网上看:
比如我们点ajax:
看到这里的password
已经被加密了,那就继续往下找:
还是加密的依旧往下找,就是找那个临界点:
value
的时候还是发现是加密的!那就继续:
fn
的时候就没什么东西了。所以就是在value
和fn
之间被加密啦。
最终定位源代码,找到加密的地方。然后就是跳转到it
。后面就是复现啦。
🌸 小结
三种方法其实大差不差,文件流程断点和 XHR 断点在确定区间后的操作也都是一样的。所以这里推荐大家使用XHR 断点,也就不用像文件流程断点需要来回切了,操作更加方便。