声明
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关.本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除.
前言
本人纯萌新一只,web逆向刚学完又跑去学安卓逆向了,最近某人扔给了我个网址,让用web逆向搞定登录,还说非常简单,我想着正好复习一下web逆向的东西,就开工了。
目标地址
aHR0cHM6Ly9zdHVkZW50LWV4YW1zZXJ2aWNlLm91Yy1vbmxpbmUuY29tLmNuLyMvbG9naW4=
参数来源分析
过debugger
打开devtools并浏览目标地址,直接给了个下马威,无限debugger,点右边的堆栈列表,查看上一个栈,看看这个是怎么来的
又一个下马威,代码混淆,先凑合看吧,选中前面的部分,浏览器直接提示constructor,或者复制到控制台也能看到,后面的部分是'debugger',应该是由constructor动态构造得到,直接在console中上相关hook代码:
let _constructor = constructor;
Function.prototype.constructor = function(s) {
if (s == "debugger") {
console.log(s);
return null;
}
return _constructor(s);
}
enter后点右边的继续执行
很好,直接失败了
这大概是把'debugger'字符串当函数直接执行了,还执行了别的操作,此路不通,稍微往下看一下,发现半边“)",嗯?这是构造了字符串,用的eval?
再往下看一下,发现了setInterval,定时器,这肯定就是无限debugger的来源了,这行语句上下各有一个函数调用了上面那个_0x4aa458,看来问题就出在这了
看到调用这个debugger函数实现,心头涌上一计,不让它调用不就可以了嘛,修改代码注释掉这2个调用就行了,在上方标签页点右键,选替换内容
保存到本地后,标签前面有个紫色小点,这时候代码是一行,没有格式化的。
这里有个坑,如果直接用这种没格式化的,后面会很难找到代码,那时候再点格式化的话,又会创建一个新的VM标签,在那里是不能修改的,点回原标签又变成了第一行,而且点右侧的栈也是跳到这个VM标签。
总之就是,先点下面的{}进行格式化,切回原标签,保存(ctrl+s)一下,再刷新页面就好了,否则一堆麻烦事。当然,这个操作也可能会有麻烦,有的混淆中如果格式化了重构函数或者自解密函数,会导致卡死,那时候就不能这么搞了,好在这里没问题。
刷新后再点栈切到这里,注释掉2个调用,再保存一下,刷新后顺利进入页面
明确参数
控制台切到网络页面,清掉前面的数据,页面随便填数据,点登录,发现3个包
逐一点开看右侧的数据,发现请求头都没带加密参数,查看载荷和预览
第一个包就应该是发送的数据,返回验证码已失效,下面的包重新发送了验证码,应该是时间太长验证码失效,重新登录的意思,验证码是编码发过来的,第三个包就是解码后的验证码
到这里就很明确了,要逆向的参数为data
和secretKey
参数分析
切回源代码页面,搜索data,总共有32个,太多了,再搜secretKey,只有2个,都打上断点
随便填数据,再点登录,成功断住
挺好的,明文密文一锅端了,再来看看里面的数据
_0x3c4346:明文,password已经加密,后面再找
_0x6d8253:ture,并参与后面的return计算,姑且认为是固定值,否则后面_0x6d8253&&为假就return不了
_0x5f4fe4:一个自执行函数返回的结果,结合下面的代码来看,应该是随机生成的密码,在控制台复制后面的自执行函数,确实返回了不同的字符串
_0x336062:把明文和密码放进函数,返回加密字符串,看着像是AES加密
_0x17475b:接收了3个参数,1是函数,2是密码,3是一个字符串
再下面就是return了,里面还有一些计算,我们先看这些东西
选中_0x336062后面的函数,点进去查看定义
很明显的一个加密函数,打上断点,重新发数据,成功断住
明显的AES加密,但这里出现了一个坑,填充模式是'ZeroPadding',不是常用的Pkcs7
用工具验证一下,没有问题
然后是_0x17475b,用上面的方法查看函数定义,发现第一参数是一个函数,把后面2个参数传进函数。
同样的查看第一参数函数的定义,这函数只接收一个参数,我???不管怎么说,在返回处下个断点先,然后不小心刷新了一下页面,重新提交后成功断住,却发现了额外惊喜
这个11不是我输入的密码吗?前面的密码也是在这里加密的,直接破案
MIG开头的是很明显的RSA公钥,加上1024,010001这2个参数,基本确定是rsa加密了,可惜没办法验证,RSA加密的结果每次都不一样。
再按F8继续执行,又回到此处,这次是之前的KEY传进来了,同时注意到尽管函数只接收1个参数,但这里还是用了第二个参数,即另外一组公钥,呵呵,这就是JS
再往下走,在最终return之前,还进行了一些操作
这里查看函数定义,仍然是第一参数是函数,把第二个参数传进去执行。查看_0xb95ba函数的定义
只在一个return之前用了传进来的参数,那么在return这里下断,成功断住
return返回的是_0x553f2b的data参数,生成_0x553f2b的函数追进去看看,然后一脸懵逼,是个VM虚拟机里的超级复杂的函数,足足有237行代码,太丧心病狂了,这叫非常简单?
大概翻了一下,找到点东西,这个函数返回code和data,里面有很多处code和data赋值,但大部分data是空值,只有2处data有赋值,直接在这2处下断,成功断住
然后就开始了漫长又痛苦的分析过程,过程略过,反正就是查看定义,看混淆的内容,查看入参出参,分析每个语句是做什么的,分析的结果如下
1.传入之前加密的密文_0x215646->data
2.生成32为随机密钥_0x1eb03a->key
3.temp = _0x1eb03a + _0x215646 + _0x1eb03a
4._0x215646 = aes加密(temp, key); //ECB模式 Pkcs7 Padding
5.生成空字符串_0x4ce71f
6.生成8位随机字符串_0x18952d并进行处理,如果处理后结果大于550,把_0x18952d赋值给_0x4ce71f,否则循环100次直到结果长度>550,都不满足则走另外流程
7.生成32位随机数组_0x4a576b,值为1-40内
8.生成空数组_0x3459bb,并把_0x4a576b赋值给_0x3459bb
9.把_0x215646按字符拆分成数组_0x188414
10.把_0x215646进行处理等到一个数字_0x3da2c6
11.在_0x188414中某个位置插入字符,算法用到了_0x3da2c6,_0x3459bb,_0x1eb03a(固定字符串'gRJly8wsdlflx8NAgWQ6VYix1TyCvnSF')
12.把_0x3459bb中的数字转换为对应的字符,组合成字符串,然后将该字符串转换为 Base64 编码的字符串,并赋值给变量 _0x2a45de
13.把数组 _0x188414中的内容合并成一个字符串,然后替换这个字符串中的所有等号(=)字符为变量 _0x4ce71f 的值,并将结果赋值给变量 _0x1af1b2
14.生成最终结果_0xfdc58c,值为_0x4ce71f + _0x2a45de(32到37位) + _0x2a45de(27到33位) + _0x2a45de(5到640位) +
_0x2a45de(16到37位) + _0x1af1b2 + _0x2a45de(0到5位) + _0x2a45de(24到27位) +
_0x2a45de(6到22位) + _0x2a45de(21到42位) + _0x2a45de(37到44位)
正当我想用python还原算法的时候,某人又找过来了,问我怎么这么慢,然后我倒了一肚子的苦水,他就问了两句话:你不会反混淆吗?你不会扣代码吗?我....
想死的心都有了,学安卓逆向给学傻了,这些全都给忘了。
把代码复制到pycharm中,全部折叠,再展开一层
这不很明显的ob混淆结构嘛,重构函数,解密函数,主函数,大数组,齐了,拿工具直接反混淆,再替换回去
这解混淆后看起来要舒服一万倍,vm算法虚拟机里的东西也全扣出来,算法函数是_0x385e31
直接node.js跑,传参调用,返回data是空值,看来走了另外一个流程,稍微分析了一下,是_0x723668值判断的时候走了另外的分支。
改简单,直接判断取反,然后继续跑,报错,CryptoJS未定义,导入就行
终于出结果了,这个data就是最终生成的值,后面测试发现secretKey和data都是走的这个流程
整个流程分析完了,回去检查一下有没有问题,结果还真发现问题了,最开始传入的明文里有个uuid,然后又开始痛苦的追栈,死活找不到
看样子像某个加密的数据,又翻了一下加密那块,也没有。
正一筹莫展的盯着这个"uuid":,突然想到可以直接搜字符串试试啊,试试又不要钱。
一下还真让我搜到了,在另外一个js文件里,就1个赋值的地方,老样子,解混淆,替换文件,下断,刷新,填数据提交一气呵成,然后没断住...
线索又断了,看样子是从_0x466aa4里取的code,上面_0x466aa4又指向this,去翻翻this吧,往上慢慢翻。然后就看见了这个
嗯?data?code?passToken?脑袋里突然就灵光一闪,去看看那个passToken!
直接进行文本对比,完美
明文还有个参数是code,就是输入的验证码,至此就全部分析完成
总结
就一句话,我太菜了。说起来确实非常简单,但是长期不用导致很多最基本的东西都忘了,反混淆,扣代码,通过字符串查找,导致浪费了很多时间,菜就要多练,那么下次见了。