尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
前端JS逆向入门:Cookie签名生成与混淆代码还原
逆向这件事很多人第一次接触都会卡在同一个地方打开开发者工具看到一坨被压缩成几万行的 JS变量名全是_0x4a2b脑子当场就空了。我做前端逆向相关的活儿有几年了带过不少新人发现真正难的不是算法本身而是“不知道该往哪儿看”。这篇就以一个最典型的入门场景——某行情查询站点的 Cookie 访问凭证是怎么在前端生成的——把整条链路拆开讲一遍怎么找入口、怎么读混淆代码、怎么把逻辑翻译出来、怎么在本地跑通、跑不通的时候怎么排查。适合刚摸到门道的前端、爬虫方向的同学也适合写过几年业务代码但从没碰过逆向的人。文中所有站点、接口、算法参数均为我在本地搭的模拟环境用来演示方法论不指向任何线上服务。1. 先把目标说清楚我们到底在逆什么1.1 一个入门级场景访问凭证是从哪来的假设有一个行情数据查询页面打开 Network 面板随便点一个接口请求头里会带着一串 Cookie其中有个字段长这样qttoken8f3c...32 位十六进制。你把这个字段删掉再发一次请求接口直接返回“身份校验失败”。这说明它不是浏览器自动带上的通用 Cookie而是页面在某个时刻用 JS 算出来、手动写进去的。这类参数在行业里非常常见只不过名字五花八门有的叫_sign、有的叫token、有的叫xx_sig长得也可能是 32 位十六进制、长串 Base64、甚至是类似1.2.3这样的三段式自定义格式。它们共同的特征是和请求参数或时间戳相关删除后接口不认且每次刷新可能变化。我们这次要做的事用一句大白话说就是搞清楚这串字符是页面里哪一段代码算出来的、用的什么输入、走的什么运算然后在一个不依赖浏览器的环境里把同样的结果算出来。注意这里是“读懂代码逻辑”不是去突破任何访问控制——如果某个接口本身要求登录或授权逆向出签名算法也改变不了这一点这一点后面会再强调一次。1.2 为什么选 Cookie 参数作为入门第一课我建议所有新手拿这个场景练手原因有三个而且都很实在。第一入口明确。请求头里就摆着那串值你有一个确定的“结果”逆向的本质就是从结果倒推过程有结果在手判断对错就变得非常简单算出来的字符串和抓包抓到的一模一样就是对了不一样就是错了。这种即时反馈对新手极其友好比那种“改了代码不知道有没有生效”的场景强太多。第二调用链短。请求参数签名往往要跟业务参数深度耦合得先理解整个业务逻辑而 Cookie 参数通常在页面加载早期就生成好并写入调用链一般只有几层找起来干净利落。第三知识点覆盖全。一个 Cookie 参数从诞生到写入会依次涉及入口定位、断点调试、混淆代码阅读、常见哈希/AES 识别、环境变量依赖。这套东西你练一遍换个站点基本还是这几招。1.3 动手之前的三条硬规矩先把丑话说在前面这三条不是走形式。只对自己有权访问的目标动手。学习逆向最好用公开的教学样本、自己搭的页面或者明确允许自动化访问的场景。别拿别人的业务系统当练手靶子也不要对接口做高频请求压力测试和功能验证是两回事。控制请求频率。哪怕目标是你获准访问的也要把并发压下来。很多站点的风控对频率的敏感度远高于对签名本身的敏感度你算法做得再漂亮一秒两百次请求照样会被拦。我一般会把请求间隔控制在秒级加上随机抖动单次任务量控制在几百条以内。别把算法拿去干超出约定范围的事。逆向能力的价值在于理解系统设计、排查线上问题、做兼容性适配而不是去薅数据。技术是中性的但用在哪里是有边界的这个边界自己心里得有数。提示本文演示用的模拟站点其算法是刻意简化过的教学版本真实系统的复杂度通常高一个量级但方法论是一致的。2. 工具链与观察方法先学会“看”2.1 工具清单与各自的定位很多人一上来就装一堆工具结果哪个都不熟。其实这个阶段的工具非常固定我列一个表你把每个工具的“职责”搞清楚就够了。工具在这个流程里的职责熟练度要求浏览器开发者工具Sources / Network断点调试、搜索、调用栈回溯、抓包必须熟练这是主战场代码格式化/美化工具把压缩代码还原成可读缩进会用即可Node.js跑原始混淆代码、补环境必须熟练基础语法要过关Pythonrequests hashlib/crypto复现算法、做批量验证中等即可编辑器VS Code 之类把代码片段拖出来本地阅读和改写顺手就行抓包代理工具可选移动端或复杂场景下的补充观察有更好没有也能干活这里面最容易被低估的是浏览器 Sources 面板。我见过不少新手代码一搜出来就整个复制到本地去了然后在编辑器里硬看效率极低。Sources 面板提供的断点、作用域查看、调用栈、变量实时求值这些能力在编辑器里全都没有。正确的顺序是先在浏览器里把逻辑理清楚再往本地搬。2.2 从请求头反推参数归属打开 Network找到那个失败的请求看请求头。你要做三件事。第一确认这串值是不是页面 JS 写的。方法很简单刷新页面然后在 Console 里执行document.cookie看这串值在不在。如果页面刚打开时它还没有过一会儿才出现那基本可以确定是 JS 动态写入的如果从第一次文档请求就带着那大概率是服务端Set-Cookie下发的这时候你该看的是响应头而不是 JS。第二观察它的变化规律。连续刷新五次把这串值复制下来对比。如果五次完全一样那它可能是个固定常量或者按天/按会话生成的如果每次都变那很可能掺了时间戳或随机数。这个观察结果会直接影响你后面找算法的方向——掺了时间戳的代码里必然有Date.now()或new Date()的痕迹。第三确认它出现在请求的哪个环节。有些站点的凭证在页面 HTML 里就内联了一段 script 直接写进去了有些是在某个 XHR 请求的回调里写入的。在 Sources 面板里给document.cookie设一个写入断点是最直接的验证手段。2.3 顺藤摸瓜的三种手法定位入口的手法我总结下来常用的就三种按成功率排序。手法一全局搜索关键字。在 Sources 面板按CtrlShiftF搜document.cookie、setCookie、cookie。这招命中率最高因为写 Cookie 的代码无论如何都绕不开这几个关键字哪怕做了混淆document和cookie这两个字符串通常还是明文的。手法二Hook 写入动作。在页面加载前打一个补丁重写document.cookie的 setter把每次写入时的调用栈打出来。这招的好处是精准——直接告诉你“是谁写的”而且能看到写入的完整值。手法三断点 调用栈回溯。如果前两招都被绕过了比如用了Object.defineProperty拦截后再还原、或者压根不写 Cookie 而是塞进请求头那就从 XHR 断点入手在发请求的那一刻暂停然后顺着调用栈往上翻。三种手法可以叠加使用实际干活时经常是先用搜索找到大致区域再用 Hook 精确定位到具体那一行。3. 定位加密入口从搜索关键字到断点命中3.1 全局搜索的正确姿势搜索也是有技巧的乱搜一通会被几百个结果淹没。我的习惯是按这个顺序来。先搜document.cookie带等号减少噪音。如果结果太多加上引号或者换成.cookie 。再不行就搜cookie单个词但这时候要看上下文——你只关心那些出现在赋值语句左侧或者split(; )附近的。还有一个特别有效的关键词组合encrypt、sign、token、hash、md5、aes。这些词是开发者写代码时的自然习惯混淆工具通常不会把它们改掉因为改掉会影响可读性和团队协作。我在模拟环境里就故意保留了这些命名就是为了复现真实场景下的搜索体验。搜索结果的每一条点进去看一眼上下文。判断标准是这个函数调用之后结果是不是被拼进了某个字符串、然后被写进 Cookie 或者塞进请求头。如果是那它就是你要找的地方。3.2 Hook document.cookie 抓写入点这是我最推荐新手先掌握的一招因为它给出的信息最直接。思路是在页面任何脚本执行之前把document.cookie的写入行为拦截下来打印调用栈。// 在 Console 中执行或者做成书签脚本在页面加载前注入 (function () { const desc Object.getOwnPropertyDescriptor(Document.prototype, cookie); Object.defineProperty(document, cookie, { get() { return desc.get.call(document); }, set(val) { console.log(%c[cookie write], color:#e00;font-weight:bold, val); console.trace(调用栈); return desc.set.call(document, val); }, configurable: true }); })();执行完之后刷新页面或者触发一次会写入 Cookie 的操作你会在控制台看到完整写入值和一条调用栈。点开调用栈里最靠近顶部、且不在浏览器内部代码里的那一帧就是算法的入口函数。这个 Hook 有个前提必须在目标脚本执行之前生效。有些页面的 Cookie 在 HTML 解析阶段就写了这时候你需要用“在页面加载前注入脚本”的能力或者干脆在Sources面板里给document.cookie那一行手动加断点。注意设置属性描述符的时候一定要保留原来的get并且最后把configurable设为true否则可能影响页面其他逻辑甚至导致你自己都改不回来只能刷新。3.3 XHR/Fetch 断点与调用栈回溯如果 Cookie 是塞在请求头里而不是真写进document.cookie那就换个思路在发起请求的那一刻停下来。Sources 面板右侧有个XHR/fetch Breakpoints加一条规则输入请求 URL 的一个片段比如/api/quote那么每次这个请求发出时都会自动断住。断住之后你会停在send()那一行然后看 Call Stack从下往上逐帧点击每点一帧就看一眼 Scope 里的局部变量。这里有个经验别一帧一帧无脑点。先看栈里有没有明显的“业务层函数名”比如getQuoteData、buildParams、refreshList这种直接跳到那一帧。真正做加密的代码通常不会有这么直白的名字但它一定在业务函数的附近往上或往下翻一两帧就能碰到。还有一种情况是用了 Fetch API 或者 axios。axios 内部会经过不少封装层栈会很长。这时候可以在栈里搜索dispatchRequest、interceptors这类关键字快速跨过框架层。3.4 条件断点、日志断点与事件断点当你的目标函数被高频调用比如滚动加载、鼠标移动触发普通断点会让人崩溃。这时候有两个救命工具。条件断点右键行号选Add conditional breakpoint输入一个条件比如arguments[0].length 20只有满足条件时才停。这个在过滤掉无效调用时极其管用。日志断点右键行号选Add logpoint输入一段表达式比如input:, arguments[0], output:, result。它不会暂停执行只是往控制台打印。当你只想观察参数变化、不想反复按 F8 的时候日志断点比断点好用十倍。我用它来快速确认“某函数在每次刷新时输入是否一致”几秒钟就能得出结论。另外还有DOM 断点和事件断点。如果 Cookie 是在某个按钮点击后才写入的直接在 Elements 面板对应节点上设“节点属性变化时中断”或者在 Sources 的 Event Listener Breakpoints 里勾上click也能快速命中。4. 代码看不懂怎么办混淆代码的降维打击4.1 认出 webpack 打包结构绝大多数站点的前端代码是打包产物认出来之后能省很多事。典型特征是文件里有一个巨大的对象字面量键是数字或短字符串值是函数然后有一个__webpack_require__之类的加载器按需调用。一旦认出来你就知道你看到的不是“一个文件”而是“几百个模块拼在一起”。那个_0x4a2b(0x1f3)的调用其实就是从模块数组里取一个模块或者一个工具函数。处理方式是在 Sources 面板点击左下角的{}格式化按钮代码会展开成正常缩进。然后用CtrlF在当前文件里搜索模块定义的特征比如entry、__esModule、exports.。把跟 Cookie 相关的那几个模块圈出来只读这几百行别管其他部分。4.2 变量重命名与表达式化简混淆后的变量名没有语义但数据结构骗不了人。看到这种代码var _0x3f2a _0x1b2c[split]()[reverse]()[join]();不管变量名多乱split、reverse、join三个方法连用它就是字符串反转。类似的高频模式还有charCodeAtfromCharCode字符编码转换、toString(16)转十六进制、padStart(2, 0)补零。把这些模式识别出来代码的可读性立刻上一个台阶。我的做法是在编辑器里开两个窗口左边放原始代码右边放一份自己重写注释的版本一边读一边把_0x3f2a改成reversedStr把_0x1b2c改成rawInput。这个过程看起来很笨但它能逼着你真正理解每一行在干什么比用改名工具一键替换要扎实得多。新人用自动工具最大的问题是名字改好了逻辑还是没懂。4.3 字符串数组与控制流平坦化更高一级的混淆会把所有字符串抽到一个数组里用下标访问比如_0x2d3e(0x0)可能是cookie。这种也不难破在 Console 里把那个解密函数通常是数组下标 偏移 解密运算单独拿出来执行一遍一次性把整个数组打印成明文做成映射表。控制流平坦化是相对麻烦的一种——一个简单的 if-else 会被展开成while(true) switch(state)的形式。遇到这个别硬读。正确姿势是找关键的几行你的目标只有一个——输入是什么、输出是什么、中间用了哪个加密库。其他的分支逻辑大多是垃圾代码或者异常处理跳过就行。判断加密库的捷径是看常量。0x67452301、0xefcdab89、0x98badcfe这组数字是 MD5 的初始化常量看到0x9e3779b9基本是 TEA 系算法看到一长串 256 个整数组成的表那多半是 AES 的 S 盒。这些“指纹”比读代码快得多。4.4 环境判断分支与浏览器指纹有一段代码经常被新手忽略混淆代码里会散落着大量typeof window ! undefined、navigator.userAgent、document.createElement(canvas)这类判断。它们存在的意义有两个——做兼容性以及做环境检测。做兼容性的部分你在 Node 里跑的时候不补也没关系只要别走到那个分支。做环境检测的部分就麻烦了它可能把navigator.userAgent或者 canvas 指纹参与运算导致你在 Node 里算出来的结果和浏览器永远不一致。判断方法很直接在浏览器里断点停住把参与运算的所有变量都看一遍。如果发现某个变量来自navigator或者screen那就必须原样带过去。我一般在采集样本的时候会把navigator.userAgent、screen.width、screen.height、时区、语言这些一次性记录下来后面全部塞进模拟环境。提示如果算法里确实掺了 canvas、WebGL 这类渲染指纹纯 Node 复现会很痛苦。这时候更务实的方案是直接接管浏览器环境用自动化工具执行页面里的原函数而不是硬啃。选路线比啃代码重要。5. 算法还原实操把混淆代码翻译成可读逻辑5.1 采样先抓够输入输出对在动笔翻译之前先做一件事采集样本。至少抓好三组越多越好。在断点停在入口函数时把arguments的每个值复制出来再把函数执行完后的返回值记录下来做成一张对照表。这三组样本是你后面验证翻译是否正确的唯一标准。我见过太多人代码翻译完了结果对不上然后不知道错在哪儿——因为根本没记录原始输入。采样的时候有几个细节要注意。一是时间戳如果输入里有Date.now()把当时的准确毫秒值记下来因为它是 13 位数字你稍后复现时时间已经变了必须用当时那个值来验证。二是编码中文参数在浏览器里是 UTF-8在 Python 里如果是默认编码可能出问题要确认清楚。三是大小写很多哈希算法输出后会被toLowerCase()或toUpperCase()处理一次这个小细节能让算法完全对不上。5.2 逐段翻译每段单独验证我习惯把整个函数拆成 3 到 5 段每段翻译完立刻验证而不是全写完再跑。举个模拟的例子假设断点追下来的逻辑是这样的function buildToken(params) { var ts Date.now(); // 段1取时间戳 var raw params _t ts saltabc; // 段2拼接字符串 var h1 md5(raw); // 段3第一次哈希 var h2 md5(h1 ts); // 段4加盐二次哈希 return b64(h2).replace(/\/g, -).replace(/\//g, _); // 段5编码URL安全替换 }对应到 Python就是import time, hashlib, base64 def build_token(params: str) - str: ts int(time.time() * 1000) # 段1注意毫秒 raw f{params}_t{ts}saltabc # 段2 h1 hashlib.md5(raw.encode(utf-8)).hexdigest() # 段3 h2 hashlib.md5((h1 str(ts)).encode(utf-8)).hexdigest() # 段4 b base64.b64encode(h2.encode(utf-8)).decode() # 段5 return b.replace(, -).replace(/, _)每一段都要拿样本核对。比如段 3 算出来的h1是不是和浏览器里md5(raw)的结果一致如果不一致问题就在段 1 或段 2时间戳位数错了或者拼接顺序错了。这种分段验证的方式能把排查范围压缩到几行代码以内。5.3 常见算法的识别特征翻译过程中你会反复遇到几类算法记住它们的“长相”能省大量时间。特征大概率是判断依据输出 32 位纯十六进制MD5有0x67452301等常量或有 32之类的位移输出 40 位纯十六进制SHA1常量0x5a827999循环 80 次输出 64 位纯十六进制SHA256常量0x428a2f98输出以结尾、含/Base64长度是 4 的倍数字符集固定输入输出等长、密文是 16 字节整数倍AES有 256 项 S 盒有key、iv参数输出长串 Base64、长度固定 172 或 344RSA有超大的十进制/十六进制常量有模幂运算一堆^、、循环自定义位运算常见于 TEA/XXTEA 或私有的轻量混淆举个我自己的教训有一次我盯着一段代码看了半小时死活认不出是什么算法最后发现它就是把字符串按 ASCII 码加了个固定偏移量再转回来——一个非常土的自定义加密。所以别被“加密”两个字吓住很多时候它就是开发者随手写的一个位移运算。先看常量、再看运算类型比从第一行读到最后一行高效得多。5.4 验证与边界情况翻译完、单组验证通过之后别急着收工再做三件事。多组验证。拿之前采的另外两组样本再跑一遍确保不是碰巧对上。如果时间戳参与运算要用样本里的历史时间戳而不是当前的。边界值测试。参数为空字符串的时候会怎样参数里有中文的时候编码对不对参数特别长的时候会不会走不同的分支有些代码会根据长度选不同算法结果回归。用复现出来的算法拼一个完整请求发出去看接口返回是不是正常的业务数据。这一步是最关键的验收因为前面所有验证都是“字符串相等”只有这一步能证明接口真的认这串值。6. Node 补环境让整段原代码直接跑起来6.1 补环境的基本套路两条路线我先说第二条——直接跑原代码。好处是绝对不会算错因为用的是原版逻辑坏处是容易缺环境变量调试起来烦。基本套路是把混淆代码整体拷到一个.js文件里用 Node 跑报什么错补什么缺什么加什么。常见需要补的东西// 1. 全局 window / self 指向自身 global.window global; global.self global; // 2. document 及 cookie global.document { cookie: , createElement: () ({ getContext: () ({}) }), getElementsByTagName: () [], addEventListener: () {} }; // 3. navigator / location global.navigator { userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, platform: Win32, language: zh-CN }; global.location { href: https://example.test/quote, hostname: example.test, protocol: https: }; // 4. 定时器与编码工具Node 里大多已有注意 atob/btoa if (typeof atob undefined) { global.atob (s) Buffer.from(s, base64).toString(binary); global.btoa (s) Buffer.from(s, binary).toString(base64); }特别注意window global这一步。很多混淆代码会写window.xxx ...来做模块间通信如果window是个独立对象你在 Node 里读global.xxx就永远是undefined而且不会报错——这是最阴险的坑。6.2 常见报错与逐个补齐我把补环境阶段高频出现的报错整理成了表遇到直接对照。报错信息原因处理办法document is not defined全局对象缺失按上面模板补global.documentnavigator is not defined同上补navigatorUA 建议直接用真实抓到的值self is not defined代码里用了self.xxxglobal.self globalxxx is not a function某个 DOM 方法被调用但补的是空对象按调用名补一个返回空值的函数Cannot read properties of undefinedwindow.某对象未定义顺藤摸瓜把那个对象补成空对象{}结果一直和浏览器不一致但不报错某个环境变量参与运算了用浏览器断点逐个对比所有参与变量执行到一半无响应代码里有while(true)或轮询逻辑用--inspect打断点或者加超时关于最后一条要多说一句。有些混淆代码里带反调试逻辑检测到非浏览器环境就会进入死循环。遇到这种情况可以在可疑的while循环处手动加一个计数器上限跑通之后再加回来。6.3 两条路线怎么选纯算法复现用 Python 重写和原码跑通Node 执行各有适用场景我一般这么判断算法简单、逻辑清晰、依赖少→ 选 Python 重写。好处是性能好、可维护、容易做批量处理而且重写过程中你对算法的理解会更透。算法复杂、混淆严重、依赖大量浏览器 API、或者核心逻辑在一个闭包里→ 选 Node 原码。强行重写成本太高还容易出细微偏差。用vm模块或者child_process调起一个独立的 Node 进程输入参数、输出结果把它当成一个黑盒服务是最省事的做法。还有个折中方案用无头浏览器执行页面里的原函数。好处是环境 100% 真实连 canvas 指纹都不用操心坏处是资源占用高、速度慢。如果你只需要每天跑几百次这个方案其实完全够用没必要为了省那点资源去啃三天代码。我个人的经验是先把 Python 版本写出来因为写的过程中会被迫真正理解逻辑如果实在啃不动再退回 Node。反过来做先跑原码再考虑重写很容易变成“能跑就行”下次换个站点还是不会。7. 常见问题速查与踩坑记录7.1 问题排查表现象可能原因排查动作搜不到document.cookie代码被拆到 iframe / Worker 里在 Sources 的 Page 面板切到对应执行上下文再搜断点根本不停代码在页面加载早期已执行完开启“在页面加载时保留日志”或改成 Hook 注入算出来的值和抓包不一致时间戳位数不对秒 vs 毫秒看代码里是Date.now()还是parseInt(Date.now()/1000)差几位、整体看着像大小写或 URL 安全替换漏了检查有没有toUpperCase、换-、/换_本地跑一次好一次坏掺了随机数找Math.random()确认服务端是否校验随机部分结果稳定但对不上掺了浏览器环境变量对比 UA、屏幕尺寸、时区、语言Node 跑起来卡死反调试死循环全局搜while、debugger加计数上限接口始终返回校验失败缺少配套的其他头字段把请求头整体对比一遍可能有配套的Referer或时间戳头这张表我在实际干活时会一直开着。绝大多数问题都能在里面找到对应项剩下的才是真正需要动脑子的部分。7.2 几条只有踩过才知道的经验第一别在混淆代码里从第一行读到最后一行。这是新手最容易犯的错。几万行代码你读三天也读不完。正确做法是先定位入口然后倒着读——从 Cookie 写入那一行往回推看这串值是怎么被拼出来的用到哪个变量那个变量又来自哪个函数。逆向的本质是目标导向不是阅读理解。第二先跑通再优化别一开始就追求完美复现。我见过有人为了让 Python 版本“干净”花两天时间把每个中间变量都还原成有意义的命名结果代码本身早就能用了。能把结果算出来、接口能通任务就完成了 90%剩下的 10% 是锦上添花随时可以做。第三抓样本时一定要记时间。这条坑我踩过不止一次。当时调试一个带时间窗口的签名代码明明翻译对了结果死活对不上折腾两小时才发现我复现时用的是当前时间而样本是半小时前抓的。现在我的习惯是抓包时把Date.now()的返回值直接写进注释里。第四遇到看不懂的加密先查常量再查库。把代码里的常量数字复制出来和常见算法的初始常量对一遍命中率非常高。真的对不上再去找有没有引入第三方库的痕迹——注意eval、Function构造函数、动态拼接的 script 标签这些都是隐藏代码的常见手法。第五留一份能复跑的验证脚本。每次搞完一个算法我会把采样数据、验证代码、以及当时的 UA/时间戳打包存起来。原因很实际半年后算法变了你得知道从哪开始改或者下次遇到结构类似的站点这份东西就是现成的模板。这个习惯看着麻烦长期看能省下大量重复劳动。第六写清楚注释尤其是“为什么”。我自己的注释习惯是不只写“这行做了 md5”而是写“这里 md5 是为了把长参数压缩成定长服务端只存前 32 位”。等你三个月后回来看记得的是逻辑不是代码。这个习惯在逆向这种“一次性的理解劳动”里价值特别高。我个人在实际操作中的体会是逆向这件事的门槛不在算法难度而在耐心和方法。大部分 Cookie 参数背后的东西说穿了就是字符串拼接加个哈希或者加个对称加密真正难的是在几万行混淆代码里把它捞出来的那个过程。而这个过程是有套路的——搜索、Hook、断点、回溯、采样、翻译、验证七步走下来八成的问题都能解决。剩下两成靠的是你对自己的浏览器和 Node 有多熟。
RELATED

