从Java到Native:Frida手动Hook的完整实战流程

Frida 这个系列的第一篇讲的是自动化批量 hook:遍历类、遍历方法、统一打点,能快速知道目标进程里有哪些方法在跑、参数长什么样。这种思路在做初步信息收集时效率很高,但真到了实战阶段,你会发现自动化脚本往往在几个关键点处失灵——最典型的就是Java.choose扫描不到实例、同一方法名存在多个重载导致脚本崩溃、或者类加载器不对导致Java.use都过不去。这些问题的共性在于:自动化少了一步“手动决策”。所以我专门把“手动 hook”拿出来写第二篇,把这套流程彻底拆开,从 Java 层到 Native 层,逐个环节说明白。

手动 hook 并不神秘,搞明白它的核心流程就够了:选定目标类、处理加载器、确定方法签名、构造 hook 逻辑、根据实例或静态上下文注入代码。每一步都有它的原理和坑。这篇会结合我在实际调试中遇到的场景,讲清楚每个动作背后的“为什么”,而不是只丢给你现成脚本。

1. 手动 hook 的基本盘:为什么自动化脚本在某些场景下会失效

1.1 自动枚举 hook 的典型局限

先说一个常见场景:目标 App 里有核心加密逻辑,用了某条encrypt方法。自动枚举脚本能把所有带encrypt名字的方法全部 hook 一遍,然后打印参数和返回值。听起来很全,但实际跑起来会碰到几个问题。

第一个是重载。一个方法名对应多个签名,比如encrypt(byte[])、encrypt(String)、encrypt(byte[], String),自动脚本如果没做签名区分,调用implementation = function(...)的时候十有八九会报TypeError: not a function,因为 Frida 不知道该替换哪一个。

第二个是实例状态。Java 方法很多是非静态方法,你拿到方法以后直接 hook,假设类本身没被加载,或者目标实例还没创建,那么即使 hook 成功,也不会有调用触发。自动脚本为了能跑起来,通常是懒扫描,找得到就 hook,找不到就跳过。问题在于,跳过的时候没有任何提示,你可能盯着日志看半天,以为目标逻辑没被触发,其实只是没 hook 对实例。

第三个是类加载器。Android 上插件化、热修复框架非常多,同一个类名可能存在于多个 ClassLoader 里。Java.use默认用的加载器可能在目标类加载之前就已经把类给初始化了,这时候你再 hook,hook 的是另一个副本,代码跑的是业务里那个副本,两边根本对不上。

1.2 手动 hook 解决问题的思路

手动 hook 的本质就是把上面这些决策点显式化。不再依赖脚本帮你猜,而是你自己根据反编译结果、调用栈、日志去确定一个精确的“hook 点”。比如你用 JADX 看完反编译代码,确定了目标方法是某个类的某个重载签名,再写脚本直接去匹配。

手动 hook 的优势在排查这三类难题时体现得很明显:

  • 精确定位重载。通过overload指定签名,只 hook 你关心的那个版本。
  • 控制实例获取方式。要么直接 hook 构造函数,要么用Java.choose在堆上扫描符合条件的目标实例。
  • 主动指定 ClassLoader。在Java.enumerateClassLoaders之后找到目标 loader,再传给Java.use,避免 hook 到“假类”。

所以手动 hook 不是让你丢掉自动化,而是在自动化的基础上多一层主动权。熟练之后你会形成一种习惯:先自动枚举看大概,再手动定点做精准 hook。

2. 手动 hook 的三块地基:Java.perform、Java.use、Java.choose

2.1 Java.perform:为什么所有 Java 操作都要包在它里

写 Frida 脚本时,你基本会见到这样的结构:

Java.perform(function () { // 所有 Java 相关操作都在这里 });

新手往往不理解这一层到底是什么作用,甚至有人直接不写Java.perform也能跑通。真实原因在于:Frida 的 JS 引擎(V8 或 QuickJS)与 ART 虚拟机(Android 运行时)是两个独立的运行时。需要在使用 Java 层 API 之前,先挂载 ART 运行时,把 JS 线程的状态切换到已附加到 Java 环境的状态。Java.perform做的是发生在 ART 运行时启动以后、并且稳定可用的前提下,注入并执行回调。

我的习惯是只要脚本里出现任何一个以Java.开头的 API 调用,就包一层Java.perform。不包的隐患是偶发性的java.lang.ClassNotFoundException或调用时机过早导致引用无效,这类问题排查成本很高,因为没有固定崩溃点,有时跑一次正常、跑两次报错。

2.2 Java.use:获取类引用的正确姿势

Java.use的基本写法是:

var TargetClass = Java.use('com.example.securechat.core.CryptoManager');

这一步会在当前默认的 ClassLoader 下查找目标类,并返回一个可用于后续操作的“类包装对象”。Java.use返回的包装对象有类方法可以直接调用,也有$new用来构造实例。

但默认加载器覆盖不了动态加载类的情况。之前线上遇到一个插件模块,核心逻辑打在一个dex文件里,由某个插件框架在运行时加载。默认情况下Java.use拉不到这个类。这时候要先用枚举函数把加载器找出来:

Java.perform(function () { Java.enumerateClassLoaders({ onMatch: function (loader) { try { var TargetClass = Java.use('com.example.securechat.core.CryptoManager'); if (TargetClass && loader.loadClass('com.example.securechat.core.CryptoManager')) { // 找到目标加载器 console.log('找到目标类加载器'); } } catch (e) {} }, onComplete: function () {} }); });

还有一种更粗暴但有效的方法:在Java.perform里调用Java.classFactory.loader = loader,然后把后续的Java.use都挂在这个 loader 下面。很多插件化场景里,这种方式比逐个枚举加载器更直接。

2.3 Java.choose:从堆上捞活动实例

如果是非静态方法,光有类不够,还要有实例。最常用的方式有两种:第一是 hook 构造函数,每次创建实例都自动处理;第二是Java.choose,在当前进程堆里扫描符合条件的实例。

Java.choose的语义是“遍历堆上所有该类型的对象”,每找到一个就回调一次:

Java.perform(function () { Java.choose('com.example.securechat.core.SessionManager', { onMatch: function (instance) { console.log('找到一个 SessionManager 实例: ' + instance); }, onComplete: function () { console.log('扫描结束'); } }); });

这里有几个关键点。onMatch的执行时机可能比较长,因为堆扫描本身开销不小,不要在回调里做太重的事情,否则卡顿明显。回调里拿到的instance是 Frida 包装后的对象,你可以直接调用它的方法,也可以对方法进行赋值 hook。

有一个容易踩的坑是:一次Java.choose可能匹配到多个实例,但你只需要其中某一个。区分实例需要有明确的标识字段,比如包名、会话 ID、某个内部状态值。在onMatch里先做条件判断,确认是目标实例后再动手。

3. 手把手搭一个手动 hook 脚本:从方法到实例

3.1 最简单的静态方法 hook

静态方法不需要实例,直接用Java.use拿类,给方法赋值implementation即可。假设目标逻辑是一个内置的校验:

Java.perform(function () { var Validator = Java.use('com.example.securechat.core.AuthValidator'); Validator.checkSign.implementation = function (data) { console.log('[AuthValidator] checkSign 被调用'); var result = this.checkSign(data); console.log('[AuthValidator] 原始返回值: ' + result); return result; }; });

注意this.checkSign(data)的写法。在替换implementation之后,this指向的是当前类实例或类的静态上下文,所以调用this.方法名才会进入原方法本体。有人会把原方法名和替换方法名混淆,或者想当然地直接调用全局变量,结果造成递归调用或不生效。

3.2 非静态方法:没有实例也要找到实例

假设你的目标方法是SessionManager.createSession(String userId),但这个类的实例是在某个登录流程里才建立的。如果你只是Java.use然后给方法赋implementation,实例没创建时,永远不会被调用。

此时有两个方案。

第一个方案是 hook 构造函数:

Java.perform(function () { var SessionManager = Java.use('com.example.securechat.core.SessionManager'); SessionManager.$init.overload('java.lang.String').implementation = function (userId) { console.log('[SessionManager] 构造函数被调用,userId = ' + userId); this.$init(userId); console.log('[SessionManager] 构造完成'); }; });

第二个方案是用Java.choose找出已有的实例:

Java.perform(function () { Java.choose('com.example.securechat.core.SessionManager', { onMatch: function (instance) { if (instance.__hooked) return; instance.__hooked = true; instance.createSession.implementation = function (userId) { console.log('[SessionManager] createSession 被调用,userId = ' + userId); var result = this.createSession(userId); return result; }; }, onComplete: function () {} }); });

这里我用了instance.__hooked作为标记,防止同一个实例被多次扫描重复 hook。__hooked是 JS 对象的自定义属性,不会写进 Java 实例本身,也不会影响业务逻辑,只是一个防重标记。

3.3 重载方法如何精准命中

重载是手动 hook 中最容易翻车的一环。在给方法赋值implementation之前,先输出该方法的全部重载签名:

var CryptoManager = Java.use('com.example.securechat.core.CryptoManager'); var overloads = CryptoManager.encrypt.overloads; overloads.forEach(function (ov) { console.log('签名: ' + ov.signature); });

然后再针对你要的那个签名写:

CryptoManager.encrypt.overload('[B', 'java.lang.String').implementation = function (data, key) { // 在这里做 hook 逻辑 var result = this.encrypt(data, key); return result; };

签名的写法要严格遵守 JNI 类型描述符:byte[]写作[B,String写作java.lang.String,int写作int,对象类型写作com.example.xxx.ClassName。如果签名写错,匹配不到方法,会直接报错。

有些工具会自动生成签名,比如 JADX 的方法签名可以直接复制后缀部分。我自己的习惯是:先用overloads打印一遍,再手动对比,避免把参数类型搞错。

3.4 返回值篡改与类型问题

hook 的终极目的是修改返回结果。常见的场景是让某个判断恒为真或直接改写加密结果。

CryptoManager.encrypt.overload('[B', 'java.lang.String').implementation = function (data, key) { var result = this.encrypt(data, key); console.log('[CryptoManager] 原始密文长度 = ' + result.length); // 返回一个固定假的密文 return Java.array('byte', [1, 2, 3, 4]); };

这里有个细节:如果原方法返回byte[],Frida JS 侧拿到的返回值是一个 Java 的字节数组包装对象,你直接return [1,2,3,4]返回一个纯 JS 数组是不行的,类型不匹配。需要借助Java.array('byte', [...])创建 Java 字节数组。如果是 String 或 int 类型,直接返回 JS 字符串和数字即可。

返回值类型处理错了,可能在运行时报TypeError: cannot read property,也可能静默失败——原方法返回 null,业务层直接崩溃。

4. Native 层手动 hook:JNI 函数与 so 符号级别的精确控制

4.1 用 Interceptor.attach 处理导出函数

Java 层的 hook 依赖 ART 的运行时机制,Native 层则要直接操作 ELF 符号。最常见的需求是 hook so 里的 JNI 函数,因为 JNI 函数名和 Java 类的包名、类名、方法名是一一对应的。

例如 Java 层调用:

public class CryptoManager { public static native byte[] encrypt(byte[] data, String key); }

对应的 JNI 符号通常是:

Java_com_example_securechat_core_CryptoManager_encrypt

手动 hook 的写法是:

var encryptPtr = Module.findExportByName('libsecurechat.so', 'Java_com_example_securechat_core_CryptoManager_encrypt'); if (encryptPtr) { Interceptor.attach(encryptPtr, { onEnter: function (args) { console.log('[Native] encrypt onEnter'); // args[0] 是 JNIEnv* // args[1] 是 jobject 或 jclass // args[2] 是 jbyteArray // args[3] 是 jstring }, onLeave: function (retval) { console.log('[Native] encrypt onLeave,返回值地址: ' + retval); } }); }

这里初学者最困惑的是args的下标。JNI 函数的前两个参数永远是JNIEnv*和jobject/jclass,从第三个参数开始才是 Java 层方法传入的参数。所以上面例子里 Java 层的data对应args[2],key对应args[3]。

4.2 强符号与弱符号:同一个函数名可能有多份实现

Module.findExportByName找的是 so 的动态导出符号表。如果 so 里同一个函数名有两个实现(比如因为链接方式或者 UPX 壳),这个方法只能拿到其中一个。遇到这种情况,需要用Process.enumerateModules()遍历所有模块,或者在Module.enumerateExports里打印导出表自己对比。

实战中还有一类情况是函数没有导出。so 内部函数做了static处理,符号表里看不到。此时需要先结合 IDA 的分析,拿到函数偏移量,然后通过基址加偏移的方式计算运行时地址:

var base = Module.findBaseAddress('libsecurechat.so'); var target = base.add(0x1234A0); // 假设 IDA 分析出的偏移是 0x1234A0 Interceptor.attach(target, { onEnter: function (args) { console.log('命中未导出函数,argument[0] = ' + args[0]); }, onLeave: function (retval) {} });

有一点要提醒:base.add(0x1234A0)里的 0x1234A0 要换算成模块加载后的相对偏移,不是文件偏移,也不是虚拟地址。通常 IDA 里看到的地址是基址 + 偏移,要减去 IDA 加载基址才能得到相对偏移。做的时候先确认 so 是否被加固、是否有反调试逻辑。

4.3 读取并修改 Native 层的内存参数

Native 层 hook 最大的优势是可以直接操作指针。比如 JNI 函数args[2]是 jbyteArray,要读取它的内容,需要用Java.vm.getEnv()拿到 JNIEnv 指针,然后调用 JNI 函数族的接口去读取数组。

onEnter: function (args) { var env = Java.vm.getEnv(); var jarray = args[2]; var data = env.getByteArrayRegion(jarray, 0, env.getArrayLength(jarray)); // data 是字节数组,可以直接打印 console.log(hexdump(data)); }

同样地,修改返回值也可以直接构造一个新的 jbyteArray 或者把原数组内容改掉。我记得有一次调试某签名算法,代码里把签名结果写进了一个全局 buffer,Java 层再取出来。直接返回一个新数组不生效,因为调用方拿的还是同一个 buffer 的引用,必须原地改写 buffer 内容。这个坑让我明白:hook 之前一定要先搞清数据流的方向,是“返回值传递”还是“引用原地修改”。

5. 实际场景实战:一个手动 hook 的完整排查过程

5.1 场景描述:某社交 App 的本地加密逻辑

模拟项目 X 是一个社交 App,它在本地做消息加密,加密函数位于com.example.securechat.core.MessageCipher。我需要 hook 这个类里的encrypt(byte[])方法,确认两个东西:第一,加密使用的密钥是否固定;第二,返回结果是否被上层二次处理。

第一步,先写一个最简单的 hook 脚本验证方法是否被调用:

Java.perform(function () { var MessageCipher = Java.use('com.example.securechat.core.MessageCipher'); MessageCipher.encrypt.overload('[B').implementation = function (data) { console.log('encrypt 被调用,输入长度: ' + data.length); var result = this.encrypt(data); console.log('encrypt 返回,长度: ' + result.length); return result; }; });

跑起来以后发现没有日志输出。这不是方法没被调用,而是类可能还没被加载。于是改脚本,先用Java.choose扫描实例:

Java.perform(function () { Java.choose('com.example.securechat.core.MessageCipher', { onMatch: function (instance) { console.log('找到实例: ' + instance); instance.encrypt.implementation = function (data) { console.log('实例 encrypt 被调用,输入长度: ' + data.length); return this.encrypt(data); }; }, onComplete: function () {} }); });

这一版开始有日志了,但是只输出了一条,说明实例是在某个特定时刻才会创建,我们对它 hook 得不够早。

第二步,动态监控构造时机。改用 hook 构造函数,保证实例一诞生就被标记:

Java.perform(function () { var MessageCipher = Java.use('com.example.securechat.core.MessageCipher'); MessageCipher.$init.overload('[B', '[B').implementation = function (key, iv) { console.log('构造函数被调用,key 字节: ' + key.length + ', iv 字节: ' + iv.length); this.$init(key, iv); }; });

这里出现了overload('[B', '[B'),是因为我用overloads输出后发现构造函数有两个重载版本,一个是(byte[] key, byte[] iv),另一个是(String algorithm)。只 hook 带密钥的那个版本。

第三步,确认类加载器无问题。如果构造函数也迟迟不触发,就要检查Java.use到底用的是哪个 ClassLoader。插件化场景尤其容易在这步卡住,处理方式就是前面说的enumerateClassLoaders,找到承载该类的加载器后切换。

5.2 场景总结:手动 hook 的标准动作

上面的排查经历其实涵盖了一套标准动作,我整理出来给你参考:

  1. 反编译确认目标类、方法签名、构造方式。
  2. Java.use获取类引用,先打印overloads看清楚重载。
  3. 判断方法是否静态,静态方法直接 hook;非静态方法考虑 hook 构造或Java.choose。
  4. 判断是否涉及动态加载类,涉及则枚举 ClassLoader。
  5. hook 时保留原始调用链,先打印后修改,避免一上来就改坏业务。
  6. 根据数据流方向决定是否需修改引用内容。

这套流程做熟了以后,手动 hook 的效率其实不比自动脚本慢,因为它跳过了大量无效 hook 点的干扰,有目标地输出日志,定位问题会非常快。

6. 手动 hook 常见问题与排查技巧实录

6.1 问题速查表

现象可能原因排查方向
hook 后完全无日志类未加载、实例不存在、方法未被调用用Java.choose扫描实例;hook 构造函数
报TypeError: not a function方法有多个重载,未指定签名打印overloads,补.overload()
报ClassNotFoundExceptionClassLoader 不对enumerateClassLoaders定位目标 loader
hook 方法后业务崩溃返回值类型篡改错误用Java.array包装数组;保持原返回值类型
Java.choose很慢或卡顿堆上目标对象过多缩短回调逻辑;仅做轻量判断
Module.findExportByName返回 null符号未导出或被 strip用 IDA 分析偏移、基址 + 偏移定位
Native hook 命中但参数不对JNI 参数下标错位记住 JNI 函数前两个固定参数:env、jobject
hook 后同一个函数被重复触发多次重复 hook用自定义标记__hooked防重

6.2 几个值得单独强调的坑

第一个坑是Java.use之后立刻调用overload('byte[]')这样的写法。JNI 类型描述符里byte[]是[B,不是byte[],手写签名很容易写错。最稳的办法是先打印overloads,从输出里复制签名格式。

第二个坑是返回值是数组类型时,Java.array和普通 JS 数组混用。你直接返回[1,2,3]在某些 Frida 版本里能跑,但类型不是 Java 字节数组,后续调用方用result.length和读取元素时行为不稳定。这时候一定要显式Java.array('byte', ...)。

第三个坑是 Native 层 hook JNI 函数后,如果同时也在 Java 层 hook 了同一个法子函数,两层日志会互相干扰。建议排查时先做一层,确认没问题再做第二层,否则无法判断问题出在哪一层。

6.3 独家技巧:用调用栈定位准确类加载器

有些场景下,目标类本身被混淆了,类名混乱,一时分不清要 hook 哪个类。此时可以在报错点附近手动打一个Java.use,配合Thread.currentThread().getStackTrace()拿到 Java 层的调用栈,看看到底是从哪个类、哪个方法链进来的。

Java.perform(function () { var Throwable = Java.use('java.lang.Throwable'); Throwable.$init.implementation = function () { this.$init(); var stack = this.getStackTrace(); for (var i = 0; i < stack.length; i++) { console.log('[stack] ' + stack[i].toString()); } }; });

这招在定位“看似调用却 hook 不到”的问题时特别管用。很多时候你以为目标是 A 类的方法,调用栈打出来以后发现实际走的是 B 类的同名方法,或者中间隔了一层转发逻辑。

6.4 收尾的一个经验

手动 hook 本身并不神秘,难点全在于精确。你要钩得准,首先得看清目标;要看清目标,就得理解类加载机制、JNI 参数布局和 Frida 的封装规则。写脚本的时候不要怕多打印几行日志,console.log是调试过程中最值得信任的工具,比任何单步调试都直观。

另一个容易被忽略的点是:hook 完记得确认是否会影响 App 的稳定性。尤其是修改返回值、篡改内存这种操作,容易导致业务逻辑异常。我一般会维护一个可开关的 hook 开关模块,用全局变量控制是否生效,方便在排查后快速恢复。

手动 hook 这块写到这也差不多了,能把这些环节理顺,你就已经具备独立处理大多数动态分析需求的能力了。下一步可以往 Frida 脚本模块化、RPC 调用、以及基于 hook 结果的自动化测试方向延伸,但这些都是熟能生巧的事,先把手动这一步练扎实最关键。

相关新闻

OpenCV轮廓查找与绘制:从二值化到层级分析的实战攻略

OpenCV轮廓查找与绘制:从二值化到层级分析的实战攻略

做图像处理的时候&#xff0c;我经常跟朋友说一句话&#xff1a;如果你只会看像素值&#xff0c;那你看到的就是一堆数字&#xff1b;但如果你会看轮廓&#xff0c;你看到的才是一个个物体。OpenCV里最常被问到的功能之一&#xff0c;就是用Python做查找并绘制轮廓。今天不整虚…

2026/10/19 16:29:11 阅读更多 →
FineReport前端JS分类汇总:5步实现动态分组合计

FineReport前端JS分类汇总:5步实现动态分组合计

1. 为什么分类汇总这个老需求&#xff0c;会在FR里逼我写JS&#xff1f;先说个场景。你负责一张员工费用明细报表&#xff0c;数据集已经把每一笔费用都查出来了&#xff1a;部门、姓名、费用类别、金额&#xff0c;一共几千行。用户的要求听上去很简单——“在上面加一个合计&…

2026/10/19 16:29:11 阅读更多 →
VMware Ubuntu虚拟机磁盘扩容全流程:从分区到文件系统实战指南

VMware Ubuntu虚拟机磁盘扩容全流程:从分区到文件系统实战指南

1. 扩容前先做三个确认&#xff1a;这决定了你后面是三个命令还是四个小时先说一个我自己的经历。有次给一台 Ubuntu 虚拟机扩磁盘&#xff0c;VMware 里硬盘容量从 50GB 拉到 200GB&#xff0c;整个过程一路绿灯&#xff0c;重启系统后我满怀期待地去敲df -h&#xff0c;结果根…

2026/10/19 16:29:11 阅读更多 →

最新新闻

库卡配商科焊机走Profinet:GSD导入、组态与KRL编程实战

库卡配商科焊机走Profinet:GSD导入、组态与KRL编程实战

简介&#xff1a;这份课程资源面向工业自动化工程师、机器人调试人员及焊接产线集成学习者&#xff0c;聚焦库卡机器人与商科焊机通过Profinet建立通信的配置方法&#xff0c;解决设备互联、GSD文件导入与焊接参数协同等实际问题。资源包共4个文件&#xff0c;压缩后约3.51MB&a…

2026/10/19 17:15:42 阅读更多 →
C#原生调用FFmpeg API实现低延迟RTMP拉流与渲染

C#原生调用FFmpeg API实现低延迟RTMP拉流与渲染

简介&#xff1a;本资源是一份面向C#开发者学习FFmpeg底层API调用的实战型工程示例&#xff0c;重点解决C#生态中FFmpeg原生接口调用资料稀缺的问题。项目完整实现了通过ffmpeg.autogen库拉取RTMP流并本地解码播放的核心流程&#xff0c;代码可直接复用&#xff0c;仅需在C/C风…

2026/10/19 17:15:42 阅读更多 →
科技中介如何以专业性与流程化管理提升转化效率

科技中介如何以专业性与流程化管理提升转化效率

科技中介这个行业很有意思&#xff0c;表面上做的是“对接”&#xff0c;实际上干的是“翻译”和“信任”的活儿。客户带着一句“我要做个系统”&#xff0c;技术团队那边等着对方给一份需求文档&#xff0c;中间隔着天堑。很多科技中介做着做着就变成了纯粹拉皮条——拿到需求…

2026/10/19 17:15:42 阅读更多 →
The Agency实战:用AI专家团队告别平庸回答

The Agency实战:用AI专家团队告别平庸回答

你有没有过这种经历&#xff1a;把一个问题认真丢给AI&#xff0c;等了半天&#xff0c;它给你回了一篇工整、流畅、挑不出毛病的答案&#xff0c;但读完之后你想说的话只有一句——“这也太水了吧。”这种“水”不是错&#xff0c;甚至不是逻辑不通&#xff0c;而是所有句子都…

2026/10/19 17:15:42 阅读更多 →
推理时加载LoRA:动态挂载适配器的工程实践与踩坑指南

推理时加载LoRA:动态挂载适配器的工程实践与踩坑指南

以前我训练完LoRA&#xff0c;习惯直接合并到底座模型上&#xff0c;再保存一份完整的权重文件&#xff0c;推理时按普通模型加载。直到有一次&#xff0c;同一个底座要同时服务“写作文风、代码补全、翻译”三种能力&#xff0c;我才发现这条路子有多笨&#xff1a;为什么要为…

2026/10/19 17:15:42 阅读更多 →
XSS跨站脚本攻击全解析:从漏洞原理到防护实战

XSS跨站脚本攻击全解析:从漏洞原理到防护实战

1. 场景&#xff1a;XSS 最容易藏身的业务角落很多人对 XSS&#xff08;Cross-Site Scripting&#xff0c;跨站脚本攻击&#xff09;的第一印象&#xff0c;还停留在“给输入框塞一段<script>alert(1)</script>&#xff0c;然后页面弹了个框”这种入门演示上。但实…

2026/10/19 17:14:40 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介&#xff1a;本资源为电源技术方向的学术研究文档&#xff0c;聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略&#xff0c;面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集&#xff0c;共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →