在二进制文件中,字符串对逆向工程师非常有用:它们通常包含显示给用户的消息,或者有时甚至是内部调试信息(函数或变量名),因此在反编译代码中显示它们是非常有帮助的。
然而,有时您可能会在伪代码中看到字符串命名变量,即使反汇编显示字符串很好。为什么会发生这种情况,如何解决呢?
内存访问权限
在决定是否内联显示字符串字面量时,主要标准是它所在内存区域的属性。如果内存是可写的,这意味着字符串并不是真正的常量,可能会改变,因此显示变量名更为正确。例如,以下是从解压缩的Linux内核中提取的函数的默认伪代码:
我们可以看到,字符串字面量被显示为变量名(aApicIcrReadRet
),尽管在反汇编中它是一个漂亮的字符串。如果我们跳转到它的定义(例如,通过双击)并检查段属性(编辑 > 段 > 编辑段…,或 Alt-S
),就可以解开这个谜团。我们可以看到该段被标记为可写:
为什么.rodata
(“只读数据”)具有写权限?我们不能确定,但该部分在ELF头中确实包含此标志:
段头:
[Nr] 名称 类型 地址 偏移
大小 元素大小 标志 链接 信息 对齐
[ 0] NULL 0000000000000000 00000940
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS ffffffff81000000 00001000
0000000000628281 0000000000000000 AX 0 0 4096
[ 2] .notes NOTE ffffffff81628284 00629284
0000000000000204 0000000000000000 AX 0 0 4
[ 3] __ex_table PROGBITS ffffffff81628490 00629488
0000000000002cdc 0000000000000000 A 0 0 4
[ 4] .rodata PROGBITS ffffffff81800000 0062d000
0000000000275332 0000000000000000 WA 0 0 4096
<...省略...>
标志说明:
W(写),A(分配),X(执行),M(合并),S(字符串),I(信息),
L(链接顺序),O(需要额外的操作系统处理),G(组),T(TLS),
C(压缩),x(未知),o(特定于操作系统),E(排除),
l(大),p(特定于处理器)
一种可能性是它在启动过程中实际上被设置为只读。
因此,我们问题的一个解决方案是确保该段仅具有读取(可能还有执行)权限,而没有写权限。如果您这样做,该段中的字符串字面量将内联显示:
重写访问权限
虽然更改段属性有效,但可能并不适用于所有情况。例如,一些编译器可能会将字符串常量放在与其他可写数据相同的段中,因此如果您将段权限更改为只读,反编译器可能会为使用可写变量的函数生成错误输出。您也可能遇到相反的情况:字符串常量实际上并不是常量,而只是具有默认值,因此需要标记为变量。在这种情况下,您可以使用const
或volatile
类型属性重写每个字符串变量的属性。例如,您可以通过按Y
(更改类型)并将其类型更改为const char aApicIcrReadRet[]
来编辑aApicIcrReadRet
变量的类型,而不是更改整个段的权限。
更改类型
通过此选项,只有编辑过的字符串字面量将内联显示,其他的仍然作为变量。
显示所有字符串字面量
另一种可能性是依赖于IDA对反汇编的分析,并在反汇编级别显示所有标记为字符串字面量的字符串。这可以在反编译器选项中完成(编辑 > 插件 > Hex-Rays反编译器,选项,分析选项1),通过关闭“仅打印常量字符串字面量”选项。
选项菜单
要更改此选项以适用于所有未来的数据库,请查看hexrays.cfg
中的HO_CONST_STRINGS
选项。
【点击👇进入抽奖环节】
更多文章
立即关注【二进制磨剑】公众号
原文始发于微信公众号(二进制磨剑):IDA 技巧(56) 伪代码中的字符串常量
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论