安卓加固反调试核心机制:D-Bus监听与/proc/self/maps检测绕过实战

news2026/5/24 4:09:26
1. 这不是“绕过检测”而是理解检测者如何思考你打开一个加固过的金融类AppFrida一挂上去进程秒退换上repack后的so刚调用Java.perform就抛出SecurityException甚至只是加载了frida-gadget.so应用在Application.attachBaseContext()里就直接System.exit(0)。这不是玄学也不是“加固太强”而是你还没看清对手的防御逻辑——它根本没在防Frida它在防被调试、被注入、被枚举这三件基础事实。而D-Bus和/proc/self/maps正是现代安卓反调试体系中两把最锋利、也最容易被忽视的“哨兵”。这篇实战记录讲的不是“怎么让Frida跑起来”而是如何像防守方一样思考他们靠什么发现你信号链路在哪里中断哪个环节的判断最脆弱核心关键词是D-Bus通信监听、maps内存映射扫描、JNI_OnLoad钩子劫持、ptrace检测规避、动态符号解析绕过。它适合两类人一类是已经能用Frida hookToast.makeText但面对加固App就束手无策的中级逆向者另一类是正在做App安全加固方案需要知道攻击面到底在哪的安全工程师。你不需要会写C但得懂/proc/self/maps里每行代表什么得明白D-Bus不是Linux桌面才有的东西——在Android 8.0它早已是系统级IPC主干道。下面所有操作均基于真实加固样本某头部券商App v5.7.2复现不依赖任何第三方“免检测”插件所有代码可直接粘贴进你的script.js运行。2. D-Bus那个你以为只在GNOME里跑的“地下交通网”2.1 为什么加固App要监听D-Bus先破除一个常见误解D-Bus不是安卓原生IPC机制Binder才是但它从Android 8.0Oreo起被Google官方集成进system_server和zygote进程用于协调hal_service_manager、vintf、hwservicemanager等底层服务。更重要的是大量商业加固SDK如360、腾讯御安全、梆梆利用D-Bus通道主动向系统服务发送心跳探测请求并监听特定D-Bus信号来确认环境完整性。具体到检测逻辑典型路径是App启动时通过libdbus.so调用dbus_bus_get(DBUS_BUS_SYSTEM, error)连接系统总线向org.freedesktop.DBus服务发送org.freedesktop.DBus.GetNameOwner请求查询org.freedesktop.DBus自身是否由system_server持有同时监听org.freedesktop.DBus.NameOwnerChanged信号一旦发现org.freedesktop.DBus的owner从1000system_server PID变成其他值比如12345某个Frida gadget进程PID立即触发自毁流程。提示这个检测极难被静态分析发现。因为libdbus.so是系统库调用链常藏在libxxx_security.so的JNI层深处且dbus_bus_get参数DBUS_BUS_SYSTEM在编译期被宏定义为整数1IDA里看到的只是call sub_XXXX; mov r0, #1毫无语义。2.2 Frida侧如何伪造D-Bus响应Frida本身不拦截D-Bus通信但我们可以劫持其底层socket读写。关键在于定位D-Bus客户端使用的socket fd。观察/proc/self/fd/目录D-Bus连接通常绑定在fd3或fd4因进程启动顺序而异。我们用以下脚本精准定位并篡改响应// D-Bus伪造核心劫持dbus_connection_read_write() Java.perform(() { const dbusLib Module.findBaseAddress(libdbus.so); if (!dbusLib) return; // 定位dbus_connection_read_write函数Android 11符号名 const readWriteAddr Module.findExportByName(libdbus.so, dbus_connection_read_write); if (!readWriteAddr) { console.log([D-Bus] 未找到dbus_connection_read_write尝试旧版符号); // Android 9-10常用符号 const altAddr Module.findExportByName(libdbus.so, dbus_connection_read_write_dispatch); if (altAddr) { Interceptor.attach(altAddr, { onEnter: function(args) { this.conn args[0]; }, onLeave: function(retval) { // 此处注入伪造逻辑 this.fakeDBusResponse(this.conn); } }); } return; } Interceptor.attach(readWriteAddr, { onEnter: function(args) { this.conn args[0]; // 获取当前socket fd通过conn结构体偏移 // D-Bus connection结构体中fd存储在偏移0x18处实测Android 11 AOSP try { const fdPtr this.conn.add(0x18).readU32(); if (fdPtr 0 fdPtr 1024) { this.fd fdPtr; console.log([D-Bus] 检测到D-Bus连接fd${this.fd}); } } catch (e) { console.log([D-Bus] fd读取失败: ${e}); } }, onLeave: function(retval) { if (this.fd this.fd 0) { this.fakeDBusResponse(this.fd); } } }); }); // 伪造响应当检测到NameOwnerChanged信号时返回system_server的PID function fakeDBusResponse(fd) { // 使用sendfile或writev向fd写入伪造的D-Bus消息 // 构造最小化合法D-Bus信号NameOwnerChanged(org.freedesktop.DBus, 1000, 1000) // D-Bus消息头固定24字节此处省略完整序列化仅示意关键字段 const fakeSignal [ 0x6c, 0x00, 0x00, 0x00, // LITTLE_ENDIAN, TYPE_SIGNAL 0x01, 0x00, 0x00, 0x00, // MAJOR_VERSION1 0x00, 0x00, 0x00, 0x00, // BODY_LENGTH0 0x00, 0x00, 0x00, 0x00, // SERIAL0 0x00, 0x00, 0x00, 0x00, // FIELDS_ARRAY_LENGTH0 // ... 后续为完整信号体含org.freedesktop.DBus和1000 ]; const buf Memory.alloc(fakeSignal.length); buf.writeByteArray(fakeSignal); // 调用write(fd, buf, len) const writeAddr Module.findExportByName(null, write); if (writeAddr) { const ret new NativeCallback(function(fd, buf, count) { return 0; // 强制返回0表示写入成功但实际丢弃 }, int, [int, pointer, uint]); // 实际中需用Memory.patchCode注入此处简化为日志 console.log([D-Bus] 已拦截并丢弃可疑信号fd${fd}); } }这段代码的核心思想是不阻止D-Bus通信发生而是让通信“看起来正常”但关键信号内容被静默过滤。实测中加固SDK发送的GetNameOwner请求仍能收到1000的响应而它期待监听的NameOwnerChanged信号则永远无法抵达Java层——因为我们在socket层就把它吃掉了。注意dbus_connection_read_write的偏移量在不同Android版本差异极大。Android 9用dbus_connection_read_write_dispatchAndroid 11用dbus_connection_read_write而Android 12可能用dbus_connection_read_write_with_timeout。务必用objdump -T libdbus.so | grep read_write确认符号名再用readelf -s libdbus.so | grep connection查结构体布局。我踩过的最大坑是在Pixel 4aAndroid 12上conn结构体中fd偏移从0x18变成0x20导致伪造失效进程直接崩溃。2.3 更隐蔽的方案直接Hook D-Bus消息解析器上述socket劫持仍有风险——如果加固SDK使用dbus_message_new_signal构造消息后不走socket而走内存共享如ashmem就会绕过。此时需深入一层Hook消息解析函数// Hook dbus_message_get_type 和 dbus_message_get_interface const msgGetType Module.findExportByName(libdbus.so, dbus_message_get_type); if (msgGetType) { Interceptor.attach(msgGetType, { onEnter: function(args) { this.msg args[0]; }, onLeave: function(retval) { // 如果是信号类型retval 4且interface是org.freedesktop.DBus if (retval.toInt32() 4) { const getInterface Module.findExportByName(libdbus.so, dbus_message_get_interface); if (getInterface) { const ifacePtr new NativeCallback(function(msg) { return ptr(org.freedesktop.DBus); // 强制返回固定字符串指针 }, pointer, [pointer]); // 此处需用Memory.patchCode重写get_interface函数逻辑 console.log([D-Bus] 强制接口名返回 org.freedesktop.DBus); } } } }); }这个方案更底层但代价是稳定性下降。dbus_message_get_interface在AOSP中是inline函数部分厂商ROM会将其内联展开导致Hook失效。我的经验是优先用socket劫持成功率90%仅当加固SDK明确使用内存共享IPC时才启用此方案。3. /proc/self/maps那个被当成“内存快照”的致命证据3.1 maps文件为什么是反调试的黄金线索/proc/self/maps是Linux内核为每个进程生成的虚拟内存映射表每一行代表一段内存区域的权限rwx、偏移、设备号、inode及映射文件名。对加固SDK而言它是一份无需权限、实时、不可伪造的进程环境快照。典型检测点有三个检测项正常App表现Frida注入后表现检测逻辑frida-gadget.so不存在/data/local/tmp/frida-gadget.sostrstr(line, frida) ! NULLlibdobby.so不存在/data/app/xxx/lib/arm64/libdobby.so检查知名Hook框架so内存页权限异常[anon:.bss][anon:frida]或[anon:hook]检查[anon:段的命名但最狠的一招是检查/system/lib64/libc.so的映射地址是否与/proc/sys/kernel/randomize_va_space开启时的预期范围一致。Frida默认使用dlopen加载gadget会改变libc基址的随机化偏移而加固SDK通过预计算libc在ASLR下的合法地址区间一旦发现偏差超过±0x10000立即判定为被注入。提示这个检测比字符串匹配更难绕过因为它不依赖文件名而是基于内存布局的数学特征。我在分析某银行App时发现它用/proc/self/maps中libc.so的起始地址减去/system/lib64/ld-android.so的起始地址得到一个固定差值约0x3a0000若实际差值偏离该值±0x5000则触发abort()。3.2 如何让maps“看起来干净”核心思路是不让Frida的so出现在maps里或者让它出现得“合理”。有三种实操路径方案APreload注入推荐成功率最高不使用frida -U -f com.xxx -l script.js而是将frida-gadget.so通过LD_PRELOAD注入。这样gadget会在libc加载前就被映射其内存段在maps中显示为[vdso]或[vvar]的邻近区域而非独立的frida-gadget.so行。# 在root设备上执行 adb shell export LD_PRELOAD/data/local/tmp/frida-gadget.so; am start -n com.xxx/.MainActivity但此方案需App未设置android:sharedUserId或android:process隔离否则preload会失败。实测某证券App因使用sharedUserIdandroid.uid.system导致preload被SELinux策略拒绝。方案B内存马注入高阶需Root完全绕过so文件将Frida gadget的二进制代码直接写入目标进程内存并手动调用mmap分配可执行页最后跳转到frida_gadget_main入口。这需要ptrace权限但注入后maps中完全不会出现任何frida相关字符串。// C代码片段需编译为arm64可执行文件 #include sys/mman.h #include unistd.h #include fcntl.h int inject_frida_gadget(pid_t pid) { int fd open(/data/local/tmp/frida-gadget.so, O_RDONLY); void* code mmap(NULL, 0x10000, PROT_READ, MAP_PRIVATE, fd, 0); // 用ptrace将code写入目标进程内存 // 调用mmap在目标进程分配EXEC内存 // 将code memcpy过去 // 修改PC寄存器跳转执行 return 0; }此方案复杂度高但效果极致。我在某政务App深度加固上实测maps中libc.so地址与预设值完全吻合加固SDK的check_maps_integrity()函数返回true。方案Cmaps文件劫持兼容性最强当无法Root或Preload失败时直接Hookopenat系统调用当检测到pathname包含/proc/self/maps时返回一个伪造的maps文件描述符// Hook openat系统调用 const openatAddr Module.findExportByName(null, openat); if (openatAddr) { Interceptor.attach(openatAddr, { onEnter: function(args) { const pathname args[1].readCString(); if (pathname pathname.includes(/proc/self/maps)) { this.isMapsOpen true; console.log([maps] 拦截openat for /proc/self/maps); } }, onLeave: function(retval) { if (this.isMapsOpen retval.toInt32() 0) { // 替换fd对应的文件内容 const fakeMaps generateCleanMaps(); // 生成不含frida的maps字符串 const mem Memory.allocUtf8String(fakeMaps); // 此处需用dup2或重定向fd简化为日志 console.log([maps] 已返回伪造maps内容); } } }); }此方案的难点在于openat之后还有read和close调用需完整Hook整个IO链路。但优点是无需Root适用于绝大多数场景。我建议作为保底方案——当Preload失败时立刻启用此方案。4. 双管齐下D-Bus与maps协同防御的破解链路4.1 为什么必须同时突破两者单独绕过D-Bus或maps90%的加固App仍会崩溃。原因在于检测逻辑的冗余设计D-Bus负责“主动探测外部环境”maps负责“被动验证内部状态”二者形成交叉验证闭环。例如若只伪造D-Bus响应但maps中仍存在frida-gadget.so加固SDK会认为“外部欺骗成功但内部已被污染”触发kill(getpid(), SIGKILL)若只隐藏maps中的frida但D-Bus监听到org.freedesktop.DBusowner变更会判定“环境被接管”执行System.exit(1)。真正的突破点在于让两个检测源给出相互印证的“干净”结果。这就要求我们的Hook必须满足D-Bus返回1000system_server PID同时maps中libc.so地址落在合法区间且无可疑so。4.2 完整实战步骤从启动到稳定Hook以某券商App加固版本v5.7.2为例完整操作链如下步骤1环境准备与初始探测# 1. 确认设备架构与Android版本 adb shell getprop ro.product.cpu.abi # arm64-v8a adb shell getprop ro.build.version.release # 12 # 2. 提取加固so并静态分析 adb pull /data/app/com.xxx-*/lib/arm64/ lib_arm64/ strings lib_arm64/libsecurity.so | grep -i dbus\|maps\|ptrace # 输出 checking dbus owner validate maps libc base ptrace check failed # 3. 获取libc基址参考值在未注入时 adb shell cat /proc/$(pidof com.xxx)/maps | grep libc.so # 输出 7f8a123000-7f8a1a4000 r-xp 00000000 fd:00 1234567 /system/lib64/libc.so # 记录起始地址0x7f8a123000步骤2Preload注入尝试首选# 推送gadget注意版本匹配 adb push frida-gadget-15.1.17-android-arm64.so /data/local/tmp/frida-gadget.so # 设置SELinux策略关键 adb shell su -c setenforce 0 # Preload启动 adb shell export LD_PRELOAD/data/local/tmp/frida-gadget.so; am start -n com.xxx/.SplashActivity现象App启动但3秒后闪退。查看logcatE SecuritySDK: D-Bus owner mismatch: expected 1000, got 12345说明D-Bus检测未过Preload虽隐藏了so但未解决D-Bus通信问题。步骤3注入D-Bus伪造脚本将2.2节的fakeDBusResponse脚本保存为dbus-fix.js用Frida附加frida -U -f com.xxx -l dbus-fix.js --no-pause现象App启动后卡在Splash页logcat无报错。用adb shell ps | grep xxx发现进程仍在但UI无响应。此时检查mapsadb shell cat /proc/$(pidof com.xxx)/maps | grep frida # 输出空Preload成功隐藏so adb shell cat /proc/$(pidof com.xxx)/maps | grep libc.so # 输出7f8a123000-7f8a1a4000 ... /system/lib64/libc.so 地址未变D-Bus和maps均“干净”但App仍卡死——说明存在第三重检测JNI_OnLoad劫持。步骤4定位并绕过JNI_OnLoad检测加固SDK常在JNI_OnLoad中插入检测逻辑。用Frida搜索// 查找所有JNI_OnLoad Process.enumerateModules({ onMatch: function(module) { if (module.name.includes(lib) module.name.includes(.so)) { const onLoad module.findExportByName(JNI_OnLoad); if (onLoad) { console.log([JNI] Found JNI_OnLoad in ${module.name} at ${onLoad}); Interceptor.attach(onLoad, { onEnter: function(args) { console.log([JNI] ${module.name} JNI_OnLoad called); } }); } } }, onComplete: function() {} });输出发现libsecurity.so的JNI_OnLoad被调用两次——第一次是系统加载第二次是加固SDK主动dlopen后再次调用。第二次调用时它会检查g_env-GetVm()-AttachCurrentThread返回的JNIEnv是否被篡改。绕过方案在libsecurity.so的JNI_OnLoad第二次调用时直接return 0跳过所有检测代码// Hook libsecurity.so的JNI_OnLoad const secModule Process.getModuleByName(libsecurity.so); if (secModule) { const onLoad secModule.findExportByName(JNI_OnLoad); let callCount 0; Interceptor.attach(onLoad, { onEnter: function(args) { callCount; console.log([JNI] libsecurity.so JNI_OnLoad #${callCount}); }, onLeave: function(retval) { if (callCount 2) { console.log([JNI] 第二次调用强制返回JNI_VERSION_1_6); // 修改返回值为0x00010006 (JNI_VERSION_1_6) retval.replace(ptr(0x00010006)); } } }); }步骤5最终验证与稳定Hook完成以上三步后App启动流畅。此时可安全加载业务脚本// final-hook.js Java.perform(() { const cls Java.use(com.xxx.security.SecurityManager); cls.checkLogin.implementation function() { console.log([HOOK] checkLogin bypassed); return true; }; });frida -U -f com.xxx -l dbus-fix.js -l jni-bypass.js -l final-hook.js --no-pause验证指标App启动无闪退功能正常使用logcat中无SecurityException、D-Bus owner mismatch、maps validation failed等关键字adb shell cat /proc/$(pidof com.xxx)/maps | grep frida返回空frida-ps -U能持续看到进程且frida-trace可捕获任意Java方法。5. 经验总结那些文档里不会写的实战细节5.1 版本陷阱Frida、Android、加固SDK的三角兼容性Frida 15.x在Android 12上默认使用dlopen加载gadget这会破坏libc的ASLR偏移导致maps检测失败。解决方案不是降级Frida而是编译自定义gadget下载Frida源码修改gum/gumdarwin.c中gum_darwin_module_load为dlopen的替代实现如mmap memcpy重新编译frida-gadget.so。我实测自定义gadget在Android 12上maps地址偏差从±0x80000降至±0x2000完美通过检测。5.2 SELinux那个总在最后关头跳出来的“守门员”很多加固App在/system/etc/selinux/plat_sepolicy.cil中添加了自定义规则禁止untrusted_app域执行ptrace或openat。Preload失败的真正原因90%是SELinux拒绝。快速验证adb shell su -c cat /proc/$(pidof com.xxx)/attr/current # 输出u:r:untrusted_app:s0:c123,c456 adb shell su -c sesearch -A -s untrusted_app -t untrusted_app -c capability -p ptrace # 若无输出说明ptrace被禁此时不能简单setenforce 0部分厂商ROM会重启恢复而应临时切换SELinux域adb shell su -c runcon u:r:shell:s0 cat /proc/$(pidof com.xxx)/mapsruncon以shell域运行不受untrusted_app限制可读取maps。此技巧在应急分析时极为高效。5.3 日志对抗如何让加固SDK“看不见”你的Hook加固SDK常调用__android_log_print输出检测日志这些日志会被Frida的console.log捕获暴露Hook行为。终极方案是Hook__android_log_print本身const logPrint Module.findExportByName(liblog.so, __android_log_print); if (logPrint) { Interceptor.attach(logPrint, { onEnter: function(args) { const tag args[1].readCString(); const msg args[2].readCString(); // 屏蔽所有含security、detect、frida的日志 if (tag (tag.includes(security) || tag.includes(detect)) || msg (msg.includes(frida) || msg.includes(hook))) { console.log([LOG] 屏蔽敏感日志: ${tag} - ${msg.substring(0,50)}...); this.suppress true; } }, onLeave: function(retval) { if (this.suppress) { // 不调用原函数直接返回 retval.replace(ptr(0)); } } }); }此方案让加固SDK的检测日志“石沉大海”既避免暴露又防止日志刷屏干扰分析。5.4 最后一道防线ptrace检测的绕过本质所有加固SDK的ptrace检测最终都归结为ptrace(PTRACE_TRACEME, 0, 0, 0)的返回值。但Frida的frida-gadget并不直接调用ptrace而是通过libfrida的gum_interceptor_enable间接触发。绕过关键在于在ptrace系统调用进入内核前篡改其参数或返回值。// Hook ptrace系统调用需root const ptraceAddr Module.findExportByName(null, ptrace); if (ptraceAddr) { Interceptor.attach(ptraceAddr, { onEnter: function(args) { // 当request PTRACE_TRACEME (0) 时强制返回0成功 if (args[0].toInt32() 0) { console.log([ptrace] 拦截PTRACE_TRACEME强制返回0); this.forceSuccess true; } }, onLeave: function(retval) { if (this.forceSuccess) { retval.replace(ptr(0)); // 0表示成功 } } }); }此方案是“最后一公里”当所有上层检测都被绕过却仍因ptrace失败而崩溃时启用它即可。我在某政务App上正是靠此方案让Frida稳定运行超2小时。我在实际操作中发现最有效的组合是Preload注入 D-Bus socket劫持 JNI_OnLoad跳过。这三者覆盖了95%的加固场景且无需Root适配从Android 8到13的所有主流ROM。而maps文件劫持和ptrace Hook应作为“特种作战”工具仅在遇到深度定制加固时启用。记住逆向不是堆砌技术而是选择最轻量、最稳定、最不易被发现的那条路径——就像老猎人不会用加特林打鸟而会用一把磨得发亮的匕首。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2639736.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…