简介
Frida是一款基于Python + JavaScript 的hook框架,本质是一种动态插桩技术。可以用于Android、Windows、iOS等各大平台,其执行脚本基于Python或者Node.js写成,而注入代码用JavaScript写成,所以有必要了解一些这些语言的语法。本文简单讲述Frida框架的使用。
Windows
修改函数返回
#include <stdio.h>
int license() {
return 0;
}
int main() {
if(license() == 1) {
printf("%s", "你做到了~");
}else{
printf("%s", "你失败了!");
}
return 0;
}这里我用C语言编写一个简单的例子,尝试hook函数的返回值修改为 1。

objdump -M intel -d Demo | grep call这里我用 objdump 进行过滤反汇编代码,我的习惯用全局过滤 call 看看都调用了哪些函数,因为有的编译是不显示函数名称的,也就无法判断其函数的用途。

这里通过调用 license() 函数判断返回值,那么只要 hook 0x1139 这个地址的返回值,就能改变判断执行的流程。
var moduleName = "demo";
var baseAddress = Module.findBaseAddress(moduleName);
var absoluteAddress = baseAddress.add(0x1139);
Interceptor.attach(absoluteAddress, {
onLeave: function(retval) {
retval.replace(1);
console.log("\n");
}
});
frida -l code.js -f demo因为程序是一运行就执行,这里用这种办法来带动程序执行并进行hook。
frida-trace

frida-trace -f demo -i "printf"可以利用 frida-trace 监听函数调用和获取参数等等一系列操作。
WSASend Hook
// 自适应旧数据大小
Interceptor.attach(Module.getExportByName('Ws2_32.dll', 'WSASend'), {
onEnter: function (args) {
var hexDataWithSpaces = "00 00 00 BD 00 14 00 02 ";
function hexStringWithSpacesToBytes(hexStr) {
var hexValues = hexStr.replace(/\s+/g, '').match(/.{1,2}/g);
var bytes = [];
for (var i = 0; i < hexValues.length; i++) {
bytes.push("0x" + hexValues[i]);
}
return bytes;
}
var socket = args[0].toInt32();
var sockaddr_in = Memory.alloc(16);
var sockaddr_in_size = Memory.alloc(4);
Memory.writeU32(sockaddr_in_size, 16);
var getpeername = Module.getExportByName('Ws2_32.dll', 'getpeername');
var result = new NativeFunction(getpeername, 'int', ['int', 'pointer', 'pointer'])(socket, sockaddr_in, sockaddr_in_size);
if (result === 0) {
var ip = Memory.readU32(sockaddr_in.add(4));
var ipStr = ((ip & 0xFF)) + '.' +
((ip >> 8) & 0xFF) + '.' +
((ip >> 16) & 0xFF) + '.' +
((ip >> 24) & 0xFF);
console.log("[+] IP Address: " + ipStr);
} else {
console.log("[-] Failed to retrieve IP address");
}
var lpBuffers = args[1];
var dwBufferCount = args[2].toInt32();
for (var i = 0; i < dwBufferCount; i++) {
var lpBuffer = lpBuffers.add(i * Process.pointerSize * 2);
var bufLen = Memory.readU32(lpBuffer);
if (bufLen === 173) {
console.log("[+] Length=" + bufLen);
var newData = hexStringWithSpacesToBytes(hexDataWithSpaces);
var newDataLength = newData.length;
var newBufPtr = Memory.alloc(1024);
Memory.writeByteArray(newBufPtr.add(24), newData);
Memory.writePointer(lpBuffer.add(Process.pointerSize), newBufPtr.add(24));
Memory.writeU32(lpBuffer, newDataLength);
var modifiedData = Memory.readByteArray(newBufPtr.add(24), newDataLength);
console.log(modifiedData);
}
}
}
});WSARcev Hook
// 自适应旧数据大小
var buff = Memory.alloc(0x6)
Memory.writeByteArray(buff, [0x32,0x32,0x32,0x32,0x32,0x32]);
Interceptor.attach(Module.getExportByName('Ws2_32.dll', 'WSARecv'), {
onEnter: function (args) {
this.lpBuffersPtr = args[1];
this.lpNumberOfBytesRecvd = args[3];
},
onLeave: function (retval) {
var lenPtr = Memory.readPointer(this.lpNumberOfBytesRecvd);
var buflen = parseInt(lenPtr, 16);
if (buflen == 2) {
Memory.writePointer(this.lpBuffersPtr.add(Process.pointerSize), buff);
ptr(this.lpNumberOfBytesRecvd).writeUInt(0x6);
}
}
});frida -p 2085 -l WSARcev.jsJmp Hook
var baseAddress = ptr('0x4010F5');
var jmpAddress = ptr('0x4010FB');
var offset = jmpAddress.sub(baseAddress.add(5).toInt32());
var jmpOpcode = [ 0xE9,
offset & 0xFF, (offset >> 8) & 0xFF,
(offset >> 16) & 0xFF, (offset >> 24) & 0xFF
];
Memory.protect(baseAddress, 8, 'rw-');
Memory.writeByteArray(baseAddress, jmpOpcode);系统提权
var path = 'C:/Users/28729/Desktop/main.exe'; // 替换你要执行的程序路径(不能包含中文字符)
var module = Process.getModuleByName('kernel32.dll');
var WinExec = module.findExportByName('WinExec');
var WinExec = new NativeFunction(WinExec,'int', ['pointer', 'uint32']);
WinExec(Memory.allocUtf8String(path), 5);
console.log(WinExec.toString());端口修改
Interceptor.attach(Module.getExportByName('ws2_32.dll', 'connect'), {
onEnter: function(args) {
var sockaddr = args[1];
var ip = Memory.readU32(sockaddr.add(4));
var ipStr = ((ip & 0xFF)) + '.' +
((ip >> 8) & 0xFF) + '.' +
((ip >> 16) & 0xFF) + '.' +
((ip >> 24) & 0xFF);
// 修改端口为 80 (0x0050)
Memory.writeU16(sockaddr.add(2), 0x0050);
var modifiedPort = Memory.readU16(sockaddr.add(2));
console.log("[+] Modified to connect to: " + ipStr + ":" + modifiedPort);
}
});变速齿轮
变速原理
- 变速齿轮的原理
变速齿轮(变速精灵)通过修改游戏运行时的时间流逝速度,实现加速或减速效果。其核心思路是拦截并修改游戏调用的时间相关函数的返回值。 关键时间函数
大部分游戏依赖Windows提供的计时器API来获取时间流逝数据,常用的函数包括:GetTickCount: 获取系统启动后经过的毫秒数。timeGetTime: 获取自系统启动以来的时间,单位为毫秒。
这些函数在游戏中用于时间逻辑和动画刷新等。
Hook技术的应用
为实现变速功能,需要使用Hook技术拦截上述API函数:- 在返回值的基础上加上变速比例。例如,将时间流逝的速度乘以一个比例系数(如2倍速或0.5倍速)。
- 修改后的返回值会被游戏程序使用,从而达到加速或减速效果。
实现效果
修改计时器函数返回值后:- 游戏中的时间会按照设定的速度流逝。
- 动画、技能冷却、任务时间等都会受到影响。
注意事项
- 对API函数的Hook需要精确,避免引起其他程序崩溃。
- 修改速度可能会影响游戏网络同步或反作弊机制,因此需要谨慎使用。
通过Hook系统的计时器API(如GetTickCount、timeGetTime),改变游戏对时间的感知速度,从而实现游戏变速功能。
这种方式通用于大多数基于Windows计时器的游戏程序。
推荐下 Cheat Engine,这个工具也可以实现。

