Pay attention to WeChat public account: K brother crawler, QQ exchange group: 808574309, continue to share advanced crawler, JS/Android reverse engineering and other technical dry goods!
statement
All the content in this article is for learning and communication only. The captured content, sensitive URLs, and data interfaces have been desensitized, and it is strictly forbidden to use them for commercial or illegal purposes. Otherwise, all consequences arising therefrom will have nothing to do with the author. Infringement, please contact me to delete it immediately!
Reverse target
- Goal: A Chuanggang login interface
- Homepage:
aHR0cHM6Ly9tLndjYmNoaW5hLmNvbS9sb2dpbi9vdGhlci1sb2dpbi5odG1s
- Interface:
aHR0cHM6Ly9tLndjYmNoaW5hLmNvbS9hcGkvbG9naW4vbG9naW4=
Reverse parameters:
- Query String Parameters:
rnd: 0.22465933864494048
- Request Payload:
password: "25D55AD283AA400AF464C76D713C07AD"
- Query String Parameters:
Ready to work
The encryption of this site is actually not complicated. You can quickly locate the encrypted code by directly searching for the encryption parameters, but this article mainly introduces the use of Fiddler packet capture software, with plug-ins, and the use of hooks to locate the encrypted location. If you encounter an encryption parameter search If there are not enough, or there are too many search results, Hook is a more efficient method that can help you quickly locate the encrypted entry. For detailed knowledge about Hook, I have a detailed introduction in the previous article of Brother K: JS Reverse Hook, eat The hot pot was singing, and suddenly he was robbed by the gangsters!
The packet capture software used in this article can be Fiddler official website . The official website provides five different versions/services:
- Fiddler Everywhere: Cross-platform packet capture software, supporting MacOS, Windows and Linux, equivalent to an improved and enhanced version of the Classic version;
- Fiddler Classic: Traditional Fiddler, only supports Windows, this version is generally used by Windows users;
- Fiddler Jam: browser plug-in, chargeable, mainly used for website optimization, security troubleshooting, etc.;
- Fiddler Cap: Lightweight packet capture software designed for non-technical users, only supports Windows;
- Fiddler Core: Fiddler core, embeddable .NET library, chargeable.
If you are a MacOS or Linux user, you can choose Fiddler Everywhere. If you are a Windows user, it is recommended to choose Fiddler Classic, how to use the packet capture software, certificate configuration, etc. It is not introduced here, there are many tutorials on the Internet for reference.
It should be noted that Fiddler itself does not have Hook function, you need to write your own plug-ins, and only the Fiddler Classic version supports plug-ins, you can refer to Fiddler Classic plug-in writing document , which means that MacOS and Linux users may not be able to hook through the Fiddler plug-in , But don’t worry, MacOS and Linux users can do Hook by writing browser plug-ins. Brother K will also write a practical article to demonstrate how to write browser plug-ins to hook, so stay tuned!
Regarding the Hook plug-in of Fiddler Classic, some big guys have already written it. The plug-in of the programming cat is used here. The version of Fiddler Classic must be >= v4.6.3. In addition to the Hook function, there are also JS debugging, memory roaming, JSON analysis, common data encryption and decryption, etc. The plug-in can be obtained by entering the keyword [ Fiddler plug-in K brother crawler. The installation method is also in the compressed package, so I won't repeat it here.
Reverse process
Packet capture analysis
Just enter an account password, click login, capture the packet and locate the login interface aHR0cHM6Ly9tLndjYmNoaW5hLmNvbS9hcGkvbG9naW4vbG9naW4=
, POST request, Request Payload, the password password is encrypted, in addition, Query String Parameters also contains a parameter of rnd, every request will be Change. Then password and rnd are the targets of this reversal.
Parameter reverse
password
We know that in JavaScript, the JSON.stringify()
method is used to convert a JavaScript object or value into a JSON string, and the JSON.parse()
method is used to convert a JSON string into a JavaScript object. Some sites will use it when transmitting a username and password to a web server. For these two methods, in this case, the JSON.stringify()
method is used. For this method, we write a hook script:
(function() {
var stringify = JSON.stringify;
JSON.stringify = function(params) {
console.log("Hook JSON.stringify ——> ", params);
debugger;
return stringify(params);
}
})();
The entire Hook script is an IIFE immediate call function expression (also called self-executing function, immediate execution function, etc.). With the help of the Fiddler plug-in, it can be run before the entire web page is loaded. First, a variable stringify
defined to retain the original JSON.stringify
method, and then re Write the JSON.stringify
method, and the JSON.stringify
method is encountered, and it will be interrupted immediately, and finally the received parameters will be returned to the original JSON.stringify
method for processing to ensure normal data transmission.
Put the Hook script in the Fiddler plug-in, F12 opens the packet capture, refresh the web page, re-enter the account password and click login, you can see the successful disconnection:
The password at this time is already encrypted. If you want to locate the encrypted place, you need to look at the Call Stack on the right, that is, the call stack, which shows which functions have been processed in turn before the JSON.stringify()
After debugging, loginAction
method, you can see that the variables V and N are the plaintext account passwords in turn, and after the a.hex_md5(N)
processing, the passwords are encrypted and clear. This is the key encryption entry function.
Follow up a.hex_md5()
, which is actually the hex_md5()
method. It can be seen from the name that it is a simple MD5 encryption, except that all lowercase letters are converted to uppercase letters, which can be implemented in Python:
import hashlib
encrypted_password = hashlib.md5("12345678".encode('utf-8')).hexdigest().upper()
print(encrypted_password)
# 25D55AD283AA400AF464C76D713C07AD
In order to practice the stripping of the JS code, we strip the encrypted code:
Copy the code in the entire md5.js file, you can find that there is a define
keyword at the beginning of the code. This writing is called AMD specification in JavaScript, and the full name is Asynchronous Module Definition, which is the asynchronous module loading mechanism; at the end, there is module.exports = j
, which provides The method of exposing the interface is convenient to call in other files. If you are interested, you can learn about it on Baidu. In the local debugging process, directly delete define
and the ending module.exports = j
, and then use the statement j.hex_md5()
call.
rnd
This rnd parameter is directly after the login URL. It must have been spliced together with the original URL through a certain method, so we can Hook the login URL and break it after the URL is generated. It is similar to the password. Write as follows Hook script:
(function () {
var open = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, url, async) {
if (url.indexOf("rnd") != -1) {
debugger;
}
return open.apply(this, arguments);
};
})();
XMLHttpRequest
object is used to exchange data with the server in the background. It can return the response of the web server synchronously or asynchronously. The XMLHttpRequest.open()
method will initiate a request. The basic syntax is: XMLHttpRequest.open(method, url, async, user, password)
. For details, please refer to MDN XMLHttpRequest.open() , where The url parameter is the complete url of the request. There is a prototype object prototype. All JavaScript objects inherit properties and methods from a prototype object. For details, please refer to the introduction of JavaScript prototype
In the above Hook code, a variable open
defined to retain the original XMLHttpRequest.open
method, and then the XMLHttpRequest.open
method is rewritten to determine if the first occurrence of the rnd string value in the URL is not -1, that is, the URL contains the rnd string, then execute The debugger statement will be interrupted immediately.
Similarly, put the Hook script in the Fiddler plug-in, F12 opens the packet capture, refreshes the web page, re-enter the account password and clicks to log in, and you can see the successful disconnection:
And before looking for the same password encryption entry method, see Call Stack is still the right call stack, one by one up the debugger to a process, you can see there are a a.url = c.addUrlParam(a.url, "rnd", Math.random());
, in JavaScript Math.random()
return between 0 (included) function ~ 1 (not included) is a pseudo-random number, it is not difficult to see that the value of rnd is a random number.
Then in Python, a pseudo-random number between 0 (inclusive) and 1 (not included) can be implemented using the random module:
import random
random_number = random.uniform(0, 1)
print(random_number)
Complete code
GitHub pays attention to K brother crawler, and continues to share crawler-related code! Welcome star! https://github.com/kgepachong/
following 161654ea2d54a3 only demonstrates part of the key code and cannot be run directly! complete code warehouse address: https://github.com/kgepachong/crawler/
JavaScript encryption code
function c(x, c) {
x[c >> 5] |= 128 << c % 32,
x[(c + 64 >>> 9 << 4) + 14] = c;
for (var a = 1732584193, _ = -271733879, y = -1732584194, d = 271733878, i = 0; i < x.length; i += 16) {
var b = a
, B = _
, D = y
, E = d;
a = h(a, _, y, d, x[i + 0], 7, -680876936),
d = h(d, a, _, y, x[i + 1], 12, -389564586),
y = h(y, d, a, _, x[i + 2], 17, 606105819),
_ = h(_, y, d, a, x[i + 3], 22, -1044525330),
a = h(a, _, y, d, x[i + 4], 7, -176418897),
d = h(d, a, _, y, x[i + 5], 12, 1200080426),
y = h(y, d, a, _, x[i + 6], 17, -1473231341),
_ = h(_, y, d, a, x[i + 7], 22, -45705983),
a = h(a, _, y, d, x[i + 8], 7, 1770035416),
d = h(d, a, _, y, x[i + 9], 12, -1958414417),
y = h(y, d, a, _, x[i + 10], 17, -42063),
_ = h(_, y, d, a, x[i + 11], 22, -1990404162),
a = h(a, _, y, d, x[i + 12], 7, 1804603682),
d = h(d, a, _, y, x[i + 13], 12, -40341101),
y = h(y, d, a, _, x[i + 14], 17, -1502002290),
_ = h(_, y, d, a, x[i + 15], 22, 1236535329),
a = g(a, _, y, d, x[i + 1], 5, -165796510),
d = g(d, a, _, y, x[i + 6], 9, -1069501632),
y = g(y, d, a, _, x[i + 11], 14, 643717713),
_ = g(_, y, d, a, x[i + 0], 20, -373897302),
a = g(a, _, y, d, x[i + 5], 5, -701558691),
d = g(d, a, _, y, x[i + 10], 9, 38016083),
y = g(y, d, a, _, x[i + 15], 14, -660478335),
_ = g(_, y, d, a, x[i + 4], 20, -405537848),
a = g(a, _, y, d, x[i + 9], 5, 568446438),
d = g(d, a, _, y, x[i + 14], 9, -1019803690),
y = g(y, d, a, _, x[i + 3], 14, -187363961),
_ = g(_, y, d, a, x[i + 8], 20, 1163531501),
a = g(a, _, y, d, x[i + 13], 5, -1444681467),
d = g(d, a, _, y, x[i + 2], 9, -51403784),
y = g(y, d, a, _, x[i + 7], 14, 1735328473),
_ = g(_, y, d, a, x[i + 12], 20, -1926607734),
a = v(a, _, y, d, x[i + 5], 4, -378558),
d = v(d, a, _, y, x[i + 8], 11, -2022574463),
y = v(y, d, a, _, x[i + 11], 16, 1839030562),
_ = v(_, y, d, a, x[i + 14], 23, -35309556),
a = v(a, _, y, d, x[i + 1], 4, -1530992060),
d = v(d, a, _, y, x[i + 4], 11, 1272893353),
y = v(y, d, a, _, x[i + 7], 16, -155497632),
_ = v(_, y, d, a, x[i + 10], 23, -1094730640),
a = v(a, _, y, d, x[i + 13], 4, 681279174),
d = v(d, a, _, y, x[i + 0], 11, -358537222),
y = v(y, d, a, _, x[i + 3], 16, -722521979),
_ = v(_, y, d, a, x[i + 6], 23, 76029189),
a = v(a, _, y, d, x[i + 9], 4, -640364487),
d = v(d, a, _, y, x[i + 12], 11, -421815835),
y = v(y, d, a, _, x[i + 15], 16, 530742520),
_ = v(_, y, d, a, x[i + 2], 23, -995338651),
a = A(a, _, y, d, x[i + 0], 6, -198630844),
d = A(d, a, _, y, x[i + 7], 10, 1126891415),
y = A(y, d, a, _, x[i + 14], 15, -1416354905),
_ = A(_, y, d, a, x[i + 5], 21, -57434055),
a = A(a, _, y, d, x[i + 12], 6, 1700485571),
d = A(d, a, _, y, x[i + 3], 10, -1894986606),
y = A(y, d, a, _, x[i + 10], 15, -1051523),
_ = A(_, y, d, a, x[i + 1], 21, -2054922799),
a = A(a, _, y, d, x[i + 8], 6, 1873313359),
d = A(d, a, _, y, x[i + 15], 10, -30611744),
y = A(y, d, a, _, x[i + 6], 15, -1560198380),
_ = A(_, y, d, a, x[i + 13], 21, 1309151649),
a = A(a, _, y, d, x[i + 4], 6, -145523070),
d = A(d, a, _, y, x[i + 11], 10, -1120210379),
y = A(y, d, a, _, x[i + 2], 15, 718787259),
_ = A(_, y, d, a, x[i + 9], 21, -343485551),
a = C(a, b),
_ = C(_, B),
y = C(y, D),
d = C(d, E)
}
return Array(a, _, y, d)
}
function a(q, c, a, x, s, t) {
return C(y(C(C(c, q), C(x, t)), s), a)
}
function h(c, h, g, d, x, s, t) {
return a(h & g | ~h & d, c, h, x, s, t)
}
function g(c, h, g, d, x, s, t) {
return a(h & d | g & ~d, c, h, x, s, t)
}
function v(c, h, g, d, x, s, t) {
return a(h ^ g ^ d, c, h, x, s, t)
}
function A(c, h, g, d, x, s, t) {
return a(g ^ (h | ~d), c, h, x, s, t)
}
function _(a, h) {
var g = b(a);
g.length > 16 && (g = c(g, a.length * U));
for (var v = Array(16), A = Array(16), i = 0; 16 > i; i++)
v[i] = 909522486 ^ g[i],
A[i] = 1549556828 ^ g[i];
var _ = c(v.concat(b(h)), 512 + h.length * U);
return c(A.concat(_), 640)
}
function C(x, c) {
var a = (65535 & x) + (65535 & c)
, h = (x >> 16) + (c >> 16) + (a >> 16);
return h << 16 | 65535 & a
}
function y(c, a) {
return c << a | c >>> 32 - a
}
function b(c) {
for (var a = Array(), h = (1 << U) - 1, i = 0; i < c.length * U; i += U)
a[i >> 5] |= (c.charCodeAt(i / U) & h) << i % 32;
return a
}
function B(c) {
for (var a = "", h = (1 << U) - 1, i = 0; i < 32 * c.length; i += U)
a += String.fromCharCode(c[i >> 5] >>> i % 32 & h);
return a
}
function D(c) {
for (var a = F ? "0123456789ABCDEF" : "0123456789abcdef", h = "", i = 0; i < 4 * c.length; i++)
h += a.charAt(c[i >> 2] >> i % 4 * 8 + 4 & 15) + a.charAt(c[i >> 2] >> i % 4 * 8 & 15);
return h
}
function E(c) {
for (var a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", h = "", i = 0; i < 4 * c.length; i += 3)
for (var g = (c[i >> 2] >> 8 * (i % 4) & 255) << 16 | (c[i + 1 >> 2] >> 8 * ((i + 1) % 4) & 255) << 8 | c[i + 2 >> 2] >> 8 * ((i + 2) % 4) & 255, v = 0; 4 > v; v++)
h += 8 * i + 6 * v > 32 * c.length ? S : a.charAt(g >> 6 * (3 - v) & 63);
return h
}
var F = 0
, S = ""
, U = 8
, j = {
hex_md5: function (s) {
return D(c(b(s), s.length * U)).toUpperCase()
},
b64_md5: function (s) {
return E(c(b(s), s.length * U))
},
str_md5: function (s) {
return B(c(b(s), s.length * U))
},
hex_hmac_md5: function (c, a) {
return D(_(c, a))
},
b64_hmac_md5: function (c, a) {
return E(_(c, a))
},
str_hmac_md5: function (c, a) {
return B(_(c, a))
}
};
function getSign(){
var c = (new Date).getTime();
var N = j.hex_md5(c).toUpperCase();
return N
}
function getEncryptedPassword(password) {
return j.hex_md5(password)
}
// 测试样例
// console.log(getEncryptedPassword("12345678"))
// console.log(getSign())
Python login key code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import random
import hashlib
import execjs
import requests
login_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
def get_enpwd_and_sign_by_javascript(password):
with open('encrypt.js', 'r', encoding='utf-8') as f:
encrypt_js = execjs.compile(f.read())
encrypted_password = encrypt_js.call('getEncryptedPassword', password)
sign = encrypt_js.call('getSign')
return encrypted_password, sign
def get_enpwd_and_sign_by_python(password):
timestamp = str(int(time.time() * 1000))
encrypted_password = hashlib.md5(password.encode('utf-8')).hexdigest().upper()
sign = hashlib.md5(timestamp.encode('utf-8')).hexdigest().upper()
return encrypted_password, sign
def get_rnd():
rnd = 'rnd' + str(random.uniform(0, 1))
return rnd
def login(username, encrypted_password, sign, rnd):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
json = {
"auth": {
"timestamp": str(int(time.time() * 1000)),
"sign": sign
},
"username": username,
"password": encrypted_password
}
response = requests.post(url=login_url, params=rnd, json=json, headers=headers)
print(response.json())
def main():
username = input('请输入登录账号: ')
password = input('请输入登录密码: ')
# 通过 JavaScript 代码获取加密后的密码和 sign
encrypted_password, sign = get_enpwd_and_sign_by_javascript(password)
# 通过 Python 代码获取加密后的密码和 sign
# encrypted_password, sign = get_enpwd_and_sign_by_python(password)
rnd = get_rnd()
login(username, encrypted_password, sign, rnd)
if __name__ == '__main__':
main()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。