资讯动态

IDA自定义字符串编码类型:告别GBK乱码,高效标注交叉引用

发布时间:2026/9/13 22:08:29 来源:尧图企业网站定制
前阵子接手一个老控制器的固件加载进 IDA 后第一件事就是 ShiftF12 扫字符串。结果很让人崩溃Strings 窗口里全是\xFF\xFD之类的乱码关键业务日志、错误提示一条都搜不出来。切换到 UTF-8、Unicode 类型全都不对最后才发现原始字符串是 GBK 编码而 IDA 的默认字符串类型列表里压根没有 GBK 这一项。这就是今天想聊的重点在 IDA 里自定义字符串编码类型。这个能力虽然不常被单独拿出来讲但遇到非标准编码时它能把“手动一个字节一个字节地按 A 键”变成“一键批量标注”对分析效率和交叉引用生成都有决定性影响。即便你现在还没遇到这种需求也建议先把原理和操作路径摸清楚因为国内软件、工控固件、老式嵌入式 RTOS 里GBK、私有点阵编码、变长编码出现的频率远比你想象的高。1. 从乱码说起默认字符串类型覆盖不到的地方IDA 默认内置的字符串类型其实已经覆盖了大部分常规场景C 类型以00结尾的单字节字符、C_16UTF-16LE、C_32UTF-32LE等等。自动扫描的时候它会在二进制数据里寻找符合“可打印字符 终结符”模式的连续字节命中之后就把它标记成一个字符串字面量同时生成交叉引用。这个机制对 ELF、PE 里常见的 ASCII、UTF-8、UTF-16 字符串非常有效所以大多数时候大家打开 IDA 直接就能看到一堆可读字符串。但默认机制有三个明显的盲区。第一个盲区是本地化编码。GBK、GB2312、BIG5、Shift-JIS 这类编码虽然绝大部分字节落在可打印范围内但 IDA 并不会按照这些编码去切分字符串。它会把中文字符串按单字节 ASCII 来处理结果就是一个完整的 GBK 中文被拆成一堆半截的乱码字符甚至因为中间某个字节恰好是0x7F之类控制字符而提前截断。你说它是字符串吗是但没法看。第二个盲区是没有终结符的定长字符串。很多嵌入式固件和通信协议栈里字符串根本不以00结尾而是由长度字段管理或者固定占用一个结构体成员的大小。默认的 C 类型遇到这种数据会要么扫不出来要么把整段结构体数据全吞进去识别出一个超长的“字符串”。第三个盲区是私有编码和加密字符串。游戏客户端、加壳程序、部分商业软件会把字符串做异或、移位、自创编码只靠字符集判断是不可能识别出来的。自定义字符串编码类型解决的就是第一个和第二个盲区以及第三个盲区里的“字符串范围标记”这一半问题。它的核心价值可以概括成一句话你告诉 IDA 这段连续数据按什么规则算字符串IDA 就会把它当字符串对待自动产生 xref。至于里面的字节能不能直接显示成人话那是编码解析层的事后面再细说。2. 自定义类型的关键参数宽度、终结符与对齐在新建自定义字符串类型之前先把 IDA 的字符串类型定义拆开看看。一个字符串类型本质上是四件事的组合名字、每个字符的宽度、遇到什么序列算结束、显示时如何渲染。搞清楚这四个参数你就能应对绝大多数自定义串类型。Name类型名称比如GBK_CN、MY_ENC。它会被加入字符串类型列表后续在数据段上手动标记或字符串窗口里筛选时都可以直接引用。Character size / Width每个逻辑字符占几个字节。ASCII 是 1UTF-16 是 2UTF-32 是 4。这里有一个很多人第一次会绕弯子的点对于 GBK 这种变长编码你不用担心“每字符字节数不是固定值”的问题。因为自定义类型里填 1意味着 IDA 把每个字节当作一个存储单元来扫描它不会主动去做 GBK 双字符合并。换句话说你定义的是“这是一个字节流字符串”而不是“这是一个符合 Unicode 规范的编码字符串”。这一点非常重要。Termination终结符序列也就是碰到哪些字节序列就认为字符串结束。C 类型是00UTF-16LE 是00 00Pascal 类型则没有终结符靠最前面的长度字段决定边界。自定义的时候你可以填写多个终结符序列也可以直接指定为“没有终结符”。Alignment对齐字节数。影响扫描时的候选地址步进一般填 1 就够了。如果目标字符串都按 4 字节对齐存放填 4 可以让扫描结果更干净。Display这项决定 IDA 如何把这些字节渲染成字符。常见的选项是 ASCII、Unicode还有“代码页”相关的显示模式。这里要说清楚Display 只影响屏幕上你看到什么不影响字符串在 IDB 里存储和 xref 的生成逻辑。一个常见的误解是“自定义字符串类型 让 IDA 识别出中文”。不是的。自定义类型负责的是“字符串切分规则”而不是“字符映射规则”。如果你定义了一个字节宽度为 1、终结符为00的类型IDA 就能把一块 GBK 数据整体标记成一个字符串并且后续你可以给这块标记单独设置显示编码。如果你希望它直接显示成可读中文还得在显示层面再往前走一步比如使用代码页选项或者脚本修正。这个区别在后面实操时会体现得很明显。下表是一个直观对比帮助理解默认类型和自定义类型在参数上的差异参数C 类型UTF-16LE 类型自定义 GBK 类型示例每字符字节数121典型终结符0000 0000是否理解变长编码否否否按字节流处理显示中文不支持不支持需配合代码页/脚本自动 xref支持支持支持3. 手把手操作新建一个 GBK 自定义字符串类型并应用接下来是实操环节。以我手头的 IDA 7.7 和 8.3 为例不同版本入口位置略有差异但思路完全一致。老版本 6.x 里可能藏在Options菜单下面新版本基本都在 Strings 窗口里管理。第一步打开 Strings 窗口。按 ShiftF12或者菜单栏View - Open subviews - Strings。这一步大多数人都熟。第二步进入字符串类型管理入口。在 Strings 窗口的工具栏上找到那个看起来像列表加齿轮的按钮有的版本叫“String literal types”有的版本在右键菜单里叫“String types...”。点击之后会弹出一个对话框里面列出当前所有字符串类型比如C、C_16、C_32、Pascal等。第三步新增类型。在对话框里点Add然后填写参数。这里我实际用的是下面这套NameGBK_CNCharacter size1Termination00Alignment1Display选择“ASCII”或按代码页显示中的合适项填完保存类型列表里就出现了GBK_CN。如果你是在对一个已经分析过的数据库操作记得回到 Strings 窗口点一下Refresh让 IDA 用新类型重新扫描字符串。第四步手动标注数据。如果自动扫描没有覆盖到目标位置你可以在反汇编或者 Hex View 里选中一段数据按AltA设置字符串类型或者在右键菜单里选择Data再选择刚建好的GBK_CN。此时 IDA 会按你定义的规则从选中位置开始尝试切分出字符串并生成一个字符串节点。第五步验证 xref。切回 IDA View你会看到刚才的位置出现了一个带引号的字符串标记。用鼠标点它按X查看交叉引用如果代码里有访问这块数据的指令引用就会列出来。到这一步自定义类型已经真正改变了分析结果——这块数据在 IDB 里从“一堆字节”变成了“一个字符串对象”。这里有个小细节自动扫描和手动标注的结果不冲突。你可以先用默认类型扫一遍再把不满意的地方挑出来手动应用自定义类型。IDA 会用新的解释覆盖旧的标记不会留下两个重复的字符串节点。4. 更硬核一点加密字符串和乱码数据的脚本化识别自定义字符串类型解决的是“切分规则”问题但遇到异或、加解密的字符串光定义类型是不够的。这类数据必须先还原出明文才能判断边界、标记字符串。处理这类场景我一般直接用 IDAPython把解密逻辑和字符串识别逻辑写在一起跑完一段脚本字符串列表里该有的就都有了。举个例子。假设某固件里的字符串是每个字节和0x5A异或之后存放在内存里的那么我可以这样处理import ida_bytes import idc start 0x401000 # 目标段起始 end 0x404000 # 目标段结束 key 0x5A # 异或密钥 def is_printable(ch): return 0x20 ch 0x7E buf [ida_bytes.get_byte(i) for i in range(start, end)] i 0 while i len(buf): ch buf[i] ^ key if is_printable(ch): length 0 while i length len(buf) and is_printable(buf[i length] ^ key): length 1 if length 4: # 太短就不建了避免噪音 # 在原始地址上创建字符串第二个参数是长度 ida_bytes.append_strlit(start i, length) i length continue i 1 # 如果还想让分析结果刷新出来在 IDA 里按一次空格回到反汇编视图 # 或者调用 ida_auto.auto_wait()这段脚本的思路是先把目标段数据读进来逐字节异或然后检查异或结果是不是可打印字符。如果连续一段都是可打印字符就认为这里是一个字符串调用append_strlit在原始地址上创建字符串节点。长度小于 4 的就跳过避免误报太多。需要注意不同 IDA 版本的 IDAPython API 名称有微小差异。老版本里你可能会用idc.create_strlit(ea, size)新版本里ida_bytes.append_strlit(ea, size)用起来更方便。实际写的时候以你当前版本自带的 Python 帮助文本为准实在不确定就直接在 IDA 的 Python 命令行里dir(ida_bytes)看一眼。脚本跑完之后如果还是想在字符串列表里看到这些新标记回 Strings 窗口刷新一下即可。这里又回到前面说的自定义类型你完全可以在脚本里把append_strlit指向自定义的GBK_CN类型这样解密还原和 GBK 切分一次搞定。不过大多数情况下我是先按 ASCII 范围找边界标记完成后再切换到 GBK 类型两个步骤互不干扰反而更容易排查问题。另外还有一种更“暴力”但很直接的做法如果二进制里字符串是整体密文不是逐字节操作那就先在脚本里解密出一份明文 Dump然后在 IDA 里用“File - Load file - Additional binary file”的方式把明文数据加载到附加段再在明文段上做字符串扫描。虽然分析的是附加段而不是原镜像但对你理解逻辑、找到引用代码位置已经完全够用了。5. 避坑记录这些情况别硬套自定义类型实际操作中我在自定义字符串类型上踩过不少坑这里总结几个最常见的希望能帮你省点时间。坑一GBK 当 UTF-16 标记。有些新手看到中文乱码第一反应是“这是宽字符”于是建一个每字符 2 字节的自定义类型。结果字符串长度翻倍xref 全部错位越分析越乱。判断编码最靠谱的方法是先看 Hex View如果中文后面跟着大量00那大概率是 UTF-16LE如果是一串连续的0xB0 0xA1 0xB1 0xBE这类高字节在前、没有任何00穿插那基本就是 GBK。不确定的时候可以先在字符串窗口里用默认类型扫一遍看看哪些字符串能正常显示反向判断目标数据的编码风格。坑二终结符设置不当导致长字符串吞并数组。如果目标数据里既有字符串又有普通数据数组而你定义的类型终结符太短或者干脆设置为“无终结符”IDA 会把后面一整块连续数据都识别成一个字符串。最典型的是把结构体数组误标成超长字符串看起来长度几百字节实际上早就不该算字符串了。解决办法是先看数据里真正的边界是什么是00、是00 00、还是长度字段。我一般会在自定义类型里把终结符设置成和数据分布一致宁可少识别也不要错识别。坑三静态标记对动态解密数据无意义。如果程序运行时才解密字符串静态分析阶段你看到的只是密文自定义类型再正确也只能把密文切分成一块一块并生成指向密文的 xref。这种场景下我的建议是不要在原始段上花太多时间。要么跟到解密函数下断点动态 dump 出明文段要么写模拟脚本把解密逻辑跑一遍在还原后的数据上做分析。坑四自定义类型在多人协作时的来源问题。如果你是从 UI 上创建的自定义字符串类型它存在于当前 IDB 里换台机器打开同一个 IDB 是能看到的。但如果你是通过修改ida.cfg或启动脚本注入的类型那别人打开 IDB 时如果没有同样的配置文件或脚本字符串标记还在类型定义却变回默认值显示和后续操作都会出问题。所以团队协作时要么约定自定义类型只通过 UI 维护并跟随 IDB 走要么把配置文件和项目一起提交避免“类型丢失”。坑五xref 没刷出来。有时候标记了字符串但按 X 看不到引用并不是标记失败而是分析进度还没完成。解决办法很简单等 IDA 左下角自动分析进度条跑完或者手动执行一次重新分析。脚本里创建完字符串后也可以调用ida_auto.auto_wait()等待分析队列清空再去看 xref 就基本正常了。6. 一点实战体会我在实际分析中最常用到自定义字符串编码类型的场景反而不是那种特别复杂的私有加密而是非常朴素的中文 GBK 字符串。很多国内老工业软件、路由固件、医疗设备固件字符串全是 GBK 保存但 IDA 默认类型里就是没有这种选项。以前遇到这种问题我的处理方式是写一个 IDAPython 脚本把乱码字符串的每个字节转成\xXX再人肉对照效率很低。后来搞明白自定义字符串编码类型这一套逻辑两步操作新建GBK_CN类型、批量应用字符串列表里直接就能筛出目标内容xref 也有了整个分析进度快了很多。另外搭配好显示编码是一个容易忽略的细节。我给 GBK 字符串标注完之后一般会在 Hex View 里确认一下字节内容然后使用 IDA 的字符串显示设置把类型切到支持中文字符集的渲染方式。如果还是乱码就先检查数据到底是不是 GBK别一上来就怀疑 IDA 没识别对。很多时候识别范围是对的只是显示层没选对。这功能本身不复杂难的是理解它背后的机制它定义的是“切分规则”不是“字符集映射”。想清楚这一点你就能在自定义字符串编码类型遇到任何问题时快速判断到底是边界切错了、终结符不对、还是显示层的问题而不至于对着一个乱码字符串干瞪眼。最后留一个小技巧如果你经常分析某类固件可以把常用的字符串类型定义、IDAPython 解码脚本整理成一个独立的 IDC 或 Python 初始化脚本每次加载固件后直接跑一遍自动把GBK_CN这类类型注册好顺便扫描出可读字符串段。一次配置长期受益。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价