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 the consequences arising therefrom will have nothing to do with the author. Infringement, please contact me to delete it immediately!
Reverse target
- Goal: G game login
- Homepage:
aHR0cHM6Ly93d3cuZ205OS5jb20v
- Interface:
aHR0cHM6Ly9wYXNzcG9ydC5nbTk5LmNvbS9sb2dpbi9sb2dpbjM=
Reverse parameters:
Query String Parameters:password: kRtqfg41ogc8btwGlEw6nWLg8cHcCW6R8JaeM......
Reverse process
Packet capture analysis
When you come to the homepage, just enter an account password, click login, capture the packet and locate the login interface aHR0cHM6Ly9wYXNzcG9ydC5nbTk5LmNvbS9sb2dpbi9sb2dpbjM=
, in the GET request, Query String Parameters, the password password is encrypted.
Encrypted entry
If you search for the keyword password directly, you will find that there are too many results that are not easy to locate. It is easier to locate the encrypted entry using XHR breakpoints. For XHR breakpoint debugging, please refer to K brother’s previous tutorial: [JS Reverse 100 Cases] XHR Breakpoint Debugging , Steam login reverse , as shown in the figure below, you can see the key statement a.encode(t.password, s)
, t.password
is the plaintext password, and s
is the timestamp.
Follow up with the a.encode()
function. This function is still in home.min.js. Observing this part of the code, you can find that JSEncrypt is used, and there is a setPublicKey method to set the public key. It can be seen that it should be RSA encryption. The password and the timestamp are combined into a | combination, which is RSA-encrypted and then URL-encoded to obtain the final result, as shown in the following figure:
RSA encryption found the public key, in fact, you can directly use Python's Cryptodome module to implement the encryption process, the code is as follows:
import time
import base64
from urllib import parse
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
password = "12345678"
timestamp = str(int(time.time() * 1000))
encrypted_object = timestamp + "|" + password
public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDq04c6My441Gj0UFKgrqUhAUg+kQZeUeWSPlAU9fr4HBPDldAeqzx1UR92KJHuQh/zs1HOamE2dgX9z/2oXcJaqoRIA/FXysx+z2YlJkSk8XQLcQ8EBOkp//MZrixam7lCYpNOjadQBb2Ot0U/Ky+jF2p+Ie8gSZ7/u+Wnr5grywIDAQAB"
rsa_key = RSA.import_key(base64.b64decode(public_key)) # 导入读取到的公钥
cipher = PKCS1_v1_5.new(rsa_key) # 生成对象
encrypted_password = base64.b64encode(cipher.encrypt(encrypted_object.encode(encoding="utf-8")))
encrypted_password = parse.quote(encrypted_password)
print(encrypted_password)
Even if you don't use Python, we can also refer to the JSEncrypt module to implement this encryption process (refer to JSEncrypt GitHub use the module), as shown below:
/*
引用 jsencrypt 加密模块,如果在 PyCharm 里直接使用 require 引用最新版 jsencrypt,
运行可能会提示 jsencrypt.js 里 window 未定义,直接在该文件定义 var window = this; 即可,
也可以使用和网站用的一样的 2.3.1 版本:https://npmcdn.com/jsencrypt@2.3.1/bin/jsencrypt.js
也可以将 jsencrypt.js 直接粘贴到此脚本中使用,如果提示未定义,直接在该脚本中定义即可。
*/
JSEncrypt = require("jsencrypt")
function getEncryptedPassword(t, e) {
var jsEncrypt = new JSEncrypt();
jsEncrypt.setPublicKey('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDq04c6My441Gj0UFKgrqUhAUg+kQZeUeWSPlAU9fr4HBPDldAeqzx1UR92KJHuQh/zs1HOamE2dgX9z/2oXcJaqoRIA/FXysx+z2YlJkSk8XQLcQ8EBOkp//MZrixam7lCYpNOjadQBb2Ot0U/Ky+jF2p+Ie8gSZ7/u+Wnr5grywIDAQAB');
var i = e ? e + "|" + t : t;
return encodeURIComponent(jsEncrypt.encrypt(i));
}
var password = "12345678";
var timestamp = (new Date).getTime();
console.log(getEncryptedPassword(password, timestamp));
webpack rewrite
The title of this article is webpack rewriting actual combat, so obviously the purpose of this article is to practice JavaScript modular programming webpack code rewriting. Now most sites use this writing method, but not all sites are like the sites encountered in this article. , It can be easily implemented by other methods. Often most sites require you to pull down his source code to restore the encryption process. The JavaScript modular programming, namely webpack, has a detailed introduction in the previous articles of Brother K : crawler reverse basics, understand JavaScript modular programming webpack
A standard webpack as a whole is an IIFE immediate call function expression, in which there is a module loader, that is, a function that calls a module. The function generally has a function.call()
or function.apply()
. The parameter passed by IIFE is a list or dictionary, which is Some modules that need to be called are similar to:
!function (allModule) {
function useModule(whichModule) {
allModule[whichModule].call(null, "hello world!");
}
}([
function module0(param) {console.log("module0: " + param)},
function module1(param) {console.log("module1: " + param)},
function module2(param) {console.log("module2: " + param)},
]);
Observing the encryption code of this site, you will find that all the encryption methods are in home.min.js. At the beginning of this file, you can see that the whole is an IIFE immediate call function expression. function e
is a key method .call()
, which can be judged This function is a module loader, and the parameter passed later is a dictionary, which is an object method, that is, the module function that needs to be called. This is a typical webpack writing method, as shown in the following figure:
Next, we complete the rewrite of the webpack code in 4 steps, and extract the original code to implement the encryption process.
1. Find IIFE
IIFE immediate call function expression, also called immediate execution function, self-executing function, extract the IIFE frame from the source code, and then put useful code in it:
!function (t) {
}({
})
2. Find the module loader
As we have function.call()
before, the method with 06180f715395ef or function.apply()
is the module loader, which is the method of calling the module. In this example, function e
is the module loader. Just pull it out, and other redundant codes can be deleted directly. , Note that i
is used inside, so i
should also be extracted:
!function (t) {
function e(s) {
if (i[s])
return i[s].exports;
var n = i[s] = {
exports: {},
id: s,
loaded: !1
};
return t[s].call(n.exports, n, n.exports, e),
n.loaded = !0,
n.exports
}
var i = {};
}({
})
3. Find the called module
Re-encrypted to place the first module. 3, n
inside encode
method returns the final result is encrypted, as shown below:
The second module is 4. You can see that the this.jsencrypt.encrypt(i)
method in module 3 is actually the method in line 3340 that is called. This method is in module 4. The method located in module 4 can be found in the browser developer tool source On the page, put the mouse cursor in front of the function and slide it up until the beginning of the module. You can also use editors such as VS Code to paste the entire home.min.js code, then select to collapse all the code, and then search for this function , You can quickly locate which module.
After confirming that modules 3 and 4 are used, all the codes of these two modules can be deducted. The general code structure is as follows (the specific code of module 4 is too long and has been deleted):
!function (t) {
function e(s) {
if (i[s])
return i[s].exports;
var n = i[s] = {
exports: {},
id: s,
loaded: !1
};
return t[s].call(n.exports, n, n.exports, e),
n.loaded = !0,
n.exports
}
var i = {};
}(
{
4: function (t, e, i) {},
3: function (t, e, i) {
var s;
s = function (t, e, s) {
function n() {
"undefined" != typeof r && (this.jsencrypt = new r.JSEncrypt,
this.jsencrypt.setPublicKey("-----BEGIN PUBLIC KEY-----略-----END PUBLIC KEY-----"))
}
var r = i(4);
n.prototype.encode = function (t, e) {
var i = e ? e + "|" + t : t;
return encodeURIComponent(this.jsencrypt.encrypt(i))
},
s.exports = n
}.call(e, i, e, t),
!(void 0 !== s && (t.exports = s))
}
}
)
Here we need to understand a place, and that is the code module 3 in a line var r = i(4);
, where i
is 3: function (t, e, i) {}
, pass over i
, which in turn module 3 module loader calls, that .call(n.exports, n, n.exports, e)
inside a parameter is i
, I said before when I explained the basics, .call
specifies the point of the this object in the function body, and does not represent the real parameter, so the first n.exports
not a parameter, starting from the second parameter, n
Calculate, then i
is actually .call(n.exports, n, n.exports, e)
in e
, so var r = i(4);
actually the module loader function e
calling module 4. Since module 4 is an object here, it’s best to write var r = i("4");
here. This is a number, so it can run successfully. If the module 4 If the name becomes func4 or other names, then quotation marks must be added when calling.
4. Export encryption function
At present, the key encryption code has been stripped off. The last step is to export the encryption function for us to call. First, define a global variable, such as eFunc, and then use the statement eFunc = e
after the module loader to export the module loader:
var eFunc;
!function (t) {
function e(s) {
if (i[s])
return i[s].exports;
var n = i[s] = {
exports: {},
id: s,
loaded: !1
};
return t[s].call(n.exports, n, n.exports, e),
n.loaded = !0,
n.exports
}
var i = {};
eFunc = e
}(
{
4: function (t, e, i) {},
3: function (t, e, i) {}
}
)
Then define a function, pass in the plaintext password, and return the encrypted password:
function getEncryptedPassword(password) {
var timestamp = (new Date).getTime();
var encryptFunc = eFunc("3");
var encrypt = new encryptFunc;
return encrypt.encode(password, timestamp)
}
Among them, timestamp is the timestamp, because we finally need to call the n.prototype.encode
in module 3, so first call module 3 and return the n function in module 3 (you can run the code in the browser to view the result step by step), Then it is new out, the encode method of n is called, and the encrypted result is returned.
Since then, the encryption code of webpack has been stripped, and finally debugging will find that the navigator and window are not defined, just define it:
var navigator = {};
var window = global;
Here is an extension. In the browser, the window is actually the global. There is no window in nodejs, but there is a global, which is similar to the browser's window object type and is a globally accessible object. Therefore, in the nodejs environment, the window can be defined as global, if it is defined as empty, it may cause other errors.
Complete code
GitHub pays attention to K brother crawler, and continues to share crawler-related code! Welcome star! https://github.com/kgepachong/
only part of the key code is demonstrated and cannot be run directly! complete code warehouse address: https://github.com/kgepachong/crawler/
JavaScript encryption key code architecture
Method 1: webpack rewrite the source code to implement RSA encryption:
var navigator = {};
var window = global;
var eFunc;
!function (t) {
function e(s) {
if (i[s])
return i[s].exports;
var n = i[s] = {
exports: {},
id: s,
loaded: !1
};
return t[s].call(n.exports, n, n.exports, e),
n.loaded = !0,
n.exports
}
var i = {};
eFunc = e;
}(
{
4: function (t, e, i) {},
3: function (t, e, i) {}
}
)
function getEncryptedPassword(password) {
var timestamp = (new Date).getTime();
var encryptFunc = eFunc("3");
var encrypt = new encryptFunc;
return encrypt.encode(password, timestamp)
}
// 测试样例
// console.log(getEncryptedPassword("12345678"))
Method 2: Directly use the JSEncrypt module to implement RSA encryption:
/*
引用 jsencrypt 加密模块,此脚适合在 nodejs 环境下运行。
1、使用 require 语句引用,前提是使用 npm 安装过;
2、将 jsencrypt.js 直接粘贴到此脚本中使用,同时要将结尾 exports.JSEncrypt = JSEncrypt; 改为 je = JSEncrypt 导出方法。
PS:需要定义 var navigator = {}; var window = global;,否则提示未定义。
*/
// ========================= 1、require 方式引用 =========================
// var je = require("jsencrypt")
// =================== 2、直接将 jsencrypt.js 复制过来 ===================
/*! JSEncrypt v2.3.1 | https://npmcdn.com/jsencrypt@2.3.1/LICENSE.txt */
var navigator = {};
var window = global;
// 这里是 jsencrypt.js 代码
function getEncryptedPassword(t) {
var jsEncrypt = new je();
jsEncrypt.setPublicKey('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDq04c6My441Gj0UFKgrqUhAUg+kQZeUeWSPlAU9fr4HBPDldAeqzx1UR92KJHuQh/zs1HOamE2dgX9z/2oXcJaqoRIA/FXysx+z2YlJkSk8XQLcQ8EBOkp//MZrixam7lCYpNOjadQBb2Ot0U/Ky+jF2p+Ie8gSZ7/u+Wnr5grywIDAQAB');
var e = (new Date).getTime();
var i = e ? e + "|" + t : t;
return encodeURIComponent(jsEncrypt.encrypt(i));
}
// 测试样例
// console.log(getEncryptedPassword("12345678"));
Python login key code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
import json
import time
import random
import base64
from urllib import parse
import execjs
import requests
from PIL import Image
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
login_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
verify_image_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
check_code_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
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'
}
session = requests.session()
def get_jquery():
jsonp = ''
for _ in range(21):
jsonp += str(random.randint(0, 9))
jquery = 'jQuery' + jsonp + '_'
return jquery
def get_dict_from_jquery(text):
result = re.findall(r'\((.*?)\)', text)[0]
return json.loads(result)
def get_encrypted_password_by_javascript(password):
# 两个 JavaScript 脚本,两种方法均可
with open('gm99_encrypt.js', 'r', encoding='utf-8') as f:
# with open('gm99_encrypt_2.js', 'r', encoding='utf-8') as f:
exec_js = f.read()
encrypted_password = execjs.compile(exec_js).call('getEncryptedPassword', password)
return encrypted_password
def get_encrypted_password_by_python(password):
timestamp = str(int(time.time() * 1000))
encrypted_object = timestamp + "|" + password
public_key = "脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler"
rsa_key = RSA.import_key(base64.b64decode(public_key)) # 导入读取到的公钥
cipher = PKCS1_v1_5.new(rsa_key) # 生成对象
encrypted_password = base64.b64encode(cipher.encrypt(encrypted_object.encode(encoding="utf-8")))
encrypted_password = parse.quote(encrypted_password)
return encrypted_password
def get_verify_code():
response = session.get(url=verify_image_url, headers=headers)
with open('code.png', 'wb') as f:
f.write(response.content)
image = Image.open('code.png')
image.show()
code = input('请输入图片验证码: ')
return code
def check_code(code):
timestamp = str(int(time.time() * 1000))
params = {
'callback': get_jquery() + timestamp,
'ckcode': code,
'_': timestamp,
}
response = session.get(url=check_code_url, params=params, headers=headers)
result = get_dict_from_jquery(response.text)
if result['result'] == 1:
pass
else:
raise Exception('验证码输入错误!')
def login(username, encrypted_password, code):
timestamp = str(int(time.time() * 1000))
params = {
'callback': get_jquery() + timestamp,
'encrypt': 1,
'uname': username,
'password': encrypted_password,
'remember': 'checked',
'ckcode': code,
'_': timestamp
}
response = session.get(url=login_url, params=params, headers=headers)
result = get_dict_from_jquery(response.text)
print(result)
def main():
# 测试账号:15434947408,密码:iXqC@aJt8fi@VwV
username = input('请输入登录账号: ')
password = input('请输入登录密码: ')
# 获取加密后的密码,使用 Python 或者 JavaScript 实现均可
encrypted_password = get_encrypted_password_by_javascript(password)
# encrypted_password = get_encrypted_password_by_python(password)
# 获取验证码
code = get_verify_code()
# 校验验证码
check_code(code)
# 登录
login(username, encrypted_password, code)
if __name__ == '__main__':
main()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。