某天,我大哥讓我幫忙弄個設備的網頁爆破腳本,說是前端加密了,算法看不懂是啥
于是打開網頁瞅瞅(不貼登錄界面圖了,省的得罪人)
先查看網頁源代碼

一個簡單的form表單,126行僅一個submit按鈕,91行form也沒有額外的提交事件
F12打開控制臺,看看提交按鈕的監聽事件

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

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

在265、266行對maUser和maPass進行了處理,來看下這兩個對象是如何定義的

第7行定義了變量

132、133行進行了賦值,返回的是name值為a和p的input框,就是登錄頁面的用戶名和密碼框

再來看加密函數

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


然后點擊控制臺右側的
進入函數內部(或者直接F11)
先是進入了Jquery的val()函數,這個不管

然后來到了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,過程中并沒有見到對fn的length進行修改,所以恒為0a是傳入的密碼,肯定為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

測試加解密腳本

一顆小胡椒
HACK學習呀
看雪學苑
安全圈
雷石安全實驗室
黑白之道
信息安全與通信保密雜志社
看雪學苑
公安三所網絡安全法律研究中心
Coremail郵件安全
一顆小胡椒
Coremail郵件安全