马蜂窝 2021 年 9 月 4 日
- 第一次会返回一段
cookie
- 带上这个
Cookie
请求返回一段混淆的代码
- 混淆的代码是一个自执行 在执行的时候就会设置
Cookie
再次带上就可以返回真实的页面了
>
极简壁纸 2021 年 9 月 11 日
- 涉及:JS 混淆 , AES 加密 无限 DEBUG 以及 滑块验证码识别
- 滑块识别完成之后会返回一段 token 这段 token 加上之前请求体携带的 secretKey 加上滑块的距离 再进行一次 AES 加密,可以得到一个 headers 请求头加密数据
captcha
- 带着这个加密数据去访问网页会得到一段被加密数据
- POST 请求下载 ID 后就可以请求真实下载地址了,这里需要将 headers 补齐才会返回数据。
去*儿登录(2022 年 3 月 29 日)
- 这个网站登录有一个滑块,这个滑块是官网自己写的,没什么难度。
- 用这个滑块生成的一个密文,得到一个 token,拿着这个 token 去登录就可以通过了。
- 一个小技巧 故意输错账号密码 可以一直滑动
- 滑动之后,右侧已经生成值,根据调用栈找到目标位置。 >
- 往上找一层,发现 o 就是加密值,跟进再次查看。
- 可以看到就是 AES 加密,d 就是一个 json,
- track 其实就是滑动轨迹,通过一个鼠标事件,不断往里面加数据。 >
- 上面这张是重新滑动的,可以看到轨迹变少了。因为滑动只有一个方向,所以是可以写死的。
- 将上面的所有分析,变为代码,结果如下。。。。 >
- 第二个重定向主页,响应头已经设置 Cookie,登录成功。。。。
M 眼电影(2023 年 2 月 1 日)
首先这个网站的
with
参数 在url
和headers
中都有 那么可以hook
setRequestHeader
从上图可以看到
XMLHttpRequest
方法都被hook
了 进入到open
send
方法内部下断 单步走 就会跟到其中
w
是指纹 这里不构造指纹 我们把JS
放到本地 通过框架去跑 不修改JS
内部代码直接放在环境里面跑 我们知道上面那段
JS
会hook
那我们创建一个XMLHttpRequest
让它去改 我们再主动调用读取
attributes
后直接报错了 查到是NamedNodeMap
一个节点集合 把他补上 (返回一个可迭代对象)第二次报错 在官网是一个方法 没补全而已
document.getElementById(metaId).getAttribute("content")
getElementById
这个方法没补 因为需要网页的特殊元素参数 比如这里就需要一个content
这里可以在
setRequestHeader
内部 把 js 代码内部设置的值导出 就可以获取到密文
🐧 音乐 V(2023 年 2 月 14 日)
- VMP 是原子级操作 定义变量 赋值 都是需要操作的指令
- 算法还原前置
总栈
先进后出 模拟方法的调用大量的 *.push() *.pop()
OP
指向下一行指令 或 字节t++
O+=4
- 方法参数栈 局部作用域栈
let v = argument; v.push()
- 代码段
- 哪个参数大量使用了
OP
进行读取 谁就是代码b[t++]
b[O++]
- 哪个参数大量使用了
- 方法的调用
- 因为
fromCharCode
解回来的代码都是str
并且内部函数的调用不知道是多少个参数 所以还会使用call
apply
eval
- 需要注意需要有上面的特征的才是
- 因为
d[n[t++]]()
根据上面的规则 可以分析出d
是总栈n
是代码字节集t
是OP
i
在右侧可以看出是局部方法参数栈- 这里需要打印出局部参数栈 看下调用流程 分析出原始算法
i[i.length - 1] += String.fromCharCode(n[t++])
在这一行可以发现特征fromCharCode
并且前面对 总栈i
进行操作- 所以可以在此进行插桩
console.log(JSON.stringify(i,(k,v)=>{if (v===window) {return "window"}return v;}) + '\r\n')
for (var h = !1; !h; ) {
h = d[n[t++]]();
window.logcat +=
JSON.stringify(i, (k, v) => {
if (v === window) {
return "window";
}
return v;
}) + "\r\n";
}
// 注意要加在那个调用的for循环里面
import hashlib
import re
hash_value = '"{"comm":{"cv":4747474,"ct":24,"format":"json","inCharset":"utf-8","outCharset":"utf-8","notice":0,"platform":"yqq.json","needNewCode":1,"uin":0,"g_tk_new_20200303":5381,"g_tk":5381},"req_1":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"4809023160","songmid":["002zqRMh2oSuv5"],"songtype":[0],"uin":"0","loginflag":1,"platform":"20"}},"req_2":{"module":"music.musicasset.SongFavRead","method":"IsSongFanByMid","param":{"v_songMid":["002zqRMh2oSuv5"]}},"req_3":{"method":"GetCommentCount","module":"music.globalComment.GlobalCommentRead","param":{"request_list":[{"biz_type":1,"biz_id":"394327271","biz_sub_type":0}]}},"req_4":{"module":"music.musichallAlbum.AlbumInfoServer","method":"GetAlbumDetail","param":{"albumMid":"001a1BQI2V2PtA"}},"req_5":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"7013258812","songmid":["002zqRMh2oSuv5"],"songtype":[0],"uin":"0","loginflag":1,"platform":"20"}}}"'
_hash = hashlib.md5()
_hash.update(hash_value.encode('utf-8'))
hash_code = _hash.hexdigest().upper()
print(hash_code)
result1 = ''.join([hash_code[v] for v in [22, 5, 10, 27, 17, 21, 28, 31]])
result2 = ''.join([hash_code[v] for v in [19, 12, 4, 3, 2, 8, 7, 26]])
arr1 = [212, 45, 80, 68, 195, 163, 163, 203, 157, 220, 254, 91, 204, 79, 104, 6]
arr2 = [25, 66, 1, 58, 90, 78, 123, 71, 33, 12, 178, 93, 40, 166, 62, 233]
_map = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15}
arr3 = []
for v in range(0, len(hash_code), 2):
n1 = _map[hash_code[v]] * 16
n2 = _map[hash_code[v + 1]]
n3 = n1 ^ n2
n4 = n3 ^ arr2[v // 2]
arr3.append(n4)
arr4 = []
for v in range(0, len(arr3) - 3, 3):
n1 = arr3[v] >> 2 # 索引值
n2 = arr3[v] & 3
n3 = n2 << 4
n4 = n3 + (arr3[v + 1] >> 4) # 索引值
n5 = arr3[v + 1] & 15
n6 = n5 << 2
_a1 = arr3[v + 2] >> 6
n7 = n6 ^ _a1 # 索引值
n8 = arr3[v + 2] & 63 # 索引值
arr4.append(n1)
arr4.append(n4)
arr4.append(n7)
arr4.append(n8)
arr4.append(arr3[15] >> 2)
arr4.append((arr3[15] & 3) << 4)
result4 = ''.join(["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[v] for v in arr4])
result4 = re.sub(r'[\\/+]', '', result4)
print(f'zzb{result1}{result4}{result2}'.lower()) # 'zzbbacf0a7cwtkr90cnovabaujugylolqdda071d8'
print('zzbbacf0a7cwtkr90cnovabaujugylolqdda071d8')
- 检测点
- window
- navigator
- location
- Headless 无头浏览器
- reg test
navigator.userAgent
- location.host.indexOf(“qq.com”)
总结
在计算机中 减法的操作性能低 正常情况下不会使用
可选运算符
+ - * / << >> ^ | & %
就是猜上面的代码中 一些固定的参数可能也是变动的 还需要再去分析怎么生成
瑞丽点选
当网页请求之后 返回的
html
内 可以定位到i : 149e494640******5e3a9bcf10617
- 这个信息可以确定是网页请求的图片背景 然后通过
background-position
来还原被混淆的图片 - 这里每次的偏移都是不一样的
- 这个信息可以确定是网页请求的图片背景 然后通过
也就是从图片上截取指定位置的图片 渲染页面上
+ 其中
-80
代表第二行 + 每个图片宽20
高80
还原核心
# 创建空白图片 透明背景
new_image_back = Image.new('RGBA', (300, 200), (255, 255, 255))
new_image_back_x = 0
new_image_back_y = 0
for div in divs:
_x, _y = div.split('n: ')[1].replace(';', '').replace('px', '').split(' ')
x = int(_x) * -1
y = int(_y) * -1
# 从 back_image 中截取图片 粘贴到 new_image_back 中
new_image_back.paste(back_image.crop((x, y, x + 20, y + 80)), (new_image_back_x, new_image_back_y))
new_image_back_x += 20
if new_image_back_x == 300:
new_image_back_x = 0
new_image_back_y += 80
- 然后把提示信息也增加到底部 进行识别
识别
- 这里识别的时候要注意 发过去的图片大小需要调整为和网页端一致 要不然坐标偏移结果就是错的
- 可以通过返回的坐标画点 看下是否准确