相关推荐

软件版本管理与Git实战指南

软件版本管理与Git实战指南

1. 软件版本管理基础概念 在软件开发领域,版本控制是贯穿整个项目生命周期的核心实践。我刚入行时曾遇到过这样的场景:团队同时修改同一个文件导致代码覆盖,最后花了整整三天才恢复数据。这个惨痛教训让我深刻认识到版本管理的重要性。 软件…

📅 2026/10/31 12:28:11
OpenClaw开源AI助手:模块化设计与多模态交互解析

OpenClaw开源AI助手:模块化设计与多模态交互解析

1. OpenClaw项目概述 OpenClaw(小龙虾)是2026年最热门的开源AI助手项目,在GitHub上已获得30万星标。作为一个全功能的AI Agent框架,它支持从基础对话到复杂任务自动化的一系列功能。不同于传统的聊天机器人,OpenClaw的…

📅 2026/10/31 12:28:11
AI写代码的局部最优困境:三类隐性知识如何喂给模型

AI写代码的局部最优困境:三类隐性知识如何喂给模型

代码能编译通过,和代码能留在线上跑三年,是两回事。最近我越来越觉得,AI写代码的问题不是它不够聪明,而是它总在局部最优上打转——单看它生成的那段代码,逻辑严密、注释规范、甚至性能都挑不出大毛病;可一…

