此程序的令牌长 57 个字符,并使用 200KB 查找表进行加密。密文作为 57 个双字索引嵌入应用程序中。反调试是作为对 IsDebuggerAttached 的简单检查实现的,它在 57 个好的或 57 个坏的索引之间进行选择。
当应用程序启动时,令牌被解密,然后一次散列一个字符。我对查找表的操作感兴趣,而不是反转或检查散列。
索引表的代码由应用程序在单独的线程中运行。线程过程从 0x4037A0 开始,但是它包含一些无效的操作码来混淆反汇编程序。通过在执行之前填充 NOP 来修补无效代码(参见:0x403998)
查表功能需要 3 次索引操作来解析编码的关键字符。每个双字索引都以 0xc800 为模,以确保它位于 200KB 表内。最终的索引操作包括基于索引奇偶校验的偏移量。
对应查表的C程序是:
unsigned int lookup(unsigned char *cipher_table, unsigned int cipher_index)
{
unsigned int parity_check = 0, offset = 0;
cipher_index = *((unsigned int *)(cipher_table + (4 * (cipher_index % 0xc800))));
cipher_index = *((unsigned int *)(cipher_table + (4 * (cipher_index % 0xc800))));
if (cipher_index != 0) {
parity_check = cipher_index;
do {
if (!parity(parity_check & 0xFF)) offset = ~offset;
parity_check = parity_check >> 8;
} while (parity_check > 0);
}
cipher_index = *((unsigned int *)(cipher_table + (4 * ((cipher_index % 0xc800) + (offset & 1)))));
return ((~cipher_index & 0xFE000000) | cipher_index & 0x01FFFFFF) ^ 0xFE000000;
}
下面是一个完整的 C 密钥解密例程。它需要“1.exe”二进制文件,因为它从中读取了 200KB 的查找表。
#include <stdio.h>
#include <stdlib.h>
const unsigned char xor_data[57] = {
0xD1, 0x18, 0x52, 0xF3, 0x4B, 0x29, 0xB5, 0xE6,
0x2E, 0x60, 0x10, 0x1B, 0x59, 0x3E, 0x4D, 0xC1,
0x27, 0xAB, 0xA4, 0x82, 0x7F, 0x5B, 0x07, 0xA4,
0xE3, 0x1C, 0xE3, 0x93, 0x74, 0xBB, 0x3A, 0x62,
0x39, 0xE0, 0xDE, 0xE3, 0x85, 0x7A, 0x05, 0x29,
0x4F, 0xF1, 0x70, 0x1C, 0x70, 0x5E, 0xB3, 0xBF,
0x0A, 0x74, 0x97, 0x49, 0xFB, 0xE4, 0xC8, 0xF6,
0x5F
};
const unsigned int cipher_index[57] = {
0xd12479b8, 0x02802dd4, 0x9390b528, 0x6d13be10,
0x38637098, 0xadb0df76, 0x492afd8d, 0x6cda8589,
0x3f327fb3, 0xb559ed2c, 0x0d379569, 0xee50590a,
0xeffc3faf, 0xb183ff7c, 0x4942fe7a, 0x3f9f0383,
0xe5e8796e, 0x4acdb7e3, 0x6f7778ac, 0x9cfbd58d,
0x5d58a9d4, 0xb53ee0e8, 0x4f0bc8f7, 0x6ddcf35a,
0x0b32d6f3, 0xec181a05, 0xebf6aab4, 0x727beb1b,
0xd19b1f42, 0x0cd03ae5, 0xc901c555, 0x9e6123ed,
0x4805bbd3, 0x0b4e3b5f, 0xe74991a5, 0x16e56a67,
0xcdbf9035, 0x0c3fb795, 0x4e46ed21, 0x3098a99c,
0x0be2219a, 0x89008aba, 0xd1e8cb77, 0x8d0a0f39,
0xbf93eaae, 0xdd1f5179, 0x27aa29da, 0x0687579f,
0xe4961b86, 0x00047b96, 0xdf66fc9d, 0xe4ff21b6,
0x056c231b, 0xb94a2217, 0x2e385b22, 0xd9315588,
0x975aec60
};
unsigned int lookup(unsigned char *cipher_table, unsigned int cipher_index);
int main()
{
// read lookup table from 1.exe
unsigned char *cipher_table = (unsigned char*)malloc(0x32000);
if (cipher_table == NULL) {
printf("Failed to allocate 200K lookup table\r\n");
return -1;
}
FILE *file = NULL;
if (fopen_s(&file, "1.exe", "r") != 0) {
printf("Failed to open 1.exe\r\n");
return -1;
}
fseek(file, 0x6430, 0);
fread(cipher_table, 4, 0xc800, file);
fclose(file);
// Look up 57 key characters via indexes
for (int i = 0; i < 57; i++) {
char key = (char)((lookup(cipher_table, cipher_index[i]) >> ((i & 3) * 8)) ^ (xor_data[i]));
printf("%c", key);
}
printf("\r\n");
free(cipher_table);
return 0;
}
// true = even; false=odd
bool parity(unsigned char check)
{
check = (check & 0x55) + ((check >> 1) & 0x55);
check = (check & 0x33) + ((check >> 2) & 0x33);
return (((check & 0x0F) + ((check >> 4) & 0x0F)) % 2);
}
unsigned int lookup(unsigned char *cipher_table, unsigned int cipher_index)
{
unsigned int parity_check = 0, offset = 0;
cipher_index = *((unsigned int *)(cipher_table + (4 * (cipher_index % 0xc800))));
cipher_index = *((unsigned int *)(cipher_table + (4 * (cipher_index % 0xc800))));
if (cipher_index != 0) {
parity_check = cipher_index;
do {
if (!parity(parity_check & 0xFF)) offset = ~offset;
parity_check = parity_check >> 8;
} while (parity_check > 0);
}
cipher_index = *((unsigned int *)(cipher_table + (4 * ((cipher_index % 0xc800) + (offset & 1)))));
return ((~cipher_index & 0xFE000000) | cipher_index & 0x01FFFFFF) ^ 0xFE000000;
}
令牌是(剧透):
<****TKN!34C38983E67F17EE146C25B8838E428D8B0AE58!TKN****>