1. 从“看热闹”到“看门道”逆向中的编码与加密刚入门逆向那会儿我总盯着那些花里胡哨的算法库和复杂的汇编跳转觉得那才是“真功夫”。后来踩坑踩多了才明白真正卡住新手脖子的往往不是那些高深的虚拟机保护或者混淆而是最基础的编码转换和标准加密算法。你费了九牛二虎之力跟到关键函数结果发现它传进去的是一串U2FsdGVkX1...或者内存里一堆0x41 0x42 0x43如果认不出这是Base64或者ASCII直接就懵了。再比如你看到程序调用了CryptEncrypt或者某个libcrypto.so里的函数却不知道它用的是AES还是DES是ECB还是CBC模式那后续的密钥查找、数据解密就无从谈起。所以我把逆向学习中的“编码”和“加密”比作是“识字”和“语法”。编码是信息的表现形式就像把一句话写成英文、拼音或摩斯电码内容没变只是样子变了。而加密是信息的保护手段就像把这句话锁进保险箱没有钥匙密钥你就看不懂原意。在逆向分析中我们经常需要先“识字”识别并解码各种编码才能去尝试“开锁”分析加密逻辑并获取密钥。这篇文章我就结合自己踩过的坑和实战经验系统梳理一下逆向工程中最常遇到的编码与加密算法重点讲清楚怎么识别、怎么分析、怎么搞定。2. 逆向中的“识字”课常见编码识别与处理编码本身不是加密它只是为了方便传输、存储或显示而对数据进行的一种格式转换通常是公开、可逆的。在逆向中快速识别编码类型能帮你立刻看清数据的“真面目”。2.1 Base64网络传输的“常客”Base64可以说是出场率最高的编码没有之一。它的特征极其明显字符串通常由A-Z、a-z、0-9、、/以及填充符组成长度通常是4的倍数。为什么逆向中总遇到它因为二进制数据比如加密后的密文、图片、序列化数据直接放在JSON、XML或URL里传输会出问题有控制字符。Base64把它们变成纯文本就安全了。所以你在HTTP请求/响应体、配置文件、注册表、甚至内存字符串里看到的一长串看似乱码的字符首先就该怀疑是Base64。识别与实战处理肉眼识别看到类似dGVzdA、U2FsdGVkX18OpenSSL Salted开头密文的特征这种格式的八九不离十。工具验证在线网站很多在线工具支持Base64解码随手一贴就知道是不是。CyberChef这个“网络瑞士军刀”的From Base64模块非常好用。Python脚本自己写几行代码验证最灵活。import base64 import re def try_decode_base64(data): # 先简单清理一下可能的引号或空格 data data.strip(\\ \n\r\t) # Base64正则匹配宽松版 if re.match(r^[A-Za-z0-9/]*{0,2}$, data) and len(data) % 4 0: try: decoded base64.b64decode(data, validateTrue) # 尝试以UTF-8解码如果不是文本就显示hex try: return decoded.decode(utf-8) except: return decoded.hex() # 返回十六进制表示 except Exception as e: return f解码失败: {e} return 不符合Base64特征 test_str U2FsdGVkX19q4zEwWL2hP2VJ5V4hX8NpFw print(try_decode_base64(test_str))逆向中的坑变种Base64有些实现会修改码表比如把/换成-_URL安全的Base64或者自定义码表。这就需要你逆向编码函数还原出码表。多层嵌套CTF中常见数据被反复Base64编码多次。解决思路就是写个循环直到解码结果不再符合Base64特征。包含换行有些格式如PEM证书的Base64会每76字符加一个换行解码前需要去掉。注意U2FsdGVkX1开头的字符串是OpenSSL使用Salted前缀的加密结果再进行Base64编码的典型特征这提示你后面很可能接的是一个对称加密如AES、DES的密文。2.2 十六进制Hex编码内存的直白写照十六进制编码就是把每个字节转换成两个0-9a-f的字符。它在逆向中几乎无处不在因为这是查看内存原始数据最直观的方式。识别与实战处理特征字符串仅由0-9和a-f或A-F组成长度通常是偶数。常见场景硬编码密钥/IV在二进制文件的字符串区域你可能会发现像0123456789ABCDEF这样的字符串这很可能就是一个16字节的AES密钥的Hex表示。网络数据包某些协议或自定义协议会用Hex格式传输二进制数据。调试器显示IDA、OllyDbg、Ghidra等工具的内存窗口默认就是以Hex形式展示数据。工具与代码Pythonbytes.fromhex()和binascii.hexlify()是常用函数。hex_str 48656c6c6f20576f726c64 # Hello World的Hex bytes_data bytes.fromhex(hex_str) print(bytes_data.decode(utf-8)) # 输出: Hello World raw_data bHello hex_str raw_data.hex() print(hex_str) # 输出: 48656c6c6f2.3 URL编码/百分号编码网络请求的“安全帽”URL编码是为了把URL中不允许的字符如空格、中文、、转换成%XX的形式其中XX是字符的ASCII码十六进制值。识别与实战处理特征字符串中包含大量%符号如%20空格、%E4%B8%AD“中”字的UTF-8编码。逆向场景分析HTTP请求参数、Cookie时一定会遇到。你需要解码后才能看到参数的真实值。处理浏览器开发者工具Network面板会自动解码显示。Python使用urllib.parse模块。from urllib.parse import unquote, quote encoded name%3D%E5%BC%A0%E4%B8%89%26age%3D20 decoded unquote(encoded, encodingutf-8) print(decoded) # 输出: name张三age20 # 编码 raw key值 encoded quote(raw, safe) # safe表示不对/和等字符进行编码 print(encoded)2.4 Unicode与UTF-8字符串的“内心世界”在Windows逆向和涉及多国语言的程序中Unicode编码是绕不开的。内存中一个英文字符可能占2字节UTF-16LE而中文字符在UTF-8下占3字节。识别与实战处理特征UTF-16LEWindows宽字符内存中英文字符后跟一个0x00字节如H存为0x48 0x00。字符串常以LHello形式出现在代码中。UTF-8变长编码ASCII字符不变中文等字符占多个字节如0xE4 0xB8 0xAD代表“中”。逆向要点在IDA等反编译工具中注意字符串的显示方式。有时你需要手动指定编码才能正确显示字符串。跟踪函数调用时注意区分strlen单字节和wcslen宽字节。遇到乱码时尝试用不同的编码GBK、GB2312、UTF-8、UTF-16去解码很可能有意外收获。Python的bytes_data.decode(gbk, errorsignore)可以帮你快速尝试。3. 逆向中的“开锁”术常见加密算法分析识别了数据的“样子”编码接下来就要对付它的“保护壳”加密。逆向分析加密算法的目标通常不是破解算法本身现代加密算法几乎不可破解而是找到密钥和加密模式。3.1 对称加密算法同一把钥匙对称加密加解密用同一个密钥。逆向中找到这个密钥是关键。3.1.1 AES (Advanced Encryption Standard)这是目前最主流、最安全的对称加密算法。如何识别字符串特征代码中可能出现AES、RijndaelAES的原名、CryptImportKey、EVP_aes_256_cbcOpenSSL等常量字符串。密钥长度AES的密钥长度固定为128、192或256位即16、24、32字节。如果你在代码或数据中发现一个固定长度为16/24/32字节的数据块它很可能是AES密钥。S-BoxAES算法内部有一个固定的256字节的替换表S-Box。在IDA中搜索字节序列0x63, 0x7C, 0x77, 0x7B...AES S-Box的开头如果能找到基本可以确定使用了AES。API/库调用在Windows上可能调用CryptAPICryptEncrypt/CryptDecrypt在Linux/Android上可能链接libcrypto.soOpenSSL并使用AES_encrypt、EVP_CipherInit_ex等函数。模式分析关键 只知道是AES还不够模式Mode决定了安全性。逆向时必须确定模式。ECB (Electronic Codebook)最不安全相同的明文块加密后得到相同的密文块。在数据中能看到重复的块比如加密一张纯色图片密文会有规律重复。尽量避免使用但一些老旧或简单系统仍用。CBC (Cipher Block Chaining)最常用需要一个初始化向量IV。每个明文块先与前一个密文块异或再加密。IV通常是随机的并和密文一起传输常放在密文开头。你需要找到IV和密钥。其他模式CTR、GCM等也较常见分析思路类似重点是找到nonce/IV和密钥。实战逆向步骤定位加密函数通过字符串、导入表或调用栈找到加密入口。跟踪密钥来源密钥可能是硬编码、从文件读取、通过网络下发、或由用户输入派生如PBKDF2。用调试器如x64dbg, IDA Debugger, Frida在加密函数下断点回溯密钥生成或传入的过程。确定模式与IV观察加密函数的参数。对于CBC模式通常会有一个独立的IV参数。IV可能硬编码也可能是随机生成后与密文拼接。验证用找到的密钥、IV和模式编写脚本尝试解密一段已知的密文看是否能得到有意义的明文。3.1.2 DES/3DES (Data Encryption Standard/Triple DES)DES已不安全3DES仍在一些遗留系统中使用。密钥长度DES为8字节实际56位3DES为16或24字节。识别搜索字符串DES、3DES、TripleDES。DES的S-Box也与AES不同。API调用类似AES。逆向要点与AES思路完全一致重点是找密钥。注意3DES的密钥可能包含三个8字节密钥K1, K2, K3。3.1.3 SM4国密算法在国内的软件、物联网设备、金融系统中越来越常见。它是国家标准分组长度和密钥长度均为128位。识别字符串SM4、GM/T 0002-2012。常量SM4有固定的FK和CK常量数组。在IDA中搜索这些常量是定位SM4代码的可靠方法。例如FK数组通常为{0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc}。库可能使用libsm4.so、gmssl或厂商自实现的库。模式同样支持ECB、CBC等模式分析思路同AES。实战我曾分析过一个智能家居设备的固件其网络通信数据就是用SM4-CBC加密的。通过逆向固件在初始化函数里找到了硬编码的密钥和IV藏在一个看似无关的配置字符串处理函数里。用Python的gmssl库成功解密了通信数据。3.2 非对称加密算法公钥与私钥非对称加密使用一对密钥公钥加密私钥解密或反之用于签名。逆向中通常目标是拿到私钥或者理解其加解密/签名流程。3.2.1 RSA最经典的非对称算法。密钥长度模数n的位数常见有1024、2048、4096位。如何识别字符串RSA、PublicKey、PrivateKey、BEGIN RSA PRIVATE KEY。大整数RSA的模数n、公钥指数e通常是655370x10001都是非常大的整数在IDA的静态分析中这些大整数常量非常显眼。API/库OpenSSL的RSA_public_encrypt、RSA_private_decryptWindows的CryptImportKey导入RSA类型的密钥。逆向分析目标获取私钥这是最理想的情况。私钥可能硬编码在程序中非常危险、存放在配置文件或密钥库中。搜索-----BEGIN RSA PRIVATE KEY-----这样的PEM头。理解流程如果拿不到私钥就分析程序如何使用公钥。例如客户端用服务器公钥加密一个临时生成的对称密钥会话密钥然后后续通信使用对称加密。这时你的目标就变成了获取这个被加密的会话密钥或者在内存中拦截它。工具openssl命令行工具可以方便地解析PEM格式的密钥并进行加解密操作。3.2.2 SM2国密非对称国产非对称算法基于椭圆曲线密码学ECC。识别字符串SM2、ECC、GM/T 0003-2012。同样有固定的参数如椭圆曲线方程、基点G等。逆向思路与RSA类似寻找公钥/私钥对。SM2的公钥是一对坐标x, y私钥是一个大整数。分析时需关注国密算法库的调用。3.3 哈希与消息认证码验证“身份”哈希如MD5、SHA1、SHA256、SM3将任意数据映射为固定长度的摘要不可逆。常用于校验数据完整性、密码存储需加盐。消息认证码如HMAC在哈希基础上加入了密钥用于验证消息来源和完整性。识别字符串MD5、SHA、HMAC、摘要。哈希函数调用特征明显输入数据输出固定长度MD5:16字节 SHA256:32字节。逆向中的作用校验绕过程序可能用哈希校验文件或关键数据是否被篡改。你需要修改数据后重新计算正确的哈希值并替换。密码破解如果程序存储的是明文密码的哈希值且未加盐或盐已知你可以尝试彩虹表碰撞或暴力破解。但加盐的哈希很难破解。密钥派生HMAC-SHA256常用于从主密钥派生出子密钥需要分析其输入密钥、数据是什么。4. 实战逆向案例拆解一个Android Native层的3DES通信协议理论说再多不如看个实例。假设我们有一个Android应用其核心通信协议在Native层so库中用3DES加密。我们的目标是解密其网络数据包。4.1 初步侦察使用Jadx-GUI打开APK搜索loadLibrary或System.load找到加载的so库名比如libprotocol.so。在Java层搜索与加密、网络相关的关键词encryptdecryptCipherDES3DES发现JNI调用native_encrypt_data和native_decrypt_data。4.2 静态分析so库用IDA Pro打开libprotocol.so。导出函数窗口找到Java_com_xxx_app_NativeHelper_native_encrypt_data。分析该函数发现它调用了另一个内部函数do_3des_cbc。进入do_3des_cbc发现它调用了DES_set_key_unchecked和DES_ncbc_encrypt这是OpenSSL的函数。确认算法3DES-CBC。关键寻找密钥和IV。在do_3des_cbc函数开头或调用它的上层函数中追踪传入的参数。发现密钥24字节和IV8字节来自于两个全局变量g_enc_key和g_enc_iv。查看这两个全局变量的交叉引用发现它们在JNI_OnLoad函数中被初始化。分析JNI_OnLoad发现密钥和IV是通过一个固定的字符串经过一个简单的XOR和MD5变换后生成的。我们拿到了密钥和IV的生成逻辑。4.3 动态验证与Frida Hook静态分析得出的结论需要验证。我们用Frida写个Hook脚本。Java.perform(function() { var NativeHelper Java.use(com.xxx.app.NativeHelper); // Hook native方法打印输入输出 var encryptPtr Module.findExportByName(libprotocol.so, Java_com_xxx_app_NativeHelper_native_encrypt_data); Interceptor.attach(encryptPtr, { onEnter: function(args) { // args[1]是jobjectargs[2]是jbyteArray (明文) this.input Java.vm.getEnv().getByteArrayElements(args[2], null); var inputLen Java.vm.getEnv().getArrayLength(args[2]); console.log([] native_encrypt_data 输入长度: inputLen); // 可以在这里把字节数组转为hex打印 console.log(输入Hex: bytesToHex(this.input, inputLen)); }, onLeave: function(retval) { // retval是jbyteArray (密文) var output Java.vm.getEnv().getByteArrayElements(retval, null); var outputLen Java.vm.getEnv().getArrayLength(retval); console.log([] native_encrypt_data 输出长度: outputLen); console.log(输出Hex: bytesToHex(output, outputLen)); // 用我们静态分析得到的算法、密钥、IV验证是否能解密密文 // 这里可以调用一个Python脚本或本地函数进行验证 verifyDecryption(bytesToHex(output, outputLen)); } }); });运行Frida脚本触发应用的网络请求成功抓取到加密前后的数据。4.4 编写解密脚本根据静态分析得到的密钥生成逻辑和动态验证确认的模式3DES-CBC用Python编写解密脚本。from Crypto.Cipher import DES3 from Crypto.Util.Padding import unpad import hashlib import binascii def derive_key_iv(): # 根据逆向出的逻辑生成密钥和IV seed_str some_hardcoded_string_from_so # 假设是 seed_str 经过一些变换和MD5得到key_material md5 hashlib.md5() md5.update(seed_str.encode(utf-8)) key_material md5.digest() # 16字节 # 假设密钥是 key_material 重复拼接而成IV是 key_material 的前8字节 key (key_material * 2)[:24] # 3DES密钥需要24字节 iv key_material[:8] # CBC IV需要8字节 return key, iv def decrypt_packet(ciphertext_hex): key, iv derive_key_iv() cipher DES3.new(key, DES3.MODE_CBC, iv) ciphertext binascii.unhexlify(ciphertext_hex) # 注意OpenSSL的CBC模式默认使用PKCS#7填充 plaintext_padded cipher.decrypt(ciphertext) plaintext unpad(plaintext_padded, DES3.block_size) return plaintext.decode(utf-8) # 使用Frida抓取到的密文进行测试 captured_cipher a1b2c3d4e5f6... # 替换为实际密文 try: print(decrypt_packet(captured_cipher)) except Exception as e: print(解密失败:, e)运行脚本成功解密出可读的JSON或协议数据逆向成功。5. 进阶技巧与工具链提升逆向效率掌握了基础识别和分析方法后一些工具和技巧能让你事半功倍。5.1 特征码与常量搜索这是定位加密/哈希函数最快的方法。在IDA中搜索字符串算法名、API名、错误信息。搜索字节序列AES的S-Box、SM4的FK/CK常量、MD5/SHA256的初始化魔数如MD5的0x674523010xEFCDAB89等。搜索指令模式某些算法有独特的操作序列。5.2 动态调试与内存dump静态分析有时会陷入复杂的数据流。动态调试可以直接看到运行时状态。下断点在标准库函数如OpenSSL的AES_encrypt或自定义加密函数入口下断。观察参数查看传入的密钥、IV、明文/密文缓冲区。内存搜索在程序内存中直接搜索可能的密钥如搜索固定字符串、或特定长度的随机数据。Cheat Engine、x64dbg的内存搜索功能很好用。5.3 使用Frida进行高级Hook对于Android/iOS/Windows应用Frida是无敌的。Hook加密函数不仅能打印输入输出还能替换密钥或修改返回值。跟踪密钥生命周期Hook密钥生成、存储、传输的所有相关函数。主动调用直接调用so库中的解密函数对抓到的密文进行解密无需重写算法。5.4 密码学算法识别工具PEiD/Krypto ANALyzer (KANAL)老牌PE文件分析工具插件可以识别一些常见的加密常量。IDA的FindCrypt插件IDA的神器能自动扫描二进制文件中的多种加密算法AES, DES, MD5, SHA, RSA等的常量并高亮显示。Binwalk常用于固件分析也能识别一些加密/压缩算法的特征。5.5 编写自己的识别脚本针对特定项目可以结合已知特征用Python写脚本批量分析。import re import binascii def scan_for_crypto_constants(binary_data): # AES S-Box 开头部分特征 aes_sbox_start bytes([ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 ]) # MD5 初始化常量 (A, B, C, D) md5_init [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] md5_bytes b.join([i.to_bytes(4, little) for i in md5_init]) findings [] if aes_sbox_start in binary_data: findings.append(发现AES S-Box特征) if md5_bytes in binary_data: findings.append(发现MD5初始化常量特征) # ... 添加更多算法特征 return findings with open(target.bin, rb) as f: data f.read() print(scan_for_crypto_constants(data))6. 总结与心法逆向思维养成最后分享几点在逆向编码和加密时的心得大胆假设小心求证看到一个字符串先猜它可能是Base64或Hex。看到一个固定长度的数据块先猜它可能是密钥或IV。然后用工具或脚本去验证你的猜想。上下文是关键数据出现在哪里是网络包、配置文件、还是内存字符串它周围有什么函数调用这些上下文信息是判断算法类型的重要线索。从易到难先编码后加密先尝试Base64、URLDecode、HexDecode这些简单的解码操作。把数据还原成原始二进制形式后再分析其加密特征。善用已知信息如果程序调用了libcrypto-1.1.so的EVP_aes_256_cbc那几乎可以断定是AES-256-CBC。如果字符串里有SM4_ECB那方向就非常明确了。动态与静态结合静态分析给你蓝图动态调试给你实景。两者结合才能高效定位关键点。不要只盯着IDA反汇编的代码看一定要让程序跑起来。工具只是延伸思路才是根本再好的工具也无法替代你对算法原理和程序逻辑的理解。花时间弄清楚一个算法的流程比你盲目尝试十个工具更有用。逆向编码和加密就像侦探破案你需要仔细观察所有线索数据特征、字符串、函数调用并利用你的知识编码、加密算法原理进行推理和验证。这个过程充满挑战但每一次成功解密都像是解开一道精妙的谜题那种成就感正是逆向工程的魅力所在。希望这篇总结能帮你少走些弯路更快地找到“开锁”的那把钥匙。
逆向工程中的编码与加密算法识别与实战分析
1. 从“看热闹”到“看门道”逆向中的编码与加密刚入门逆向那会儿我总盯着那些花里胡哨的算法库和复杂的汇编跳转觉得那才是“真功夫”。后来踩坑踩多了才明白真正卡住新手脖子的往往不是那些高深的虚拟机保护或者混淆而是最基础的编码转换和标准加密算法。你费了九牛二虎之力跟到关键函数结果发现它传进去的是一串U2FsdGVkX1...或者内存里一堆0x41 0x42 0x43如果认不出这是Base64或者ASCII直接就懵了。再比如你看到程序调用了CryptEncrypt或者某个libcrypto.so里的函数却不知道它用的是AES还是DES是ECB还是CBC模式那后续的密钥查找、数据解密就无从谈起。所以我把逆向学习中的“编码”和“加密”比作是“识字”和“语法”。编码是信息的表现形式就像把一句话写成英文、拼音或摩斯电码内容没变只是样子变了。而加密是信息的保护手段就像把这句话锁进保险箱没有钥匙密钥你就看不懂原意。在逆向分析中我们经常需要先“识字”识别并解码各种编码才能去尝试“开锁”分析加密逻辑并获取密钥。这篇文章我就结合自己踩过的坑和实战经验系统梳理一下逆向工程中最常遇到的编码与加密算法重点讲清楚怎么识别、怎么分析、怎么搞定。2. 逆向中的“识字”课常见编码识别与处理编码本身不是加密它只是为了方便传输、存储或显示而对数据进行的一种格式转换通常是公开、可逆的。在逆向中快速识别编码类型能帮你立刻看清数据的“真面目”。2.1 Base64网络传输的“常客”Base64可以说是出场率最高的编码没有之一。它的特征极其明显字符串通常由A-Z、a-z、0-9、、/以及填充符组成长度通常是4的倍数。为什么逆向中总遇到它因为二进制数据比如加密后的密文、图片、序列化数据直接放在JSON、XML或URL里传输会出问题有控制字符。Base64把它们变成纯文本就安全了。所以你在HTTP请求/响应体、配置文件、注册表、甚至内存字符串里看到的一长串看似乱码的字符首先就该怀疑是Base64。识别与实战处理肉眼识别看到类似dGVzdA、U2FsdGVkX18OpenSSL Salted开头密文的特征这种格式的八九不离十。工具验证在线网站很多在线工具支持Base64解码随手一贴就知道是不是。CyberChef这个“网络瑞士军刀”的From Base64模块非常好用。Python脚本自己写几行代码验证最灵活。import base64 import re def try_decode_base64(data): # 先简单清理一下可能的引号或空格 data data.strip(\\ \n\r\t) # Base64正则匹配宽松版 if re.match(r^[A-Za-z0-9/]*{0,2}$, data) and len(data) % 4 0: try: decoded base64.b64decode(data, validateTrue) # 尝试以UTF-8解码如果不是文本就显示hex try: return decoded.decode(utf-8) except: return decoded.hex() # 返回十六进制表示 except Exception as e: return f解码失败: {e} return 不符合Base64特征 test_str U2FsdGVkX19q4zEwWL2hP2VJ5V4hX8NpFw print(try_decode_base64(test_str))逆向中的坑变种Base64有些实现会修改码表比如把/换成-_URL安全的Base64或者自定义码表。这就需要你逆向编码函数还原出码表。多层嵌套CTF中常见数据被反复Base64编码多次。解决思路就是写个循环直到解码结果不再符合Base64特征。包含换行有些格式如PEM证书的Base64会每76字符加一个换行解码前需要去掉。注意U2FsdGVkX1开头的字符串是OpenSSL使用Salted前缀的加密结果再进行Base64编码的典型特征这提示你后面很可能接的是一个对称加密如AES、DES的密文。2.2 十六进制Hex编码内存的直白写照十六进制编码就是把每个字节转换成两个0-9a-f的字符。它在逆向中几乎无处不在因为这是查看内存原始数据最直观的方式。识别与实战处理特征字符串仅由0-9和a-f或A-F组成长度通常是偶数。常见场景硬编码密钥/IV在二进制文件的字符串区域你可能会发现像0123456789ABCDEF这样的字符串这很可能就是一个16字节的AES密钥的Hex表示。网络数据包某些协议或自定义协议会用Hex格式传输二进制数据。调试器显示IDA、OllyDbg、Ghidra等工具的内存窗口默认就是以Hex形式展示数据。工具与代码Pythonbytes.fromhex()和binascii.hexlify()是常用函数。hex_str 48656c6c6f20576f726c64 # Hello World的Hex bytes_data bytes.fromhex(hex_str) print(bytes_data.decode(utf-8)) # 输出: Hello World raw_data bHello hex_str raw_data.hex() print(hex_str) # 输出: 48656c6c6f2.3 URL编码/百分号编码网络请求的“安全帽”URL编码是为了把URL中不允许的字符如空格、中文、、转换成%XX的形式其中XX是字符的ASCII码十六进制值。识别与实战处理特征字符串中包含大量%符号如%20空格、%E4%B8%AD“中”字的UTF-8编码。逆向场景分析HTTP请求参数、Cookie时一定会遇到。你需要解码后才能看到参数的真实值。处理浏览器开发者工具Network面板会自动解码显示。Python使用urllib.parse模块。from urllib.parse import unquote, quote encoded name%3D%E5%BC%A0%E4%B8%89%26age%3D20 decoded unquote(encoded, encodingutf-8) print(decoded) # 输出: name张三age20 # 编码 raw key值 encoded quote(raw, safe) # safe表示不对/和等字符进行编码 print(encoded)2.4 Unicode与UTF-8字符串的“内心世界”在Windows逆向和涉及多国语言的程序中Unicode编码是绕不开的。内存中一个英文字符可能占2字节UTF-16LE而中文字符在UTF-8下占3字节。识别与实战处理特征UTF-16LEWindows宽字符内存中英文字符后跟一个0x00字节如H存为0x48 0x00。字符串常以LHello形式出现在代码中。UTF-8变长编码ASCII字符不变中文等字符占多个字节如0xE4 0xB8 0xAD代表“中”。逆向要点在IDA等反编译工具中注意字符串的显示方式。有时你需要手动指定编码才能正确显示字符串。跟踪函数调用时注意区分strlen单字节和wcslen宽字节。遇到乱码时尝试用不同的编码GBK、GB2312、UTF-8、UTF-16去解码很可能有意外收获。Python的bytes_data.decode(gbk, errorsignore)可以帮你快速尝试。3. 逆向中的“开锁”术常见加密算法分析识别了数据的“样子”编码接下来就要对付它的“保护壳”加密。逆向分析加密算法的目标通常不是破解算法本身现代加密算法几乎不可破解而是找到密钥和加密模式。3.1 对称加密算法同一把钥匙对称加密加解密用同一个密钥。逆向中找到这个密钥是关键。3.1.1 AES (Advanced Encryption Standard)这是目前最主流、最安全的对称加密算法。如何识别字符串特征代码中可能出现AES、RijndaelAES的原名、CryptImportKey、EVP_aes_256_cbcOpenSSL等常量字符串。密钥长度AES的密钥长度固定为128、192或256位即16、24、32字节。如果你在代码或数据中发现一个固定长度为16/24/32字节的数据块它很可能是AES密钥。S-BoxAES算法内部有一个固定的256字节的替换表S-Box。在IDA中搜索字节序列0x63, 0x7C, 0x77, 0x7B...AES S-Box的开头如果能找到基本可以确定使用了AES。API/库调用在Windows上可能调用CryptAPICryptEncrypt/CryptDecrypt在Linux/Android上可能链接libcrypto.soOpenSSL并使用AES_encrypt、EVP_CipherInit_ex等函数。模式分析关键 只知道是AES还不够模式Mode决定了安全性。逆向时必须确定模式。ECB (Electronic Codebook)最不安全相同的明文块加密后得到相同的密文块。在数据中能看到重复的块比如加密一张纯色图片密文会有规律重复。尽量避免使用但一些老旧或简单系统仍用。CBC (Cipher Block Chaining)最常用需要一个初始化向量IV。每个明文块先与前一个密文块异或再加密。IV通常是随机的并和密文一起传输常放在密文开头。你需要找到IV和密钥。其他模式CTR、GCM等也较常见分析思路类似重点是找到nonce/IV和密钥。实战逆向步骤定位加密函数通过字符串、导入表或调用栈找到加密入口。跟踪密钥来源密钥可能是硬编码、从文件读取、通过网络下发、或由用户输入派生如PBKDF2。用调试器如x64dbg, IDA Debugger, Frida在加密函数下断点回溯密钥生成或传入的过程。确定模式与IV观察加密函数的参数。对于CBC模式通常会有一个独立的IV参数。IV可能硬编码也可能是随机生成后与密文拼接。验证用找到的密钥、IV和模式编写脚本尝试解密一段已知的密文看是否能得到有意义的明文。3.1.2 DES/3DES (Data Encryption Standard/Triple DES)DES已不安全3DES仍在一些遗留系统中使用。密钥长度DES为8字节实际56位3DES为16或24字节。识别搜索字符串DES、3DES、TripleDES。DES的S-Box也与AES不同。API调用类似AES。逆向要点与AES思路完全一致重点是找密钥。注意3DES的密钥可能包含三个8字节密钥K1, K2, K3。3.1.3 SM4国密算法在国内的软件、物联网设备、金融系统中越来越常见。它是国家标准分组长度和密钥长度均为128位。识别字符串SM4、GM/T 0002-2012。常量SM4有固定的FK和CK常量数组。在IDA中搜索这些常量是定位SM4代码的可靠方法。例如FK数组通常为{0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc}。库可能使用libsm4.so、gmssl或厂商自实现的库。模式同样支持ECB、CBC等模式分析思路同AES。实战我曾分析过一个智能家居设备的固件其网络通信数据就是用SM4-CBC加密的。通过逆向固件在初始化函数里找到了硬编码的密钥和IV藏在一个看似无关的配置字符串处理函数里。用Python的gmssl库成功解密了通信数据。3.2 非对称加密算法公钥与私钥非对称加密使用一对密钥公钥加密私钥解密或反之用于签名。逆向中通常目标是拿到私钥或者理解其加解密/签名流程。3.2.1 RSA最经典的非对称算法。密钥长度模数n的位数常见有1024、2048、4096位。如何识别字符串RSA、PublicKey、PrivateKey、BEGIN RSA PRIVATE KEY。大整数RSA的模数n、公钥指数e通常是655370x10001都是非常大的整数在IDA的静态分析中这些大整数常量非常显眼。API/库OpenSSL的RSA_public_encrypt、RSA_private_decryptWindows的CryptImportKey导入RSA类型的密钥。逆向分析目标获取私钥这是最理想的情况。私钥可能硬编码在程序中非常危险、存放在配置文件或密钥库中。搜索-----BEGIN RSA PRIVATE KEY-----这样的PEM头。理解流程如果拿不到私钥就分析程序如何使用公钥。例如客户端用服务器公钥加密一个临时生成的对称密钥会话密钥然后后续通信使用对称加密。这时你的目标就变成了获取这个被加密的会话密钥或者在内存中拦截它。工具openssl命令行工具可以方便地解析PEM格式的密钥并进行加解密操作。3.2.2 SM2国密非对称国产非对称算法基于椭圆曲线密码学ECC。识别字符串SM2、ECC、GM/T 0003-2012。同样有固定的参数如椭圆曲线方程、基点G等。逆向思路与RSA类似寻找公钥/私钥对。SM2的公钥是一对坐标x, y私钥是一个大整数。分析时需关注国密算法库的调用。3.3 哈希与消息认证码验证“身份”哈希如MD5、SHA1、SHA256、SM3将任意数据映射为固定长度的摘要不可逆。常用于校验数据完整性、密码存储需加盐。消息认证码如HMAC在哈希基础上加入了密钥用于验证消息来源和完整性。识别字符串MD5、SHA、HMAC、摘要。哈希函数调用特征明显输入数据输出固定长度MD5:16字节 SHA256:32字节。逆向中的作用校验绕过程序可能用哈希校验文件或关键数据是否被篡改。你需要修改数据后重新计算正确的哈希值并替换。密码破解如果程序存储的是明文密码的哈希值且未加盐或盐已知你可以尝试彩虹表碰撞或暴力破解。但加盐的哈希很难破解。密钥派生HMAC-SHA256常用于从主密钥派生出子密钥需要分析其输入密钥、数据是什么。4. 实战逆向案例拆解一个Android Native层的3DES通信协议理论说再多不如看个实例。假设我们有一个Android应用其核心通信协议在Native层so库中用3DES加密。我们的目标是解密其网络数据包。4.1 初步侦察使用Jadx-GUI打开APK搜索loadLibrary或System.load找到加载的so库名比如libprotocol.so。在Java层搜索与加密、网络相关的关键词encryptdecryptCipherDES3DES发现JNI调用native_encrypt_data和native_decrypt_data。4.2 静态分析so库用IDA Pro打开libprotocol.so。导出函数窗口找到Java_com_xxx_app_NativeHelper_native_encrypt_data。分析该函数发现它调用了另一个内部函数do_3des_cbc。进入do_3des_cbc发现它调用了DES_set_key_unchecked和DES_ncbc_encrypt这是OpenSSL的函数。确认算法3DES-CBC。关键寻找密钥和IV。在do_3des_cbc函数开头或调用它的上层函数中追踪传入的参数。发现密钥24字节和IV8字节来自于两个全局变量g_enc_key和g_enc_iv。查看这两个全局变量的交叉引用发现它们在JNI_OnLoad函数中被初始化。分析JNI_OnLoad发现密钥和IV是通过一个固定的字符串经过一个简单的XOR和MD5变换后生成的。我们拿到了密钥和IV的生成逻辑。4.3 动态验证与Frida Hook静态分析得出的结论需要验证。我们用Frida写个Hook脚本。Java.perform(function() { var NativeHelper Java.use(com.xxx.app.NativeHelper); // Hook native方法打印输入输出 var encryptPtr Module.findExportByName(libprotocol.so, Java_com_xxx_app_NativeHelper_native_encrypt_data); Interceptor.attach(encryptPtr, { onEnter: function(args) { // args[1]是jobjectargs[2]是jbyteArray (明文) this.input Java.vm.getEnv().getByteArrayElements(args[2], null); var inputLen Java.vm.getEnv().getArrayLength(args[2]); console.log([] native_encrypt_data 输入长度: inputLen); // 可以在这里把字节数组转为hex打印 console.log(输入Hex: bytesToHex(this.input, inputLen)); }, onLeave: function(retval) { // retval是jbyteArray (密文) var output Java.vm.getEnv().getByteArrayElements(retval, null); var outputLen Java.vm.getEnv().getArrayLength(retval); console.log([] native_encrypt_data 输出长度: outputLen); console.log(输出Hex: bytesToHex(output, outputLen)); // 用我们静态分析得到的算法、密钥、IV验证是否能解密密文 // 这里可以调用一个Python脚本或本地函数进行验证 verifyDecryption(bytesToHex(output, outputLen)); } }); });运行Frida脚本触发应用的网络请求成功抓取到加密前后的数据。4.4 编写解密脚本根据静态分析得到的密钥生成逻辑和动态验证确认的模式3DES-CBC用Python编写解密脚本。from Crypto.Cipher import DES3 from Crypto.Util.Padding import unpad import hashlib import binascii def derive_key_iv(): # 根据逆向出的逻辑生成密钥和IV seed_str some_hardcoded_string_from_so # 假设是 seed_str 经过一些变换和MD5得到key_material md5 hashlib.md5() md5.update(seed_str.encode(utf-8)) key_material md5.digest() # 16字节 # 假设密钥是 key_material 重复拼接而成IV是 key_material 的前8字节 key (key_material * 2)[:24] # 3DES密钥需要24字节 iv key_material[:8] # CBC IV需要8字节 return key, iv def decrypt_packet(ciphertext_hex): key, iv derive_key_iv() cipher DES3.new(key, DES3.MODE_CBC, iv) ciphertext binascii.unhexlify(ciphertext_hex) # 注意OpenSSL的CBC模式默认使用PKCS#7填充 plaintext_padded cipher.decrypt(ciphertext) plaintext unpad(plaintext_padded, DES3.block_size) return plaintext.decode(utf-8) # 使用Frida抓取到的密文进行测试 captured_cipher a1b2c3d4e5f6... # 替换为实际密文 try: print(decrypt_packet(captured_cipher)) except Exception as e: print(解密失败:, e)运行脚本成功解密出可读的JSON或协议数据逆向成功。5. 进阶技巧与工具链提升逆向效率掌握了基础识别和分析方法后一些工具和技巧能让你事半功倍。5.1 特征码与常量搜索这是定位加密/哈希函数最快的方法。在IDA中搜索字符串算法名、API名、错误信息。搜索字节序列AES的S-Box、SM4的FK/CK常量、MD5/SHA256的初始化魔数如MD5的0x674523010xEFCDAB89等。搜索指令模式某些算法有独特的操作序列。5.2 动态调试与内存dump静态分析有时会陷入复杂的数据流。动态调试可以直接看到运行时状态。下断点在标准库函数如OpenSSL的AES_encrypt或自定义加密函数入口下断。观察参数查看传入的密钥、IV、明文/密文缓冲区。内存搜索在程序内存中直接搜索可能的密钥如搜索固定字符串、或特定长度的随机数据。Cheat Engine、x64dbg的内存搜索功能很好用。5.3 使用Frida进行高级Hook对于Android/iOS/Windows应用Frida是无敌的。Hook加密函数不仅能打印输入输出还能替换密钥或修改返回值。跟踪密钥生命周期Hook密钥生成、存储、传输的所有相关函数。主动调用直接调用so库中的解密函数对抓到的密文进行解密无需重写算法。5.4 密码学算法识别工具PEiD/Krypto ANALyzer (KANAL)老牌PE文件分析工具插件可以识别一些常见的加密常量。IDA的FindCrypt插件IDA的神器能自动扫描二进制文件中的多种加密算法AES, DES, MD5, SHA, RSA等的常量并高亮显示。Binwalk常用于固件分析也能识别一些加密/压缩算法的特征。5.5 编写自己的识别脚本针对特定项目可以结合已知特征用Python写脚本批量分析。import re import binascii def scan_for_crypto_constants(binary_data): # AES S-Box 开头部分特征 aes_sbox_start bytes([ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 ]) # MD5 初始化常量 (A, B, C, D) md5_init [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] md5_bytes b.join([i.to_bytes(4, little) for i in md5_init]) findings [] if aes_sbox_start in binary_data: findings.append(发现AES S-Box特征) if md5_bytes in binary_data: findings.append(发现MD5初始化常量特征) # ... 添加更多算法特征 return findings with open(target.bin, rb) as f: data f.read() print(scan_for_crypto_constants(data))6. 总结与心法逆向思维养成最后分享几点在逆向编码和加密时的心得大胆假设小心求证看到一个字符串先猜它可能是Base64或Hex。看到一个固定长度的数据块先猜它可能是密钥或IV。然后用工具或脚本去验证你的猜想。上下文是关键数据出现在哪里是网络包、配置文件、还是内存字符串它周围有什么函数调用这些上下文信息是判断算法类型的重要线索。从易到难先编码后加密先尝试Base64、URLDecode、HexDecode这些简单的解码操作。把数据还原成原始二进制形式后再分析其加密特征。善用已知信息如果程序调用了libcrypto-1.1.so的EVP_aes_256_cbc那几乎可以断定是AES-256-CBC。如果字符串里有SM4_ECB那方向就非常明确了。动态与静态结合静态分析给你蓝图动态调试给你实景。两者结合才能高效定位关键点。不要只盯着IDA反汇编的代码看一定要让程序跑起来。工具只是延伸思路才是根本再好的工具也无法替代你对算法原理和程序逻辑的理解。花时间弄清楚一个算法的流程比你盲目尝试十个工具更有用。逆向编码和加密就像侦探破案你需要仔细观察所有线索数据特征、字符串、函数调用并利用你的知识编码、加密算法原理进行推理和验证。这个过程充满挑战但每一次成功解密都像是解开一道精妙的谜题那种成就感正是逆向工程的魅力所在。希望这篇总结能帮你少走些弯路更快地找到“开锁”的那把钥匙。