【技术分享】Frida Hook篇
2023-04-25 / 0 评论 / 488 阅读 / 33 点赞

【技术分享】Frida Hook篇

发光的神
2023-04-25 / 0 评论 / 488 阅读 / 正在检测是否收录...

简介

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。

lpw0x1yo.png

objdump -M intel -d Demo | grep  call

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

lpw1ixqv.png

这里通过调用 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");
    }
});

lpw1p0b0.png

frida -l code.js -f demo

因为程序是一运行就执行,这里用这种办法来带动程序执行并进行hook。

frida-trace

lpw1xokd.png

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.js

Jmp 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);
    }
});

变速齿轮

变速原理

  1. 变速齿轮的原理
    变速齿轮(变速精灵)通过修改游戏运行时的时间流逝速度,实现加速或减速效果。其核心思路是拦截并修改游戏调用的时间相关函数的返回值。
  2. 关键时间函数
    大部分游戏依赖Windows提供的计时器API来获取时间流逝数据,常用的函数包括:

    • GetTickCount: 获取系统启动后经过的毫秒数。
    • timeGetTime: 获取自系统启动以来的时间,单位为毫秒。

    这些函数在游戏中用于时间逻辑和动画刷新等。

  3. Hook技术的应用
    为实现变速功能,需要使用Hook技术拦截上述API函数:

    • 在返回值的基础上加上变速比例。例如,将时间流逝的速度乘以一个比例系数(如2倍速或0.5倍速)。
    • 修改后的返回值会被游戏程序使用,从而达到加速或减速效果。
  4. 实现效果
    修改计时器函数返回值后:

    • 游戏中的时间会按照设定的速度流逝。
    • 动画、技能冷却、任务时间等都会受到影响。
  5. 注意事项

    • 对API函数的Hook需要精确,避免引起其他程序崩溃。
    • 修改速度可能会影响游戏网络同步或反作弊机制,因此需要谨慎使用。

通过Hook系统的计时器API(如GetTickCounttimeGetTime),改变游戏对时间的感知速度,从而实现游戏变速功能。
这种方式通用于大多数基于Windows计时器的游戏程序。

推荐下 Cheat Engine,这个工具也可以实现。

lgn6v9ak.png

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);
        };
    }
}
33

评论 (0)

取消
0:00