当前位置:首页>python>这个霸总会Python(13/14)- “`dlopen` 二进制深渊”

这个霸总会Python(13/14)- “`dlopen` 二进制深渊”

  • 2026-06-30 13:40:04
这个霸总会Python(13/14)- “`dlopen` 二进制深渊”

机房门被推开的时候,陈默的手已经按在了电源线上。

不是风。

是一个人的轮廓,站在应急灯的光锥边缘。头发散乱,外套上沾着雨渍,手里握着一个黑色的 U 盘。

程思语。

她没出声,径直走进来,把 U 盘放在 ThinkPad 旁边。然后坐在机架底座上,脱掉湿透了的外套。

「你回来了。」陈默说。这三个字比他预想的轻。

「快了半截。」她的声音哑得厉害,「他们比我想的快。老宅外面停了两辆没牌照的车——我爸的人已经到了合肥。」

「证书呢?」

程思语没回答。她把 U 盘插进 ThinkPad。终端自动挂载,显示一个文件:

```

shenma_codesign.p12

```

「拿到了。」她说,「但我妈书房的窗户碎了。他们知道我进过老宅。」

陈默盯着那个文件。代码签名证书。有了它,就可以重新编译神码 AI 的 C 扩展,植入真正的 `raise` 逻辑,替换那些被编译进二进制层的 `except: pass`。

「他们什么时候到科学岛?」

「如果从老宅直接过来——」程思语看了一眼手机,「四十分钟。如果他们在等指令——时间未知。」

陈默把 U 盘里的证书复制到本地。然后打开终端。

「四十分钟。够写一个 C 扩展修复器。」

「你之前说 Python 碰不到 C 扩展内部。」

「那就不从 Python 碰。」

陈默敲下了第一行代码——不是 Python,是终端命令:

```bash

objdump -d /lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so \

  | grep -A 20 'PyErr_SetString'

```

输出像瀑布一样滚过屏幕。几十个 `PyErr_SetString` 调用点散布在反汇编代码中——这些是 C 扩展里真正的错误处理路径。但陈默要找的不是这些。他找的是那些**没有** `PyErr_SetString` 的 try-catch 块。

「C 扩展的异常处理是靠 `PyErr_SetString` 和 `PyErr_SetObject` 传播到 Python 层的。」他说,「如果程泰来在 C 层面把 `PyErr_SetString` 调用删了——」

「异常就不会传播。C 层的错误被吞了,Python 层永远不知道。」程思语接上。

「对。而且 C 扩展里的 `except: pass` 不是 Python 字节码——它是 C 语言的 `setjmp` / `longjmp` 结构,或者是 C++ 的异常处理表。」

陈默打开 `readelf`:

```bash

readelf -S /lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so \

  | grep -E '(eh_frame|gcc_except_table|ARM.extab)'

```

输出:

```

  .eh_frame_hdr     PROGBITS    0000000000002a40  00002a40

  .eh_frame         PROGBITS    0000000000002b00  00002b00

```

「有异常处理表。」陈默说,「说明编译的时候带了异常处理支持。但这些表——可能已经被篡改了。」

程思语凑过来:「你能读异常处理表?」

「不能直接读。但可以用 `ctypes` 加载 `.so`,然后从二进制层面分析它的异常处理覆盖率。」

陈默新建了一个 Python 文件——`binary_raiser.py`。他敲下的速度比之前任何一章都快:

```python

import ctypes

import sys

# 用 ctypes.CDLL 加载 C 扩展,绕过 Python 的 import 链

lib = ctypes.CDLL('/lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so')

# 通过 dlsym 获取 C 扩展的 PyInit 函数指针

# 标准的 C 扩展入口点

init_func_name = b'PyInit__settle_core'

try:

    init_ptr = ctypes.pythonapi.PyCapsule_Import(

        ctypes.c_char_p(init_func_name), 0

    )

except Exception as e:

    print(f"PyCapsule_Import 失败: {e}")

    # 回退:手动调用 dlsym

    libc = ctypes.CDLL(None)  # 加载全局符号

    dlsym = libc.dlsym

    dlsym.restype = ctypes.c_void_p

    init_ptr = dlsym(

        lib._handle,

        init_func_name

    )

    print(f"dlsym 获取 PyInit 指针: {hex(init_ptr or 0)}")

```

程思语看着代码皱眉:「你用 `ctypes.CDLL` 加载 `.so`,然后从 `dlsym` 获取 `PyInit` 函数——你这是从 Python 里手动调用 C 扩展的入口?」

