如何反转异或移位操作

How to reverse XOR-shift operations

我有以下异或移位编码器

char a[] = "text";
int b = strlen(a);
for (int c = 0; c < b; c++) {
    if (c > 0) a[c] ^= a[c-1];
    a[c] ^= a[c] >> 3;
    a[c] ^= a[c] >> 2;
    a[c] ^= a[c] >> 1;
    printf("%02x", (unsigned int)(a[c]));
}

我想将十六进制输出恢复为原始字符串。

考虑到移位在某些情况下会消除右侧无法再检索的位,这甚至有可能吗?如果可能的话,如何恢复这些操作?

是的,没那么难。只需将 xor-shit 结果映射回其原始值即可。考虑这个函数:

char f(char c) {
    c ^= c >> 3;
    c ^= c >> 2;
    c ^= c >> 1;
    return c;
}

您应该能够让自己满意,对于每个可能的输入值(从 0 到 127),都有一个对应的唯一输出。 (当然,如果将 char 更改为 unsigned char,则该函数将适用于任何 8 位输入。)

使用这个函数,你可以很容易地创建一个数组来执行这个函数的逆运算:

char g(char c) {
    static char *map = NULL;
    if (!map) {
        map = malloc(128);
        for (int i=0; i<128; i++) map[f(i)] = i;
    }
    return map[c];
}

然后就写一个函数依次读取字符串的每个字符,应用这个反向函数,然后将结果与前一个值异或:

char *decode(char *s, int len) {
    char *start = s, last = 0;
    while (len--) {
        char tmp = *s;
        *s = last ^ g(*s);
        last = tmp;
        s++;
    }
    return start;
}

considering that the shift would in some cases eliminate bits on the right side

其实这是微不足道的,特别是因为比特被消除了!

让我们反转您示例中的最后一步,您应该能够从那里向后工作。让我们将 8 位数字的数字视为名为 a 到 h 的布尔变量以及生成的布尔变量 i 到 p:

abcdefgh ^
0abcdefg =
ijklmnop

我们只看到i到p,但我们想找到a到h。请注意 a^0=i,但是任何与 0 异或的东西都只是它本身,所以我们知道 a。我们还知道:b^a=j,如果我们用“a”对两边进行异或,我们得到 b^a^a=j^a。 Xor 的特殊之处在于对具有相同值的东西进行两次异或将其抵消,因此等式简化为 b=j^a。现在我们有 b。我相信你知道如何继续这个并获得其余的数字并最终获得种子。

查找 table 在这种情况下仅使用 8 位值就可以工作,但也可以在没有 8 位值的情况下进行这种反转。

要反转 x ^= x >> a 做:

x ^= x >> a;
x ^= x >> (2*a);
x ^= x >> (4*a);
x ^= x >> (8*a);
// ...

直到移位量a超过类型的位宽。考虑到这一点,您可以拥有像这样的编码和解码功能:

void encode(unsigned char* str, size_t len) {
    for (int c = 0; c < len; c++) {
        if (c > 0) str[c] ^= str[c-1];
        str[c] ^= str[c] >> 3;
        str[c] ^= str[c] >> 2;
        str[c] ^= str[c] >> 1;
    }
}

void decode(unsigned char* str, size_t len) {
    for (int c = len - 1; c >= 0; c--) {
        str[c] ^= str[c] >> 1;
        str[c] ^= str[c] >> 2;
        str[c] ^= str[c] >> 4;
        
        str[c] ^= str[c] >> 2;
        str[c] ^= str[c] >> 4;
        
        str[c] ^= str[c] >> 3;
        str[c] ^= str[c] >> 6;
        if (c > 0) str[c] ^= str[c-1];
    }
}