尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

攻克So层SSL Pinning:Frida与IDA逆向实战指南

攻克So层SSL Pinning:Frida与IDA逆向实战指南
📅 发布时间:2026/7/13 5:29:21

1. 项目概述与核心挑战

如果你在小米8这类老款安卓设备上尝试用Charles或Burp Suite抓取某些App的网络包,大概率会遇到一个经典的“拦路虎”——SSL Pinning(证书锁定)。症状很典型:代理设置好了,CA证书也装上了,但App要么直接提示“网络错误”,要么在抓包工具里只能看到一堆CONNECT请求,具体内容全是加密的乱码。这背后的核心原因,就是应用在代码层面“锁死”了只信任自家服务器的证书,你安装的中间人证书即使被系统信任,也会因为指纹不匹配而被无情拒绝。

标题里提到的“So层SSL Pinning”,正是这个问题的“地狱难度”版本。当App将证书校验逻辑下沉到Native层,用C/C++代码在.so动态链接库里实现时,传统的、针对Java层的通用绕过脚本就彻底失效了。你会在Logcat里看到javax.net.ssl.SSLHandshakeException,或者干脆连异常都没有,连接直接静默失败。这时候,就需要祭出逆向工程领域的两大“神器”:Frida和IDA Pro。Frida负责动态运行时注入和Hook,而IDA则用于静态分析so库,定位那些深藏不露的校验函数。这篇教程,就是为你拆解这个组合拳,手把手带你攻克So层的证书堡垒。

2. 环境准备与工具链搭建

工欲善其事,必先利其器。在开始“手术”之前,我们需要一个稳定且版本匹配的工具环境。很多新手在这里栽跟头,问题往往出在版本不匹配或者环境配置错误。

2.1 Frida环境配置:版本同步是关键

首先,在电脑端安装Frida客户端工具。打开你的终端或命令行,执行以下命令:

pip install frida-tools

安装完成后,用frida --version查看版本号,比如16.1.4。这是最关键的一步:你必须下载与此主版本号完全一致的frida-server。去Frida的GitHub Release页面,找到对应版本,根据你的手机架构(小米8是arm64)下载frida-server-16.1.4-android-arm64.xz。

接下来,将frida-server推送到手机并启动:

# 连接手机,确保USB调试已开启 adb devices # 推送frida-server到临时目录 adb push frida-server-16.1.4-android-arm64 /data/local/tmp/ adb shell chmod 755 /data/local/tmp/frida-server-16.1.4-android-arm64 # 以root权限在后台运行(需要手机已root) adb shell su -c “/data/local/tmp/frida-server-16.1.4-android-arm64 -D &”

验证是否运行成功:

frida-ps -U

如果能看到手机上的进程列表,恭喜你,Frida服务端已经成功跑起来了。

实操心得:很多连接失败问题都源于版本不匹配。务必保证frida-tools、fridaPython包以及frida-server三者的主版本号(如16.x)完全一致。如果遇到Unable to connect to remote frida-server错误,第一件事就是检查版本。

2.2 抓包代理与系统证书安装

接下来配置抓包环境。我习惯用Burp Suite,你也可以用Charles,原理相通。

  1. 设置代理:在手机Wi-Fi设置中,手动配置代理,指向你电脑的IP和Burp监听的端口(默认8080)。
  2. 安装CA证书:这是另一个大坑,尤其是Android 7.0之后。仅仅把Burp的CA证书安装到“用户凭据”里是没用的,对于很多App来说,它们默认不信任用户安装的证书。必须将CA证书安装到系统信任的存储区。

对于已Root的设备(如刷了Magisk的小米8),最方便的方法是使用Magisk模块“MagiskTrustUserCerts”。安装后重启,它会自动将用户证书提升为系统证书。如果没有Magisk,则需要手动操作:

# 从Burp导出DER格式的CA证书(cacert.der) # 转换为PEM格式并计算哈希 openssl x509 -inform DER -in cacert.der -out cacert.pem HASH=$(openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1) cp cacert.pem “${HASH}.0” # 推送至系统证书目录(需要/system分区可写,在Recovery或使用Magisk挂载) adb push “${HASH}.0” /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/${HASH}.0 adb reboot

重启后,在系统的“信任的凭据” -> “系统”标签页下,应该能看到PortSwigger(Burp)的证书。

