【问题标题】:What's the purpose of "AND AL,0xFF"?“AND AL,0xFF”的目的是什么?
【发布时间】:2017-07-27 21:33:30
【问题描述】:

我正在阅读一个反汇编的 win32 c++ 程序,我看到了很多:

AND AL,0xFF

这完全没有意义还是编译器会生成这些?

这是一个更长的例子:

movsx   eax, byte ptr [ebx]
shl     eax, 18h
movsx   edx, byte ptr [ebx+1]
shl     edx, 10h
add     eax, edx
movsx   ecx, byte ptr [ebx+2]
shl     ecx, 8
add     eax, ecx
movsx   edx, byte ptr [ebx+3]
add     eax, edx
xor     edx, edx
call    sub_43B55C
mov     ecx, eax
mov     edx, eax
sar     ecx, 10h
and     al, 0FFh      # <----
sar     edx, 8
and     cl, 0FFh      # <----
mov     [esi], cl
and     dl, 0FFh      # <----
mov     [esi+1], dl
mov     [esi+2], al
add     ebx, 4
add     esi, 3
inc     ebp
cmp     ebp, 6
jl      short loc_43B5E4

在这些操作之后没有检查标志,所以这不是目的。在AND 之后,ALCLDL 中的值将被移动到[ESI + n]

【问题讨论】:

  • 不知道之前发生了什么,很难说。假设 EAX 是一个指针,它需要在 16 字节边界上对齐。如果为真,这将设置 ZF,因此指向指令的目的只是设置标志。或者,如果编译器设置适当,则可以使用test al, 0xff
  • 感谢您的评论。没有检查标志。 ANDing 似乎在字节被移动到内存之前发生。我用周围的代码更新了上下文。
  • 看起来很奇怪。也许这个指令用于填充或作为钩子的标记。
  • 也许它只是一个优化不佳的编译器,未能优化掉成语foo = bar &amp; 0xff。什么程序生成了这个代码?
  • @fuz :恕我直言,这是最有可能发生的情况,因为代码看起来像一些二进制/位级(重新)打包函数。

标签: assembly bitwise-operators eflags


【解决方案1】:

正如@fuz 所建议的,这只是优化器的错误,它没有将foo &amp; 0xff 识别为在原始函数中最有可能使用它的上下文中的无操作。

在将项目的编译设置设置为“Release”后,我使用 Borland C++ Builder 6 编译了以下代码 sn-p:

unsigned char foobar(int foo) { return (foo >> 16) & 0xff; }

这与您提供的反汇编中执行的操作非常相似。我们有一个 32 位的值,我们想要移动给定数量的位,然后将其转换为字节值,本质上将原始值的 16-23 位作为一个字节返回。输入参数是 int 类型,以便生成 sar 指令而不是 shr :很可能在原始代码中也使用了 int

在使用 objconv 编译和反汇编生成的 .obj 之后(因为我不知道如何从 C++ Builder 的 IDE 中启用汇编列表),我得到了这个:

@foobar$qi PROC NEAR
;  COMDEF @foobar$qi
        push    ebp                                     ; 0000 _ 55
        mov     ebp, esp                                ; 0001 _ 8B. EC
        mov     eax, dword ptr [ebp+8H]                 ; 0003 _ 8B. 45, 08
        sar     eax, 16                                 ; 0006 _ C1. F8, 10
        and     al, 0FFFFFFFFH                          ; 0009 _ 24, FF
        pop     ebp                                     ; 000B _ 5D
        ret                                             ; 000C _ C3
@foobar$qi ENDP

如您所见,多余的and 仍然存在。可以忽略反汇编中的 32 位立即数,因为指令的编码清楚地表明实际代码流中的立即数是 8 位的:无论如何,没有其他有效选项与 8 位寄存器。

Microsoft Visual Studio C++ 6 似乎也犯了同样的错误,但对整个 32 位寄存器进行操作(因此由于 32 位立即数而产生了 3 个字节),清除了高位 - 这是不必要的,看看函数的返回值是如何被显式声明为 8 位的:

?foobar@@YAEH@Z PROC NEAR                               ; foobar
; 1    : unsigned char foobar(int foo) { return (foo >> 16) & 0xff; }
  00000 55               push    ebp
  00001 8b ec            mov     ebp, esp
  00003 8b 45 08         mov     eax, DWORD PTR _foo$[ebp]
  00006 c1 f8 10         sar     eax, 16                        ; 00000010H
  00009 25 ff 00 00 00   and     eax, 255               ; 000000ffH
  0000e 5d               pop     ebp
  0000f c3               ret     0
?foobar@@YAEH@Z ENDP                                    ; foobar

同时,the oldest version of gcc available on godbolt 正确地将其编译为本质上只是一个转变,除了由于调用约定而导致的列表之间的自然差异。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-24
    • 1970-01-01
    • 2014-11-08
    • 1970-01-01
    • 2019-04-12
    • 2018-11-14
    • 2013-08-15
    • 2010-09-16
    相关资源
    最近更新 更多