我想通过x86_64二进制文件,反汇编指令来了解有关ptrace函数的更多信息。目标是检查字节是否是指令前缀之一。
我在Intel® 64 and IA-32 Architectures Software Developer’s Manual中找到了一些信息(第2卷,第2章)。
2.1.1 INSTRUCTION PREFIXES
部分显示以下前缀:
0x26
] ES段覆盖0x36
] SS段覆盖前缀0x2E
] CS段覆盖前缀或未采用分支0x3E
] DS段覆盖前缀或分支0x64
] FS段覆盖前缀0x65
] GS段覆盖前缀0x66
]操作数大小覆盖前缀0x67
]地址大小覆盖前缀0xF0
] LOCK前缀0xF2
] REPNE / REPNZ前缀或BND前缀0xF3
] REP或REPE / REPZ前缀在视觉上,this chart显示黄色的前缀。
如果我想知道一个字节是否是前缀,我将尝试高效并检查是否可以执行二进制操作。
如果我把0x26
,0x36
,0x2E
和0x3E
作为一个整体。基数2(00100110
,00110110
,00101110
和00111110
)中的这些数字显示了一个共同的部分:001XX110
。
如果我的字节在此组中,则可以找到11100111
(0xE7
)的二元运算。
大。现在,如果我采取第二组包含0x64
,0x65
,0x66
和0x67
(01100100
,01100101
,01100110
,01100111
),我发现了另一个共同部分:011001XX
。
然后,11111100
(0xFC
)的二进制运算可以找到该字节是否在第二组中。
问题出现在剩余的指令前缀(0xF0
,0xF2
和0xF3
):没有共同的部分。 11111100
(0xFC
)的操作将让字节0xF1
。
如果字节不是0xF1
,一种解决方案是检查。
因此,C中可能的实现将是:
if ((byte & 0xE7) == 0x26) {
/* This `byte` is a ES, SS, CS or DS segment override prefix */
}
if ((byte & 0xFC) == 0x64) {
/* This `byte` is a FS, GS, Operand-size or address-size override prefix */
}
if ((byte & 0xFC) == 0xF0) {
if (byte != 0xF1) {
/* This `byte` is a LOCK, REPN(E/Z) or REP(_/E/Z) prefix */
}
}
来自英特尔,除了最后一组可以只检入一个操作外,我会这样做。
然后,最后一个问题是:如果字节是0xF0,0xF2或0xF3,我可以检入一个操作吗?
然后,最后一个问题是:如果字节是0xF0,0xF2或0xF3,我可以检入一个操作吗?
最接近一条指令的是:
;ecx = the byte
bt [table],ecx ;Is the byte F0, F2 or F3?
jc .isF0F2orF3 ; yes
但是,有时前缀不被视为前缀(例如pause
指令,其编码类似于rep nop
以与旧CPU兼容)。
另请注意,对于高速反汇编程序,最快的方法可能是“跳表驱动”,其中一个寄存器指向对应于解码器状态的表,另一个寄存器包含指令的下一个字节,如:
;ebx = address of table corresponding to the decoder's current state
movzx eax,byte [esi] ;eax = next byte of the instruction
inc esi ;esi = address of byte after the next byte of this instruction
jmp [ebx+eax*4] ;Go to the code that figures out what to do
在这种情况下,跳转到的一些代码将设置一些标志而不更改当前表(例如,初始表中的0xF3条目将导致跳转到设置“看到rep前缀”标志的代码),并且跳转到的一些代码将切换到另一个表(例如,初始表中的0x0F条目将导致跳转到代码,该代码将EBX
更改为指向用于以0x0F, ...
开头的所有指令的完全不同的表) ;并且跳转到的一些代码将显示指令(并重置解码器的状态)。
例如;对于pause
,代码可能是:
table0entryF3:
or dword [prefixes],REP
movzx eax,byte [esi] ;eax = next byte of the instruction
inc esi ;esi = address of byte after the next byte
jmp [ebx+eax*4]
table0entry90:
mov edx,instructionNameString_NOP
test dword [prefixes],REP ;Was it a PAUSE or NOP?
je doneInstruction_noOperands ; NOP, current name is right
and dword [prefixes],~REP ; PAUSE, pretend the REP prefix wasn't there
mov edx,instructionNameString_PAUSE ; and use the right name
jmp doneInstruction_noOperands
doneInstruction_noOperands:
call displayPrefixes
call displayInstructionName
mov dword [prefixes],0 ;Reset prefixes
mov ebx,table0 ;Switch current table back to the initial table
movzx eax,byte [esi] ;eax = first byte of next instruction
inc esi ;esi = address of byte after the next byte
jmp [ebx+eax*4]