首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >英特尔X86组装:如何区分多位宽是一个论点?

英特尔X86组装:如何区分多位宽是一个论点?
EN

Stack Overflow用户
提问于 2018-05-08 00:00:32
回答 1查看 831关注 0票数 0

在下列程序集中:

代码语言:javascript
运行
复制
mov     dx, word ptr [ebp+arg_0]
mov     [ebp+var_8], dx

考虑到这是一个集合的C函数,有多少位宽(C函数的参数) arg_0?(局部变量C变量) var_8有多少位宽?也就是说,它是短的,int的,等等。

由此看来,var_8是16位,因为dx是16位寄存器.但我对arg_0不太确定。

如果程序集还包含此行,则:

代码语言:javascript
运行
复制
ecx, [ebp+arg_0]

这是否意味着arg_0是一个32位的值?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-08 09:57:29

为了解决这个问题,有三个原则需要理解。

  1. 汇编程序必须能够推断出正确的长度. 尽管英特尔的语法没有使用像AT&T语法这样的大小后缀,但汇编程序仍然需要一种方法来查找操作数的大小。

如果存储的大小为32位(请注意后缀mov [var], 1 ),则模糊指令movl $1, var是AT&T语法中的movl $1, var,因此很容易判断直接操作数的大小。

接受Intel语法的汇编程序需要一种推断此大小的方法,有四种广泛使用的选项:

例如,当涉及到寄存器时,

代码语言:javascript
运行
复制
- _It is inferred from the other operand._

就是这种情况。

mov [var], dx是一个16位的商店。-明文规定。

mov WORD [var], dx

MASM-语法汇编程序在大小之后需要一个PTR,因为它们的大小说明符只允许在内存操作数上使用,而不是直接或其他任何地方。

这是我喜欢的形式,因为它是明确的,它是突出的,它是不容易出错的(mov WORD [var], edx是无效的)。-从上下文中推断。

var db 0 mov var,1;仅MASM/TASM。将尺寸与标签相关联

MASM-语法汇编程序可以推断,由于var是用db声明的,所以它的大小是8位,存储也是这样(默认情况下)。

这是我不喜欢的形式,因为它使代码更难阅读(组装的一个好处是指令语义的“局部性”),并将高级别的概念(如类型和存储大小等低级别概念混合在一起)。这就是为什么NASM的语法不支持魔法/非本地大小关联。。-在大多数情况下,只有一个正确的尺寸

push、分支和所有指令都是这样,它们的操作数大小取决于内存模型或代码大小。

对于某些指令,可以重写实际使用的大小,但默认情况是一个明智的选择。(例如push 123)

简而言之,汇编程序必须有一种方法来判断其大小,否则它将拒绝代码。(或者一些低质量的汇编程序(如emu8086 )对于模棱两可的情况有一个默认的操作数大小。)

如果您正在查看反汇编代码,反汇编程序通常采取安全的一面,并始终明确说明大小。

如果没有,您必须求助于手动检查操作码,如果反汇编程序不会显示操作码,则是时候更改操作码了。

反汇编程序不难找到操作数的大小,因为它正在反汇编的二进制代码是由CPU执行的,指令操作码是操作数大小的代码。

  1. C语言有意地松散了C类型映射到位数的方式

尝试从反汇编中推断变量的类型并非徒劳无功,但我们也必须考虑到平台,而不仅仅是体系结构。

讨论了这里使用的主要模型。

数据类型LP64 ILP64 LLP64 ILP32 LP32 char 8 8 8短16 16 16 _int32 32 int 32 64 32 32 16 64 32 32 32长64 64指针64 64 64 32 32

x86_64上的窗口使用LLP64。x86-64上的其他OSes通常使用x86-64系统V,这是一个LP64模型。

  1. 程序集没有类型,程序员可以利用该

即使是编译器也可以利用这一点。

在链接类型为long long (64位)的long long变量为1的ORed变量时,clang只在低字节下使用ORing的REX前缀。如果变量立即用两个dword加载或一个qword重新加载,则会导致存储转发失速,因此这可能不是一个好的选择,特别是在32位模式下,or dword [bar], 1大小相同,并且很可能作为两个32位二分之一重新加载。

如果你不小心地看分解的代码,他们就可以推断出bar是8位的。

这种技巧,其中一个变量或一个对象,被部分访问是常见的。

为了正确猜测变量的大小,需要一些专门知识。

例如,结构成员通常是填充的,因此在它们之间存在未使用的空间,这可能会使没有经验的用户认为每个成员都比它大。

堆栈具有精确的对齐要求,这也是可能会扩大参数的大小的要求。

经验法则是编译器通常倾向于保持堆栈16字节对齐,并且自然地对齐所有变量。多个窄变量被打包到一个dword中。。当通过堆栈传递函数args时,每个函数都被填充到32位或64位,但这并不适用于堆栈上局部变量的布局。

最终回答你的问题

是的,在第一个代码片段中,您可以假设arg_0的值是16位宽。

注意,由于它是传递到堆栈上的函数arg,所以它实际上是32位,但是上面的16位没有被使用。

如果代码中出现的mov ecx, [ebp+arg_0]比您对arg_0值大小的猜测要晚得多,那么它肯定至少是32位。

它不太可能是64位(64位类型在32位代码中是罕见的,我们可以做这个赌注),所以我们可以得出它是32位。

显然,第一个片段是那些只使用变量的一部分的技巧之一。

这就是如何处理反求工程--一个变量的大小--进行猜测,验证它是否与代码的其余部分一致,如果不是,则重新访问它,重复。

随着时间的推移,你会做出更好的猜测,根本不需要修改。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50223972

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档