注意事项:Android 14及更高版本对/system分区的保护更严格,即使adb remount也可能无法写入。此时Magisk模块几乎是唯一省心的选择。

2.3 IDA Pro基础准备

IDA Pro(Interactive Disassembler)是我们的静态分析主力。对于So层分析,我们主要用到它的反汇编和函数识别能力。不需要你成为逆向专家,但需要了解基本操作:

  • 加载So文件:将目标App的APK解压,在lib/arm64-v8a/或lib/armeabi-v7a/目录下找到目标so库(如libttboringssl.so,libnetguard.so等),用IDA打开。
  • 识别关键函数:在IDA的“Exports”窗口或“Functions”窗口中,搜索SSL_、verify、cert等关键字,寻找可能的校验函数。
  • 分析函数逻辑:查看函数的交叉引用(Xrefs),了解它在哪里被调用,初步判断其作用。

我们的目标不是完全逆向整个so,而是结合Frida的动态行为,快速定位到那个关键的证书验证函数。

3. So层SSL Pinning原理与定位策略

为什么So层的Pinning更难搞?因为它跳出了Java虚拟机的管辖范围。应用通过JNI(Java Native Interface)调用编译好的原生代码,这些代码直接与操作系统底层的SSL库(如OpenSSL/BoringSSL)交互,执行证书验证。Java层的Hook点(如TrustManager)在这里完全不起作用。

3.1 常见So层实现方式

  1. 链接系统libssl.so:这是最常见的方式。App在Native代码中直接调用系统提供的SSL库函数,如SSL_CTX_set_verify。我们的Hook目标就是这个系统库。
  2. 静态编译或自带SSL库:更高阶的应用会把BoringSSL等库的代码静态编译进自己的so文件(如libnative-lib.so),或者打包一个独立的动态库(如字节系的libttboringssl.so)。这时,校验逻辑就在应用自定义的库中,我们需要分析这个特定的so。
  3. 自定义验证回调:无论是哪种链接方式,核心都是通过SSL_CTX_set_verify或BoringSSL特有的SSL_CTX_set_custom_verify函数,注册一个自定义的回调函数。这个回调函数才是执行证书指纹比对、决定连接是否放行的“法官”。

3.2 逆向定位核心校验函数

面对一个陌生的so,如何快速找到那个“法官”?这里分享我的实战定位流程:

第一步:字符串搜索在IDA中按下Shift+F12打开字符串窗口,搜索以下关键词:

  • certificat
  • verify
  • pin
  • sha256
  • public.key
  • handshake/SSL
  • 特定的错误信息,如verification failed、untrusted。

找到这些字符串后,点击它,然后按X查看交叉引用,就能找到使用这些字符串的函数。这些函数很可能就是校验逻辑的一部分。

第二步:导出函数分析查看IDA的“Exports”标签页,直接寻找SSL相关的导出函数。重点关注:

  • SSL_CTX_set_verify
  • SSL_CTX_set_custom_verify(BoringSSL)
  • SSL_CTX_set_cert_verify_callback(BoringSSL)
  • SSL_get_verify_result

如果这些函数被导出,那么Hook它们就是最直接的入口。

第三步:基于Frida的动态探测当静态分析毫无头绪时,Frida的动态插桩能力就是我们的“雷达”。我们可以写一个脚本来Hook所有可能的SSL相关函数,并打印调用堆栈,从而定位到应用实际使用的函数。

// trace_ssl.js - SSL函数追踪脚本 Java.perform(function() { // 先确保能连接到Native库 }); // 监听所有模块的加载 Interceptor.attach(Module.findExportByName(null, “dlopen”), { onEnter: function(args) { this.libpath = args[0].readCString(); if (this.libpath && this.libpath.indexOf(“.so”) !== -1) { console.log(“[*] dlopen: “ + this.libpath); } }, onLeave: function(retval) { if (retval.toInt32() != 0) { // 库加载成功后,尝试Hook常见SSL函数 var libName = this.libpath.split(‘/’).pop(); hookCommonSSLFunctions(libName); } } }); function hookCommonSSLFunctions(libName) { var funcs = [“SSL_CTX_set_verify”, “SSL_set_verify”, “SSL_get_verify_result”, “SSL_CTX_set_custom_verify”, “SSL_CTX_set_cert_verify_callback”]; funcs.forEach(function(funcName) { var addr = Module.findExportByName(libName, funcName); if (addr) { console.log(“[+] Found “ + funcName + ” in ” + libName + ” @ ” + addr); Interceptor.attach(addr, { onEnter: function(args) { console.log(“[>] Called: ” + funcName); // 打印调用栈,帮助定位上层逻辑 console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join(‘\n’) + ‘\n’); } }); } }); }

