某天,我大哥讓我幫忙弄個設備的網頁爆破腳本,說是前端加密了,算法看不懂是啥

于是打開網頁瞅瞅(不貼登錄界面圖了,省的得罪人)

先查看網頁源代碼

一個簡單的form表單,126行僅一個submit按鈕,91行form也沒有額外的提交事件

F12打開控制臺,看看提交按鈕的監聽事件

有一個submit事件,點擊鏈接跳轉到login.js

又調用了evtMainAuth()函數,Ctrl+F搜索login.js中的evtMainAuth

265266行maUsermaPass進行了處理,來看下這兩個對象是如何定義的

第7行定義了變量

132133行進行了賦值,返回的是name值為ap的input框,就是登錄頁面的用戶名和密碼框

再來看加密函數

用戶名僅做了去空字符處理,密碼使用了$.fn.en進行處理,當前頁面沒有找到這個函數的定義,直接在266行打斷點,然后登錄一次

然后點擊控制臺右側的進入函數內部(或者直接F11)

先是進入了Jqueryval()函數,這個不管

然后來到了jquery.plugin.js

找到了fn.en的定義

d.fn.en = function (a) {    f = l();    return 0 >= this.length && "string" === d.type(a) ? 0 >= a.length ? "" : h(a) : this.each(function () {        var b = d(this), a = b.val(), c;        0 >= a.length || (c = b.data("v"),        "string" === d.type(c) && 0 < c.length && (b.val(c),            b.removeData("v")),            c = h(a),            b.val(c),            b.data("v", a),            b.one("input propertychange", k))    })}

其中的函數l()

function l() {    for (var a = 0; 0 >= a || 255 <= a;)        a = Math.floor(255 * Math.random());    return a}

函數h()

function g(a) {    var b = "";    16 > a && (b = "0");    return b + a.toString(16)}function h(a) {    for (var b = [g(f)], e, c = 0, d = a.length; c < d; ++c)        e = a.charCodeAt(c),            255 >= e ? b.push(g(e ^ f)) : (b.push(g(Math.floor(e / 256) ^ f)),                b.push(g(e % 256 ^ f)));    return b.join("").toUpperCase()}

開始分析加密算法

先看fn.en函數

第1行l()函數的返回值賦值給定義的變量f

l()函數代碼如下

  • Math.random()是返回一個0<= number <1的隨機數
  • Math.floor()是返回小于等于給定數字的最大整數

也就是說,l()返回了一個0<= number < 255的隨機數

接著看fn.en,接下來是一個return語句,但用到了三目運算,為了方便閱讀,這里將代碼進行格式化

this.length為0且a的變量類型為string時,返回8~16行的三目運算結果,否則,返回20~29行的函數

這里在控制臺看一下

  • this在函數中為函數所屬的對象,就是fn,過程中并沒有見到對fnlength進行修改,所以恒為0
  • a是傳入的密碼,肯定為string類型

故返回值為8~16行的三目運算結果

再來看8~16行的表達式

a(傳入的密碼)長度為0時,返回空字符串,否則返回h(a)的結果

h()就是加密的具體函數了,分析一下h()函數

先看循環的步數,起始是c=0,結束條件是c<d(a.length),每步步長是++c

也就是對a字符串進行遍歷

b的初始值是[g(f)],看看g()函數

  • a小于16時,返回的是0+a的16進制字符串,例如a=13,返回的是”0d”
  • a大于等于16時,返回的是a的16進制字符串,例如a=18,返回的是”12”

g函數也就是將10進制轉換為16進制字符串

數組b的初始值也就是[f的16進制字符串],f取值范圍是0<=f<255,也就是初始值為”00”至”ff”

e變量每步會執行e = a.charCodeAt(c),也就是遍歷獲取a字符串的每個字符的ASCII值

然后進行一個三目運算,e<=255時,b數組添加g(e^f),否則添加g(Math.floor((e/256)^f)

X^Y,是按位異或,每一個對應的位,兩個不相同返回1,相同則返回0

例如


200^255200    =1100 1000255    =1111 1111結果  =0011 0111十進制  =55
55^10055    =0011 0111100    =0110 0100結果  =0101 0011十進制  =83

再回到e,當e>255的時候,就超出了ASCII的范圍,屬于UNICODE字符串了,先e/256再去按位異或f,再向b添加e對256取余后和f的按位異或結果

e<=255,在ASCII時(a-zA-Z0-9特殊符號都在其中了),僅需正常返回g(e^f)即可

所以算法流程如圖

搞清楚算法后,開始編寫加密及解密腳本

對了,還有個數學知識,a^b=c,那么c^b=a

加密腳本

#!/usr/env python# author Waximport randomimport sys
def getKey():    return int(255 * random.random())
def octToHexStr(number):    if number < 16:        return '0' + str(hex(number))[2:]
    return str(hex(number))[2:]
def en(data, key):    enData = ""    if data == "":        return enData
    if key == '':        key = getKey()
    enData += octToHexStr(key)
    for chr in data:        e = ord(chr)        if e <= 255:            enData += octToHexStr(e^key)        else:            enData += octToHexStr(int(e/256) ^ key)            enData += octToHexStr(e % 256 ^ key)
    return enData.upper()
if __name__ == '__main__':    if len(sys.argv) > 1 and len(sys.argv) <=3:        data = sys.argv[1].strip()        key = ''        if sys.argv[2]:            key = int(sys.argv[2])
        print('data:'+data)        print('key :'+str(key))        print(en(data, key))    else:        print('usage:')        print('\tpython encrypt.py data')        print('\tpython encrypt.py data key_oct')

解密腳本


#!/usr/env python# author Waximport sys
def de(enData):    key = HexStrToOct(enData[0:2])    enData = enData[2:]    data = ""
    while len(enData)/2 > 0:        chrHexStr = enData[0:2]        data += chr(HexStrToOct(chrHexStr)^key)        enData = enData[2:]
    return data
def HexStrToOct(hexStr):    return int.from_bytes(bytes.fromhex(hexStr))
if __name__ == '__main__':    if len(sys.argv) == 2:        enData = sys.argv[1]        print(de(enData))    else:        print('usage:')        print('\tpython decrypt.py data')

測試一下,先登錄一次,密碼輸入的是12345

加密結果是8DBCBFBEB9B8

前兩位8D就是生成的隨機數,十進制是141

測試加解密腳本