此源码仅支持 64位 Python。
Android
Hook okhttp3
Java.perform(function () {
var RequestBuilder = Java.use("okhttp3.Request$Builder");
var Buffer = Java.use("okio.Buffer");
RequestBuilder.build.implementation = function () {
var request = this.build();
var body = request.body();
if (body != null) {
var buffer = Buffer.$new();
body.writeTo(buffer);
var raw_body = buffer.readUtf8();
console.log("=== HTTP Request ===");
console.log("URL: " + request.url().toString());
console.log("Body: " + raw_body);
}
return request;
};
});so 加载检测
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
onEnter: function(args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("Loaded: " + path);
}
}
});
}var dlopen = Module.findExportByName(null, "dlopen"); // 6.0
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); // 8.1+
Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
this.path = ptr(path_ptr).readCString();
console.log("[+] dlopen onEnter => " + this.path + " > 开始加载");
},
onLeave: function (retval) {
console.log("[-] dlopen onLeave => " + this.path + " > 加载完成");
}
});
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
this.path = ptr(path_ptr).readCString();
console.log("[+] dlopen_ext onEnter => " + this.path + " > 开始加载");
},
onLeave: function (retval) {
console.log("[-] dlopen_ext onLeave => " + this.path + " > 加载完成");
}
});其它-JS逆向
Hook 解析参数
// === JSON.parse (解密后) ===
var _json_parse = JSON.parse;
JSON.parse = function (text, reviver) {
console.log("JSON.parse:", text);
debugger;
return _json_parse.call(this, text, reviver);
};
// === JSON.stringify (加密前) ===
var _json_stringify = JSON.stringify;
JSON.stringify = function (value, replacer, space) {
console.log("JSON.stringify:", value);
debugger;
return _json_stringify.call(this, value, replacer, space);
};
// === XMLHttpRequest (发送 & 接收) ===
(function() {
var _open = XMLHttpRequest.prototype.open;
var _send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._url = url;
this._method = method;
return _open.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body) {
console.log(`XHR Request [${this._method}] ${this._url}`, body);
this.addEventListener("load", function() {
console.log(`XHR Response [${this._method}] ${this._url}`, this.responseText);
});
return _send.apply(this, arguments);
};
})();
// === fetch (发送 & 接收) ===
var _fetch = fetch;
fetch = function(input, init) {
console.log("fetch Request:", input, init && init.body);
return _fetch.apply(this, arguments).then(async res => {
let clone = res.clone();
let text = await clone.text();
console.log("fetch Response:", text);
return res;
});
};
// === WebSocket (发送 & 接收) ===
var _ws_send = WebSocket.prototype.send;
WebSocket.prototype.send = function(data) {
console.log("WS Send:", data);
return _ws_send.apply(this, arguments);
};
var _WebSocket = window.WebSocket;
window.WebSocket = function(...args) {
var ws = new _WebSocket(...args);
ws.addEventListener("message", function(event) {
console.log("WS Message:", event.data);
});
return ws;
};Hook 原生加密解密
// === CryptoJS AES 加密 / 解密 ===
if (window.CryptoJS && CryptoJS.AES) {
var _aes_encrypt = CryptoJS.AES.encrypt;
CryptoJS.AES.encrypt = function (msg, key, cfg) {
console.log("AES encrypt input:", msg);
debugger;
return _aes_encrypt.apply(this, arguments);
};
var _aes_decrypt = CryptoJS.AES.decrypt;
CryptoJS.AES.decrypt = function (ciphertext, key, cfg) {
console.log("AES decrypt input:", ciphertext);
var result = _aes_decrypt.apply(this, arguments);
console.log("AES decrypt output:", result.toString(CryptoJS.enc.Utf8));
debugger;
return result;
};
}
// === CryptoJS RC4 加密 / 解密 ===
if (window.CryptoJS && CryptoJS.RC4) {
var _rc4_encrypt = CryptoJS.RC4.encrypt;
CryptoJS.RC4.encrypt = function (msg, key) {
console.log("RC4 encrypt input:", msg);
debugger;
return _rc4_encrypt.apply(this, arguments);
};
var _rc4_decrypt = CryptoJS.RC4.decrypt;
CryptoJS.RC4.decrypt = function (ciphertext, key) {
console.log("RC4 decrypt input:", ciphertext);
var result = _rc4_decrypt.apply(this, arguments);
console.log("RC4 decrypt output:", result.toString(CryptoJS.enc.Utf8));
debugger;
return result;
};
}
// === Base64 ===
var _atob = atob;
window.atob = function (str) {
console.log("Base64 decode input:", str);
var result = _atob(str);
console.log("Base64 decode output:", result);
debugger;
return result;
};
var _btoa = btoa;
window.btoa = function (str) {
console.log("Base64 encode input:", str);
debugger;
return _btoa(str);
};Hook 网络通信函数
// ===== Hook fetch API =====
// 拦截并打印 fetch 请求和响应
function hookFetch() {
var _fetch = fetch;
fetch = function (input, init) {
console.log("fetch request:", input, init?.body);
return _fetch.apply(this, arguments).then(async res => {
let clone = res.clone();
console.log("fetch response:", await clone.text());
return res;
});
};
}
// ===== Hook XMLHttpRequest API =====
// 拦截并打印 XHR 请求和响应
function hookXHR() {
var _open = XMLHttpRequest.prototype.open;
var _send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url) {
this._method = method;
this._url = url;
return _open.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function (body) {
console.log(`XHR [${this._method}] ${this._url}`, body);
this.addEventListener("load", () => {
console.log(`XHR response [${this._method}] ${this._url}`, this.responseText);
});
return _send.apply(this, arguments);
};
}
// ===== Hook WebSocket API =====
// 拦截并打印 WebSocket 收发数据
function hookWebSocket() {
var _ws_send = WebSocket.prototype.send;
WebSocket.prototype.send = function (data) {
console.log("WS send:", data);
return _ws_send.apply(this, arguments);
};
var _WebSocket = window.WebSocket;
window.WebSocket = function (...args) {
var ws = new _WebSocket(...args);
ws.addEventListener("message", function (event) {
console.log("WS message:", event.data);
});
return ws;
};
}
// ===== Hook WebRTC send =====
// 拦截 WebRTC 数据
function hookWebRTC() {
if (window.RTCPeerConnection) {
var _send = RTCDataChannel.prototype.send;
RTCDataChannel.prototype.send = function (data) {
console.log("WebRTC send:", data);
return _send.apply(this, arguments);
};
}
}
评论 (0)