📅 2026/10/31 12:28:11
MORE NEWS

更多资讯

📰

FPGA实现8比特图像比特平面分层的工程实践

1. 项目概述:为什么8比特图像的比特平面分层值得在FPGA上硬核实现? 你有没有遇到过这种情况:用MATLAB写完一个图像处理算法,仿真效果很惊艳,但一搬到实际硬件上就卡壳——要么资源吃紧,要么时序不收敛&…

📰

从安装到跑通:ponytail插件实战与skill玩法全记录

搞了一个下午,总算是把 ponytail 这个插件从安装到实际跑通整明白了。起因很直接,手头有个内容整理的需求,散落的各种片段和素材越来越多,光靠手工维护已经明显拖慢节奏,就到处找轻量一点的工具。搜着搜着就看到 ponyt…

📰

IndexedDB前端数据库实战指南

1. 初识IndexedDB:前端存储的进阶选择 当我们需要在浏览器中存储超过5MB数据时,LocalStorage就显得力不从心了。IndexedDB作为浏览器内置的NoSQL数据库,完美解决了这个问题。我仍记得第一次在项目中尝试用IndexedDB存储用户行为数据时&#x…

📰

100万token输出:Gemini 4 Argon重塑长内容生成与Agent工作流

1. 这次发布到底意味着什么 如果你在AI圈混了超过半年,大概已经对"上下文窗口"这个词免疫了——从几千token到几十万token,每一轮数字刷新都会刷一波屏,然后热度很快消散。但这次Gemini 4 Argon发布的"单次输出上限达100万tok…

📰

OpenRig:开源角色绑定方案如何重塑动画生产管线

做角色动画这些年,我越来越确认一件事:绑定(Rig)这个东西,看着不起眼,却是整个动画流水线里最承上启下的环节。模型师交过来一个高模,动画师等着它动起来,主美盯着形变质量&#xff…

📰

SVG波浪动画原理与现代框架集成实战

简介:这是一份面向前端开发者与网页设计学习者的HTML5动态背景特效资源,聚焦SVG矢量动画与jQuery交互实践,适用于官网首页、活动页或作品集网站的视觉增强场景。资源以轻量级波浪翻滚动效为核心,通过SVG路径CSS3关键帧动画实现平滑…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