「对。不走 `import` 链,不走 Python 的模块系统。直接操作共享库。」

陈默继续敲。下一段代码是核心——他要从二进制层面分析这个 C 扩展的所有导出函数,检查每个函数里是否有合法的异常传播路径:

```python

# 遍历 C 扩展的符号表,找到所有 Python 可调用的函数

# 用 ctypes 获取函数指针后,通过内存分析检查其行为

# 方法:查找 .dynsym 段中的函数符号

import struct

def read_elf_symbols(so_path):

    """用纯 Python + ctypes 解析 ELF 符号表"""

    with open(so_path, 'rb') as f:

        elf = f.read()

    # ELF header

    if elf[:4] != b'\x7fELF':

        raise ValueError("不是 ELF 文件")

    # 64-bit ELF

    e_shoff = struct.unpack_from('<Q', elf, 0x28)[0]  # Section header offset

    e_shentsize = struct.unpack_from('<H', elf, 0x3A)[0]

    e_shnum = struct.unpack_from('<H', elf, 0x3C)[0]

    # 遍历 section headers 找 .dynsym

    for i in range(e_shnum):

        sh_off = e_shoff + i * e_shentsize

        sh_name_idx = struct.unpack_from('<I', elf, sh_off)[0]

        sh_type = struct.unpack_from('<I', elf, sh_off + 4)[0]

        sh_addr = struct.unpack_from('<Q', elf, sh_off + 0x10)[0]

        sh_offset = struct.unpack_from('<Q', elf, sh_off + 0x18)[0]

        sh_size = struct.unpack_from('<Q', elf, sh_off + 0x20)[0]

        if sh_type == 11:  # SHT_DYNSYM

            return sh_offset, sh_size, sh_addr

    return None, None, None

sym_offset, sym_size, sym_addr = read_elf_symbols(

    '/lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so'

)

print(f"动态符号表偏移: 0x{sym_offset:x}, 大小: {sym_size}")

```

「你在手写 ELF 解析器?」程思语的声音里带着难以置信。

「不需要 libelf。Python 的 `struct` 读 ELF 足够了。我们现在在二进制的层面——标准库的 `import` 是公路,`ctypes.CDLL` 是越野车,而 `struct.unpack` 读 ELF 是趴在泥里用手挖。」

他敲完这段,终端输出:

```

动态符号表偏移: 0x1a800, 大小: 0x3000

```

「找到了。现在遍历符号,标记所有导出函数。」陈默继续:

```python

ENTRY_SIZE = 0x18  # 64-bit ELF 符号表条目大小

with open('/lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so', 'rb') as f:

    f.seek(sym_offset)

    symbols = []

    for _ in range(sym_size // ENTRY_SIZE):

        raw = f.read(ENTRY_SIZE)

        st_name = struct.unpack_from('<I', raw, 0)[0]

        st_info = raw[4]

        st_other = raw[5]

        st_shndx = struct.unpack_from('<H', raw, 6)[0]

        st_value = struct.unpack_from('<Q', raw, 8)[0]

        st_size = struct.unpack_from('<Q', raw, 16)[0]

        # STT_FUNC (2) 且 st_value > 0 (有地址)

        if st_info & 0xf == 2 and st_value > 0:

            symbols.append({

                'addr': st_value,

                'size': st_size,

                'name_offset': st_name,

            })

print(f"找到 {len(symbols)} 个函数符号")

```

终端显示:

```

找到 87 个函数符号

```

「八十七个 C 函数。」陈默说,「其中每一个都可能包含 `except: pass` 的二进制等价物——C 语言的 setjmp 异常处理。」

程思语指着一行:「你能从二进制分析里面有没有异常处理?」

「不能精确分析。」陈默说,「但可以做一个启发式检测——检查函数的汇编代码里有没有 `PyErr_SetString` 或 `PyErr_SetObject` 调用。如果一个函数处理了错误条件但没有调用任何 `PyErr_*` 函数——它就是可疑的。」

他敲下新代码——这次结合了 `objdump` 的输出和 `ctypes`:

```python

import subprocess

def check_pyerr_in_function(so_path, func_addr, func_size):

    """用 objdump 反汇编函数范围,检查 PyErr 调用"""

    result = subprocess.run(

        ['objdump', '-d', '--start-address=' + hex(func_addr),

         '--stop-address=' + hex(func_addr + func_size), so_path],

        capture_output=True, text=True

    )

    disassembly = result.stdout

    # 检查 PyErr 调用

    has_pyerr = 'PyErr_SetString' in disassembly or \

                'PyErr_SetObject' in disassembly or \

                'PyErr_Format' in disassembly

    return has_pyerr, disassembly

# 批量扫描所有大函数 (>100 字节的可能性是真正的逻辑函数)

suspicious = []

for sym in symbols:

    if sym['size'] > 100:

        has_pyerr, _ = check_pyerr_in_function(

            '/lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so',

            sym['addr'], sym['size']

        )

        if not has_pyerr:

            suspicious.append(sym)

print(f"可疑函数(无 PyErr 调用): {len(suspicious)}")

```

终端输出:

```

可疑函数(无 PyErr 调用):6

```

程思语的声音绷紧了:「六个函数完全没有错误传播?」

「可能只是不产生错误——也可能是程泰来把 `PyErr_SetString` 调用全部删了,只剩下返回 NULL。」

陈默从 U 盘里提取出原始的、未署名的神码 AI C 扩展源码——壳公司内网的备份,在程泰来下毒之前的版本:

```bash

diff <(objdump -d original_settle.o) <(objdump -d _settle_core.so)

```

输出几乎占满了整个终端。陈默目光扫过第一屏——然后停在了一行上:

```

-        call    PyErr_SetString

+        nop

+        nop

+        nop

+        nop

+        nop

```

五条 NOP 指令,覆盖了一个原本的 `PyErr_SetString` 调用。

「证据。」程思语的声音冷冷的,「不是没写异常处理——是写好了,然后在编译之后用二进制补丁把调用抹成了 NOP。」

「他改的不是源码。是编译好的 `.so`。」陈默说,「在 ELF 层面,直接写二进制补丁。」

程思语沉默了几秒。

「那如果你能把这些 NOP 修复回来——」

「把那五个 NOP 恢复成 `call PyErr_SetString`。」

陈默的手指悬在键盘上方。他在想一个问题:怎么在不知道原始调用地址的情况下,重建 `call` 指令?

`call` 指令在 x86-64 上是 `E8 <rel32>`——五字节。相对偏移需要知道目标地址。

「我恢复不了原来的 `call`。」陈默说,「因为我不知道 `PyErr_SetString` 在这个 `.so` 文件里的 PLT 偏移。」

「但你可以做别的——」

「对。我可以在这个位置插入一个 `ud2` 指令,让程序在到达这段路径时立刻崩溃。有 crash 就有 traceback——总比静默失败强。」

陈默的手指飞起来:

```python

import mmap

import os

def patch_nop_to_ud2(so_path, nop_offset, nop_count):

    """将 NOP (0x90) 序列替换为 UD2 (0x0F 0x0B) 触发显式崩溃"""

    with open(so_path, 'r+b') as f:

        with mmap.mmap(f.fileno(), 0) as mm:

            # 定位到 NOP 序列

            nop_bytes = mm[nop_offset:nop_offset + nop_count]

            if nop_bytes != b'\x90' * nop_count:

                print(f"偏移 0x{nop_offset:x} 不是纯 NOP 序列")

                return False

            # UD2 指令:0F 0B

            ud2 = b'\x0f\x0b'

            # 剩余字节用 INT3 (0xCC) 填充

            patch = ud2 + b'\xcc' * (nop_count - 2)

            mm[nop_offset:nop_offset + nop_count] = patch

            print(f"已修补 0x{nop_offset:x}: NOP→UD2+INT3")

            return True

# 从 objdump 找出所有被替换的 PyErr 位置

result = subprocess.run(

    ['objdump', '-d', '/lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so'],

    capture_output=True, text=True

)

for line in result.stdout.split('\n'):

    if 'nop' in line and 'call' in result.stdout.split(line)[0][-100:]:

        # 提取地址

        pass  # 简化:实际需要更精细的解析

```

「太慢了。」陈默删掉那段,换了一个更直接的方法——在运行时用 `ctypes` 注入一个监控层:

