实战破解4399HASH验证

本文最后更新于:2020年4月11日 晚上

前景

4399作为一个小型的游戏平台,代码安全以及复杂方面远不及BAT等大型公司来的难。因此被我当作爬虫的热身网站。我曾把4399的登录包,注册包,以及一大推的获取个人信息包都写了。然后是要实战一些模拟操作了。


而当我转向尝试去模拟4399群组中关注他人时,却被恶心到了,除了常规的验证码,提交的包中还多了一个HASH参数。略懂电脑基础知识对的应该都不陌生,虽然可能不是传统意义上的hash,但必然是一串验证代码。

而这次我们就来实战攻破4399的HASH验证。

准备

Python[模块:json,requests,matplotlib,execjs]
Chrome
VS Code

正式开始

我们先尝试用浏览器进行抓包分析。

avatar

可以看到当我们点击关注时,系统会发送一个关注包,这时他会自动判断(玄学)是否需要输入验证码。如果需要则会再发送两个包,一个是图中的geturl?type=zone

avatar

可以看到,这个包的作用是获得验证码的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def geturl(cookie):
get_headers={
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
'Connection': 'keep-alive',
'Cookie': cookie,
'Host': 'my.4399.com',
'Referer': 'http://my.4399.com/u/2576802349',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
rsp = requests.get(url="http://my.4399.com/plugins/captcha/index-getUrl?type=zone",headers=get_headers)
url=json.loads(rsp.text)
url = url["result"]["url"]
return url

根据这个包返回的格式我们判断出是json文件,所以利用json模块进行转换,然后按照格式取出包含在其中的URL。


这个时候我们还无法看出HASH的具体作用,我们输入验证码继续看看提交了什么

avatar

可以看到,当我们提交了验证码,后面多了一个index-check的包,大概能猜出是验证码验证进行check。其中三个参数,type:zone.cap就是验证码了,hash就是我们这篇要解决的内容了。

我们知道了用处,回去看下之前那个GET-HASH包

avatar

返回的是一堆json代码,我们尝试在命令提示符运行,可以看到,完美符合提交的HASH值。

avatar

接下来的难点就是如何通过这个包返回的代码,利用python计算出hash,如果直接是json代码运行还可以,但是我们仔细观察可以看出,在返回的代码中,最后一句的大致意思是给网页中ID为cap_hash的对象赋值hash,但是在python中,我们并不会有相关的对象,所以我们得想办法取出我们想要的代码。
我首先想到的是读取行

1
2
3
listd=rsp.text.split("\n")
for i in listd:
print(i,"\n","————————————————————————————————————————————————————————")

返回的就有点奇怪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var _4ba4cacd070fe=/*8/"e0*//*+f c62'c6-*/'_aa6b9'/*3=b8d'*/+/*5'5 3*/'69b11784ed'/*9"b/;*/+/*,-5=4/;+_*/'a4f581c4'/*;==,"18*/+/*/'+++9/9-*/'af15edcd1'/*a 48;4,,9*/+ 
————————————————————————————————————————————————————————
''/*8f3c2+2;61*/;var _1eff743fb70c3ef68=/*3+=1+/- 5*/''/*;4ead70"*/;var _f98ed25c1ead835f23e9fa=decodeURIComponent(/*"4ce8*//*f"+9-,d1c8*/'%29%00'/*=5--5_6+e8*/+/*f9c0=*/'%13%16%3'/*'+d"c2 d*/+/*46 50*/'D%5DU%0CW'/*/1f7db*/+/*4/d1"9c/d*/'%00%00'/*'3+'d54*/+/*c 854*/'R%5BW'/*9-/b+3*/+/*87-9_;*/'T%05Y%00W'/*c"'00*/+/*--a;2*/'%04%0D'/*4ce44_*/+/*a,b94_0_-*/'RZ%0C%0'/*2_3e+b57f*/+/*_'__"c-/*/'5%05%0'/*04+a6 b2,*/+/*=+=42*/'7Q%5DSP'/*9474e*/+/*'ee5c'/ad*/'T%06jT%0'/*a2,";"b 2*/+/*f_- _1d/ef*/'7S__CW%'/*5b,e6*/+/*638fd*/'01EXXV'/*5f,+/67-*/+/*946f33*/'%1CL%1F%'/*f5/9;868f*/+/*;9_;+4-*/'17U%14'/*-42b210'-*/+/*20";+*/'%15gRZ%01X'/*3_,a8*/+/*aeb0-*/'VP%03VQ%0'/*,ad39"*/+/* ,01f_2/1'*/'1%02%01%3'/*/2590/3a*/+/*5'=57ff,b1*/'EV%05%0'/*"208=3/*/+/*0=599/"'*/'2R%0FS%0'/*79_-1",*/+/*==='9='0;e*/'0_%16%16'/*;7/-3*/+/*=b61;a8-5*/'%0CgW%5CQ'/*,5b/f554*/+/*90",a7*/'X%04%07'/*e2/03076*/+/*92/ae-*/'%03%0B%04%'/*69+0/__2_6*/+/*' fd25_*/'01RQ%07'/*34a3e,"*/+/*4-f3251c*/'%06QQTU%01'/*9ec"9 0*/+/*2 8f/f*/'%08t%'/*d+ 5fef7=*/+/*3c6c3*/'5CF%07W%0'/*d;92d*/+/*;=4db'0*/'F%11%'/*a"182+-de*/+/*5ce 6,,c0*/'02%3DR%0'/*1=9a7=*/+/*1/;9//255b*/'8%02%01%04'/*_0+=a/*/+/*a6257+09*/'%04RR%01'/*5/ 9-*/+/*2,2-;f2*/'%04S%'/*3,'377*/+/*6"=6"b*/'08PTPUV%0'/*929a3d1/*/+/* 1a5cb*/'7P%5C'/*7a'0"b*/+/*,_3173+/_*/'O%5EC%0'/*b846+"96,"*/+/*;c4-23*/'7jWP%0'/*d8',99-8a*/+/*8"='8bb'5,*/'2Z%0A'/*6170/8b,9_*/+/*_be658_*/'%11%02%3D'/*699=-=e*/+/*27f9809*/'R%08%02'/* d97+6105;*/+/*9+36/4+c7*/'%01%04'/*81+"2*/+/*,1; c7=c*/'%04RR%01'/*6 3,+4;1*/+/*7fd d/d'b0*/'%04S%'/*1;0571_*/+/*'a5'+_+- ,*/'08PTPUV'/*ab-5e=e*/+/*"5fe e802*/'%07P%5C'/*e6a2,d*/+/*=+_;ed-*/'O%5ECMfPF'/*41=b"5*/+/*=24b1'=a*/'%0D%3'/*70d_9c*/+/*,;_b0;5a*/'DZ%0F'/*33967*/+/*5+3; */'%0C%5B%01P'/*,93352f*/+/*4 ,'0*/'%01%0'/*+;3+_8+*/+/*5+;0,/ 9ec*/'B%01%07'/*+ c; */+/*/b;3_6c,*/'%02QUQQ%0C'/*'b1f54*/+/* 0 2;*/'%01UQX'/*2ad=3f-*/+/*_ 5240f*/'M%0C%12P%'/*;4f2d91*/+/*2_d'8;4*/'05D_n%3CXT'/*f2d3"*/+/*,38/66cb*/'%0FRX%00%'/* 3;;_*/+/*;67"6'_1*/'0AWSW%0'/*a_13_94'a*/+/*772a_9,*/'7Y%03%'/*+c+6f6;d-*/+/*d;13a1e66+*/'01PQ%02%0'/*a+5611*/+/*-"7b2*/'3%0C%13%'/*86cc"=-*/+/*4f"a'f*/'0CDQ%'/*ba6b3+;*/+/*bef5'61*/'04A%0Aj%'/*6;'"6"a"/*/+/*3+_f4'6*/'06%5D'/*094/5fc52;*/+/*e"d2b9+97*/'V%5D%0'/*d064b"b7eb*/+/*8f3=ae;8*/'1%3EWR%0'/*+=-'4*/+/*_7383a*/'3%00_%06X'/*34;"3,6;8*/+/*44c bca ea*/'UU%05%07%0'/*+0c5,*/+/*e0d880_3f*/'EQ%5CO%5C%'/*f0,,f aa9*/+/*a+9a";,*/'13S%02YWU'/*e6-3,36e"*/+/*0e428,, "8*/'%05%03S'/*d /119-e'*/+/*3;/d-40c2-*/'%16%0E%3'/*f87/;24b,*/+/*'e bfb,*/'A%07Z'/*/_554*/+/*c6+=588-c_*/'Q%08o%00W'/*=fecc*/+/*ce 3"ce*/'%05W%5'/*ad1_"505*/+/*,+a-d*/'BP%09%03'/*6 ee7,;/*/+/*=5-7a0a*/'%06U%03'/*e9df5c*/+/*d=6 1*/'%08%02%'/*cd'915,/31*/+/*'f7b/,d*/'00%5DJ%'/*,3/6d*/+/*4-6b0"6c11*/'09A%03'/*b9f001*/+/*1,549=5cf */'%5E%04S%'/*84a6f; */+/*1'6"/-,"*/'00XVW%07SC'/*3_9'7"b 8*/+/*8;_45/0;"9*/'X%16T'/*1030a/ _ce*/+/*228,ac*/'%2B%14%13'/*18;_6*/+/*657; ++3"*/'XBfU%00W%0'/*a+0f f_9*/+/*7;26,*/'8%01V%0E%0'/*280/09a//*/+/*'aa7 _*/'7P%06'/*/2cc9*/+/*3 b5_*/'%07%0'/*_b9 ==*/+/*4ca/df+*/'4%07%02%'/*_-0/630*/+/*4= ''9d2b-*/'5C%05'/*'d=_3d7b"*/+/*-f"37cd9*/'S%02%04'/*/=3=de603*/+/*;,5a3*/'_%0AH%5E'/*2f6+'0b3*/+
————————————————————————————————————————————————————————
''/*eb -1bd1"*/);
————————————————————————————————————————————————————————
for(i=0; i<_f98ed25c1ead835f23e9fa.length;i++)
————————————————————————————————————————————————————————
_1eff743fb70c3ef68 += String.fromCharCode(_4ba4cacd070fe.charCodeAt(i%_4ba4cacd070fe.length)^_f98ed25c1ead835f23e9fa.charCodeAt(i));
————————————————————————————————————————————————————————
eval(_1eff743fb70c3ef68);
————————————————————————————————————————————————————————
document.getElementById('cap_hash').value=_dc5511ecc1a84115c98dc6d8730755fe();
————————————————————————————————————————————————————————

当我们多尝试几次后,会发现返回的代码是固定七行,只有第七行是我们需要进行修改的,所以我们将前六行写入一个新的字符串中

1
2
for i in listd[0:6]:
hashcode_str = hashcode_str + i + "\n"

这边的listd好像有点类似range,也是左闭右开,所以只能取出前6行。
然后把这个字符串用execjs的模块转换一下

1
a = execjs.compile(hashcode_str)

第七行其实我们只需要用到后面的方法调用,前面的赋值语句是不需要的。观察分析其实前面赋值对象的语句应该是固定的,所以我们可以直接通过长度来进行取值,我用鼠标一个个字符串数过去的,我们要的刚好是第42个字符开始,即“_”,后面则是一串随机的字母数字,我们取到底就好。
这边还有一个我在运行时遇到的问题,execjs调用命令后,好像结尾并不能加”:”,否则会报错,所以应该是

1
a.eval(listd[6][42:-1])

总的获取hash代码就是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def gethash(cookie):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.70",
"Cookie":cookie
}
url = "http://my.4399.com/plugins/captcha/index-getHash?_=1565359334744"
rsp = requests.get(url,headers=headers)
rsp.encoding ="utf-8"
hashcode_str = ""
listd=rsp.text.split("\n")
sum=0
for i in listd[0:6]:
hashcode_str = hashcode_str + i + "\n"
sum+=1
print(sum)
print(len(listd))
a = execjs.compile(hashcode_str)
return a.eval(listd[6][42:-1])

然后我们就可以完成这次要实现的关注模拟了

avatar