反爬蟲之字型反爬蟲

凌天labs發表於2019-06-27

0x0前言

反爬蟲和爬蟲之間的較量已經爭鬥多年,不管是攻還是守,已經持續N年,這是一個沒有硝煙的戰場,大家都知道爬蟲和反爬之家的道高一尺魔高一丈的關係。但這個方案可以很大程度上可以增加普通爬蟲的採整合本,在不使用OCR的前提下,算是比較極致的方案了。當然方案有很多種,層出不窮的各種方法,這裡介紹的時候反爬蟲的中的一種比較實用的方案,字型反爬也就是自定義字型反爬透過呼叫自定義的ttf檔案來渲染網頁中的文字,而網頁中的文字不再是文字,而是相應的字型編碼,透過複製或者簡單的採集是無法採集到編碼後的文字內容!必須透過程式去處理才能達到採整合本。

 

 

效果展示!

0x1思路

細心的人會問,為什麼不把所有的內容都替換成編碼呢?這個就涉及到載入和渲染速度的問題。還有如果啟動字型反爬蟲,基本上已經告別SEO了,請仔細考慮中間的厲害關係,你懂得!

 

我們知道,單純漢字就有好幾千個,還有各種字元,有的還包含各種外國人的字串!如果全部放到自定義字型庫中的話,這個檔案灰常大,幾十兆是肯定有的了,那後果啥樣就很清楚了,載入肯定很慢,更糟糕的是如此之多的字型需要瀏覽器去渲染,那效果,卡到爆!!!

 

為了解決這個問題,我們可以選擇只渲染少量的、部分的文字,假設50個字,那麼字型庫就會小到幾十K了,相當於一個小圖片而已,加上CDN加速之類的,解決了。具體網路上又N種方法參考方法我會貼在下面!

 

如此簡單?50個字兒呢可不是隨便隨便選擇的,要選擇那些爬蟲採集不到就會很大改變整個語句的語義的詞,直接點吧,也就是量詞、否定詞之類的。如原文“我有一頭朱佩琪,我從來都不騎”,我們把其中的“一”、“不”放到我們的自定義字型檔中,這樣一來,爬蟲採集到的就是“我有頭朱佩琪,我從來都騎”,嘿嘿,如果加上數字就更叼了,“漏洞版本 .. ???“ 是不是很猥瑣。

 

但是上述方法早已讓網路各位大神破解一遍了,這可如何是好呢?辦法總是有的!如果讓“叼”字的編碼隨機變化,但字型資訊不變,瀏覽器渲染出來還是“叼”字那不就完美了,於是,每個網頁載入的時候,都隨機載入了一套字型庫,字型庫的內容還是50個字,但每個字的順序編碼都是變化的,雖然我們打亂了關鍵字的編碼順序,但是每個字對應的字型資訊是不變的,例如,“是”字一共有9劃,每一筆劃都有相應的x、y座標資訊,瀏覽器正是根據這些筆劃資訊渲染

 


如果吧,unicode編碼和x,y座標都騷做改動。他需要採集我的每一套字型庫並且建立關係,這樣增加的爬蟲的成本,美滋滋。

0x3實現

基於微軟雅黑字型檔資訊,抽取其中的關鍵字的字型資訊,生成ttf 後 使用下方程式碼 開始隨機然後隨機生成上千套字型檔,後文章顯示時隨機從檔案或者褲中查詢出一套字型檔,並把文章中的關鍵字替換成Unicode編碼進行渲染!

# encoding: utf-8
# -*-*-
# By:連長 『zh (www.lianzhang.org)』
# -*-*-
import random

from fontTools import ttx
from fontTools.ttLib import TTFont


def random_unicode(lengths):  # 隨機生成Unicode字符集
    while True:
        shuma = ((str(random.sample(random_list, int(lengths))).replace('\'', '')).replace(',', '')). \
            replace(' ', '').replace('[', '').replace(']', '')
        if shuma[0].isalpha():
            return shuma
        else:
            continue


def TTFontsXML(filenames):  # 轉換成XMl 到臨時目錄
    filenametemp = "temp/toolstemp.xml"
    font = TTFont(filenames)
    font.saveXML(filenametemp)
    return filenametemp


def TTFonts(filenames):  # 轉換XML轉換ttf
    try:
        print("開始轉換字型!!!" + filenames)
        ttx.main([filenames])
        print ("-----------------------------------")
    except Exception as e:
        print ("Something went wrong converting ttx -> ttf/otf:")
        print (e)
        exit()


