Windows逆向工程入门之整数类型
文章目录
- C++ 数据类型与内存管理:汇编视角深度解析
- 1. 基本数据类型与内存布局
- 数据类型的内存占用
- 2. 数据赋值的汇编实现
- 寄存器与内存操作
- 3. 无符号整数的存储与运算
- 无符号数的二进制表示
- 4. 有符号整数的补码表示
- 补码运算与溢出检测
- 5. 整数溢出的汇编检测
- 溢出标志位分析
- 6. 类型转换的汇编实现
- 符号扩展与零扩展
- 7. 全局变量的内存管理
- 数据段与访问方式
- 8. 局部变量的栈管理
- 栈帧结构与变量寻址
- 9. 综合逆向分析示例
- 完整的程序分析
- 关键逆向工程技术总结
- 1. 数据类型识别
- 2. 符号性判断
- 3. 变量类型识别
- 4. 内存访问模式
C++ 数据类型与内存管理:汇编视角深度解析
1. 基本数据类型与内存布局
数据类型的内存占用
#include <iostream>
#include <Windows.h>void demonstrateDataTypes() {char a = 'A';short b = 0x1234;int c = 0x12345678;long long d = 0x123456789ABCDEF0;std::cout << "Sizeof char: " << sizeof(a) << " bytes" << std::endl;std::cout << "Sizeof short: " << sizeof(b) << " bytes" << std::endl;std::cout << "Sizeof int: " << sizeof(c) << " bytes" << std::endl;std::cout << "Sizeof long long: " << sizeof(d) << " bytes" << std::endl;
}
对应的汇编代码分析:
; char a = 'A';
mov byte ptr [ebp-1], 65 ; 41h = 'A', 1字节存储; short b = 0x1234;
mov eax, 1234h ; 立即数加载到寄存器
mov word ptr [ebp-6], ax ; 使用AX寄存器(16位)存储到内存; int c = 0x12345678;
mov dword ptr [ebp-0Ch], 12345678h ; 直接32位内存写入; long long d = 0x123456789ABCDEF0;
mov dword ptr [ebp-18h], 9ABCDEF0h ; 低32位
mov dword ptr [ebp-14h], 12345678h ; 高32位
内存布局可视化:
地址 值 说明
0x0019FF74 41 char a = 'A' (1字节)
0x0019FF70 34 12 short b = 0x1234 (小端序)
0x0019FF6C 78 56 34 12 int c = 0x12345678 (小端序)
0x0019FF64 F0 DE BC 9A long long d 低32位
0x0019FF68 78 56 34 12 long long d 高32位
2. 数据赋值的汇编实现
寄存器与内存操作
void demonstrateAssignment() {// 立即数赋值int immediate = 0xDEADBEEF;// 寄存器间赋值int reg_to_reg = immediate;// 内存到内存赋值(实际通过寄存器)int memory_copy = reg_to_reg;// 表达式赋值int expression = immediate + reg_to_reg;
}
汇编代码深度分析:
; int immediate = 0xDEADBEEF;
mov dword ptr [ebp-4], 0DEADBEEFh ; 直接内存写入; int reg_to_reg = immediate;
mov eax, dword ptr [ebp-4] ; 1. 内存加载到寄存器
mov dword ptr [ebp-8], eax ; 2. 寄存器存储到内存; int memory_copy = reg_to_reg;
mov ecx, dword ptr [ebp-8] ; 再次通过寄存器中转
mov dword ptr [ebp-0Ch], ecx; int expression = immediate + reg_to_reg;
mov edx, dword ptr [ebp-4] ; 加载immediate
add edx, dword ptr [ebp-8] ; 与reg_to_reg相加
mov dword ptr [ebp-10h], edx ; 结果存储
关键观察:
- x86架构不支持内存到内存的直接移动
- 所有数据移动都通过寄存器中转
mov
指令根据操作数大小决定数据传输量
3. 无符号整数的存储与运算
无符号数的二进制表示
void unsignedArithmetic() {unsigned char uc = 255;unsigned short us = 65535;unsigned int ui = 4294967295U;// 无符号运算unsigned int result1 = ui + 1; // 溢出到0unsigned int result2 = ui - 1; // 正常递减std::cout << "uc = " << (int)uc << std::endl;std::cout << "us = " << us << std::endl; std::cout << "ui = " << ui << std::endl;std::cout << "ui + 1 = " << result1 << std::endl;std::cout << "ui - 1 = " << result2 << std::endl;
}
汇编实现分析:
; unsigned char uc = 255;
mov byte ptr [ebp-1], 0FFh ; 0xFF = 255; unsigned int result1 = ui + 1;
mov eax, dword ptr [ebp-0Ch] ; 加载ui
add eax, 1 ; 无符号加法
mov dword ptr [ebp-10h], eax ; 存储结果; 无符号比较指令
cmp eax, 100
ja above_label ; 无符号大于跳转
jb below_label ; 无符号小于跳转
无符号数的关键特征:
- 所有位都表示数值大小
- 使用
add
、sub
等无符号运算指令 - 比较使用
ja
(above)、jb
(below)等无符号跳转
4. 有符号整数的补码表示
补码运算与溢出检测
void signedArithmetic() {int positive = 42;int negative = -42;int max_int = 2147483647;int min_int = -2147483648;// 补码验证std::cout << "42的补码: " << std::hex << positive << std::endl;std::cout << "-42的补码: " << std::hex << negative << std::endl;// 有符号溢出int overflow = max_int + 1;std::cout << "MAX_INT + 1 = " << overflow << std::endl;
}
汇编层面分析:
; int negative = -42;
mov dword ptr [ebp-8], 0FFFFFFD6h ; -42的补码表示; 有符号加法
mov eax, dword ptr [ebp-0Ch] ; 加载max_int
add eax, 1 ; 加法运算
mov dword ptr [ebp-14h], eax ; 结果可能溢出; 有符号比较
cmp eax, 100
jg greater_label ; 有符号大于跳转
jl less_label ; 有符号小于跳转; 符号扩展指令
movsx eax, byte ptr [ebp-1] ; 字节符号扩展到双字
movsx ebx, word ptr [ebp-6] ; 字符号扩展到双字
补码运算特点:
- 最高位为符号位(0正1负)
- 使用
movsx
进行符号扩展 - 比较使用
jg
(greater)、jl
(less)等有符号跳转
5. 整数溢出的汇编检测
溢出标志位分析
void demonstrateOverflow() {unsigned int u_max = 0xFFFFFFFF;int s_max = 0x7FFFFFFF;int s_min = 0x80000000;unsigned int u_overflow = u_max + 1;int s_overflow = s_max + 1;std::cout << "无符号溢出: " << u_overflow << std::endl;std::cout << "有符号溢出: " << s_overflow << std::endl;
}
汇编标志位分析:
; 无符号加法及溢出检测
mov eax, 0FFFFFFFFh
add eax, 1 ; 结果=0, CF=1(进位标志)
jc carry_occurred ; 如果CF=1跳转; 有符号加法及溢出检测
mov ebx, 7FFFFFFFh
add ebx, 1 ; 结果=0x80000000, OF=1(溢出标志)
jo overflow_occurred ; 如果OF=1跳转; 带进位加法
mov ecx, 0FFFFFFFFh
adc ecx, 0 ; ecx = ecx + 0 + CF
CPU标志位说明:
- CF(进位标志):无符号运算溢出时设置
- OF(溢出标志):有符号运算溢出时设置
- ZF(零标志):结果为0时设置
- SF(符号标志):结果为负时设置
6. 类型转换的汇编实现
符号扩展与零扩展
void typeConversionExamples() {// 符号扩展char negative_char = -100;short sign_extended = negative_char; // 应该保持-100int sign_extended_int = negative_char;// 零扩展unsigned char positive_char = 200;unsigned short zero_extended = positive_char; // 应该保持200unsigned int zero_extended_int = positive_char;// 截断unsigned int big_value = 0x12345678;unsigned short truncated_short = big_value; // 0x5678unsigned char truncated_char = big_value; // 0x78std::cout << "符号扩展: " << sign_extended << std::endl;std::cout << "零扩展: " << zero_extended << std::endl;std::cout << "截断: " << std::hex << truncated_short << std::endl;
}
汇编转换指令:
; 符号扩展 (MOVSX)
movsx eax, byte ptr [ebp-1] ; 字节→双字,带符号扩展
movsx ebx, word ptr [ebp-6] ; 字→双字,带符号扩展; 零扩展 (MOVZX)
movzx ecx, byte ptr [ebp-7] ; 字节→双字,零扩展
movzx edx, word ptr [ebp-0Ah] ; 字→双字,零扩展; 截断操作
mov eax, dword ptr [ebp-10h] ; 加载32位值
mov word ptr [ebp-14h], ax ; 存储低16位(截断)
mov byte ptr [ebp-15h], al ; 存储低8位(截断)
7. 全局变量的内存管理
数据段与访问方式
// 全局变量定义
int initialized_global = 0x12345678; // .data段
int uninitialized_global; // .bss段
const int read_only_global = 0xABCD; // .rdata段void accessGlobalVariables() {// 读取全局变量int local_copy = initialized_global;// 修改全局变量uninitialized_global = 0x9999;// 全局变量地址访问int* global_ptr = &initialized_global;*global_ptr = 0x5555;std::cout << "全局变量值: " << std::hex << initialized_global << std::endl;std::cout << "全局变量地址: " << global_ptr << std::endl;
}
汇编层面的全局变量访问:
; int local_copy = initialized_global;
mov eax, dword ptr [initialized_global] ; 直接内存访问
mov dword ptr [ebp-4], eax; uninitialized_global = 0x9999;
mov dword ptr [uninitialized_global], 9999h; int* global_ptr = &initialized_global;
mov dword ptr [ebp-8], offset initialized_global; *global_ptr = 0x5555;
mov eax, dword ptr [ebp-8] ; 加载指针值
mov dword ptr [eax], 5555h ; 通过指针写入
内存段说明:
- .data段:已初始化的全局/静态变量
- .bss段:未初始化的全局/静态变量(程序启动时清零)
- .rdata段:只读数据(常量)
8. 局部变量的栈管理
栈帧结构与变量寻址
void stackFrameDemo(int param1, int param2) {// 局部变量声明int local1 = 0x1111;int local2 = 0x2222;int local3 = 0x3333;// 数组局部变量int array[4] = {0x4444, 0x5555, 0x6666, 0x7777};// 复杂表达式int result = param1 + param2 + local1 + array[2];std::cout << "结果: " << std::hex << result << std::endl;// 查看栈地址std::cout << "参数1地址: " << ¶m1 << std::endl;std::cout << "局部变量地址: " << &local1 << std::endl;std::cout << "数组地址: " << array << std::endl;
}
栈帧的汇编布局:
; 函数序言
push ebp
mov ebp, esp
sub esp, 30h ; 为局部变量分配栈空间; 参数访问 (ebp+偏移)
mov eax, dword ptr [ebp+8] ; 访问param1
mov ebx, dword ptr [ebp+0Ch] ; 访问param2; 局部变量初始化 (ebp-偏移)
mov dword ptr [ebp-4], 1111h ; local1
mov dword ptr [ebp-8], 2222h ; local2
mov dword ptr [ebp-0Ch], 3333h ; local3; 数组初始化
mov dword ptr [ebp-1Ch], 4444h ; array[0]
mov dword ptr [ebp-18h], 5555h ; array[1]
mov dword ptr [ebp-14h], 6666h ; array[2]
mov dword ptr [ebp-10h], 7777h ; array[3]; 数组元素访问
mov ecx, dword ptr [ebp-14h] ; 访问array[2]; 函数尾声
mov esp, ebp ; 恢复栈指针
pop ebp ; 恢复基址指针
ret
典型栈帧布局:
高地址
[ebp+0Ch] param2
[ebp+8] param1
[ebp+4] 返回地址
[ebp] 保存的ebp ← ebp指向这里
[ebp-4] local1
[ebp-8] local2
[ebp-0Ch] local3
[ebp-10h] array[3]
[ebp-14h] array[2]
[ebp-18h] array[1]
[ebp-1Ch] array[0] ← esp指向这里(分配后)
低地址
9. 综合逆向分析示例
完整的程序分析
#include <iostream>
#include <Windows.h>// 全局变量
int global_counter = 0;int calculateSum(int a, int b) {int local_sum = a + b;global_counter++;return local_sum;
}void memoryPatternDemo() {// 小端序验证int value = 0x12345678;unsigned char* byte_ptr = (unsigned char*)&value;std::cout << "小端序内存布局:" << std::endl;for(int i = 0; i < 4; i++) {std::cout << "地址 " << (void*)(byte_ptr + i) << ": 0x" << std::hex << (int)byte_ptr[i] << std::endl;}// 函数调用分析int result = calculateSum(0x100, 0x200);std::cout << "计算结果: 0x" << std::hex << result << std::endl;std::cout << "全局计数器: " << global_counter << std::endl;
}int main() {std::cout << "模块基址: 0x" << GetModuleHandle(NULL) << std::endl;std::cout << "全局变量地址: 0x" << &global_counter << std::endl;memoryPatternDemo();return 0;
}
对应的关键汇编模式:
; 函数调用序列
push 200h ; 参数2入栈
push 100h ; 参数1入栈
call calculateSum ; 函数调用
add esp, 8 ; 清理栈参数; calculateSum 函数内部
calculateSum proc
push ebp
mov ebp, esp
sub esp, 4 ; 局部变量空间; local_sum = a + b
mov eax, [ebp+8] ; 参数a
add eax, [ebp+0Ch] ; 参数b
mov [ebp-4], eax ; 存储到local_sum; global_counter++
mov ecx, [global_counter]
add ecx, 1
mov [global_counter], ecx; return local_sum
mov eax, [ebp-4] ; 返回值通过EAX传递mov esp, ebp
pop ebp
ret
calculateSum endp
关键逆向工程技术总结
1. 数据类型识别
- 字符/字节:
byte ptr
操作,1字节传输 - 短整型:
word ptr
操作,2字节传输 - 整型:
dword ptr
操作,4字节传输 - 长整型:可能需要多个
dword
操作
2. 符号性判断
- 无符号:使用
ja
、jb
、movzx
指令 - 有符号:使用
jg
、jl
、movsx
指令
3. 变量类型识别
- 全局变量:直接内存访问,固定地址
- 局部变量:
[ebp-偏移]
方式访问 - 参数:
[ebp+偏移]
方式访问
4. 内存访问模式
- 小端序:低字节在低地址
- 栈增长:向低地址方向增长
- 对齐:变量通常按自然边界对齐
通过这种深度的汇编分析,可以准确理解C++代码的内存行为,为逆向工程和性能优化提供坚实基础。