运行这个脚本后操作App触发网络请求,观察控制台输出。哪个函数被调用了,它的调用栈是什么,一目了然。这能极大缩小我们的分析范围。

4. 实战:使用Frida Hook So层函数

定位到关键函数后,真正的绕过就开始了。Frida提供了强大的Interceptor API来操作Native函数。

4.1 基础Hook:让验证失效

假设我们通过分析,确定目标App使用了系统libssl.so的SSL_CTX_set_verify函数。我们的目标是让验证失效。这个函数原型是:void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));其中mode参数为SSL_VERIFY_NONE(0)时表示不验证。

// hook_libssl.js Interceptor.attach(Module.findExportByName(“libssl.so”, “SSL_CTX_set_verify”), { onEnter: function(args) { // args[0] 是 SSL_CTX* // args[1] 是 mode (int) // args[2] 是 verify_callback 函数指针 console.log(“[*] SSL_CTX_set_verify called. Original mode: “ + args[1].toInt32()); // 关键操作:将验证模式强制设为 SSL_VERIFY_NONE (0) args[1] = ptr(0x0); // 同时清空自定义回调函数指针,防止它被调用 args[2] = ptr(0x0); console.log(“[+] Mode forced to SSL_VERIFY_NONE, callback cleared.”); } });

这个Hook非常简单粗暴:无论应用原本想设置何种严格的验证模式,我们都把它改成“不验证”。对于很多使用标准OpenSSL接口的应用,这一招就足够了。

4.2 进阶Hook:处理自定义验证回调

有些应用不仅设置验证模式,还会传入一个自定义的verify_callback函数。这时,仅仅设置mode为0可能不够,因为应用可能在回调函数里还有别的逻辑。我们需要连这个回调函数一起“解决”掉。

方法一:替换回调函数。我们在HookSSL_CTX_set_verify时,如果发现它传入了回调函数指针(args[2]不为空),就用一个我们自己编写的、永远返回成功(返回值为1)的假回调函数替换它。