def Editfile(fontsjson, files):
    random_list = ["a", "v", "x", "s", "q", "w", "e", "r", "t", "y", "u", "i", "o", "z", "x", "c", "v", "b", "n", "m",
                   "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ]
    shuma = ((str(random.sample(random_list, int(25))).replace('\'', '')).replace(',', '')). \
        replace(' ', '').replace('[', '').replace(']', '')
    filenametemp = "temp/" + shuma + ".xml"

    try:
        with open(files, 'r+') as fileOpen:
            data = fileOpen.read()
            fileOpen.close()

        for key in fontsjson.keys():
            data = data.replace(str(relationdic[key]), str(fontsjson[key]))
            data = data.replace(str(relationdic[key]).upper(), str(fontsjson[key]).upper())
        with open(filenametemp, 'w') as f:
            f.write(data)
            f.close()
        return shuma + ".ttf", filenametemp
    except Exception, ex:
        print ex
        filenametemp = "error"
        filenames = ""
        return filenametemp, filenames


if __name__ == '__main__':
    random_list = ['e', 'a', 'd', 'f', 'c', 'b', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    ttf_patn = "FontTest.ttf"  # 請輸入ttf檔案絕對路徑:
    unicodelengths = 4  # 輸入 UniCode 長度:
    ttfnumber = 10  # 輸入生成多少個檔案:
    relationdic = {"0": "e2f1", "1": "efab", "2": "eba3", "3": "eba5", "4": "edfd", "5": "c57f", "6": "e261",
                   "7": "f4d2", "8": "bad5", "9": "d4c2",
                   "A": "bfec5", "B": "fc736", "C": "e6d21", "D": "be4a9", "E": "c0e8f", "F": "d3c26", "G": "b18a0",
                   "H": "acb06", "I": "fd33e", "J": "fd36e", "K": "d417c", "L": "ad31e", "M": "ec95a", "N": "b39ce",
                   "O": "d508a", "P": "a961d", "Q": "a76b0", "R": "b7f12", "S": "b0426", "T": "d5941", "U": "ede47",
                   "V": "fc5a6", "W": "ed947", "X": "fd781", "Y": "b761a",
                   "Z": "af370", "a": "fde89", "b": "ecb21", "c": "c123c", "d": "b4c2c", "e": "cbbc7", "f": "c10cb",
                   "g": "cb78b", "h": "fdac7", "i": "076fe", "j": "d0def", "k": "ed6de", "l": "eaa1a", "m": "de1e9",
                   "n": "9eaa5", "o": "123e5", "p": "e12e2", "q": "e5efd", "r": "e6ea9", "s": "e1e8a", "t": "b8eac",
                   "u": "23e1c", "v": "ea6ac",
                   "w": "b87de", "x": "e5dac", "y": "2ccea",
                   "z": "3ada9"}  # 必須當前混淆ttf填入關係 否則不知道誰是誰了。。。生成的json 檔案也是錯誤的。。
    try:

        macs = len(relationdic) + 50 * ttfnumber  # 可能會有重複 多加點
        tem_list = []
        for x in range(0, int(macs)):
            tem_list.append(random_unicode(unicodelengths))
        tem_list = list(set(tem_list))  # 去重

        tempfontsxmlpa = TTFontsXML(ttf_patn)  # 轉換到臨時XML地址。。

        okjson = []

        for f in range(0, ttfnumber):
            relationdictemp = relationdic.copy()
            for key in relationdictemp.keys():
                b = random.sample(tem_list, 1)
                tem_list.remove(b[0])
                relationdictemp[key] = b[0]
            filenames, filenametemp = Editfile(relationdictemp, tempfontsxmlpa)
            if filenametemp != "error":  # 如果返回修改成功 啟動轉換ttf的程式!
                TTFonts(filenametemp)
                jsonSeve = {"url": filenames, "data": relationdictemp}
                okjson.append(jsonSeve)



    except Exception, ex:
        print ex

 

Git:https://github.com/hackxx/AntiCrawlerFontGeneration

 

上述程式碼中 不帶x,y混淆,請自己修改程式碼謝謝,如有需要請留言!

0x4參考

https://www.jianshu.com/p/4d28dd440cdd

 

https://github.com/FantasticLBP/Anti-WebSpider

相關文章