😶‍🌫️

シングルバイトXORエンコーディングを簡易的に実装

に公開

「初めてのマルウェア解析」でXORエンコーディングについて学習したので、実装して色々実際に試してみました。

シングルバイトXORについて

シングルバイトXORは、平文の各バイトと暗号化キーでXORされます。

シンプルなXORエンコーディング

ファイルを読み込み、シンプルなシングルバイトXORを行うプログラムをC言語で実装しました。

実装

#include <stdio.h>
#include <stdlib.h>

void xor_encrypt_decrypt(char* data, size_t len, unsigned char key) {
    for (size_t i = 0; i < len; i++) {
        data[i] ^= key;
    }
}

int main(int argc, char* argv[]) {
    if (argc != 4) {
        printf("Usage: %s <input> <output> <key_byte>\n", argv[0]);
        return 1;
    }

    FILE* in = fopen(argv[1], "rb");
    FILE* out = fopen(argv[2], "wb");
    unsigned char key = (unsigned char)strtoul(argv[3], NULL, 0);

    fseek(in, 0, SEEK_END);
    size_t size = ftell(in);
    rewind(in);

    char* buffer = malloc(size);
    fread(buffer, 1, size, in);

    xor_encrypt_decrypt(buffer, size, key);
    fwrite(buffer, 1, size, out);

    fclose(in);
    fclose(out);
    free(buffer);

    return 0;
}

実行

$ gcc -g xor_crypt.c -o xor_crypt

$ cat ./input/input.txt
hello_xor_crypt

$ xxd ./input/input.txt
00000000: 6865 6c6c 6f5f 786f 725f 6372 7970 74    hello_xor_crypt

$ ./xor_crypt.exe ./input/input.txt ./output/encrypted.bin 0x7A

XORエンコーディングの確認

平文がわからなくなりました。

$ xxd ./output/encrypted.bin
00000000: 121f 1616 1525 0215 0825 1908 030a 0e    .....%...%.....

NULLバイトを無視するXORエンコーディング

XORでは、NULLバイト(0x00)が暗号化キーとXORされると、暗号化キーにエンコードされます。

また、暗号キーと同じ値とXORされた場合は、NULLバイトになります。

$ python

>>> c = 0x00
>>> key = 0x7a
>>> hex(c ^ key)
'0x7a'
>>> hex(key ^ key)
'0x0'

NULLバイトを含めたXORエンコーディングの確認

先ほどで作成した、xor_crypt.exeでNULLバイトを含めたファイルをXORエンコーディングしてみます。

$ printf 'hello_xor_crypt\00\00\00\00\00' >> ./input/input_null.txt

$ xxd ./input/input_null.txt
00000000: 6865 6c6c 6f5f 786f 725f 6372 7970 7400  hello_xor_crypt.
00000010: 0000 0000                                ....

$ ./xor_crypt.exe ./input/input_null.txt ./output/encrypted_null.bin 0x7A

下記のように、NULLバイトが暗号キーの値になっています。そのため、暗号キーの特定が容易になります。

$ xxd ./output/encrypted_null.bin
00000000: 121f 1616 1525 0215 0825 1908 030a 0e7a  .....%...%.....z
00000010: 7a7a 7a7a                                zzzz

実装

暗号キーの特定を回避するため、NULLバイトと暗号キーのXORを無視する実装をしてみます。

#include <stdio.h>
#include <stdlib.h>

void xor_encrypt_decrypt(char* data, size_t len, unsigned char key) {
    for (size_t i = 0; i < len; i++) {
        if (data[i] == '\00' || data[i] == key) {
            continue;
        }
        data[i] ^= key;
    }
}

int main(int argc, char* argv[]) {
    if (argc != 4) {
        printf("Usage: %s <input> <output> <key_byte>\n", argv[0]);
        return 1;
    }

    FILE* in = fopen(argv[1], "rb");
    FILE* out = fopen(argv[2], "wb");
    unsigned char key = (unsigned char)strtoul(argv[3], NULL, 0);

    fseek(in, 0, SEEK_END);
    size_t size = ftell(in);
    rewind(in);

    char* buffer = malloc(size);
    fread(buffer, 1, size, in);

    xor_encrypt_decrypt(buffer, size, key);
    fwrite(buffer, 1, size, out);

    fclose(in);
    fclose(out);
    free(buffer);

    return 0;
}

実行

$ gcc -g ignore_null_xor_crypt.c -o ignore_null_xor_crypt

$ xxd ./input/input_null.txt
00000000: 6865 6c6c 6f5f 786f 725f 6372 7970 7400  hello_xor_crypt.
00000010: 0000 0000                                ....

$ ./ignore_null_xor_crypt.exe ./input/input_null.txt ./output/encrypted_null.bin 0x7A

NULLバイト無視の確認

NULLバイトを無視するつくりのため、0x00のままになっています。

$ xxd ./output/encrypted_null.bin
00000000: 121f 1616 1525 0215 0825 1908 030a 0e00  .....%...%......
00000010: 0000 0000

終わりに

実際に試してみようとすると、C言語を書いたり、NULLバイトを含むファイルの作成方法を調べたり、参考書を読むだけでは得られない情報も得られるため、積極的に続けていきたいです。

Discussion