以下代码能够确定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 :(得分:5)
我将从右边的0开始计算位数。
将11111111
添加到零字节(00000000
)时,溢出位(第8位)不与value + 0x7EFEFEFF
的溢出相同位。
将11111111
添加到非零字节时,溢出位(第8位) 与value + 0x7EFEFEFF
相同的溢出位不同。
程序只检查这些位。
这是代码的数学表示(a
是值):
result = ((a + magic) ^ !a) & !magic
,其中
magic
是0x7EFEFEFF
^
表示按位xor &
表示按位和!
表示按位反转,也称为xor'd 0xFFFFFFFF
要了解0x7EFEFEFF
的作用,请查看它的二进制表示:
01111110 11111110 11111110 11111111
0
是魔术溢出位。这些是8,16,24和31位。
让我们看一些例子。
eax = 0x00000000
a = 00000000 00000000 00000000 00000000
a+magic = 01111110 11111110 11111110 11111111
!a = 11111111 11111111 11111111 11111111
当我们a+magic
与!a
xor时,我们得到:
result = 10000001 00000001 00000001 00000000
这里看看魔术位。它们都是1
。
然后,我们只需0
又称and
10000001 00000001 00000001 00000000
结果清除其余位(这里都是!magic
)。如您所知,and
为0只是将0分配给该位,而and
为1对该位没有任何作用。
最终结果:
10000001 00000001 00000001 00000000
eax = 0x00000001
a = 00000000 00000000 00000000 00000001
a+magic = 01111110 11111110 11111111 00000000
!a = 11111111 11111111 11111111 11111110
当我们a+magic
与!a
xor时,我们得到:
result = 10000001 00000001 00000000 11111110
看看神奇的位。第16,24和31位为1.第8位为0.
1
。否则就是0
。然后我们再次使用and
!magic
结果清除非魔术位。
最终结果:
10000001 00000001 00000000 00000000
eax = 0x34003231
a = 00110100 00000000 00110010 00110001
a+magic = 10110010 11111111 00110001 00110000
!a = 11001011 11111111 11001101 11001110
当我们a+magic
与!a
xor时,我们得到:
result = 01111001 00000000 11111100 11111110
只有第24位是1
清除非魔术位后,最终结果是:
00000001 00000000 00000000 00000000
eax = 0x34323331
a = 00110100 00110010 00110011 00110001
a+magic = 10110011 00110001 00110010 00110000
!a = 11001011 11001101 11001100 11001110
当我们a+magic
与!a
xor时,我们得到:
result = 01111000 11111100 11111110 11111110
清除非魔术位后,最终结果是:
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;
}