CTF-RE 从0到N:c语言是如何利用逻辑运算符拆分变量和合并的
我们经常能看到这种代码
for ( i = 0; i <= 9; ++i )
{
dest += v9;
v17 = (dest << 16) ^ v17 | HIWORD(dest) ^ v17;
v13 += v17;
v9 = (v13 << 12) ^ v9 | (v13 >> 20) ^ v9;
dest += v9;
v17 = (dest << 8) ^ v17 | HIBYTE(dest) ^ v17;
v13 += v17;
v9 = (v13 << 7) ^ v9 | (v13 >> 25) ^ v9;
v6 += v10;
v18 = (v6 << 16) ^ v18 | HIWORD(v6) ^ v18;
v14 += v18;
v10 = (v14 << 12) ^ v10 | (v14 >> 20) ^ v10;
v6 += v10;
v18 = (v6 << 8) ^ v18 | HIBYTE(v6) ^ v18;
v14 += v18;
v10 = (v14 << 7) ^ v10 | (v14 >> 25) ^ v10;
v7 += v11;
v19 = (v7 << 16) ^ v19 | HIWORD(v7) ^ v19;
v15 += v19;
v11 = (v15 << 12) ^ v11 | (v15 >> 20) ^ v11;
v7 += v11;
v19 = (v7 << 8) ^ v19 | HIBYTE(v7) ^ v19;
v15 += v19;
v11 = (v15 << 7) ^ v11 | (v15 >> 25) ^ v11;
v8 += v12;
v20 = (v8 << 16) ^ v20 | HIWORD(v8) ^ v20;
v16 += v20;
v12 = (v16 << 12) ^ v12 | (v16 >> 20) ^ v12;
v8 += v12;
v20 = (v8 << 8) ^ v20 | HIBYTE(v8) ^ v20;
v16 += v20;
v12 = (v16 << 7) ^ v12 | (v16 >> 25) ^ v12;
dest += v10;
v20 = (dest << 16) ^ v20 | HIWORD(dest) ^ v20;
v15 += v20;
v10 = (v15 << 12) ^ v10 | (v15 >> 20) ^ v10;
dest += v10;
v20 = (dest << 8) ^ v20 | HIBYTE(dest) ^ v20;
v15 += v20;
v10 = (v15 << 7) ^ v10 | (v15 >> 25) ^ v10;
v6 += v11;
v17 = (v6 << 16) ^ v17 | HIWORD(v6) ^ v17;
v16 += v17;
v11 = (v16 << 12) ^ v11 | (v16 >> 20) ^ v11;
v6 += v11;
v17 = (v6 << 8) ^ v17 | HIBYTE(v6) ^ v17;
v16 += v17;
v11 = (v16 << 7) ^ v11 | (v16 >> 25) ^ v11;
v7 += v12;
v18 = (v7 << 16) ^ v18 | HIWORD(v7) ^ v18;
v13 += v18;
v12 = (v13 << 12) ^ v12 | (v13 >> 20) ^ v12;
v7 += v12;
v18 = (v7 << 8) ^ v18 | HIBYTE(v7) ^ v18;
v13 += v18;
v12 = (v13 << 7) ^ v12 | (v13 >> 25) ^ v12;
v8 += v9;
v19 = (v8 << 16) ^ v19 | HIWORD(v8) ^ v19;
v14 += v19;
v9 = (v14 << 12) ^ v9 | (v14 >> 20) ^ v9;
v8 += v9;
v19 = (v8 << 8) ^ v19 | HIBYTE(v8) ^ v19;
v14 += v19;
v9 = (v14 << 7) ^ v9 | (v14 >> 25) ^ v9;
}
HIBYTE/WODRD是什么?
前置
在C语言中,HIWORD
和 HIBYTE
是用于提取变量中特定部分(如高16位或高8位)的宏或函数,它们通常用于对整型值的高字节(或高位)进行操作。这些宏常用于处理多字节数据(如32位整数)时,从中提取出特定的部分,ida预定义了这些函数,在检测到特定特征后会将表达式化简为这些函数.
1. HIWORD
HIWORD
是一个宏,用于提取一个32位整数的 高16位(高字)。在一个32位整数中,前16位被称为高16位,后16位称为低16位。
假设你有一个32位整数 x
,其二进制形式为:
x = 0x12345678 // 十六进制表示
HIWORD(x)
会提取出高16位,结果为 0x1234
。
在C语言中,HIWORD
宏通常可以定义为:
#define HIWORD(x) (((x) >> 16) & 0xFFFF)
这个宏的工作原理是:
- 通过
x >> 16
将x
右移16位,使得高16位移动到低16位的位置。 - 然后用
& 0xFFFF
掩码保留低16位,即得到原来高16位的值。
示例代码:
#include <stdio.h>
#define HIWORD(x) (((x) >> 16) & 0xFFFF)
int main() {
unsigned int x = 0x12345678; // 32位整数
unsigned short high = HIWORD(x);
printf("HIWORD(x) = 0x%X\n", high); // 输出高16位
return 0;
}
输出:
HIWORD(x) = 0x1234
2. HIBYTE
HIBYTE
是一个宏,用于提取一个16位整数的 高8位(高字节)。它主要用于处理16位整数的高8位。
假设你有一个16位整数 x
,其二进制形式为:
x = 0x1234 // 十六进制表示
HIBYTE(x)
会提取出高8位,结果为 0x12
。
在C语言中,HIBYTE
宏通常可以定义为:
#define HIBYTE(x) (((x) >> 8) & 0xFF)
这个宏的工作原理是:
- 通过
x >> 8
将x
右移8位,使得高8位移动到低8位的位置。 - 然后用
& 0xFF
掩码保留低8位,即得到原来高8位的值。
示例代码:
#include <stdio.h>
#define HIBYTE(x) (((x) >> 8) & 0xFF)
int main() {
unsigned short x = 0x1234; // 16位整数
unsigned char high = HIBYTE(x);
printf("HIBYTE(x) = 0x%X\n", high); // 输出高8位
return 0;
}
输出:
HIBYTE(x) = 0x12
总结
HIWORD(x)
用于提取32位整数的 高16位。HIBYTE(x)
用于提取16位整数的 高8位。- 其他的HIxxx以此类推
>>
,<<
,|
,&
是怎么拆分合并变量的?
在C语言中,位运算符 &
(按位与)、>>
(右移)和 |
(按位或)通常用于拆分和合并变量。这些运算符在处理位级数据时非常有用,可以用来提取数据的某些部分或组合多个值。
示例
假设我们有两个16位的整数 high
和 low
,并且想要将它们合并为一个32位的整数。
#include <stdio.h>
int main() {
unsigned short high = 0x1234; // 高16位
unsigned short low = 0x5678; // 低16位
unsigned int combined;
// 合并高16位和低16位
combined = ((unsigned int)high << 16) | low;
printf("High = 0x%X\n", high);
printf("Low = 0x%X\n", low);
printf("Combined = 0x%X\n", combined);
return 0;
}
让我们来看一些更复杂的例子
在 Base64 编码过程中,我们会遇到大量的位操作,例如将3个字节的数据转换为4个字符,并且在编码和解码过程中,通常会使用 按位与(&) 和 按位或(|) 等位操作来处理数据。
1. Base64 编码
Base64 编码的过程涉及到以下步骤:
- 将输入的每3个字节(24位)分成4组6位数据。
- 每6位映射到一个Base64字符。
例如,假设我们有一段数据 "Man"
,它的 ASCII 码值为:
'M'
-> 0x4D -> 01001101'a'
-> 0x61 -> 01100001'n'
-> 0x6E -> 01101110
这3个字符的二进制数据合并成一个24位的长整数(0x4D616E
)。接下来我们将这个24位数据分成4个6位数据:
0x4D616E (二进制表示为 010011010110000101101110)
- 第1组:010011 -> 19
- 第2组:010110 -> 22
- 第3组:000101 -> 5
- 第4组:101110 -> 46
然后这些值将映射到 Base64 字符集中的对应字符。
Base64 编码示例:
#include <stdio.h>
#include <string.h>
#define BASE64_CHARSET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
// Base64 编码函数
void base64_encode(const unsigned char *input, char *output) {
int i = 0, j = 0;
unsigned char a3[3], a4[4];
int input_len = strlen((char *)input);
while (input_len--) {
a3[i++] = *(input++);
if (i == 3) {
a4[0] = (a3[0] >> 2) & 0x3F; // 获取前6位
a4[1] = ((a3[0] & 0x03) << 4) | (a3[1] >> 4) & 0x3F; // 获取中间6位
a4[2] = ((a3[1] & 0x0F) << 2) | (a3[2] >> 6) & 0x3F; // 获取后6位
a4[3] = a3[2] & 0x3F; // 获取最后6位
for (i = 0; i < 4; i++)
output[j++] = BASE64_CHARSET[a4[i]];
i = 0;
}
}
if (i) {
for (int k = i; k < 3; k++)
a3[k] = '\0'; // 填充空字节
a4[0] = (a3[0] >> 2) & 0x3F;
a4[1] = ((a3[0] & 0x03) << 4) | (a3[1] >> 4) & 0x3F;
a4[2] = ((a3[1] & 0x0F) << 2) | (a3[2] >> 6) & 0x3F;
for (int k = 0; k < (i + 1); k++) {
output[j++] = BASE64_CHARSET[a4[k]];
}
while ((i++ < 3))
output[j++] = '='; // 填充等号
}
output[j] = '\0';
}
int main() {
unsigned char input[] = "Man"; // 要编码的字符串
char output[50];
base64_encode(input, output);
printf("Base64 Encoded: %s\n", output);
return 0;
}
解析:
- 输入数据:我们输入
"Man"
,其 ASCII 值分别是'M' = 0x4D
,'a' = 0x61
,'n' = 0x6E
。 - 合并成24位:三个字节合并成
0x4D616E
,对应的二进制是010011010110000101101110
。 - 分为4组6位:
- 第一组:
010011
->19
- 第二组:
010110
->22
- 第三组:
000101
->5
- 第四组:
101110
->46
- 第一组:
- 映射到Base64字符集:通过
BASE64_CHARSET
将这4个6位的值映射到对应的字符,得到 Base64 编码结果为"TWFu"
。
2. Base64 解码
解码过程与编码过程相反,主要步骤包括:
- 读取4个 Base64 字符,并将它们转换为4个6位的值。
- 将这4个6位的值合并为3个字节。
#include <stdio.h>
#include <string.h>
#define BASE64_CHARSET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
// 查找字符在Base64字符集中的位置
int base64_decode_char(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
return -1; // 错误字符
}
// Base64 解码函数
void base64_decode(const char *input, unsigned char *output) {
int i = 0, j = 0;
unsigned char a3[3], a4[4];
while (input[i]) {
for (int k = 0; k < 4; k++) {
if (input[i] == '=')
a4[k] = 0;
else
a4[k] = base64_decode_char(input[i]);
i++;
}
a3[0] = (a4[0] << 2) | (a4[1] >> 4);
a3[1] = (a4[1] << 4) | (a4[2] >> 2);
a3[2] = (a4[2] << 6) | a4[3];
for (int k = 0; k < 3; k++) {
if (a3[k] != 0)
output[j++] = a3[k];
}
}
output[j] = '\0';
}
int main() {
char input[] = "TWFu"; // Base64 编码的字符串
unsigned char output[50];
base64_decode(input, output);
printf("Decoded: %s\n", output);
return 0;
}
解析:
- 输入数据:我们输入
"TWFu"
,这是"Man"
的 Base64 编码。 - 转换为6位组:每个字符对应一个6位的值。
- 还原为原始数据:将4个6位的值合并为3个字节,得到解码后的数据
"Man"
。
附全1掩码表
位数 | 掩码(十六进制) | 掩码(二进制) | 解释 |
---|---|---|---|
1位 | 0x1 | 0000000000000000000000000000000000000000000000000000000000000001 | 提取最低 1 位 |
2位 | 0x3 | 0000000000000000000000000000000000000000000000000000000000000011 | 提取最低 2 位 |
3位 | 0x7 | 0000000000000000000000000000000000000000000000000000000000000111 | 提取最低 3 位 |
4位 | 0xf | 0000000000000000000000000000000000000000000000000000000000001111 | 提取最低 4 位 |
5位 | 0x1f | 0000000000000000000000000000000000000000000000000000000000011111 | 提取最低 5 位 |
6位 | 0x3f | 0000000000000000000000000000000000000000000000000000000000111111 | 提取最低 6 位 |
7位 | 0x7f | 0000000000000000000000000000000000000000000000000000000001111111 | 提取最低 7 位 |
8位 | 0xff | 0000000000000000000000000000000000000000000000000000000011111111 | 提取最低 8 位 |
9位 | 0x1ff | 0000000000000000000000000000000000000000000000000000000111111111 | 提取最低 9 位 |
10位 | 0x3ff | 0000000000000000000000000000000000000000000000000000001111111111 | 提取最低 10 位 |
11位 | 0x7ff | 0000000000000000000000000000000000000000000000000000011111111111 | 提取最低 11 位 |
12位 | 0xfff | 0000000000000000000000000000000000000000000000000000111111111111 | 提取最低 12 位 |
13位 | 0x1fff | 0000000000000000000000000000000000000000000000000001111111111111 | 提取最低 13 位 |
14位 | 0x3fff | 0000000000000000000000000000000000000000000000000011111111111111 | 提取最低 14 位 |
15位 | 0x7fff | 0000000000000000000000000000000000000000000000000111111111111111 | 提取最低 15 位 |
16位 | 0xffff | 0000000000000000000000000000000000000000000000001111111111111111 | 提取最低 16 位 |
17位 | 0x1ffff | 0000000000000000000000000000000000000000000000011111111111111111 | 提取最低 17 位 |
18位 | 0x3ffff | 0000000000000000000000000000000000000000000000111111111111111111 | 提取最低 18 位 |
19位 | 0x7ffff | 0000000000000000000000000000000000000000000001111111111111111111 | 提取最低 19 位 |
20位 | 0xfffff | 0000000000000000000000000000000000000000000011111111111111111111 | 提取最低 20 位 |
21位 | 0x1fffff | 0000000000000000000000000000000000000000000111111111111111111111 | 提取最低 21 位 |
22位 | 0x3fffff | 0000000000000000000000000000000000000000001111111111111111111111 | 提取最低 22 位 |
23位 | 0x7fffff | 0000000000000000000000000000000000000000011111111111111111111111 | 提取最低 23 位 |
24位 | 0xffffff | 0000000000000000000000000000000000000000111111111111111111111111 | 提取最低 24 位 |
25位 | 0x1ffffff | 0000000000000000000000000000000000000001111111111111111111111111 | 提取最低 25 位 |
26位 | 0x3ffffff | 0000000000000000000000000000000000000011111111111111111111111111 | 提取最低 26 位 |
27位 | 0x7ffffff | 0000000000000000000000000000000000000111111111111111111111111111 | 提取最低 27 位 |
28位 | 0xfffffff | 0000000000000000000000000000000000001111111111111111111111111111 | 提取最低 28 位 |
29位 | 0x1fffffff | 0000000000000000000000000000000000011111111111111111111111111111 | 提取最低 29 位 |
30位 | 0x3fffffff | 0000000000000000000000000000000000111111111111111111111111111111 | 提取最低 30 位 |
31位 | 0x7fffffff | 0000000000000000000000000000000001111111111111111111111111111111 | 提取最低 31 位 |
32位 | 0xffffffff | 0000000000000000000000000000000011111111111111111111111111111111 | 提取最低 32 位 |
33位 | 0x1ffffffff | 0000000000000000000000000000000111111111111111111111111111111111 | 提取最低 33 位 |
34位 | 0x3ffffffff | 0000000000000000000000000000001111111111111111111111111111111111 | 提取最低 34 位 |
35位 | 0x7ffffffff | 0000000000000000000000000000011111111111111111111111111111111111 | 提取最低 35 位 |
36位 | 0xfffffffff | 0000000000000000000000000000111111111111111111111111111111111111 | 提取最低 36 位 |
37位 | 0x1fffffffff | 0000000000000000000000000001111111111111111111111111111111111111 | 提取最低 37 位 |
38位 | 0x3fffffffff | 0000000000000000000000000011111111111111111111111111111111111111 | 提取最低 38 位 |
39位 | 0x7fffffffff | 0000000000000000000000000111111111111111111111111111111111111111 | 提取最低 39 位 |
40位 | 0xffffffffff | 0000000000000000000000001111111111111111111111111111111111111111 | 提取最低 40 位 |
41位 | 0x1ffffffffff | 0000000000000000000000011111111111111111111111111111111111111111 | 提取最低 41 位 |
42位 | 0x3ffffffffff | 0000000000000000000000111111111111111111111111111111111111111111 | 提取最低 42 位 |
43位 | 0x7ffffffffff | 0000000000000000000001111111111111111111111111111111111111111111 | 提取最低 43 位 |
44位 | 0xfffffffffff | 0000000000000000000011111111111111111111111111111111111111111111 | 提取最低 44 位 |
45位 | 0x1fffffffffff | 0000000000000000000111111111111111111111111111111111111111111111 | 提取最低 45 位 |
46位 | 0x3fffffffffff | 0000000000000000001111111111111111111111111111111111111111111111 | 提取最低 46 位 |
47位 | 0x7fffffffffff | 0000000000000000011111111111111111111111111111111111111111111111 | 提取最低 47 位 |
48位 | 0xffffffffffff | 0000000000000000111111111111111111111111111111111111111111111111 | 提取最低 48 位 |
49位 | 0x1ffffffffffff | 0000000000000001111111111111111111111111111111111111111111111111 | 提取最低 49 位 |
50位 | 0x3ffffffffffff | 0000000000000011111111111111111111111111111111111111111111111111 | 提取最低 50 位 |
51位 | 0x7ffffffffffff | 0000000000000111111111111111111111111111111111111111111111111111 | 提取最低 51 位 |
52位 | 0xfffffffffffff | 0000000000001111111111111111111111111111111111111111111111111111 | 提取最低 52 位 |
53位 | 0x1fffffffffffff | 0000000000011111111111111111111111111111111111111111111111111111 | 提取最低 53 位 |
54位 | 0x3fffffffffffff | 0000000000111111111111111111111111111111111111111111111111111111 | 提取最低 54 位 |
55位 | 0x7fffffffffffff | 0000000001111111111111111111111111111111111111111111111111111111 | 提取最低 55 位 |
56位 | 0xffffffffffffff | 0000000011111111111111111111111111111111111111111111111111111111 | 提取最低 56 位 |
57位 | 0x1ffffffffffffff | 0000000111111111111111111111111111111111111111111111111111111111 | 提取最低 57 位 |
58位 | 0x3ffffffffffffff | 0000001111111111111111111111111111111111111111111111111111111111 | 提取最低 58 位 |
59位 | 0x7ffffffffffffff | 0000011111111111111111111111111111111111111111111111111111111111 | 提取最低 59 位 |
60位 | 0xfffffffffffffff | 0000111111111111111111111111111111111111111111111111111111111111 | 提取最低 60 位 |
61位 | 0x1fffffffffffffff | 0001111111111111111111111111111111111111111111111111111111111111 | 提取最低 61 位 |
62位 | 0x3fffffffffffffff | 0011111111111111111111111111111111111111111111111111111111111111 | 提取最低 62 位 |
63位 | 0x7fffffffffffffff | 0111111111111111111111111111111111111111111111111111111111111111 | 提取最低 63 位 |
64位 | 0xffffffffffffffff | 1111111111111111111111111111111111111111111111111111111111111111 | 提取最低 64 位 |
代码
def generate_full_mask_table():
# Markdown 表头
markdown = "| 位数 | 掩码(十六进制) | 掩码(二进制) | 解释 |\n"
markdown += "|---------|------------------|-------------------------------|---------------------------|\n"
# 生成掩码表
for i in range(1, 65):
mask = (1 << i) - 1 # 生成 i 位全1掩码
hex_mask = hex(mask) # 十六进制表示
bin_mask = bin(mask)[2:].zfill(64) # 二进制表示,填充到64位
markdown += f"| {i}位 | {hex_mask} | {bin_mask} | 提取最低 {i} 位 |\n"
return markdown
# 输出掩码表
markdown_table = generate_full_mask_table()
print(markdown_table)