```python

import ctypes.util

# 获取 libpthread 中的 __libc_dlopen_mode

# Python 的 ctypes.CDLL 底层就是调用 dlopen

# 我们劫持它

_libc = ctypes.CDLL(None)

_real_dlopen = _libc.dlopen

_real_dlopen.restype = ctypes.c_void_p

_real_dlopen.argtypes = [ctypes.c_char_p, ctypes.c_int]

@ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int)

def _hooked_dlopen(filename, flags):

    """劫持 dlopen:在加载共享库之前做安全检查"""

    if filename and b'shenma' in filename:

        print(f"[RaiseGuard] dlopen 拦截: {filename.decode()}")

    # 调用原始的 dlopen

    handle = _real_dlopen(filename, flags)

    if handle and filename and b'shenma' in filename:

        print(f"[RaiseGuard] 已加载: {filename.decode()}")

        # TODO: 在此处注入异常处理修补逻辑

    return handle

# 将 GLIBC 的符号表里的 dlopen 替换为我们的钩子

# 注意:ctypes 默认使用 RTLD_LOCAL,hook 需要 LD_PRELOAD 级别

```

陈默敲到 `TODO` 那里停了。他盯着那行注释,手指停在键盘上方。

程思语看出来了:「有困难?」

「Python 的 `ctypes.CDLL` 使用 `dlopen` 的默认行为——RTLD_LOCAL。就算我 hook 了 `_libc.dlopen`,Python 内部加载 C 扩展用的是自己的 `_Py_dlopen` 封装,走的是不同的调用链。」

「那条调用链——」

「在 CPython 的 `Modules/_ctypes/_ctypes.c` 里。它不调用 `dlopen` 本身——它调用 `PyCapsule_Import`,然后走的内部 `_ctypes` 扩展的加载路径。」

陈默打开了 CPython 的源码——从本地检出的一份 3.11 源码里找到了关键行:

```python

# Python/importdl.c

dl_funcptr _PyImport_FindSharedFuncptr(

    const char *prefix,

    const char *shortname,

    const char *pathname,

    FILE *fp)

{

    // ...

    handle = _Py_dlopen(pathname, dlopenflags);

    // ...

}

```

「`_Py_dlopen`。」程思语念出声,「不是一个标准 C 库函数——是 CPython 内部对 `dlopen` 的封装。」

「对。它在 `Python/dynload_shlib.c` 里定义。即使我 hook 了 C 库的 `dlopen`,CPython 内部用的是自己的符号。」

程思语沉默了几秒。然后她说:「那你怎么办?」

陈默没有回答。他打开了一个新的 Python 文件——文件名叫做 `shadow_loader.py`:

```python

import sys

import ctypes

# 核心思路:不劫持 dlopen,而是劫持 Python 内部用于

# 加载 C 扩展的 _bootstrap_external 模块

# 在 Python 3.11 中,ExtensionFileLoader.create_module

# 最终调用 _imp.create_dynamic,这是一个内置函数

#

# 但我们有一个更底层的路径:

# 通过 ctypes 直接访问 CPython 的 C API

pythonapi = ctypes.pythonapi

# PyImport_AppendInittab — 在解释器初始化前注册内置模块

# 但我们不能重启解释器...

#

# 另一个路径:sys.setdlopenflags

# 控制 dlopen 的 flag,但没有我们需要的拦截能力

# 真正的方案:

# 利用 LD_PRELOAD + ctypes 构建一个透明的 C 扩展监控层

# 1. 生成一个小的 C 语言共享库,使用 LD_PRELOAD

# 2. 这个库劫持 _Py_dlopen

# 3. 在加载 C 扩展后,扫描其异常处理表

```

陈默停下手指。

「我需要写一个 C 文件。」他说。

程思语看着他:「你会 C?」

「不会写好看的 C。但会写够用的 C。」

他打开一个终端窗口,敲下:

```c

/* shadow_dlopen.c — LD_PRELOAD 劫持 CPython 的 _Py_dlopen */

#define _GNU_SOURCE

#include <dlfcn.h>

#include <string.h>

#include <stdio.h>

/* 原始的 dlopen 函数指针 */

static void* (*real_dlopen)(const char*, int) = NULL;

/* 在库被加载后的回调 */

void _shadow_on_load(const char* path, void* handle) {

    fprintf(stderr, "[ShadowDL] 加载: %s\n", path);

    /* TODO: 扫描异常处理表,修补 NOP */

}

/* 劫持 dlopen */

void* dlopen(const char* filename, int flags) {

    if (!real_dlopen) {

        real_dlopen = dlsym(RTLD_NEXT, "dlopen");

    }

    void* handle = real_dlopen(filename, flags);

    if (handle && filename && strstr(filename, "shenma")) {

        _shadow_on_load(filename, handle);

    }

    return handle;

}

```

「LD_PRELOAD 劫持 `dlopen`。」程思语说,「这个钩子在 C 层面,在 CPython 调用 `_Py_dlopen` 之前就生效——因为 `_Py_dlopen` 最终也调 `dlopen`。」

「对。Python 层面的 import hook 拦不住 C 扩展的加载。但 C 层面的 LD_PRELOAD 可以——因为它是最底层的函数替换。」

陈默编译了 shadow_dlopen.c:

```bash

gcc -shared -fPIC -o shadow_dlopen.so shadow_dlopen.c -ldl

```

然后他写了一段 Python 测试——在同一个进程里利用 `ctypes` 注入 LD_PRELOAD 级别的钩子:

```python

# 利用 ctypes 加载 shadow_dlopen.so

# 这会替换进程内所有的 dlopen 调用

import ctypes

# 使用 RTLD_GLOBAL 使 shadow 中的符号全局可见

shadow = ctypes.CDLL('./shadow_dlopen.so', mode=ctypes.RTLD_GLOBAL)

# 现在任何 dlopen 调用 — 包括 CPython 内部的 — 

# 都会先经过我们的 shadow

# 测试:用 import 加载一个 C 扩展

import importlib

spec = importlib.util.find_spec('_settle_core')

print(f"规范: {spec}")

```

终端输出:

```

[ShadowDL] 加载: /lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so

```

「它工作了。」程思语说。

「它工作了。」陈默重复。他的声音里没有兴奋。只有确认——就像验证了一个数学定理。

但他下一步没有继续写 C 扩展的修补逻辑。他停下来,看了一眼屏幕右上角的电量:61%。

然后他做了一个决定。

「这个 LD_PRELOAD + ctypes 的路径——可以做更深的事。」他打开了一个新的终端,「不只是检测。不只是修补 NOP。而是——」

「——重新编译。」程思语替他说完。

陈默摇头:「没有完整的源码,重新编译不可能。但 `dlopen` 之后,在 `PyInit_` 函数执行之前——我可以在内存里修补 `.so` 的 `.text` 段。」

「运行时二进制修补?」

「运行时。内存中。用 `mmap` + `ctypes` 写可执行内存。」

陈默写了一段危险的代码——直接修改已加载共享库的内存:

```python

import ctypes

import sys

# 获取 _settle_core 模块对象

settle = sys.modules.get('_settle_core')

if not settle:

    print("模块未加载")

    sys.exit(1)

# 通过 ctypes 获取模块的 __spec__.origin 对应的 .so 文件

# 然后找到它在内存中的加载基址

# 方法:从 /proc/self/maps 读取

with open('/proc/self/maps', 'r') as f:

    for line in f:

        if '_settle_core' in line and 'r-xp' in line:

            # 可执行段

            parts = line.split()

            addr_range = parts[0]

            start, end = [int(x, 16) for x in addr_range.split('-')]

            size = end - start

            print(f"代码段: 0x{start:x} - 0x{end:x} ({size} 字节)")

            # 使用 ctypes 内存操作 + mmap 修改保护

            libc = ctypes.CDLL(None)

            mprotect = libc.mprotect

            mprotect.restype = ctypes.c_int

            mprotect.argtypes = [

                ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int

            ]

            PROT_READ = 0x1

            PROT_WRITE = 0x2

            PROT_EXEC = 0x4

            # 将代码段改为可写

            page_start = start & ~(0x1000 - 1)  # 页对齐

            ret = mprotect(

                ctypes.c_void_p(page_start),

                size + (start - page_start),

                PROT_READ | PROT_WRITE | PROT_EXEC

            )

            print(f"mprotect: {ret}")

            if ret == 0:

                # 成功!现在可以写内存了

                # 定位到 NOP 序列的位置

                nop_offset = start + 0x2a3f  # 从 objdump 得到的偏移

                buf = (ctypes.c_uint8 * 5).from_address(nop_offset)

                old = bytes(buf)

                print(f"原始字节: {old.hex()}")

                # 写入 UD2 (0F 0B) + INT3 (CC CC CC)

                buf[0] = 0x0F

                buf[1] = 0x0B  # UD2

                buf[2] = 0xCC  # INT3

                buf[3] = 0xCC  # INT3

                buf[4] = 0xCC  # INT3

                new = bytes(buf)

                print(f"修补后字节: {new.hex()}")

                # 恢复保护

                mprotect(

                    ctypes.c_void_p(page_start),

                    size + (start - page_start),

                    PROT_READ | PROT_EXEC

                )

```

程思语看着这段代码,脸上没有表情。但她放在膝盖上的手指微微颤了一下。

「你在做的事——」她说,「是从 Python 里,通过 `/proc/self/maps` 找到 C 扩展的内存地址,然后调用 `mprotect` 把代码段改成可写,然后用 `ctypes` 直接往内存里写机器码。」

「是。」

「这已经超出 Python 的范畴了。这是在用 Python 写二进制注入工具。」

「Python 是胶水语言。」陈默说,「它的胶水能粘到机器码层面。」

终端显示:

```

代码段: 0x7f2a3c000000 - 0x7f2a3c004000 (16384 字节)

mprotect: 0

原始字节: 9090909090

修补后字节: 0f0bcccccc

```

五个 NOP,变成了一个 UD2 加三个 INT3。如果任何代码路径执行到这个位置——程序会立刻崩溃,操作系统会生成 core dump 和 traceback。

静默的 `except: pass`,变成了世界看得见的 crash。

「一个。」陈默说。

「还有十二个。」程思语说。

陈默没有停下来。他写了一个循环——先反汇编出 `.so` 里所有被替换的 `PyErr_SetString` 位置,然后全部修补成 UD2:

```python

def patch_all_nop_sites(so_path):

    """找到并修补所有疑似被 NOP 覆盖的 PyErr 调用点"""

    result = subprocess.run(

        ['objdump', '-d', so_path],

        capture_output=True, text=True

    )

    patches = []

    lines = result.stdout.split('\n')

    for i, line in enumerate(lines):

        # 查找连续的 NOP 指令序列 (至少 3 条)

        if 'nop' in line:

            # 确认这是否出现在一个可能调用 PyErr 的位置

            # 特征:前面有 test/jne 或 cmp/jne 的分支

            context = '\n'.join(lines[max(0, i-5):i])

            if any(x in context for x in ['test', 'cmp', 'jne', 'je', 'jz', 'jnz']):

                addr_str = line.split(':')[0].strip()

                try:

                    addr = int(addr_str, 16)

                    patches.append(addr)

                except ValueError:

                    continue

    # 去重:只保留每组 NOP 序列的第一个地址

    # (简化版本)

    return patches

nop_sites = patch_all_nop_sites(

    '/lib/shenma/core/_settle_core.cpython-311-x86_64-linux-gnu.so'

)

print(f"找到 {len(nop_sites)} 个可疑 NOP 位置")

```

终端输出:

```

找到 5 个可疑 NOP 位置

```

「五个。」程思语说。

「五个 NOP 补丁点,覆盖一个 C 扩展。」陈默说,「还有二十三个 C 扩展要处理。」

他打开了下一个 `.so` 文件——`_image_processor.cpython-311-x86_64-linux-gnu.so`。医疗影像处理的 C 扩展。就是那个在程思语的报告中,让 CT 报告标注被静默吞掉的元凶。

陈默的手指停了。

不是因为代码。

是因为他听到了什么。

远处,科学岛主楼的铁门——又响了。这一次不是猫。是金属撞击水泥的声音。然后是说话声——模糊的、隔着几堵墙的人声。

程思语站起来,把应急灯关掉。

机房陷入彻底的黑暗,只有 ThinkPad 的屏幕亮着,在两个人的脸上投下冷白色的光。

「来不及一个一个修了。」陈默的声音在黑暗中很低,「十三个不可达的 C 路径,二十三个 C 扩展。一个一个写 UD2——时间不够。」

「那你打算怎么办?」

陈默打开了一个新文件——文件名:`injector.py`。

「不一个一个修。写一个通用的——在 `dlopen` 之后、`PyInit` 之前,自动扫描代码段里的 NOP 序列并替换为 UD2。」

「你要写一个二进制的运行时自动修复器?」

陈默已经开始敲了:

```python

def auto_patch_c_extension(handle, so_path):

    """在 C 扩展初始化前,自动查找并修补 NOP 异常处理"""

    # 通过 dlpi_addr 获取基址

    from ctypes import pythonapi, c_void_p, c_char_p, c_int, POINTER

    # 使用 dl_iterate_phdr 遍历共享库

    # 或者从 /proc/self/maps 里找

    with open('/proc/self/maps', 'r') as f:

        for line in f:

            if so_path.split('/')[-1] in line and 'r-xp' in line:

                parts = line.split()

                start_str = parts[0].split('-')[0]

                end_str = parts[0].split('-')[1]

                start = int(start_str, 16)

                end = int(end_str, 16)

                # 在这个段里搜索 5 个连续的 NOP (0x90)

                # 这大概率是被替换的 PyErr_SetString 调用

                with open('/proc/self/mem', 'rb') as mem:

                    mem.seek(start)

                    code = mem.read(end - start)

                nop_seq = b'\x90' * 5

                pos = 0

                while True:

                    pos = code.find(nop_seq, pos)

                    if pos == -1:

                        break

                    addr = start + pos

                    # 检查上下文:前面是否有条件分支

                    ctx_start = max(0, pos - 16)

                    ctx = code[ctx_start:pos]

                    if any(b in ctx for b in [b'\x0f\x84', b'\x0f\x85', b'\x75', b'\x74', b'\x77']):

                        # 有控制流,可能是真正的补丁点

                        self._patch_ud2(addr)

                        print(f"[AutoPatch] UD2 @ 0x{addr:x}")

                    pos += 5

```

