Assembly 在汇编程序中导出优化strlen?

Assembly 在汇编程序中导出优化strlen?,assembly,Assembly,下面的代码能够确定DWORD的一个或多个字节是否设置为0 mov eax, value mov edx, 07EFEFEFFh add edx, eax xor eax, 0FFFFFFFFh xor eax, edx and eax, 081010100h 例如,如果我们输入34323331h,eax=0 然而,如果我们输入1字节设置为00的内容,比如34003231h,eax!=0 我知道这段代码是做什么的,但我不明白它是如何做到的。这在数学上是如何工作的?有人能用比特向我解释这个过程吗?

下面的代码能够确定DWORD的一个或多个字节是否设置为0

mov eax, value
mov edx, 07EFEFEFFh
add edx, eax
xor eax, 0FFFFFFFFh
xor eax, edx
and eax, 081010100h
例如,如果我们输入34323331h,eax=0 然而,如果我们输入1字节设置为00的内容,比如34003231h,eax!=0

我知道这段代码是做什么的,但我不明白它是如何做到的。这在数学上是如何工作的?有人能用比特向我解释这个过程吗?这是如何推导出来的


它应该相对简单,但我就是看不到它

我将从右边的0开始计算位

简短答复: 将
11111111
添加到零字节(
00000000
)时,溢出位(第8位)
值+0x7EFEFF
的相同溢出位没有差异

11111111
添加到非零字节时,溢出位(第8位)
value+0x7EFEFEFF
的相同溢出位不同

程序只是检查这些位

长答覆: 这是代码的数学表示形式(
a
是值):

在哪里

  • magic
    0x7EFEFEFF
  • ^
    表示按位异或
  • &
    表示按位和
  • 表示按位反转,也称为异或,与
    0xffffff
要了解
0x7efeff
的作用,请查看其二进制表示:

01111110 11111110 11111110 11111111
0
是神奇的溢出位。这些是第8、16、24和31位

让我们看几个例子

示例1:
eax=0x00000000
当我们用
异或
a+magic
时!a
我们得到:

result    = 10000001 00000001 00000001 00000000
result    = 10000001 00000001 00000000 11111110
result    = 01111001 00000000 11111100 11111110
result    = 01111000 11111100 11111110 11111110
看看这些神奇的片段。它们都是
1

然后,我们只需通过
清除其余的位(这里都是
0
),并通过
100000010000000100000000
aka
对结果进行运算!魔术
。如您所知,
ing by 0仅将0分配给该位,
ing by 1对该位不做任何操作

最终结果:

10000001 00000001 00000001 00000000
10000001 00000001 00000000 00000000
示例2:
eax=0x00000001
当我们用
异或
a+magic
时!a
我们得到:

result    = 10000001 00000001 00000001 00000000
result    = 10000001 00000001 00000000 11111110
result    = 01111001 00000000 11111100 11111110
result    = 01111000 11111100 11111110 11111110
看看这些神奇的部分。第16、24和31位为1。第8位是0

  • 第8位表示第一个字节。如果第一个字节不是零,则第8位此时变为
    1
    。否则它是
    0
  • 第16位表示第二个字节。同样的逻辑
  • 第24位表示第三个字节
  • 第31位表示第四个字节
然后我们再次通过
清除非魔法位,并用
对结果进行加密!魔术

最终结果:

10000001 00000001 00000001 00000000
10000001 00000001 00000000 00000000
示例3:
eax=0x34003231
当我们用
异或
a+magic
时!a
我们得到:

result    = 10000001 00000001 00000001 00000000
result    = 10000001 00000001 00000000 11111110
result    = 01111001 00000000 11111100 11111110
result    = 01111000 11111100 11111110 11111110
只有第24位是
1

清除非魔法位后,最终结果为:

00000001 00000000 00000000 00000000
00000000 00000000 00000000 00000000 (zero)
示例4:
eax=0x34323331
当我们用
异或
a+magic
时!a
我们得到:

result    = 10000001 00000001 00000001 00000000
result    = 10000001 00000001 00000000 11111110
result    = 01111001 00000000 11111100 11111110
result    = 01111000 11111100 11111110 11111110
清除非魔法位后,最终结果为:

00000001 00000000 00000000 00000000
00000000 00000000 00000000 00000000 (zero)

我编写了一个用于演示的测试用例:

#include <stdint.h> // uint32_t
#include <stdio.h> // printf

//assumes little endian
void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    int i, j;

    for (i = size - 1; i >= 0; i--) {
        for (j = 7; j >= 0; j--) {
            byte = b[i] & (1 << j);
            byte >>= j;
            printf("%u", byte);
        }

        printf(" ");
    }
}

int main()
{
    uint32_t a = 0;
    uint32_t d = 0;
    const uint32_t magic = 0x7EFEFEFF;
    const uint32_t magicRev = magic ^ 0xFFFFFFFF;

    const uint32_t numbers[] = {
        0x00000000, 0x00000001, 0x34003231,
        0x34323331, 0x01010101
    };


    for (int i = 0; i != sizeof(numbers) / sizeof(numbers[ 0 ]); i++) {
        a = numbers[ i ];
        d = magic;

        printf("a:            ");
        printBits(sizeof(a), &a);
        printf("\n");

        d = a + d;

        printf("a+magic:      ");
        printBits(sizeof(d), &d);
        printf("\n");

        a = a ^ 0xFFFFFFFF;

        printf("!a:           ");
        printBits(sizeof(a), &a);
        printf("\n");

        a = a ^ d;

        printf("result:       ");
        printBits(sizeof(a), &a);
        printf("\n");

        a = a & magicRev;

        printf("              ");
        printBits(sizeof(a), &a);

        if (a == 0) {
            printf(" (zero)\n");
        } else {
            printf(" (at least one)\n");
        }

        printf("\n");
    }

    return 0;
}
#包括//uint32\t
#包括//printf
//假设小端点
无效打印位(大小\u t常量大小,无效常量*常量ptr)
{
无符号字符*b=(无符号字符*)ptr;
无符号字符字节;
int i,j;
对于(i=size-1;i>=0;i--){
对于(j=7;j>=0;j--){
字节=b[i]&(1>=j;
printf(“%u”,字节);
}
printf(“”);
}
}
int main()
{
uint32_t a=0;
uint32_t d=0;
常数uint32\u t magic=0x7efeff;
常数uint32\u t magicRev=magic^0xffffff;
常数32_t编号[]={
0x00000000,0x00000001,0x34003231,
0x34323331,0x01010101
};
对于(int i=0;i!=sizeof(数字)/sizeof(数字[0]);i++){
a=数字[i];
d=魔法;
printf(“a:”);
打印位(sizeof(a),&a);
printf(“\n”);
d=a+d;
printf(“a+magic:”);
打印位(尺寸f(d)和d);
printf(“\n”);
a=a^0xFFFFFFFF;
printf(“!a:”);
打印位(sizeof(a),&a);
printf(“\n”);
a=a^d;
printf(“结果:”);
打印位(sizeof(a),&a);
printf(“\n”);
a=a&magicRev;
printf(“”);
打印位(sizeof(a),&a);
如果(a==0){
printf(“(零)\n”);
}否则{
printf(“(至少一个)\n”);
}
printf(“\n”);
}
返回0;
}

要检查值是否包含零字节,还有其他更快的方法: