Fork me on GitHub

百变IP的IP地址反爬

难度: ★☆☆☆☆ 1星

一、目标

目标网站:
https://www.baibianip.com/home/free.html
在这个网站的IP列表中的IP地址,是通过JS解密出来的:
0
查看源代码(view-source:https://www.baibianip.com/home/free.html),页面返回的时候没有IP地址,IP地址是通过这行JS计算并显示的:
1
本次的目标就是破解这个加密。


二、分析

在上面的截图里,解密函数的名字是FFdisharmony,同时它在script下直接调用了,说明FFdisharmony应该是一个全局函数,能够在console上调用的,尝试调用一下:

2

这就有点尴尬,从Elements选中一个IP地址,看下它的解密函数,这里就不贴图了,在上一部分有这个图,可以观察到script里解密的函数名似乎与其它地方不太一样,这个函数的名字似乎是一直是在变的,同时通过在

view-source:https://www.baibianip.com/home/free.html

页面上快速刷新也能够观察到解密函数的名字一直在变,不过这个没关系,从Elements中的script中复制这个函数名字然后粘贴到console执行就可以了,然后双击跟进去:

3

跟进去之后是这样的:

01
02
03
function FFassimilated (s) {
    document . write (ddip(s));
}

同时注意到这个函数没有被匿名函数包围啥的,说明这个ddip函数的作用域很有可能是全局范围的,因此回到console中输入ddip回车:

4

然后双击跟进去,就能拿到这个函数的源代码了:

01
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代码的地方定义的这个方法,不过这个无所谓了,反正我们已经得到了源码:

5

同时这里我还多刷新跟进去了几次验证了一下,这个ddip没有多套逻辑,也不会再变了, 这样看来FFassimilated之类的函数名字只是为了把它包一层保护起来,虽然也没护住...

上面的ddip整理一下就是这个样子的:

01
02
03
04
05
06
07
08
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的代码:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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,同样的方法即可得到源码:

01
02
03
function rot(t, u, v) {
    return String.fromCharCode(((t - u + v) % (v * 2)) + u)
}

至此,r13的逻辑就比较清楚了,就是把原始的字符串的每个字符向右轮转13位,这个专业的叫法是凯撒加密。

回到ddip继续看,还有一个$.base64.decode,同样的方法在console粘贴跟进去拿到其代码:

01
02
03
04
05
06
07
08
09
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上运行:

01
$.base64.decode("Q0MxMTAwMTEwMA==")

得到了原文“CC11001100”,说明这应该是标准的base64,那么其对应的js代码不再重要,调用标准库即可。

前面有几个函数是从VM中拿到的,实际上定义它们是在这个文件中:

https://www.baibianip.com/home/indexjs?_t=1606231394.2582

在下面有两个eval:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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解密,得到:

01
02
03
04
05
06
07
08
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逻辑已经捋清楚了,接下来就是编码实现。


三、编码实现

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/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())


仓库:

https://github.com/CC11001100/misc-crawler-public/tree/master/001-anti-crawler-js-re/01-002-www.baibianip.com


请注意爬虫文章具有时效性,本文写于2020-11-25日。

posted @   CC11001100  阅读(695)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2019-11-25 flash逆向练习:以逆向的方式通关flash游戏《谈判专家》
点击右上角即可分享
微信分享提示