pyd_Hook

查看 11|回复 0
作者:fine906   
[md]做pyd的题做吐了,想骂人
动态看,一坨
静态看,更是依托
所以,我要写一个通用的hook脚本,hhhhhhh
Hook编写
在Linux下通过pip install Cython安装。安装完毕后执行cython --version,如果输出了版本号即安装成功。
安装完成后,我们创建一个hook_test项目,需要创建hook_test.pyx和setup.py两个文件。
# setup.py
from distutils.core import setup
from Cython.Build import cythonize
# setup() 函数是核心,它定义了如何构建你的模块
setup(
    # 'name' 字段是这个模块的名称,可以自定义
    name='hook_test',
    ext_modules=cythonize("hook_test.pyx")
)
# hook_test.pyx
def process_data(a, b, c, d, e):
    num1 = a + b
    print("PyNumber_Add:", num1)
    num2 = a - b
    print("PyNumber_Subtract:", num2)
    num3 = a * b
    print("PyNumber_Multiply:", num3)
    num4 = a & b
    print("PyNumber_And:", num4)
    num5 = a | b
    print("PyNumber_Or:", num5)
    num6 = a ^ b
    print("PyNumber_Xor:", num6)
    num7 = a > 1
    print("PyNumber_Rshift:", num8)
    b >>= 1
    print("PyNumber_InPlaceRshift:", b)
    num10 = a ** 2
    print("PyNumber_Power:", num10)
    num11 = a % 100
    print("PyNumber_Remainder:", num11)
    num13 = e[0]
    print("PyNumber_Index:", num13)
    c += a
    print("PyNumber_InPlaceAdd:", c)
    return a == b
然后使用下述命令进行编译:
python setup.py build_ext --inplace
获得pyd文件
随后:
# challenge.py
import hook_test
import os
import sys
print(sys.executable)
print(os.getpid())
if hasattr(sys, "set_int_max_str_digits"):
    sys.set_int_max_str_digits(0)