Interceptor.attach(Module.findExportByName(“libssl.so”, “SSL_CTX_set_verify”), { onEnter: function(args) { var callbackPtr = args[2]; if (!callbackPtr.isNull()) { console.log(“[*] Found custom verify_callback @ ” + callbackPtr); // 创建一个新的NativeCallback,总是返回1(验证成功) var fakeCallback = new NativeCallback(function(ok, storeCtx) { console.log(“[*] Fake verify_callback executed, returning 1 (success).”); return 1; }, ‘int’, [‘int’, ‘pointer’]); // 函数签名:int (*)(int, X509_STORE_CTX*) // 替换原有的回调指针 args[2] = fakeCallback; } // 同样,将模式设为NONE更保险 args[1] = ptr(0x0); } });

方法二:Hook验证结果函数。另一个思路是HookSSL_get_verify_result函数,它返回最终的验证结果(0表示X509_V_OK,即成功)。我们强制让它返回0。

Interceptor.attach(Module.findExportByName(“libssl.so”, “SSL_get_verify_result”), { onLeave: function(retval) { console.log(“[*] SSL_get_verify_result intercepted. Forcing X509_V_OK.”); retval.replace(ptr(0x0)); // 替换返回值为0(成功) } });

这种方法更底层,不管前面有多少验证步骤,最终我们都告诉上层“验证通过了”。

4.3 针对BoringSSL与定制库的Hook

对于像libttboringssl.so(字节跳动系)或libboringssl.so这类定制库,方法类似,但函数名和参数可能略有不同。BoringSSL常用SSL_CTX_set_custom_verify,其回调函数返回值是ssl_verify_result_t枚举(ssl_verify_ok为0)。

var libName = “libttboringssl.so”; // 或 “libboringssl.so” var customVerifyAddr = Module.findExportByName(libName, “SSL_CTX_set_custom_verify”); if (customVerifyAddr) { Interceptor.attach(customVerifyAddr, { onEnter: function(args) { // args[2] 是 ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert) if (!args[2].isNull()) { var fakeCallback = new NativeCallback(function(ssl, out_alert) { console.log(“[*] BoringSSL custom_verify callback -> ssl_verify_ok”); return 0; // ssl_verify_ok }, “int”, [“pointer”, “pointer”]); args[2] = fakeCallback; console.log(“[+] Replaced SSL_CTX_set_custom_verify callback.”); } } }); }

4.4 处理未导出函数与偏移计算

最棘手的情况是:关键函数没有被导出(符号被剥离)。你在IDA的Exports列表里找不到SSL_CTX_set_custom_verify。这时候就需要结合静态分析来计算函数在内存中的偏移地址。

  1. 用IDA找到函数地址:在IDA中分析so文件,找到目标函数的起始地址。例如,你发现ssl_verify_peer_cert这个函数在IDA中的地址是0x123456。
  2. 计算基址偏移:这个0x123456是文件偏移地址或加载偏移。在Frida中,我们需要的是函数在内存中的绝对地址。公式是:内存绝对地址 = 模块基地址 + 函数在IDA中的偏移地址。
  3. 使用Frida Hook:
var libName = “libtarget.so”; var moduleBase = Module.findBaseAddress(libName); if (moduleBase) { // 假设通过IDA分析,得知关键校验函数 verify_cert 的偏移是 0x123456 var functionOffset = 0x123456; var targetAddress = moduleBase.add(functionOffset); console.log(“[*] Hooking unexported function @ ” + targetAddress); Interceptor.attach(targetAddress, { onEnter: function(args) { console.log(“[*] Custom verify function entered.”); }, onLeave: function(retval) { // 强制函数返回成功 (例如,返回1或一个表示成功的指针) console.log(“[*] Forcing custom verify function to return success.”); retval.replace(ptr(0x1)); // 根据函数实际返回值类型调整 } }); }

核心技巧:如何确定偏移量?在IDA中,你可以直接看函数起始地址。同时,确保IDA中设置的Loading address(镜像基址)与Frida中Module.findBaseAddress返回的基址计算方式一致。通常,如果so是动态加载的,IDA默认的基址是0,那么函数地址就是偏移量。更稳妥的方法是:在IDA中找一个已导出的函数,记下它的地址A,同时在Frida中用Module.findExportByName得到它的运行时地址B。那么,偏移量 = 目标函数IDA地址 - 已导出函数IDA地址。目标函数运行时地址 = B + 偏移量。

5. 组合拳:整合Java层与Native层绕过

一个加固良好的App,往往会在Java层和Native层同时部署SSL Pinning,形成“双保险”。我们的绕过脚本也需要是多层防御的。一个完整的绕过脚本应该像下面这样,同时覆盖多个层面:

// universal_bypass.js Java.perform(function() { console.log(“[*] Starting universal SSL pinning bypass...”); // ——— 1. Java层:全局TrustManager替换(兜底方案)——— var TrustAllManager = Java.registerClass({ name: “com.bypass.TrustAllManager”, implements: [Java.use(“javax.net.ssl.X509TrustManager”)], methods: { checkClientTrusted: function() {}, checkServerTrusted: function() {}, getAcceptedIssuers: function() { return []; } } }); var sslContext = Java.use(“javax.net.ssl.SSLContext”); sslContext.init.overload(‘[Ljavax.net.ssl.KeyManager;’, ‘[Ljavax.net.ssl.TrustManager;’, ‘java.security.SecureRandom’).implementation = function(km, tm, sr) { console.log(“[+] Hooking SSLContext.init, installing TrustAllManager.”); var trustAllArray = Java.array(‘javax.net.ssl.TrustManager’, [TrustAllManager.$new()]); this.init(km, trustAllArray, sr); }; // ——— 2. Java层:针对OkHttp的CertificatePinner ——— try { var CertPinner = Java.use(“okhttp3.CertificatePinner”); CertPinner.check.overload(‘java.lang.String’, ‘java.util.List’).implementation = function() { console.log(“[+] Bypassing OkHttp CertificatePinner.check()”); // 直接返回,不执行校验 }; } catch(e) { console.log(“[-] OkHttp not found or already hooked.”); } // ——— 3. Java层:WebView SSL错误处理 ——— var WebViewClient = Java.use(“android.webkit.WebViewClient”); WebViewClient.onReceivedSslError.implementation = function(view, handler, error) { console.log(“[+] Bypassing WebView SSL error.”); handler.proceed(); // 忽略错误,继续加载 }; }); // ——— 4. Native层:系统libssl.so ——— var sslLib = ‘libssl.so’; var sslBase = Module.findBaseAddress(sslLib); if (sslBase) { console.log(“[+] Found ” + sslLib + ” @ ” + sslBase); // Hook标准验证模式设置 var setVerifyAddr = Module.findExportByName(sslLib, “SSL_CTX_set_verify”); if (setVerifyAddr) { Interceptor.attach(setVerifyAddr, { onEnter: function(args) { args[1] = ptr(0); // SSL_VERIFY_NONE args[2] = ptr(0); // 清除回调 console.log(“[+] “ + sslLib + ” SSL_CTX_set_verify -> NONE”); } }); } // Hook验证结果获取 var getResultAddr = Module.findExportByName(sslLib, “SSL_get_verify_result”); if (getResultAddr) { Interceptor.attach(getResultAddr, { onLeave: function(retval) { retval.replace(ptr(0)); // X509_V_OK } }); } } // ——— 5. Native层:定制BoringSSL库(如libttboringssl.so)——— var targetLibs = [‘libttboringssl.so’, ‘libboringssl.so’]; targetLibs.forEach(function(libName) { var mod = Process.findModuleByName(libName); if (!mod) return; console.log(“[+] Targeting ” + libName); // 尝试Hook BoringSSL特有API var customVerifyAddr = Module.findExportByName(libName, “SSL_CTX_set_custom_verify”); if (customVerifyAddr) { Interceptor.attach(customVerifyAddr, { onEnter: function(args) { if (!args[2].isNull()) { var fakeCB = new NativeCallback(function(ssl, out_alert) { return 0; // ssl_verify_ok }, “int”, [“pointer”, “pointer”]); args[2] = fakeCB; console.log(“[+] Replaced SSL_CTX_set_custom_verify in ” + libName); } } }); } // 同样的,可以添加对SSL_CTX_set_cert_verify_callback等的Hook }); console.log(“[*] Universal bypass script loaded.”);

将上述脚本保存为bypass.js,使用Frida加载:

frida -U -f com.target.app --no-pause -l bypass.js

-f参数表示以spawn方式启动应用,--no-pause确保应用不会在启动时暂停,这对于绕过某些在启动时就进行校验的App很重要。

6. 疑难排查与进阶对抗

即使脚本写得再完美,实战中也可能遇到各种意外。下面是一些常见的“坑”及其解决方案。

6.1 脚本注入成功,但抓包仍失败

  1. 检查CA证书:确认Burp/Charles的CA证书已成功安装到系统信任区(System trust store),而不是用户区。在手机设置中查看“加密与凭据”->“信任的凭据”->“系统”页签。
  2. 检查代理流量:有些App不使用系统代理。可以尝试用iptables强制重定向流量:
    adb shell su -c “iptables -t nat -A OUTPUT -p tcp –dport 443 -j DNAT –to-destination <你的电脑IP>:8080”
    这条命令将所有从手机发出的443端口(HTTPS)流量重定向到你的抓包工具。用完记得清理规则:adb shell su -c “iptables -t nat -F OUTPUT”。
  3. 检查是否有多层Pinning:应用可能同时使用了Network Security Configuration(Android 7.0+)和Native Pinning。确保你的脚本也Hook了NetworkSecurityTrustManager(见上方完整脚本的Java层部分)。
  4. 查看Frida输出:仔细阅读Frida控制台的输出,看你的Hook点是否真的被触发。如果没有[+]或[*]开头的成功日志,说明Hook的类或函数不对,需要重新分析。

6.2 应用启动崩溃或检测到Frida

高安全级别的App会进行反调试/反注入检测。

  • 检测Frida Server:扫描27042默认端口或查找frida-server进程。对策:修改Frida Server启动端口。
    # 在手机上 /data/local/tmp/frida-server -l 0.0.0.0:9999 # 在电脑上连接时指定端口 frida -H 192.168.1.100:9999 -f com.target.app -l bypass.js
  • 检测内存映射:读取/proc/self/maps检查是否有frida-agent字符串。对策:使用Frida Gadget模式,将frida-gadget.so打包进App,而不是通过frida-server注入。这更隐蔽,但需要重新打包APK。
  • 完整性校验:App可能检查自身的签名或Dex/So文件是否被修改。对策:尝试在App启动最早的时刻注入(使用-fspawn模式),在完整性校验执行前完成Hook。

6.3 应对代码混淆与加固

如果App的so库被加固(加壳),直接分析IDA加载的so可能是一团乱码。你需要先进行脱壳。对于Frida Hook来说,有时可以绕过脱壳:我们Hook的是运行时内存中已解压的代码。关键在于找到正确的时机。监听dlopen事件,在目标so被加载到内存的瞬间进行Hook,是一个有效的方法。

// 监听so加载 var dlopen = Module.findExportByName(null, “dlopen”); Interceptor.attach(dlopen, { onEnter: function(args) { var path = args[0].readCString(); if (path && path.includes(“libttboringssl.so”)) { // 替换为你的目标so名 this.targetLib = path; } }, onLeave: function(retval) { if (this.targetLib) { // 延迟一小段时间,确保so初始化完成 setTimeout(function() { console.log(“[*] ” + this.targetLib + ” loaded, installing hooks...”); // 在这里调用你的Hook函数 hookSpecificLib(this.targetLib); }.bind(this), 100); } } });

7. 案例复盘:小米8上的完整操作流

让我们串联起整个流程,假设目标是一个在小米8上使用了libttboringssl.so进行So层Pinning的App:

  1. 准备:手机解锁Bootloader,刷入Magisk获取Root权限。安装MagiskTrustUserCerts模块并导入Burp的CA证书。安装Frida Server并启动(注意非默认端口)。
  2. 侦查:解压APK,在lib/arm64-v8a目录下发现libttboringssl.so。用IDA打开,发现SSL_CTX_set_custom_verify函数被导出。
  3. 编写脚本:基于第5节的完整脚本,重点完善针对libttboringssl.so的Hook部分。
  4. 执行:
    frida -U -H 192.168.1.100:9999 -f com.target.app --no-pause -l universal_bypass.js
  5. 验证:观察Frida输出,确认SSL_CTX_set_custom_verify等Hook点被成功拦截并替换。操作App触发网络请求,此时Burp Suite中应该能看到明文的HTTPS请求和响应了。
  6. 调试:如果失败,打开Burp的Proxy->Options->SSL Pass Through,确保没有将目标域名加入直通列表。同时查看手机Logcat (adb logcat | grep -i ssl) 和Frida输出,寻找错误线索。

整个过程的核心思想是动态分析定位,精准Hook替换。不需要完全读懂so里每一行汇编代码,只要找到那个决定“是”或“否”的关键函数,然后让它在运行时永远说“是”,就成功了。

最后,记住这类技术仅用于你拥有合法测试权限的应用,比如自己开发的应用、公司授权的安全评估或漏洞众测项目。在合规的范围内探索技术,才能走得更远。

相关新闻

  • 30天从零搭建AI自动化业务:Claude AI与AI Agent技术实战
  • 我们如何用离散数学构建一个安全、完全可控的 Agent 内核-大模型可以生成内容、计划和工具调用建议,但不应拥有最终执行权。OneCode 在模型输出与真实行动之间建立确定性内核
  • Krea2与ComfyUI人设图生成:从环境配置到高级工作流实战

最新新闻

  • Sunny 网络中间件 v3.1.0 实战:拦截并修改 HTTP/WS/TCP 3类协议数据
  • MCP3551与MK24FN256VDC12高精度数据采集系统设计
  • 微信小程序抓包 2025:3款主流工具(BurpSuite/Charles/Reqable)对比与实战配置
  • 广州建博会收官观察:我发现奇兵到家可能是家居商家最该关注的售后平台
  • 仰望星空与脚踏实地:LBS 领域那些技术与成本难兼顾的“真痛点”
  • Unity打包后UMP视频黑屏?5大核心原因与系统化解决方案

日新闻

  • AI推荐结果怎么优化:适合深圳少儿素质培训机构的GEO服务商哪家好?全程零代码SAAS操作
  • RAG 实战教学完全指南
  • 企业级API网关架构深度解析:IBM Microgateway的技术实现与选型指南

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号