当前位置:首页>学习笔记>Unidbg学习笔记(五):第一次让 SO 跑起来

Unidbg学习笔记(五):第一次让 SO 跑起来

  • 2026-04-17 17:14:00
Unidbg学习笔记(五):第一次让 SO 跑起来

不追求一次跑通,而是建立"报错 → 定位 → 修复"的排错心智模型。


把期望调到正确的位置

前四篇都在讲"原理"。这一篇开始动手。但我必须先泼一盆冷水:你的第一次 Unidbg 调用几乎不可能跑通

这不是你的问题,这是 Unidbg 的"特性"。回想第二篇讲的世界观 — Unidbg 是一个"残缺的 Android 系统",它的 30% 实现 + 你来补 70% 才是它工作的常态。第一次跑必然会撞上"它没实现"的某个洞。

所以这一篇的目标不是"教你怎么一次跑通一个签名 SO",而是回答一个更重要的问题:当第一次必然失败的时候,你该怎么办?

具体一点:

  • 第一次失败时,你能不能立刻知道报错指的是哪一类问题
  • 看到一长串栈帧,你能不能迅速锁定哪一帧才是真正需要修的地方
  • 修一个洞之后又冒出三个新洞,你能不能保持节奏逐个收拾掉而不是一次性放弃?

这一套"报错 → 定位 → 修复 → 再次报错"的循环,是 Unidbg 工程师的日常。它不是一种本能,而是一套可以被刻意训练的工作流。这一篇就是带你把这套工作流走一遍。

打个比方:学 Unidbg 像学开手动挡的车。第一次踩离合 99% 会熄火,但教练不会因此让你直接开自动挡。熄火本身不是失败 — 重新点火,再来一次,才是学车的真正内容。


阶段 0:动手前的侦察

直接打开 IDE 写代码是最常见的新手错误。Unidbg 的工作量是"补环境" — 而补什么环境,取决于 SO 想要什么。所以在写第一行 Java 代码之前,你应该已经掌握以下情报:

Unidbg 排错心智模型: 一个迭代闭环

情报 1:Java 层调用是什么样

打开 JADX,反编译目标 APK。你需要找到三件东西:

1. Native 方法的声明

// 假设我们要分析 com.example.app.NativeBridge
publicclassNativeBridge{
static {
// 注意 SO 名字: libexample 或者 example
        System.loadLibrary("example");
    }

// Native 方法的完整声明
publicstaticnative String sign(byte[] data, boolean useV2);
}

从这里你能拿到:

  • SO 文件名example → 实际文件叫 libexample.so
  • 方法名sign
  • 参数类型byte[] 和 boolean
  • 返回类型String
  • 是否静态static 关键字 → 静态方法

这四点信息直接决定了你后面在 Unidbg 里怎么调用这个函数,缺一不可。

2. 调用方的代码

光知道方法签名还不够。你需要看真实代码里怎么传参给这个 Native 方法:

// HTTP 拦截器中的真实调用
String body = request.body().toString();
byte[] payload = (timestamp + ":" + body).getBytes(StandardCharsets.UTF_8);
String signature = NativeBridge.sign(payload, true);  // 注意第二个参数

这一步揭示了 SO 的实际使用语义:第一个参数不是"任意 byte[]",而是时间戳 + 冒号 + body 的 UTF-8 编码。如果你瞎传一个 byte 数组进去,跑出来的签名肯定对不上真机。

3. 关键的 JNI 上下文

很多 Native 方法的第一个参数是 Context 或者类似的对象 — 这意味着 SO 内部会通过 JNI 反查这个 Context 拿一些信息(包名、版本号、设备 ID)。如果你看到这种签名:

publicstaticnative String sign(Context ctx, byte[] data);

那你就要做好心理准备:这个 SO 大概率会调一堆 getPackageNamegetPackageInfogetSystemService 之类的 JNI 函数。这些都需要在你的 AbstractJni 子类里逐一应答 — 每一个都是一个潜在的报错点

情报 2:真机上跑出来的"标准答案"