# 准备输入数据
a = 1234345
b = 67890
c = 13579
d = 24680
e = [42,53]
# 调用 Cython 函数
input()
hook_test.process_data(a, b, c, d, e)
print("调用成功")
首先运行challenge.py,然后运行hook代码即可:frida -p 14240 -l hook_05.js
'use strict';
/**
* ======================================================
* Frida hook script for Python3 numeric & compare ops
* - Hooks PyNumber_* (加减乘除、位运算、取余、移位、模幂)
* - Hooks PyObject_RichCompareBool / PyObject_RichCompare (比较)
* - Hooks PyLong_FromString / PyLong_FromUnicodeObject
* - Fixed type error: use int64/uint64 instead of longlong
* - Added fixed-width HEX output (e.g. 0x12345678)
* ======================================================
*/
const logFilePath = "./log.txt";
const logFile = new File(logFilePath, "w");
function logLine(s) {
    logFile.write(s + "\n");
    logFile.flush();
    console.log(s);
}
// ---------------------------------------------------------------------
function findPythonModule() {
    const mods = Process.enumerateModulesSync();
    for (const m of mods) {
        const n = m.name.toLowerCase();
        if (n.startsWith('python3') && n.endsWith('.dll')) return m;
        if (n.startsWith('python') && n.endsWith('.dll')) return m;
    }
    throw new Error('python3*.dll not found. Use: frida -f python.exe -l hook_number_compare.js');
}
const pymod = findPythonModule();
logLine(' using python module: ' + pymod.name + ' ' + pymod.base);
// safe wrapper to resolve exports and print status
function exp(name) {
    try {
        const addr = Module.getExportByName(pymod.name, name);
        logLine(`[+] export ${name} => ${addr}`);
        return addr;
    } catch (e) {
        logLine(`[-] export ${name} not found`);
        return null;
    }
}
// ---------------------------------------------------------------------
// list of symbols
const symbols = {
    PyUnicode_AsUTF8: exp('PyUnicode_AsUTF8'),
    PyObject_Repr: exp('PyObject_Repr'),
    PyObject_Str: exp('PyObject_Str'),
    PyLong_Check: exp('PyLong_Check'),
    PyLong_AsLongLong: exp('PyLong_AsLongLong'),
    PyLong_AsUnsignedLongLongMask: exp('PyLong_AsUnsignedLongLongMask'),
    PyLong_FromString: exp('PyLong_FromString'),
    PyLong_FromUnicodeObject: exp('PyLong_FromUnicodeObject'),
    PyNumber_Add: exp('PyNumber_Add'),
    PyNumber_InPlaceAdd: exp('PyNumber_InPlaceAdd'),
    PyNumber_Subtract: exp('PyNumber_Subtract'),
    PyNumber_Multiply: exp('PyNumber_Multiply'),
    PyNumber_And: exp('PyNumber_And'),
    PyNumber_Or: exp('PyNumber_Or'),
    PyNumber_Xor: exp('PyNumber_Xor'),
    PyNumber_Lshift: exp('PyNumber_Lshift'),
    PyNumber_Rshift: exp('PyNumber_Rshift'),
    PyNumber_InPlaceRshift: exp('PyNumber_InPlaceRshift'),
    PyNumber_Power: exp('PyNumber_Power'),
    PyNumber_Remainder: exp('PyNumber_Remainder'),
    PyNumber_Index: exp('PyNumber_Index'),
    PyObject_RichCompare: exp('PyObject_RichCompare'),
    PyObject_RichCompareBool: exp('PyObject_RichCompareBool')
};
function tryCreateNativeFunction(addr, retType, argTypes, friendlyName) {
    if (!addr) return null;
    try {
        const nf = new NativeFunction(addr, retType, argTypes);
        logLine(`[+] NativeFunction created for ${friendlyName}`);
        return nf;
    } catch (e) {
        logLine(`[-] Failed creating NativeFunction for ${friendlyName}: ${e.message}`);
        return null;
    }
}
const PyUnicode_AsUTF8 = tryCreateNativeFunction(symbols.PyUnicode_AsUTF8, 'pointer', ['pointer'], 'PyUnicode_AsUTF8');
const PyObject_Repr = tryCreateNativeFunction(symbols.PyObject_Repr, 'pointer', ['pointer'], 'PyObject_Repr');
const PyObject_Str = tryCreateNativeFunction(symbols.PyObject_Str, 'pointer', ['pointer'], 'PyObject_Str');
const PyLong_Check = tryCreateNativeFunction(symbols.PyLong_Check, 'int', ['pointer'], 'PyLong_Check');
const PyLong_AsLongLong = tryCreateNativeFunction(symbols.PyLong_AsLongLong, 'int64', ['pointer'], 'PyLong_AsLongLong');
const PyLong_AsUnsignedLongLongMask = tryCreateNativeFunction(symbols.PyLong_AsUnsignedLongLongMask, 'uint64', ['pointer'], 'PyLong_AsUnsignedLongLongMask');
// ---------------------------------------------------------------------
function toHexFixed(num, width = 8) {
    try {
        let big;
        if (typeof num === 'bigint') big = num;
        else if (typeof num === 'number') big = BigInt(num >>> 0);
        else if (typeof num === 'string') {
            if (/^0x[0-9a-fA-F]+$/.test(num)) big = BigInt(num);
            else if (/^-?\d+$/.test(num)) {
                big = BigInt(num);
                if (big `;
        let hex = big.toString(16);
        if (hex.length `;
    }
}
function isDecimalString(s) {
    return typeof s === 'string' && /^-?\d+$/.test(s);
}
function isHexString(s) {
    return typeof s === 'string' && /^0x[0-9a-fA-F]+$/.test(s);
}
function formatForLogging(s, width = 8) {
    if (s === undefined || s === null) return s;
    if (isHexString(s)) return s;
    if (isDecimalString(s)) return toHexFixed(s, width);
    const m = /(-?\d+)$/.exec(String(s).trim());
    if (m) return toHexFixed(m[1], width);
    return s;
}
function safeUtf8FromPyStr(pyStrPtr) {
    if (!pyStrPtr || pyStrPtr.isNull()) return '';
    try {
        if (!PyUnicode_AsUTF8) return '';
        const cstr = PyUnicode_AsUTF8(pyStrPtr);
        if (cstr.isNull()) return '';
        return Memory.readUtf8String(cstr);
    } catch {
        return '';
    }
}
function objToStr(pyObjPtr) {
    if (!pyObjPtr || pyObjPtr.isNull()) return '';
    try {
        if (PyLong_Check) {
            const isLong = PyLong_Check(pyObjPtr);
            if (isLong === 1) {
                if (PyLong_AsUnsignedLongLongMask) {
                    const u = PyLong_AsUnsignedLongLongMask(pyObjPtr);
                    return toHexFixed(u);
                }
                if (PyLong_AsLongLong) {
                    const v = PyLong_AsLongLong(pyObjPtr);
                    return toHexFixed(v);
                }
            }
        }
        if (PyObject_Str) {
            const sObj = PyObject_Str(pyObjPtr);
            if (sObj && !sObj.isNull()) return safeUtf8FromPyStr(sObj);
        }
        if (PyObject_Repr) {
            const r = PyObject_Repr(pyObjPtr);
            if (r && !r.isNull()) return safeUtf8FromPyStr(r);
        }
        return '';
    } catch (e) {
        return ``;
    }
}
// ---------------------------------------------------------------------
// Hook PyLong_FromUnicodeObject / PyLong_FromString
if (symbols.PyLong_FromUnicodeObject) {
    Interceptor.attach(symbols.PyLong_FromUnicodeObject, {
        onEnter(args) {
            this.sRaw = objToStr(args[0]);
            this.base = args[1].toInt32();
        },
        onLeave(retval) {
            const got = objToStr(retval);
            logLine(`[PyLong_FromUnicodeObject] int(s, base=${this.base}) s=${this.sRaw} -> ${formatForLogging(got)}`);
        }
    });
}
if (symbols.PyLong_FromString) {
    Interceptor.attach(symbols.PyLong_FromString, {
        onEnter(args) {
            try { this.s = Memory.readUtf8String(args[0]); } catch (e) { this.s = ''; }
            this.base = args[2].toInt32();
        },
        onLeave(retval) {
            const got = objToStr(retval);
            logLine(`[PyLong_FromString] int(s, base=${this.base}) s=${this.s} -> ${formatForLogging(got)}`);
        }
    });
}
function symbolFor(name) {
    const map = {
        PyNumber_Add: '+',
        PyNumber_InPlaceAdd: '+=',
        PyNumber_Subtract: '-',
        PyNumber_Multiply: '*',
        PyNumber_And: '&',
        PyNumber_Or: '|',
        PyNumber_Xor: '^',
        PyNumber_Lshift: '>',
        PyNumber_InPlaceRshift: '>>=',
        PyNumber_Power: '**',
        PyNumber_Remainder: '%',
        PyNumber_Index: 'index'
    };
    return map[name] || '?';
}
// ---------------------------------------------------------------------
//
// function hookPyNumber(name, ptr) {
//     if (!ptr) return;
//     Interceptor.attach(ptr, {
//         onEnter(args) {
//             this.aPtr = args[0];
//             this.bPtr = args[1];
//             this.aRaw = objToStr(this.aPtr);
//             this.bRaw = objToStr(this.bPtr);
//             this.a = formatForLogging(this.aRaw, 8);
//             this.b = formatForLogging(this.bRaw, 8);
//             try {
//                 this.cPtr = args[2];
//                 if (this.cPtr && !this.cPtr.isNull()) {
//                     this.cRaw = objToStr(this.cPtr);
//                     this.c = formatForLogging(this.cRaw, 8);
//                 } else {
//                     this.cPtr = null;
//                 }
//             } catch (e) { this.cPtr = null; }
//         },
//         onLeave(retval) {
//             if (name === 'PyNumber_Power') {
//                 if (this.cPtr) {
//                     logLine(`[${name}] pow(${this.a}, ${this.b}, ${this.c}) = ${formatForLogging(objToStr(retval), 8)}`);
//                 } else {
//                     logLine(`[${name}] ${this.a} ** ${this.b} = `);
//                 }
//                 return;
//             }
//             logLine(`[${name}] ${this.a} ${symbolFor(name)} ${this.b} = ${formatForLogging(objToStr(retval), 8)}`);
//         }
//     });
// }
function hookPyNumber(name, ptr) {
    if (!ptr) return;
    Interceptor.attach(ptr, {
        onEnter(args) {
            // onEnter 的逻辑保持不变,依然是捕获原始指针和字符串
            this.aPtr = args[0];
            this.bPtr = args[1];
            this.aRaw = objToStr(this.aPtr);
            this.bRaw = objToStr(this.bPtr);
            this.a = formatForLogging(this.aRaw, 8);
            this.b = formatForLogging(this.bRaw, 8);
            try {
                this.cPtr = args[2];
                if (this.cPtr && !this.cPtr.isNull()) {
                    this.cRaw = objToStr(this.cPtr);
                    this.c = formatForLogging(this.cRaw, 8);
                } else {
                    this.cPtr = null;
                    this.cRaw = null;
                    this.c = null;
                }
            } catch (e) {
                this.cPtr = null;
                this.cRaw = null;
                this.c = null;
            }
        },
        onLeave(retval) {
            // --- 主要修改部分在这里 ---
            // 1. 统一检查并准备用于日志的字符串
            // 检查第一个操作数 a
            let aStr = this.aRaw.length > 32 ? "num" : this.a;
            // 检查第二个操作数 b
            let bStr = this.bRaw.length > 32 ? "num" : this.b;
            // 检查返回值 (c)
            const outRaw = objToStr(retval);
            let outFmt = outRaw.length > 32 ? "num" : formatForLogging(outRaw, 8);
            // 2. 根据不同的函数名,使用准备好的字符串来格式化日志
            if (name === 'PyNumber_Power') {
                // 对于 pow(a, b, mod),第三个参数也需要检查
                let modStr = this.c ? (this.cRaw.length > 32 ? "num" : this.c) : "None";
                logLine(`[${name}] pow(${aStr}, ${bStr}, ${modStr}) = ${outFmt}`);
                return;
            }
            // 对于 PyNumber_Remainder 和其他所有二元运算
            logLine(`[${name}] ${aStr} ${symbolFor(name)} ${bStr} = ${outFmt}`);
        }
    });
}
// ---------------------------------------------------------------------
[
    'PyNumber_Add',
    'PyNumber_InPlaceAdd',
    'PyNumber_Subtract',
    'PyNumber_Multiply',
    'PyNumber_And',
    'PyNumber_Or',
    'PyNumber_Xor',
    'PyNumber_Lshift',
    'PyNumber_Rshift',
    'PyNumber_InPlaceRshift',
    'PyNumber_Remainder',
    'PyNumber_Index',
    'PyNumber_Power'
].forEach(n => hookPyNumber(n, symbols[n]));
// 比较操作
const cmpOps = ['', '>='];
if (symbols.PyObject_RichCompareBool) {
    Interceptor.attach(symbols.PyObject_RichCompareBool, {
        onEnter(args) {
            this.ptrA = args[0];
            this.ptrB = args[1];
            this.op = args[2].toInt32();
            this.aRaw = objToStr(this.ptrA);
            this.bRaw = objToStr(this.ptrB);
            this.a = formatForLogging(this.aRaw, 8);
            this.b = formatForLogging(this.bRaw, 8);
        },
        onLeave(retval) {
            const opStr = cmpOps[this.op] || `op(${this.op})`;
            if (this.ptrA.equals(this.ptrB)) return;
            if (this.aRaw === this.bRaw && this.op === 2) return;
            logLine(`[CompareBool] ${this.a} ${opStr} ${this.b} -> ${retval.toInt32()}`);
        }
    });
}
if (symbols.PyObject_RichCompare) {
    Interceptor.attach(symbols.PyObject_RichCompare, {
        onEnter(args) {
            this.aRaw = objToStr(args[0]);
            this.bRaw = objToStr(args[1]);
            this.op = args[2].toInt32();
            this.a = formatForLogging(this.aRaw, 8);
            this.b = formatForLogging(this.bRaw, 8);
        },
        onLeave(retval) {
            logLine(`[CompareObj] ${this.a} ${cmpOps[this.op]} ${this.b} -> ${objToStr(retval)}`);
        }
    });
}
logLine(`\n✅ Hooks installed on ${pymod.name}`);
logLine(`燐 PyNumber_* operations + Compare ops are now being traced in HEX...\n`);
但是有个问题,hook不是万能的,hook不全所有的函数,比如对于Lshift、Rshift等逻辑操作来说,是hook不到的,因为有可能他就不走 Py提供的库函数,直接在pyd中自己实现了
总结了一下,我写的hook_test:
PyNumber_Add: 1302235
PyNumber_Subtract: 1166455
PyNumber_Multiply: 83799682050
PyNumber_And: 288
PyNumber_Or: 1301947
PyNumber_Xor: 1301659
PyNumber_Lshift: 4937380
PyNumber_Rshift: 33945
PyNumber_InPlaceRshift: 33945
PyNumber_Power: 1523607579025
PyNumber_Remainder: 45
PyNumber_Index: 42
PyNumber_InPlaceAdd: 1247924
CompareObj
一共14种,但是我只hook到了9种:
using python module: python312.dll 0x7ffcf6920000
[+] export PyUnicode_AsUTF8 => 0x7ffcf6ac3c58
[+] export PyObject_Repr => 0x7ffcf6998af8
[+] export PyObject_Str => 0x7ffcf6998968
[-] export PyLong_Check not found
[+] export PyLong_AsLongLong => 0x7ffcf69e17d4
[+] export PyLong_AsUnsignedLongLongMask => 0x7ffcf6a9b880
[+] export PyLong_FromString => 0x7ffcf69e3d60
[+] export PyLong_FromUnicodeObject => 0x7ffcf69e35a0
[+] export PyNumber_Add => 0x7ffcf699fd9c
[+] export PyNumber_InPlaceAdd => 0x7ffcf6a76230
[+] export PyNumber_Subtract => 0x7ffcf6951bc8
[+] export PyNumber_Multiply => 0x7ffcf69e6808
[+] export PyNumber_And => 0x7ffcf6982ba8
[+] export PyNumber_Or => 0x7ffcf6951b10
[+] export PyNumber_Xor => 0x7ffcf6951ab4
[+] export PyNumber_Lshift => 0x7ffcf6a6f47c
[+] export PyNumber_Rshift => 0x7ffcf6982980
[+] export PyNumber_InPlaceRshift => 0x7ffcf6aadfa0
[+] export PyNumber_Power => 0x7ffcf6b6d228
[+] export PyNumber_Remainder => 0x7ffcf6951a58
[+] export PyNumber_Index => 0x7ffcf69ede34
[+] export PyObject_RichCompare => 0x7ffcf69e7610
[+] export PyObject_RichCompareBool => 0x7ffcf69913d8
[+] NativeFunction created for PyUnicode_AsUTF8
[+] NativeFunction created for PyObject_Repr
[+] NativeFunction created for PyObject_Str
[+] NativeFunction created for PyLong_AsLongLong
[+] NativeFunction created for PyLong_AsUnsignedLongLongMask
✅ Hooks installed on python312.dll
燐 PyNumber_* operations + Compare ops are now being traced in HEX...
[PyNumber_Add] 0x0012d5a9 + 0x00010932 = 0x0013dedb
[PyNumber_Subtract] 0x0012d5a9 - 0x00010932 = 0x0011cc77
[PyNumber_Multiply] 0x0012d5a9 * 0x00010932 = 0x1382d9ac02
[PyNumber_And] 0x0012d5a9 & 0x00010932 = 0x00000120
[PyNumber_Or] 0x0012d5a9 | 0x00010932 = 0x0013ddbb
[PyNumber_Xor] 0x0012d5a9 ^ 0x00010932 = 0x0013dc9b
[PyNumber_Power] pow(0x0012d5a9, 0x00000002, None) = 0x162be16a991
[PyNumber_InPlaceAdd] 0x0000350b += 0x0012d5a9 = 0x00130ab4
[CompareObj] 0x0012d5a9 == 0x00008499 -> False
可以确定的是,移位或者取值操作是hook不到的
PyNumber_Lshift
PyNumber_Rshift
PyNumber_InPlaceRshift
PyNumber_Index
PyNumber_InPlaceAdd
但是,PyNumber_Remainder可能可以
依旧国赛初赛rand0m:
((int(n,16)^2654435769)>>11)**65537%4294967293
这个运算里面的%就可以hook到...........
我写的num11 = a % 100不行,woc,凭啥啊...........
实战(我要一把梭...)
2024ciscn长城杯_rand0m
不用ida哈,直接跑hook
input:12345678876543211234567887654321
log.txt
[PyLong_FromString] int(s, base=16) s=12345678 -> 0x12345678
[PyLong_FromUnicodeObject] int(s, base=16) s=12345678 -> 0x12345678
[PyNumber_Xor] 0x12345678 ^ 0x9e3779b9 = 0x8c032fc1
[PyNumber_And] 0x123456780 & 0xfa3affff = 0x22006780
[PyNumber_Add] 0x22006780 + 0x00000001 = 0x22006781
[PyNumber_Power] pow(0x00118065, 0x00010001, None) = num
[PyNumber_Remainder] num % 0xfffffffd = 0x8c219e43
[CompareObj] 0x22006781 == 0x12287f38 -> False
[PyLong_FromString] int(s, base=16) s=87654321 -> 0x87654321
[PyLong_FromUnicodeObject] int(s, base=16) s=87654321 -> 0x87654321
[PyNumber_Xor] 0x87654321 ^ 0x9e3779b9 = 0x19523a98
[PyNumber_And] 0x876543210 & 0xfa3affff = 0x72103210
[PyNumber_Add] 0x72103210 + 0x00000008 = 0x72103218
[PyNumber_Power] pow(0x00032a47, 0x00010001, None) = num
[PyNumber_Remainder] num % 0xfffffffd = 0x045ca420
[CompareObj] 0x72103218 == 0x4a30f74d -> False
[PyLong_FromString] int(s, base=16) s=12345678 -> 0x12345678
[PyLong_FromUnicodeObject] int(s, base=16) s=12345678 -> 0x12345678
[PyNumber_Xor] 0x12345678 ^ 0x9e3779b9 = 0x8c032fc1
[PyNumber_And] 0x123456780 & 0xfa3affff = 0x22006780
[PyNumber_Add] 0x22006780 + 0x00000001 = 0x22006781
[PyNumber_Power] pow(0x00118065, 0x00010001, None) = num
[PyNumber_Remainder] num % 0xfffffffd = 0x8c219e43
[CompareObj] 0x22006781 == 0x023a1268 -> False
[PyLong_FromString] int(s, base=16) s=87654321 -> 0x87654321
[PyLong_FromUnicodeObject] int(s, base=16) s=87654321 -> 0x87654321
[PyNumber_Xor] 0x87654321 ^ 0x9e3779b9 = 0x19523a98
[PyNumber_And] 0x876543210 & 0xfa3affff = 0x72103210
[PyNumber_Add] 0x72103210 + 0x00000008 = 0x72103218
[PyNumber_Power] pow(0x00032a47, 0x00010001, None) = num
[PyNumber_Remainder] num % 0xfffffffd = 0x045ca420
[CompareObj] 0x72103218 == 0x88108807 -> False
[CompareObj] 0x00003558 == 0x00003558 -> True
明显的4部分,这不爽多了嘛hhhhhhhhhhhh
调出来一部分看:
[PyLong_FromString] int(s, base=16) s=12345678 -> 0x12345678
[PyLong_FromUnicodeObject] int(s, base=16) s=12345678 -> 0x12345678
[PyNumber_Xor] 0x12345678 ^ 0x9e3779b9 = 0x8c032fc1
[PyNumber_And] 0x123456780 & 0xfa3affff = 0x22006780
[PyNumber_Add] 0x22006780 + 0x00000001 = 0x22006781
[PyNumber_Power] pow(0x00118065, 0x00010001, None) = num
[PyNumber_Remainder] num % 0xfffffffd = 0x8c219e43
[CompareObj] 0x22006781 == 0x12287f38 -> False
复现的:
input^0x9e3779b9
a1 = (input0 & 0xfa3affff) + 0x00000001
a2 = pow(x,0x00118065,) % 0xfffffffd
真实的:
第一个返回值:((int(n,16)^2654435769)>>11)**65537%4294967293
第二个返回值:((int(n,16)>5)>>23)
就差几个移位运算,一动态就出,好的,爽!
总结
嗯,除了几个移位或者取值操作捕捉不到,其他的,简直是降维
想tea等常规加密,直接就能看出来,用来trace一下,针不戳
不过还是得动调,不能直接丢弃ida,哎
哎[/md]

操作, 函数

您需要登录后才可以回帖 登录 | 立即注册