百变IP的IP地址反爬
难度: ★☆☆☆☆ 1星
一、目标
目标网站:
https://www.baibianip.com/home/free.html
在这个网站的IP列表中的IP地址,是通过JS解密出来的:
查看源代码(view-source:https://www.baibianip.com/home/free.html),页面返回的时候没有IP地址,IP地址是通过这行JS计算并显示的:
本次的目标就是破解这个加密。
二、分析
在上面的截图里,解密函数的名字是FFdisharmony,同时它在script下直接调用了,说明FFdisharmony应该是一个全局函数,能够在console上调用的,尝试调用一下:

这就有点尴尬,从Elements选中一个IP地址,看下它的解密函数,这里就不贴图了,在上一部分有这个图,可以观察到script里解密的函数名似乎与其它地方不太一样,这个函数的名字似乎是一直是在变的,同时通过在
view-source:https://www.baibianip.com/home/free.html
页面上快速刷新也能够观察到解密函数的名字一直在变,不过这个没关系,从Elements中的script中复制这个函数名字然后粘贴到console执行就可以了,然后双击跟进去:

跟进去之后是这样的:
function FFassimilated (s) {
document . write (ddip(s));
}同时注意到这个函数没有被匿名函数包围啥的,说明这个ddip函数的作用域很有可能是全局范围的,因此回到console中输入ddip回车:

然后双击跟进去,就能拿到这个函数的源代码了:
function ddip(e0){e1=r13(e0.toString());e2=$.base64.decode(e1);e3=e2.toString().substr(10);l3=e3.length;e4=e3.substr(0,l3-10);return e4}在这里有个需要注意的点是跟进去的时候展示源码的标签的名字是VMxxx形式的,说明这个代码并不是直接在JS中就有的,应该是在eval或者函数构造器之类的能够执行js代码的地方定义的这个方法,不过这个无所谓了,反正我们已经得到了源码:

同时这里我还多刷新跟进去了几次验证了一下,这个ddip没有多套逻辑,也不会再变了, 这样看来FFassimilated之类的函数名字只是为了把它包一层保护起来,虽然也没护住...
上面的ddip整理一下就是这个样子的:
function ddip(e0){
e1=r13(e0.toString());
e2=$.base64.decode(e1);
e3=e2.toString().substr(10);
l3=e3.length;
e4=e3.substr(0,l3-10);
return e4
}
同样的方法在console中输入r13跟进去,拿到r13的代码:
function r13(s) {
var b = [], c, i = s.length, a = 'a'.charCodeAt(), z = a + 26, A = 'A'.charCodeAt(), Z = A + 26;
while (i--) {
c = s.charCodeAt(i);
if (c >= a && c < z) {
b[i] = rot(c, a, 13)
} else if (c >= A && c < Z) {
b[i] = rot(c, A, 13)
} else {
b[i] = s.charAt(i)
}
}
return b.join('')
}
这里面还有一个rot,同样的方法即可得到源码:
function rot(t, u, v) {
return String.fromCharCode(((t - u + v) % (v * 2)) + u)
}
至此,r13的逻辑就比较清楚了,就是把原始的字符串的每个字符向右轮转13位,这个专业的叫法是凯撒加密。
回到ddip继续看,还有一个$.base64.decode,同样的方法在console粘贴跟进去拿到其代码:
Plugin.atob = Plugin.decode = function(coded, utf8decode) {
coded = String(coded).split('=');
var i = coded.length;
do {
--i;
coded[i] = code(coded[i], true, r64, a256, 6, 8);
} while (i > 0);coded = coded.join('');
return Plugin.raw === false || Plugin.utf8decode || utf8decode ? UTF8.decode(coded) : coded;
}
看这个名字叫base64,我们先不管其代码到底是啥,只需要验证一下是不是标准的base64就可以了,我们把“CC11001100”进行base64编码得到Q0MxMTAwMTEwMA==,然后在console上运行:
$.base64.decode("Q0MxMTAwMTEwMA==")得到了原文“CC11001100”,说明这应该是标准的base64,那么其对应的js代码不再重要,调用标准库即可。
前面有几个函数是从VM中拿到的,实际上定义它们是在这个文件中:
https://www.baibianip.com/home/indexjs?_t=1606231394.2582
在下面有两个eval:
eval(function(p, a, c, k, e, d) {
e = function(c) {
return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
}
;
if (!''.replace(/^/, String)) {
while (c--)
d[e(c)] = k[c] || e(c);
k = [function(e) {
return d[e]
}
];
e = function() {
return '\\w+'
}
;
c = 1;
}
;while (c--)
if (k[c])
p = p.replace(new RegExp('\\b' + e(c) + '\\b','g'), k[c]);
return p;
}('f c(3){4=b(3.2());5=$.a.e(4);1=5.2().6(8);9=1.d;7=1.6(0,9-8);g 7}', 17, 17, '|e3|toString|e0|e1|e2|substr|e4|10|l3|base64|r13|ddip|length|decode|function|return'.split('|'), 0, {}));
之前的博客有介绍过eval应该如何解密:
https://www.cnblogs.com/cc11001100/p/8468508.html
使用那篇文章介绍的工具,对这个eval解密,得到:
function ddip(e0) {
e1 = r13(e0.toString());
e2 = $.base64.decode(e1);
e3 = e2.toString().substr(10);
l3 = e3.length;
e4 = e3.substr(0, l3 - 10);
return e4
}
跟前面从VM中拿到代码一样,另一个r13也是在这个文件中以同样的方式定义的,这里不再赘述。
至此,所有相关的js逻辑已经捋清楚了,接下来就是编码实现。
三、编码实现
#!/usr/bin/env python3
# encoding: utf-8
"""
@author: CC11001100
"""
import base64
import re
import requests
from bs4 import BeautifulSoup
def crawl():
url = "https://www.baibianip.com/home/free.html"
html = requests.get(url).text
# 从https://www.baibianip.com/home/indexjs?_t=1606231394.2582最下面取似乎更方便
# 但是因为感觉不是很保险,于是还是老老实实从展示的地方取
doc = BeautifulSoup(html, features="html.parser")
ip_list = []
for x in doc.select(".table td script"):
# '<script>FFfatefully(\'ZGH5AmL3Zmx5ZwR2AF4lZwHhZmLhAwxkAwR1ZmNkAwRm\'); </script>'
ss = re.findall(r"'(\w+)'", str(x))
if ss:
s = ss[0]
ip_list.append(ddip(s))
return ip_list
def ddip(s):
t1 = r13_string(s)
t2 = base64.b64decode(t1.encode("UTF-8")).decode("UTF-8")
t3 = t2[10:]
return t3[0:-10]
def r13_string(s):
r = []
for c in s:
r.append(r13_char(c))
return "".join(r)
def r13_char(c):
if 'a' <= c <= 'z':
return chr((ord(c) - ord('a') + 13) % 26 + ord('a'))
elif 'A' <= c <= 'Z':
return chr((ord(c) - ord('A') + 13) % 26 + ord('A'))
else:
return c
if __name__ == "__main__":
# ['165.225.36.69', '43.250.124.98', '221.1.200.242', '1.20.99.61', '178.75.27.131', '177.129.40.78', '45.226.48.101', '96.9.73.80', '51.77.162.148', '170.185.68.14', '195.88.16.155', '178.47.139.50', '31.148.22.110', '81.163.35.120', '176.98.95.247', '114.30.75.206', '85.133.207.14', '106.249.44.10', '185.209.57.55', '186.216.81.21', '195.178.56.35', '151.106.10.52', '221.122.91.65', '81.163.36.210', '165.225.36.80', '170.239.144.2', '14.102.40.169', '212.56.218.90', '41.66.205.112', '195.98.74.141', '74.15.191.160', '71.17.253.132', '43.239.72.117', '103.4.165.122', '89.109.14.179', '51.77.211.175']
print(crawl())
仓库:


浙公网安备 33010602011771号