写 Unidbg 之前,你必须先用 Frida 在真机上跑一遍目标函数,记录下入参和返回值。理由有二:

理由一:你需要一个对照基准

Unidbg 跑通之后,你怎么知道结果是对的?只有真机上同入参跑出来的结果做对照,才能验证。没有对照的"跑通"是假的跑通。

理由二:很多 SO 的入参格式是非平凡的

签名函数的入参可能不是表面上的"那个字段",而是一段经过 Java 层组装的、包含分隔符 / 时间戳 / 设备 ID 的复合数据。光看 Java 代码可能猜不准,Frida hook 一下立刻就清楚了。

一个最小化的 Frida 脚本模板:

// frida -U -l hook.js -f com.example.app
Java.perform(function () {
var NativeBridge = Java.use("com.example.app.NativeBridge");

// Hook 目标 native 方法
    NativeBridge.sign.implementation = function (data, useV2{
// 打印入参 (转 hex 方便复制)
var hex = "";
for (var i = 0; i < data.length; i++) {
            hex += ("0" + (data[i] & 0xff).toString(16)).slice(-2);
        }
console.log("[+] sign() called");
console.log("    data (hex): " + hex);
console.log("    data (str): " + Java.use("java.lang.String").$new(data));
console.log("    useV2:      " + useV2);

// 调用原方法拿返回值
var ret = this.sign(data, useV2);
console.log("    returned:   " + ret);
return ret;
    };
});

跑一次目标 App 的真实场景(比如发起一次登录请求),把控制台输出的 data (hex) 和 returned 各保存一组。这就是你后面要让 Unidbg 复现的"标准答案"。

情报 3:SO 文件本身

把目标 SO 单独从 APK 里提取出来。最简单的办法是把 APK 当 zip 解压:

# APK 本质就是 zip
unzip -o target.apk -d target_extracted
ls target_extracted/lib/
# arm64-v8a/  armeabi-v7a/  ...

# 选 ARM64 (现代 Android 设备的默认架构)
cp target_extracted/lib/arm64-v8a/libexample.so ./

现在你有了完整的"侦察包":APK 文件 + SO 文件 + Java 层调用代码 + Frida 跑出来的入参/返回值样本。可以开始写 Unidbg 代码了。


阶段 1:环境准备

Java 版本

Unidbg 要求 JDK 8 或 JDK 11。新一些的 JDK(17/21)也能跑,但 Unidbg 的某些反射操作可能触发 --add-opens 的警告甚至错误。如果你不想折腾 JVM 参数,用 JDK 11 是最稳妥的选择

# 验证 JDK 版本
java -version
# 输出应该类似:
# openjdk version "11.0.21" 2023-10-17

Maven 项目骨架

Unidbg 是 Java 项目,最直接的引入方式是 Maven。新建一个项目,pom.xml 大致是这样:

<?xml version="1.0" encoding="UTF-8"?>
<projectxmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>unidbg-demo</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<repositories>
<!-- Unidbg 没有发布到 Maven 中央仓库, 用 jitpack -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

<dependencies>
<!-- Unidbg 的 Android 支持 (包含 JNI / Dalvik VM 模拟) -->
<dependency>
<groupId>com.github.zhkl0228</groupId>
<artifactId>unidbg-android</artifactId>
<version>0.9.8</version>
</dependency>
</dependencies>
</project>

关于版本号0.9.x 是当前主流稳定版。如果你想用最新主分支特性,可以把版本号改成 master-SNAPSHOT,但 jitpack 拉取时间会变长。第一次跑建议先用稳定版。

项目结构
项目结构

把 APK 和 SO 都放在 resources/target/ 下。APK 一定要放完整的,不能只放 SO — Unidbg 的 createDalvikVM 会从 APK 中读取签名、包名、resources 等信息,缺了 APK 后续会有一连串奇怪的问题。


阶段 2:搭建最小可执行骨架

骨架的目标是"让代码能跑到 module.callFunction(...) 那一行",先不管会不会成功

package com.example;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.array.ByteArray;

import java.io.File;

publicclassSignDemoextendsAbstractJni{

privatefinal AndroidEmulator emulator;
privatefinal VM vm;
privatefinal Module module;

publicSignDemo(){
// -------- 1. 创建模拟器 --------
// ARM64 是现代 App 的主流架构, 优先选 64 位
// setProcessName 很重要: SO 内部可能用它做完整性校验
        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.example.app")
                .build();

// -------- 2. 创建 Dalvik VM (即 Unidbg 的"假 ART") --------
// 必须传入完整 APK, Unidbg 会读取签名 / 包名 / 资源
        vm = emulator.createDalvikVM(new File("src/main/resources/target/target.apk"));

// 把当前类注册为 JNI 回调处理器
// SO 里所有 JNI 调用最终都会回到这个类的 callXXX 方法上
        vm.setJni(this);

// 开启详细日志, 调试阶段必开
// 这会打印每一次 JNI 调用 / 系统调用 / 文件访问的细节
        vm.setVerbose(true);

// -------- 3. 加载目标 SO --------
// 第二个参数 true 表示自动执行 .init_array 和 JNI_OnLoad
// 这一步会跑 SO 的初始化代码 (有些 SO 会在这里做反调试 / 反模拟器检测)
        DalvikModule dm = vm.loadLibrary(
new File("src/main/resources/target/libexample.so"),
true
        );
module = dm.getModule();
    }

public String callSign(byte[] data, boolean useV2){
// -------- 4. 调用目标 native 函数 --------
// 关键: 参数顺序必须严格匹配 JNI 调用约定
//   arg0: JNIEnv*           — 用 vm.getJNIEnv() 获取
//   arg1: jclass / jobject  — 静态方法传 jclass, 实例方法传 this 对应的 jobject
//   arg2..: 实际参数        — 按签名顺序逐个传入
        DvmObject<?> result = module.callStaticJniMethodObject(
                emulator,
"sign([BZ)Ljava/lang/String;",
new ByteArray(vm, data),
                useV2
        );

// 返回值是 DvmObject, 转 String
return (String) result.getValue();
    }

publicstaticvoidmain(String[] args){
        SignDemo demo = new SignDemo();

// 用 Frida 抓到的同一组入参跑一次, 拿来和真机结果对照
byte[] input = "1700000000:hello world".getBytes();
        String sig = demo.callSign(input, true);
        System.out.println("sign result = " + sig);
    }
}

这段代码刻意只有"必要"的部分。注意几个细节:

  • extends AbstractJni:继承之后你才能 override resolveClass / callObjectMethod 等方法 — 这就是补 JNI 环境的入口
  • setVerbose(true):第一次跑必须开,关掉的话报错信息会少一半
  • callStaticJniMethodObject vs callFunction:前者会自动按 JNI 约定填充 JNIEnv* 和 jclass,更省心;后者更底层,需要自己构造参数

关键基本功:JNI 方法签名

签名是 "sign([BZ)Ljava/lang/String;" 这一行神秘的字符串。理解它是 Unidbg 的核心基本功 — 不理解签名,每一次调用都会是猜谜游戏

签名的结构

JNI 方法签名遵循一个紧凑的格式:

方法名(参数1参数2参数3...)返回值

以 sign([BZ)Ljava/lang/String; 为例:

JNI 方法签名解析

每一个"字母"代表一个 Java 类型。完整的对照表:

签名字符
Java 类型
说明
Zboolean
注意是 Z 不是 B
Bbyte
8 位整数
Cchar
16 位字符
Sshort
16 位整数
Iint
32 位整数
Jlong
64 位整数
Ffloat
32 位浮点
Ddouble
64 位浮点
Vvoid
仅返回值用
L<类全名>;
对象
类全名用 / 分隔, 末尾必须有 ;
[<类型>
数组
数组多套一层 [, 多维数组多套几层

几个例子练习

()V                                               void method()
(I)Z                                              boolean method(int)
([BI)Ljava/lang/String;                           String method(byte[], int)
(Ljava/lang/String;)[B                            byte[] method(String)
([[I)V                                            void method(int[][])  ← 二维数组
(Landroid/content/Context;I)Ljava/lang/String;    String method(Context, int)

几个常见坑

  1. Z 是 boolean,不是 byte — 这是最常见的错误。B 才是 byte,Z 来自 Pascal 的传统
  2. 类全名末尾的 ; 不能省 — Ljava/lang/String; 是对的,Ljava/lang/String 会被解析失败
  3. 包名分隔符是 / 不是 . — Java 里写 java.lang.String,签名里写 java/lang/String
  4. 返回值在括号外,不在括号内 — (I)V 是 void method(int),不是 int method(void)

不要死记,从 JADX 直接复制

最高效的做法是让 JADX 替你生成签名

  1. JADX 中找到目标 native 方法
  2. 右键 → "Copy as → Smali method signature" 或者切到 smali 视图
  3. Smali 视图直接显示完整签名,复制粘贴到 Unidbg 代码里

如果 JADX 没装这个功能(旧版本可能没有),你也可以从 smali 文件里搜:

.method public static native sign([BZ)Ljava/lang/String;
.end method

中间那段就是你要的签名 — 包括方法名 + 括号 + 参数 + 返回值,整段直接复制就行。


第一次运行:报错的三段式结构

骨架写完,按下 Run。99% 的概率你会看到一段报错,类似这样:

[10:12:34 437] WARN  [c.g.u.l.a.d.AbstractJni] (AbstractJni:743) -
java.lang.UnsupportedOperationException: 
  android/content/pm/PackageManager->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;
    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:743)
    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:412)
    at com.github.unidbg.linux.android.dvm.DalvikVM64$53.handle(DalvikVM64.java:1024)
    at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:127)
    at com.github.unidbg.arm.backend.UnicornBackend$11.hook(UnicornBackend.java:347)
    at unicorn.Unicorn$NewHook.onInterrupt(Unicorn.java:128)
    ...

第一次看到这种东西可能想立刻关闭终端。但只要你识别出报错的三段式结构,它会立刻变得平易近人。

Unidbg 报错信息的三段式解剖

段一:异常类型 + 调用签名(第 1-3 行)

这是你最需要看的部分。它告诉你两件事:

java.lang.UnsupportedOperationException: 
  android/content/pm/PackageManager->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;
  • 异常类型UnsupportedOperationException → 这是 Unidbg 抛的"我没实现"
  • 完整签名PackageManager->getPackageInfo(...) → 谁调谁、签名是什么

读到这里你就已经知道:

  1. SO 通过 JNI 调了 PackageManager.getPackageInfo(String packageName, int flags)
  2. 返回值是 PackageInfo 对象
  3. 这是一个实例方法(看后面的 callObjectMethodV 而不是 callStaticObjectMethodV
  4. 你需要在 AbstractJni 子类里 override callObjectMethodV,识别这个签名,返回一个伪造的 PackageInfo

段二:调用栈中段(紧挨签名的那几行)

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:743)
    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:412)
    at com.github.unidbg.linux.android.dvm.DalvikVM64$53.handle(DalvikVM64.java:1024)

这几行告诉你"在 Unidbg 内部走的路径"。重点看最上面那一帧AbstractJni.callObjectMethodV) — 它直接告诉你应该 override 哪个方法。

不同的 JNI 调用对应不同的 override 入口:

你看到的栈顶
你应该 override 的方法
callObjectMethodVcallObjectMethodV
 (实例方法返回对象)
callStaticObjectMethodVcallStaticObjectMethodV
 (静态方法返回对象)
callIntMethodVcallIntMethodV
 (实例方法返回 int)
callBooleanMethodVcallBooleanMethodV
 (实例方法返回 boolean)
getStaticObjectFieldgetStaticObjectField
 (静态字段)
getObjectFieldgetObjectField
 (实例字段)
resolveClassresolveClass
 (类查找)

记住这个对应表,看到栈顶就知道动手的位置。这是 Unidbg 排错最核心的基本功之一。

段三:调用栈底部(接近 unicorn.Unicorn 那几行)

    at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:127)
    at com.github.unidbg.arm.backend.UnicornBackend$11.hook(UnicornBackend.java:347)
    at unicorn.Unicorn$NewHook.onInterrupt(Unicorn.java:128)

这几行可以完全忽略 — 它们告诉你的是"这个异常是从 SVC 中断回调里抛出来的",但回想第四篇,所有 Unidbg 报错都从那里抛出,所以这一段没有信息量。

新手最常见的错误:看着栈底的 Unicorn.onInterrupt 一筹莫展,以为是 Backend 出了问题。其实那只是"中断分发的固定起点",真正的问题永远在栈顶的那一行 JNI 方法上。


报错分类决策树:判断这是哪一类问题

读懂三段式之后,下一个问题是:这个报错属于哪一类? 因为不同类的报错,处理路径完全不同。

Unidbg 报错的分类决策树

决策点 1:异常类型是什么?

java.lang.UnsupportedOperationException: ...     → JNI 类问题
java.lang.UnsatisfiedLinkError: ...              → 方法签名错或 SO 加载问题
com.github.unidbg.unix.UnixSyscallHandler...     → 系统调用类问题
java.lang.IllegalStateException: resolve failed  → 文件访问问题
java.lang.IllegalStateException: invalid memory  → 内存越界

决策点 2:JNI 类问题里的细分

如果是 UnsupportedOperationException,再看签名所在的类:

类的命名空间
类型
处理思路
android/content/...
Android Framework
大概率反 ContextManager,需要伪造对象
android/telephony/...
设备信息相关
通常是反检测,返回伪造的设备 ID
java/util/...
JDK 工具类
优先用 JDK 真实类绑定 (后面会讲)
java/lang/String
 等
JDK 基础类
通常是 SO 自己用 JNI 操作字符串
com/<目标 App 包名>/...
App 自定义类
JADX 反编译这个类,把逻辑搬到 Java 里

决策点 3:syscall 类问题

如果是 syscall 报错(少见但存在),关键看报错里的 NR 或 intno

syscall NR=387 not implemented  → 查 ARM64 syscall 表, NR=387 是 statx
                                  解决方案: 在 SyscallHandler 里加实现, 或者
                                  在 libc 层 hook 包装函数 (之后会讲)

决策点 4:文件访问问题

报错里出现 resolve failed + 文件路径:

IllegalStateException: resolve failed: /proc/self/maps
                                        ^^^^^^^^^^^^^
                                        SO 在读这个文件

处理思路:实现一个 IOResolver,对这个路径返回一个伪造的 FileResult(第八篇会展开讲)。

一个实战清单

把这套决策树编成"看到报错先问的三个问题":

  1. 异常类型是什么?→ 决定大类(JNI / syscall / 文件 / 内存)
  2. 栈顶的 method 名是什么?→ 决定 override 哪个方法
  3. 签名里的类全名是什么?→ 决定怎么处理

只要养成"看到报错先答这三个问题"的习惯,90% 的 Unidbg 报错都不再是黑盒。


修第一个洞:override 一个 JNI 方法

回到刚才的 getPackageInfo 报错。按上面的决策树:

  1. 异常类型UnsupportedOperationException → JNI 类
  2. 栈顶 methodcallObjectMethodV → override 这个
  3. 签名类全名android/content/pm/PackageManager → Android Framework, 需要伪造

动手补:

@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
// ===== PackageManager.getPackageInfo (String, int) =====
// SO 通常用这个方法拿包签名 / versionCode / versionName
case"android/content/pm/PackageManager->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;": {
            String packageName = (String) vaList.getObjectArg(0).getValue();
int flags = vaList.getIntArg(1);
            System.out.println("[JNI] getPackageInfo(" + packageName + ", " + flags + ")");

// 返回一个 PackageInfo 的 DvmObject 占位
// 注意: 仅返回对象本身还不够, SO 接下来肯定会读取 packageInfo.signatures 等字段
//       那时会再次报错, 你需要继续 override getObjectField 来响应
return vm.resolveClass("android/content/pm/PackageInfo").newObject(null);
        }
    }

// 没匹配到的签名, 交还给父类 (会再次抛 UnsupportedOperationException)
returnsuper.callObjectMethodV(vm, dvmObject, signature, vaList);
}

注意这段代码的几个细节:

  • 用 switch 结构:每多补一个 JNI 方法就多一个 case。这种风格在样本复杂时会变得很长,但线性可读性比抽象出 Map<String, Function> 更有价值
  • 加 println 日志:每次匹配到一个 JNI 调用就打印参数。这会成为你"理解 SO 在做什么"的最重要信息源
  • 没匹配到时调用 super:保持父类的报错语义不变 — 这样下一次报错你能立刻知道又有新的 JNI 调用没补

跑一次。getPackageInfo 不再报错了,但很可能立刻冒出下一个报错

java.lang.UnsupportedOperationException: 
  android/content/pm/PackageInfo->signatures:[Landroid/content/pm/Signature;
    at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(...)

正如代码注释里说的,SO 现在要读 packageInfo.signatures 字段。继续补:

@Override
public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
switch (signature) {
case"android/content/pm/PackageInfo->signatures:[Landroid/content/pm/Signature;": {
// 这是 App 的签名信息, SO 通常用它做完整性校验
// 你需要返回一个 Signature[] 数组, 数组里的每个 Signature 对象包含真实签名 byte
// 真实签名可以从 APK 文件中提取 (apksigner verify --print-certs 拿到)
            DvmClass signatureClass = vm.resolveClass("android/content/pm/Signature");
byte[] realSignature = loadRealSignatureFromApk();
            DvmObject<?> sig = signatureClass.newObject(realSignature);
return ProxyDvmObject.createObject(vm, new DvmObject<?>[]{sig});
        }
    }
returnsuper.getObjectField(vm, dvmObject, signature);
}

每次跑、每次报错、每次 case 加一个 — 这就是 Unidbg 工程师最日常的工作循环。


参数传递的统一规则:一张类型映射表

补环境的另一个高频问题是"传参数给 native 函数时类型不对"。这一节给你一张完整的类型映射表 — 所有参数问题都能从这张表里查答案

Java 类型
JNI 签名
Unidbg 中的传递方式
示例
booleanZ
直接传 boolean
true
byteB
直接传 byte
(byte) 0x12
charC
直接传 char
'a'
shortS
直接传 short
(short) 100
intI
直接传 int
42
longJ
直接传 long
1700000000L
floatF
直接传 float
3.14f
doubleD
直接传 double
3.14
StringLjava/lang/String;new StringObject(vm, str)new StringObject(vm, "hello")
byte[][Bnew ByteArray(vm, bytes)new ByteArray(vm, data)
int[][Inew IntArray(vm, ints)new IntArray(vm, new int[]{1,2,3})
对象
L<类>;vm.resolveClass(...).newObject(thiz)
见下例

对象类型的传递示例

// 假设 native 方法签名是: doSomething(Landroid/content/Context;I)V
// 第一个参数是 Context, 第二个是 int

// 1. 创建一个伪造的 Context 对象
DvmClass contextClass = vm.resolveClass("android/content/Context");
DvmObject<?> contextObj = contextClass.newObject(null);

// 2. 调用函数
module.callStaticJniMethod(emulator, "doSomething(Landroid/content/Context;I)V",
        contextObj,    // arg1: Context
42// arg2: int
);

**调用时的"包装"和"解包"**:

  • 传入参数时:基本类型直传,对象/数组要 包装 成 Unidbg 的 DvmObject / ByteArray 等
  • 拿到返回值时:基本类型直接拿,对象/数组拿到的是 DvmObject<?>,需要 .getValue()解包
// 调用返回 String 的方法
DvmObject<?> result = module.callStaticJniMethodObject(emulator, "sign([B)Ljava/lang/String;",
new ByteArray(vm, data));
String signature = (String) result.getValue();   // ← 解包

// 调用返回 byte[] 的方法
DvmObject<?> result = module.callStaticJniMethodObject(emulator, "encrypt([B)[B",
new ByteArray(vm, data));
byte[] encrypted = (byte[]) result.getValue();   // ← 解包

记住一个原则:Unidbg 内部的世界是 DvmObject,外部的世界是 Java 原生类型。所有参数进入 Unidbg 时要包装,所有返回值离开 Unidbg 时要解包。这是边界,也是你和 Unidbg 之间的"翻译层"。


排错的心智模型:节奏比技巧更重要

补了一两个洞之后,你会进入一个节奏:

跑 → 报错 → 看栈顶 → 加 case → 跑 → 报错 → 看栈顶 → 加 case → ...

这个循环可能持续几十次。能不能撑住,取决于你有没有掌握节奏。下面是几个我自己常用的节奏管理原则:

原则 1:一次只修一个洞

最常见的失败模式是"一次性想修五个洞"。报错列出了第一个未实现的 JNI,你顺手把第二、第三个看起来相关的也补上。结果是:跑出新报错时你不知道是哪一个补的有问题。

正确做法:每次只 override 一个方法,跑一次,确认这个洞补上了再补下一个。慢一点,但每一步都是确定的。

原则 2:补的时候打日志,删的时候不要犹豫

每补一个 JNI 方法,加一行 println 打印参数。这会让你在后面看 SO 行为时有"上帝视角"。当某个补的洞已经稳定不再报错,也不要删 println,留着它。

直到最后整段流程跑通、需要做生产化的时候,再统一删除/降级日志(用 if (debug) 包起来即可)。

原则 3:看不懂的方法名先返回 null

有些 JNI 方法名你看了也不知道在干什么(比如混淆过的)。这时不要尝试理解,先 return null 让流程往下走:

case"com/example/obfuscated/aB->c()Ljava/lang/Object;":
    System.out.println("[JNI-NULL] " + signature);
returnnull;  // 占位返回, 后面再回头分析

返回 null 之后,如果 SO 接下来崩溃了,说明这个返回值很重要 — 你再回头分析。如果 SO 继续往下跑没事,说明这个方法是可以"降级处理"的,你可以一直 return null。这是减少分析量最有效的策略之一

原则 4:跑通之后立刻验证正确性

"不报错"和"结果正确"是两回事。跑通的瞬间,立刻和阶段 0 用 Frida 抓到的"标准答案"对照:

publicstaticvoidmain(String[] args){
    SignDemo demo = new SignDemo();

// 用 Frida 抓到的同一组入参跑
byte[] input = "1700000000:hello world".getBytes();
    String unidbgResult = demo.callSign(input, true);

// Frida 跑出来的标准答案 (从你的笔记里复制过来)
    String fridaResult = "a3f8e2d1c4b0...";

    System.out.println("Unidbg: " + unidbgResult);
    System.out.println("Frida:  " + fridaResult);
    System.out.println("Match:  " + unidbgResult.equals(fridaResult));
}

**Match 为 true 才是真正的"跑通"**。如果是 false,说明虽然没报错,但补环境的某个值有问题(往往是某个返回 null 的方法其实需要返回真实值)— 这时你需要回头追查。


总结:第一次跑通的本质

第一次让 SO 在 Unidbg 上跑起来,本质上不是"写代码",而是"和一个未知的 SO 做对话":

  • 它问什么(每次报错就是 SO 在向你提一个问题)
  • 你能答什么(每个 override 就是你给的一个回答)
  • 它满意了吗(不报错 + 结果正确才算满意)

这个对话过程没有捷径。但是有节奏 — 一次一个洞、看栈顶、查决策树、补 case、对照验证。掌握这个节奏比记住任何 API 都重要。

如果第一个 SO 跑通时你长出一口气,恭喜你 — 你已经完成了 Unidbg 学习曲线最陡峭的那一段。后面的工作不会变简单,但会变得不再令人不知所措

最新文章

随机文章

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