首页 > 解决方案 > 使用 AND、OR、SHR 和 SHL 指令以及数组将循环从 x86 汇编转换为 C 语言

问题描述

我不明白问题出在哪里,因为结果是正确的,但是其中有问题,我不明白。

1.这是我必须转换为 C 的 x86 代码:

%include "io.inc"  
SECTION .data
mask    DD      0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555

SECTION .text
GLOBAL CMAIN
CMAIN:
    GET_UDEC        4, EAX
    MOV             EBX, mask
    ADD             EBX, 16
    MOV             ECX, 1
.L:
    MOV             ESI, DWORD [EBX]
    MOV             EDI, ESI
    NOT             EDI
    MOV             EDX, EAX
    AND             EAX, ESI
    AND             EDX, EDI
    SHL             EAX, CL
    SHR             EDX, CL
    OR              EAX, EDX
    SHL             ECX, 1
    SUB             EBX, 4
    CMP             EBX, mask - 4
    JNE             .L

    PRINT_UDEC      4, EAX
    NEWLINE
    XOR             EAX, EAX
    RET

2.我转换后的 C 代码,当我输入 0 时,它会输出正确的答案,但我的代码中有一些错误,我不明白是什么:

#include "stdio.h"
int main(void)
{
    int mask [5] =  {0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555};


    int eax;
    int esi;
    int ebx;
    int edi;
    int edx;
    char cl = 0;
    scanf("%d",&eax);
    ebx = mask[4];
    ebx = ebx + 16;
    int ecx = 1;
    L:
    esi = ebx;
    edi = esi;
    edi = !edi;
    edx = eax;
    eax = eax && esi;
    edx = edx && edi;
    eax = eax << cl;
    edx = edx >> cl ;
    eax = eax || edx;
    ecx = ecx << 1;
    ebx = ebx - 4;

    if(ebx == mask[1]) //mask - 4
    {
        goto L;
    }

    printf("%d",eax);
    return 0;
}

标签: cassemblyx86nasmreverse-engineering

解决方案


汇编 AND 是 C 按位 &​​而不是逻辑&&。(OR 相同)。所以你想要eax &= esi.

(使用&=“复合赋值”使 C 甚至看起来像 x86 风格的 2 操作数 asm,所以我建议这样做。)

NOT 也是按位翻转所有位,而不是布尔化为 0/1。在C那是edi = ~edi;

阅读诸如https://www.felixcloutier.com/x86/not之类的 x86 指令手册,以及诸如~and之类的 C 运算符的手册,!以检查它们是否/不是您想要的。 https://en.cppreference.com/w/c/language/expressions https://en.cppreference.com/w/c/language/operator_arithmetic

您应该在调试器中单步执行您的 C 和您的 asm,以便您注意到第一个分歧,并知道要修复哪个指令/C 语句。不要只运行整个事情并查看一个数字的结果!调试器对 asm 非常有用;不要浪费你的时间没有一个。


CL 是 ECX 的低字节,而不是单独的 C 变量。您可以在 Cuint32_tuint8_tC 之间使用联合,或者只是使用eax <<= ecx&31;,因为您没有任何东西可以将 CL 与 ECX 分开编写。(x86 班次掩盖了他们的计数;该 C 语句可以编译为shl eax, cl. https://www.felixcloutier.com/x86/sal:sar:shl:shr)。ECX 的低 5 位也是 CL 的低 5 位。

SHR是逻辑右移,而不是算术,因此您unsigned至少int需要使用>>. 但实际上只是将它用于一切。


您完全错误地处理 EBX;这是一个指针。

 MOV             EBX, mask
 ADD             EBX, 16

这就像unsigned int *ebx = mask+4;

dword 的大小为 4 个字节,但 C 指针数学按类型大小缩放,因此+1是整个元素,而不是 1 个字节。所以 16 个字节是 4 个双字 = 4 个unsigned int元素。

MOV             ESI, DWORD [EBX]

这是使用 EBX 作为地址的负载。如果您在调试器中单步执行 asm,这应该很容易看出:它不仅仅是复制值。

CMP             EBX, mask - 4
JNE             .L

这是 NASM 语法;它与数组开始之前的双字地址进行比较。它实际上是一个相当正常的 do{}while 循环的底部。(为什么循环总是编译成“do...while”风格(尾跳)?

do {          // .L
   ...
} while(ebx != &mask[-1]);    // cmp/jne

它从mask数组的末尾循环,当指针越过末尾时停止。

等效地,比较可以是ebx !-= mask - 1. 我用一元&(地址)取消了[]它,以明确它是数组之前一个元素的地址。

请注意,它的跳跃相等;你有你的if()goto倒退,只在平等上跳跃。这是一个循环。


unsigned mask[]应该是static因为它在section .data,而不是在堆栈上。而不是const,因为它又.data不在.rodata(Linux)或.rdata(Windows)中)

这个不影响逻辑,只影响反编译的细节。


可能还有其他错误;我没有尝试检查所有内容。


推荐阅读