陈默还没有写完。他的眼睛停留在 `self._patch_ud2` 那一行,忽然想到一个更根本的问题。

「如果我们把所有的 `except: pass` 都在运行时触发 crash——」

「——系统会批量崩溃。」程思语接上,「生产环境承受不了。」

「对。UD2 是最好的调试工具,但最差的部署方案。」

陈默删掉了 `auto_patch_c_extension` 的整个函数体。重新开始。

这一次,他写的不再是修补。而是——重定向。

```python

# 最终方案:不修 C 扩展,也不让它崩溃

# 而是通过 LD_PRELOAD 劫持 dlopen,

# 在加载完成后、PyInit 执行前,

# 对每个 C 扩展注册一个 Python 层的异常监控

# 思路:

# 1. LD_PRELOAD 注入 shadow_dlopen.so

# 2. dlopen 完成后,在 PyInit 前,调用一个 Python 回调

# 3. Python 回调为这个 C 扩展创建对应的异常监控包装器

# 4. 所有通过 ctypes 调用 C 扩展的代码,都被自动包装 try-catch

```

程思语的声音在黑暗中响起:「你在做的——是为每个 C 函数生成一个 Python 层面的异常安全包装?」

「对。C 扩展内部的 `except: pass` 我改不了。但任何从 Python 调用 C 扩展的路径——如果 C 函数返回了错误码却没有设置异常——Python 包装器会检测到,然后主动 `raise`。」

陈默敲完最后一段代码。这是他整个二进制深潜的终点——从 `dis` 发现字节码投毒,到 `ast` 重建可信编译,到 `types.CodeType` 手写字节码,到 `sys.meta_path` 自动拦截,再到今天——`ctypes` + `dlopen` + `LD_PRELOAD` + 运行时内存修补:

```python

# 终极异常安全包装器

class CSafeWrapper:

    """为 C 扩展函数提供 Python 层的异常传播保障"""

    def __init__(self, c_func, func_name, error_indicator):

        self._c_func = c_func

        self._name = func_name

        # error_indicator: C 函数的错误返回值(如 NULL、-1)

        self._error_value = error_indicator

        self._signature = None

    def __call__(self, *args):

        result = self._c_func(*args)

        # 检查返回值是否为错误指示

        if result == self._error_value:

            # 检查 Python 的错误标志

            if not ctypes.pythonapi.PyErr_Occurred():

                # C 函数返回了错误值但没有设置异常

                # — 这是静默吞异常的特征

                raise RuntimeError(

                    f"[RaiseGuard] C 扩展 '{self._name}' "

                    f"返回了错误码但未设置 Python 异常。"

                    f"这可能是一个被沉默的异常路径。"

                )

        return result

```

「完成了。」陈默说。

「完成了什么?」

「一个从 Python 到 C 的完整防御链。」陈默在黑暗中数着:

「第一层:`sys.meta_path` 的 `RaiseImporter`——拦截 `.py` 文件的加载。」

「第二层:`types.CodeType` 的 `RaiseGuard`——修复 Python 函数的字节码。」

「第三层:`ctypes` + `dlopen` 的 `shadow_dlopen`——监控 C 扩展的加载。」

「第四层:`CSafeWrapper`——为 C 函数提供 Python 层的异常检测。」

「四层。覆盖 Python 到 C 的每一条路径。」

程思语没有说话。黑暗中,她伸手碰了碰 ThinkPad 的屏幕边缘。

「99.7% 变成了多少?」

陈默运行了最终验证:

```python

# 最终验证:测试被修补的 C 扩展

# 场景:调用一个内部吞了异常的 C 函数

settle = importlib.import_module('_settle_core')

# 替换 C 函数为安全包装

original_func = settle.critical_process

settle.critical_process = CSafeWrapper(

    original_func,

    'critical_process',

    -1  # C 函数用 -1 表示错误

)

# 触发出错路径

try:

    settle.critical_process(bad_input_data)

    print("❌ 异常被静默吞没")

except RuntimeError as e:

    print(f"✓ 异常被检测并传播: {e}")

except Exception as e:

    print(f"✓ 异常已传播: {type(e).__name__}")

```

终端输出:

```

✓ 异常被检测并传播: [RaiseGuard] C 扩展 'critical_process' 返回了错误码但未设置 Python 异常。这可能是一个被沉默的异常路径。

```

一行输出。但这一行意味着,从 Python 源码到 C 扩展二进制,再没有任何一条异常路径能够沉默。

程思语把手机按亮。时间:06:13。外面,科学岛的晨光透过机房的通风口,在地面上投下一条细长的光带。

「天亮了。」她说。

陈默站起来。他的膝盖因为久坐而发僵,但眼睛亮着——不是兴奋的光,是那种连续作战到极限之后仍然没有熄灭的东西。

「还有多久?」

「不确定。」程思语说,「但科学岛的大门——我刚才进来的时候,看到两辆黑色轿车停在环湖路上。」

陈默没有说话。他把 `shadow_dlopen.so`、`injector.py`、以及完整的 `RaiseGuard` 链打包成一个文件——`raiseguard_complete.so`。

「现在呢?」程思语问。

陈默看着屏幕上打包完成的输出,然后看向通风口那条越来越亮的晨光。

「现在——」他说,「去找你爸。」

程思语怔住了。

「不是用枪。不是用法律。是用代码。」陈默把笔记本合上,「他花三十年建了一座用 NOP 和 `except: pass` 堆起来的大厦。现在我用一个 `dlopen` 钩子,找到了他的地基漏洞。」

他站起来,把笔记本夹在腋下。

「他有两个选择——坐下来谈怎么修,或者——」

「或者什么?」

「或者我在所有人面前,运行这个 `raiseguard`。然后全世界都会看到神码 AI 在金融、医疗、物流——每一个系统里——有多少异常被系统性沉默了。」

程思语看着陈默。晨光已经照亮了她的脸。

「你说的'去找你爸'——」她说,「不是去打架。」

「是去 `import` 一个 `__future__`。」

陈默朝门口走去。程思语站在原地,看着他的背影在光里拉长。

然后她跟了上去。

他们走出机房,走进科学岛主楼的大厅。晨光透过积满灰尘的玻璃窗,在地上投下菱形的光斑。

大厅门口,两辆黑色轿车的引擎声在几十米外停住了。

车门打开的声音。

脚步声。

陈默停下脚步。程思语站在他旁边。

他没有回头去看那个站在晨光里的人。他只是在心里把最后一层防御链过了一遍——`dis`、`ast`、`types.CodeType`、`sys.meta_path`、`ctypes`、`dlopen`、`LD_PRELOAD`。

每一层都确认完毕。

「来人了。」程思语低声说。

「我知道。」

陈默把 ThinkPad 的电源线在手腕上绕了一圈。屏幕还亮着,光标停在终端最后一行的末尾:

```

[RaiseGuard] All layers active. 100% coverage.

```

百分之百。

再也没有沉默的异常。

脚步声越来越近。陈默望着大厅门口的光,等待第一个走进光里的人影。

他没有等太久。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 04:00:27 HTTP/2.0 GET : https://f.mffb.com.cn/a/497040.html
  2. 运行时间 : 0.316326s [ 吞吐率:3.16req/s ] 内存消耗:4,647.40kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=02cccaaf8b93aa0df77c7d86012a358f
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001192s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.002015s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.037001s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000820s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001543s ]
  6. SELECT * FROM `set` [ RunTime:0.011781s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.002038s ]
  8. SELECT * FROM `article` WHERE `id` = 497040 LIMIT 1 [ RunTime:0.004890s ]
  9. UPDATE `article` SET `lasttime` = 1783022427 WHERE `id` = 497040 [ RunTime:0.026781s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.002466s ]
  11. SELECT * FROM `article` WHERE `id` < 497040 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.025214s ]
  12. SELECT * FROM `article` WHERE `id` > 497040 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.019067s ]
  13. SELECT * FROM `article` WHERE `id` < 497040 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001756s ]
  14. SELECT * FROM `article` WHERE `id` < 497040 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.007064s ]
  15. SELECT * FROM `article` WHERE `id` < 497040 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002108s ]
0.321256s