Assembly 英特尔X86汇编:如何判断宽多少位是一个参数?
在以下组件中:Assembly 英特尔X86汇编:如何判断宽多少位是一个参数?,assembly,x86,int,intel,short,Assembly,X86,Int,Intel,Short,在以下组件中: 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 如果部件也包含此行: ecx, [ebp+arg_0] 这是否意味着arg_0是一个32位的值?要解决这个问题,有三个原则需要理解 汇编程序必须能
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
如果部件也包含此行:
ecx, [ebp+arg_0]
这是否意味着arg_0是一个32位的值?要解决这个问题,有三个原则需要理解
虽然英特尔的语法没有使用像“汇编”那样的大小后缀,但汇编程序仍然需要一种方法来查找操作数的大小。
如果存储的大小为32位(注意后缀
l
),则不明确的指令mov[var],1
在AT&T语法中写为movl$1,var
,因此很容易判断立即操作数的大小。接受英特尔语法的汇编程序需要一种方法来推断此大小,有四种广泛使用的选项:
- 它是从另一个操作数推断出来的。
例如,涉及寄存器时就是这种情况。
例如,
是一个16位存储李>mov[var],dx
- 明确说明。
mov-WORD[var],dx
MASM语法汇编程序在大小之后需要一个
,因为它们的大小说明符只允许在内存操作数上使用,而不允许在即时操作数或其他任何地方使用。PTR
这是我喜欢的格式,因为它清晰、突出,并且不太容易出错(
无效)李>mov-WORD[var],edx
- 它是根据上下文推断出来的
MASM语法汇编程序可以推断,因为
是用var
声明的,所以它的大小是8位,存储也是8位(默认情况下)。db
这是我不喜欢的形式,因为它使代码更难阅读(汇编的一个好处是指令语义的“局部性”),并且混合了类型之类的高级概念和存储大小之类的低级概念。这就是为什么 - 在绝大多数情况下,只有一个正确的大小
对于
、分支和所有指令,它们的操作数大小取决于内存模型或代码大小。推送
某些指令可以覆盖使用的实际大小,但默认值是一个明智的选择。(例如)
简而言之,汇编器必须有一种方法来告诉大小,否则它将拒绝代码。(或者一些低质量的汇编程序,如emu8086,在不明确的情况下有默认的操作数大小。) 如果您查看的是反汇编代码,反汇编程序通常会采取安全措施,并始终明确说明大小。
如果没有,则必须手动检查操作码,如果反汇编程序无法显示操作码,则是时候更改操作码了。
反汇编程序很容易找到操作数的大小,因为它要反汇编的二进制代码与CPU执行的相同,并且指令操作码对操作数大小进行编码。
试图从反汇编中推断变量的类型并非徒劳,但也必须考虑平台,而不仅仅是架构。
讨论了使用的主要模型: x86_64上的Windows使用LLP64。x86-64上的其他操作系统通常使用x86-64 System V ABI,一种LP64型号
.
在链接的情况下,
bar
类型的long
(64位)变量与1进行OR运算,clang
通过仅对低字节进行ORing来保留REX前缀。如果使用两个dword加载或一个qword立即重新加载变量,这会导致存储转发暂停,因此这可能不是一个好的选择,尤其是在32位模式下,或dword[bar],1
大小相同,并且可能作为两个32位的一半重新加载。如果一个人不小心地查看反汇编代码,他们可能会推断
bar
是8位的。这种部分访问变量或对象的技巧很常见。
为了正确猜测变量的大小,需要一些专业知识。
例如,结构成员通常是填充的,因此它们之间存在未使用的空间,这可能会愚弄没有经验的用户,使其认为每个成员都比实际大。
堆栈也有精确的对齐要求。
经验法则是编译器通常更喜欢保持堆栈16字节对齐,并自然对齐所有变量。当通过堆栈传递函数参数时,每个参数都被填充为32或64位,但这不适用于堆栈上局部变量的布局
arg\u 0
的值为16位宽。请注意,由于它是在堆栈上传递的函数arg,因此它实际上是32位的,但不使用上面的16位 如果
mov ecx、[ebp+arg_0]
出现在代码中的较晚时间,而您需要重新猜测arg_0
值的大小,那么肯定至少是32位。它不太可能是64位的(64位类型在32位代码中很少见,我们可以打赌),所以我们可以断定它是32位的。
显然,第一个代码片段是那些只使用部分变量的技巧之一 这就是你如何处理一个var大小的反向工程,你做一个猜测,验证它与代码的其余部分一致,如果不一致,重新访问它,重复。
随着时间的推移,你会做出几乎不需要修改的好猜测 在那里
var db 0
mov [var], 1 ; MASM/TASM only. associate sizes with labels
Datatype LP64 ILP64 LLP64 ILP32 LP32
char 8 8 8 8 8
short 16 16 16 16 16
_int32 32
int 32 64 32 32 16
long 64 64 32 32 32
long long 64 [64]
pointer 64 64 64 32 32