使用TextDecoder/TextEncoder实现ArrayBuffer与String互转后结果不一致问题
ArrayBuffer与String无损互转的问题解析与解决方案
嘿,这个问题我碰到过好多次了——你遇到的核心问题其实是用字符编码工具处理了非字符的二进制数据。让我一步步给你讲清楚:
为什么转回后长度变大?
当你用默认的TextDecoder(默认是UTF-8编码)解码ArrayBuffer时,它会把字节序列当成UTF-8文本处理。但任意二进制数据里大概率存在不符合UTF-8规则的字节序列(比如单个0xC0,或者不完整的多字节序列),这时解码器会自动用**替换字符�(U+FFFD)**来替代这些无效字节。而这个替换字符是一个3字节的UTF-8序列,转回去的时候自然就会让ArrayBuffer长度变大了。
有没有字符解码的扩展机制?
TextDecoder确实有一些配置选项,但它们不是用来实现二进制无损转字符串的:
fatal: true:开启后遇到无效字节会直接抛出DOMException,而不是偷偷替换成�,但这只能帮你发现数据不是合法UTF-8,没法实现无损转换。ignoreBOM: true:忽略UTF-8的BOM头,和二进制转字符串的无损性完全无关。
如何实现ArrayBuffer与String的无损互转?
要做到完全无损,你需要用单字节映射的编码,或者直接用二进制序列化方案。这里推荐两种最实用的方法:
方法1:使用Latin1(ISO-8859-1)编码
Latin1是单字节编码,每个字节(0x00-0xFF)对应唯一的Unicode字符(U+0000到U+00FF),完全没有替换或扩展,是最直接的无损转换方式:
// ArrayBuffer转String(Latin1编码) function abToString(ab) { return new TextDecoder('latin1').decode(new Uint8Array(ab)); } // String转回ArrayBuffer(Latin1编码) function stringToAb(str) { return new TextEncoder('latin1').encode(str).buffer; } // 适配你的Frida场景 var originalArrayBuff = Memory.readByteArray(...); // 你的Frida内存读取代码 var convertedStr = abToString(originalArrayBuff); var restoredArrayBuff = stringToAb(convertedStr); // 验证长度是否一致 console.log('原ArrayBuffer长度:', originalArrayBuff.byteLength); console.log('转回后长度:', restoredArrayBuff.byteLength); // 应该完全相等
方法2:使用Base64序列化(适合传输/存储)
如果你的需求是把二进制数据转成字符串用于传输或存储,Base64也是无损的,缺点是会增加约33%的字符串体积:
// ArrayBuffer转Base64字符串 function abToBase64(ab) { return btoa(String.fromCharCode(...new Uint8Array(ab))); } // Base64字符串转回ArrayBuffer function base64ToAb(base64Str) { const decodedStr = atob(base64Str); const buffer = new ArrayBuffer(decodedStr.length); const view = new Uint8Array(buffer); for (let i = 0; i < decodedStr.length; i++) { view[i] = decodedStr.charCodeAt(i); } return buffer; }
注:Frida的JavaScript运行环境一般支持btoa/atob,如果遇到问题可以用Buffer类(类似Node.js的用法)替代。
总结
- 永远不要用UTF-8(默认)来转任意二进制数据,这是字符编码工具,不是二进制序列化工具。
- 无损转字符串优先用Latin1编码,简单高效;如果需要跨平台传输,用Base64。
内容的提问来源于stack exchange,提问作者Hem
相关产品推荐
相关产品推荐

