X86反汇编:内存矩阵与指针之剑(3-1)

引言

曾几何时,我们眼中的 C 语言数组 arr[i] 只是一个抽象的符号,指针 *p 更是如同迷雾般让人畏惧。我们只知道照着语法书写代码,却从未真正看见过数据在内存中流淌的样子。

我们试图用高级语言的思维去理解底层,却发现总是隔着一层纱。我们不知道为什么数组越界会崩溃,不知道循环是如何在芯片的每一次跳动中完成轮回。

直到我们推开了汇编的大门。

在这里,没有所谓的'数组',只有基址与偏移的舞蹈;在这里,没有理所当然的 for 循环,只有 CMPJMP 的博弈。

我们决定不再做岸上的观望者,而是成为那个摆渡人。我们脱去 C 语言的华丽外衣,选择用最原始的汇编指令,去亲自丈量内存的每一寸土地。我们深知,只有亲手构建过这座桥梁,未来在面对复杂的逆向深渊时,才能如履平地。


第一关:序列 (The Sequence)

  • 核心知识点: 数组的定义与随机访问 (Random Access)。

  • C 语言场景: 定义一个 int a[5],然后读取 a[0]a[3]

  • 汇编看点: 观察 [ebp - Offset]这种寻址方式。你会发现数组元素在栈上是连续排列的。

  • 目的: 理解 "基地址 + 偏移量" 的物理形态。

反汇编代码

c 复制代码
push ebp
mov ebp, esp
sub esp, 20             ; 【关键点 1】 开辟 20 字节 (5 * 4字节)

; --- a[0] = 10 ---
mov dword ptr [ebp-20], 10  ; 【关键点 2】 数组首地址 (Base)

; --- a[1] = 20 ---
mov dword ptr [ebp-16], 20  ; -20 + 4 = -16

; --- a[3] = 99 ---
mov dword ptr [ebp-8], 99   ; 【关键点 3】 -20 + (3 * 4) = -8

mov esp, ebp
pop ebp
ret

具体代码

c 复制代码
void level_1() {
    // 1. 定义一个包含 5 个整数的数组
    int a[5]; 

    // 2. 给特定位置赋值
    a[0] = 10;
    a[1] = 20;
    
    // 3. 跨越访问 (访问第 4 个元素)
    a[3] = 99;
}

第二关:步幅 (The Stride)

  • 核心知识点: 不同数据类型的宽度 (Data Width)。

  • C 语言场景: 对比 int a[5]char b[5] 的访问。

  • 汇编看点:

    • int 数组步长是 4 (eax*4)

    • char 数组步长是 1。

    • 观察 mov 指令变成了 movsx (带符号扩展)movzx (零扩展)

  • 目的: 理解为什么 C 语言需要定义类型------为了告诉 CPU 每次跳几步,以及读多少字节。

反汇编代码

c 复制代码
; void __cdecl level_2_stride()

push ebp
mov ebp, esp
sub esp, 12                 ; 开辟了 12 字节空间

mov dword ptr [ebp-4], 0    ; [变量 i] = 0

LABEL_LOOP:
    mov eax, dword ptr [ebp-4]  ; 取 i
    cmp eax, 5
    jge LABEL_END

    ; === 关键操作区 ===
    mov eax, dword ptr [ebp-4]  ; 取 i 到 eax
    add eax, 65                 ; eax = i + 65 ('A' 的 ASCII 码)
    
    mov ecx, eax                ; 把值暂存到 ecx (准备截断)
    mov eax, dword ptr [ebp-4]  ; 再取 i 做索引
    
    ; 【核心考点 1】 注意这里的地址计算,乘数是多少?(默认是 *1)
    ; 【核心考点 2】 注意源操作数是 cl (ecx 的低8位)
    ; 【核心考点 3】 目标地址是 byte ptr
    mov byte ptr [ebp + eax - 12], cl 

    ; === 步进 ===
    mov eax, dword ptr [ebp-4]
    add eax, 1
    mov dword ptr [ebp-4], eax
    jmp LABEL_LOOP

LABEL_END:
    mov esp, ebp
    pop ebp
    ret
c 复制代码
void level_2_stride() {
    int i = 0;       // [ebp-4]
    char a[8];       // [ebp-12] (开辟了12字节,减去i的4字节,剩下8字节给数组)

    while (i < 5) {
        // [ebp + i - 12] = (char)(i + 65)
        a[i] = 'A' + i; 
        // 也就是 a[0]='A', a[1]='B', a[2]='C'...
        i++;
    }
}

第三关:遍历者 (The Traveler)

  • 核心知识点: 循环 + 数组 (Looping Arrays)

  • C 语言场景:for 循环给数组赋值 a[i] = i

  • 汇编看点: 最经典的组合拳。寄存器充当索引i,你会看到 mov [ebp + eax*4 - Base], eax

  • 目的: 掌握编译器如何将 "循环变量" 转化为"内存偏移量"

具体代码

c 复制代码
void level_3() {
    int a[5];   // 数组,占 20 字节
    int i;      // 循环变量,占 4 字节

    // 循环遍历
    for (i = 0; i < 5; i++) {
        a[i] = i;  // 核心操作:把 i 的值,放进 a[i] 的位置
    }
}

反汇编代码

c 复制代码
__declspec(naked) void level3(int* pa, int len)
{
	_asm{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0x40
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0x40]
		mov ecx, 0x10
		mov eax, 0xcccccccc
		rep stosd
		
		//核心代码
		//变量i
		mov dword ptr ds:[ebp - 0x4], 0
//循环头
SHORT_FOR_START:
		//取出i
		mov eax, dword ptr ds:[ebp - 0x4]
		//比较len
		cmp eax, dword ptr ds:[ebp + 0xC]
		//判断大于等于,则跳转出去
		jge SHORT_FOR_END
		
		//循环体
		//重新取出i
		mov ecx, dword ptr ds:[ebp - 0x4]
		//数组首元素地址放入eax中
		mov eax, dword ptr ds:[ebp +0x8]
		//数组索引公式
		mov DWORD ptr ds:[eax + ecx * 4], ecx

		//计数区,i++
		add ecx, 0x1
		//放回内存
		mov dword ptr ds:[ebp - 0x4], ecx
		jmp SHORT_FOR_START

SHORT_FOR_END:


		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0x40
		mov esp, ebp
		pop ebp
		ret
	}
}
	

int main(int argc, char* argv[])
{
	int arr[5] = {0};
	_asm{
		lea eax, arr
		push 5
		push eax
		call level3
		add esp, 0x8
	}
	for(int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	getchar();
	return 0;
}

第四关:投影 (The Projection)

  • 核心知识点: 取地址符 &指针变量 (Address of)

  • C 语言场景: int a = 10; int *p = &a;。

  • 汇编看点: 第一次遇到 lea (Load Effective Address) 指令。

    • 区别 mov eax, [ebp-4] (取值) lea eax, [ebp-4] (取地址)
  • 目的: 明白 "指针" 在汇编里其实就是一个普通的整数,只是它存的是别人的门牌号。

具体代码

c 复制代码
void level4()
{
    int a = 10;
    int b = 20;
    
    // 1. 定义指针 p,指向 a
    int *p = &a;
    
    // 2. 通过指针修改 a 的值
    *p = 100;
    
    // 3. 修改指针的指向,让它指向 b
    p = &b;
    
    // 4. 通过指针修改 b 的值
    *p = 200;
}

反汇编代码

c 复制代码
__declspec(naked) void level4()
{
	_asm{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0x40
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0x40]
		mov ecx, 0x10
		mov eax, 0xcccccccc
		rep stosd
		
		//核心代码
		//int a = 10;
		//int b = 20;
		mov dword ptr ds:[ebp - 0x4], 0xA
		mov dword ptr ds:[ebp - 0x8], 0x16

		// 1. 定义指针 p,指向 a
		//int *p = &a;
		//变量p,且内存存储a的地址
		//a的地址
		lea eax, dword ptr ds:[ebp - 0x4]
		//放入p内存中
		mov DWORD ptr ds:[ebp - 0xC], eax
		

		// 2. 通过指针修改 a 的值
		//*p = 100;
		//将p内存中的值放入到寄存器eax中
		mov eax, dword ptr ds:[ebp - 0xC]
		//将寄存器的值当做地址,存放数据
		mov DWORD ptr ds:[eax], 0x64
		

		// 3. 修改指针的指向,让它指向 b
		//p = &b;
		//修改p的内存值
		//首先先获取b的地址
		lea ecx, dword ptr ds:[ebp - 0x8]
		//输入到p内存中
		mov DWORD ptr ds:[ebp - 0xC], ecx
		

		// 4. 通过指针修改 b 的值
		//*p = 200;
		//获取p内存中的值
		mov eax, dword ptr ds:[ebp - 0xC]
		//将寄存器的值当做地址进行存储数据
		mov DWORD ptr ds:[eax], 0xC8


		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0x40
		mov esp, ebp
		pop ebp
		ret
	}
}

第五关:偏移之剑 (The Offset Sword)

  • 核心知识点: 指针算术运算 (Pointer Arithmetic)。\

  • C 语言场景: p++ 或者 *(p + 2)

  • 汇编看点: 这是新手的噩梦。

    • C 写的是 p+1,汇编里却是 add eax, 4

    • C 写的是 *(p+2),汇编里可能是 [eax + 8]

  • 目的: 理解指针运算的本质是 "基地址 + 索引 × 元素大小"

具体代码

c 复制代码
void level_5() {
    // 1. 在栈上开辟 3 个整数的空间
    int arr[3] = {10, 20, 30};
    
    // 2. 定义指针 p,指向数组开头
    int *p = arr; 
	
    // 3. 修改第 0 个元素
    *p = 100;
	
    // 4. 指针移向下一个元素 (核心考点)
    p++; 
	
    // 5. 修改第 1 个元素
    *p = 200;
}

反汇编代码

c 复制代码
__declspec(naked) void level5(int* pa)
{
	_asm
	{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0x40
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0x40]
		mov ecx, 0x10
		mov eax, 0xcccccccc
		rep stosd

		//核心参数
		//ebp + 0x8 -> 存储arr首元素地址
		//先进行初始化操作
		//取出地址
		mov eax, dword ptr ds:[ebp + 0x8]
		mov DWORD ptr ds:[eax], 0xa
		mov dword ptr ds:[eax + 0x4], 0x14
		mov dword ptr ds:[eax + 0x8], 0x1e
		
		// 2. 定义指针 p,指向数组开头
		//int *p = arr; 
		//取出地址
		mov eax, dword ptr ds:[ebp + 0x8]
		//放入到p中
		mov DWORD ptr ds:[ebp - 0x4], eax
		
		// 3. 修改第 0 个元素
		//*p = 100;
		//修改存储地址背后的值
		//首先取出地址
		mov eax, dword ptr ds:[ebp - 0x4]
		//然后改变这个地址的值
		mov DWORD ptr ds:[eax], 0x64
		
		// 4. 指针移向下一个元素 (核心考点)
		//p++; ++指的是挪动一个类型步长 +4
		//重新取出地址
		mov eax, dword ptr ds:[ebp - 0x4]
		//对地址 + 4
		add eax, 0x4
		//放回内存
		mov dword ptr ds:[ebp - 0x4], eax

		// 5. 修改第 1 个元素
		//*p = 200;
		//取出地址
		mov eax, dword ptr ds:[ebp - 0x4]
		//修改数据
		mov DWORD ptr ds:[eax], 0xC8

		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0x40
		mov esp, ebp
		pop ebp
		ret
	}
}
	

int main(int argc, char* argv[])
{
	int arr[3];
	_asm
	{
		lea eax, arr
		push eax
		call level5
		add esp, 0x4
	}
	getchar();
	return 0;
}

第六关:折叠空间 (The Flatland)

  • 核心知识点: 二维数组 (2D Arrays)。

  • C 语言场景: int a[5][5],对二维数组进行初始化操作。

  • 汇编看点: 内存是线性的,没有二维。二维数组在汇编里是被"拍扁"的。

    • 你会看到乘法: Row * Width + Col
  • 目的: 看穿多维数组的线性本质。

具体代码

c 复制代码
void level_5() {
    int i = 0;
	int j = 0;
	int arr[i][j];
	for(int i = 0; i < 5; i++)
	{
		for(int j = 0; j < 5; j++)
		{
			arr[i][j] = 0;
		}
	}
}

反汇编代码

c 复制代码
__declspec(naked) void level6()
{
	_asm{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0xa0
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0xa0]
		mov ecx, 0x28
		mov eax, 0xcccccccc
		rep stosd

		//核心代码
		//变量i、j
		mov dword ptr ds:[ebp - 0x4], 0 //i = 0;
		mov dword ptr ds:[ebp - 0x8], 0 //j = 0
//外层检查
SHORT_OUTER_CHECK:
		//提取i
		mov eax, dword ptr ds:[ebp - 0x4]
		//判断
		cmp eax, 5
		//条件
		jge SHORT_FOR_END

//外层循环体
SHORT_OUTER_LOOP:

		//重置j = 0
		mov DWORD ptr ds:[ebp - 0x8], 0
//---------------------------------
//内层检查
SHORT_INSIDE_CHECK:
		//提取j
		mov ecx, dword ptr ds:[ebp - 0x8]
		cmp ecx, 5
		jge SHORT_OUTER_INCREMENT

//内层循环体
SHORT_INSIDE_LOOP:
		//记录首元素地址
		lea edx, dword ptr ds:[ebp - 0x6c]
		//改变eax -> 索引公式 (eax * 5 + ecx) * 4
		//重新读取i
		mov eax, dword ptr ds:[ebp - 0x4]
		imul eax, 5
		add eax, ecx
		//首元素 + 偏移量 -> 修改值
		mov dword ptr ds:[edx + eax * 4], 0

//内层累加
SHORT_INSIDE_INCREMENT:
		//重新提取j,避免寄存器污染
		mov ecx, dword ptr ds:[ebp - 0x8]
		//j++
		add ecx, 0x1
		//写回内存
		mov dword ptr ds:[ebp - 0x8], ecx
		//无条件跳转回标签头
		jmp SHORT_INSIDE_CHECK

//---------------------------------

//外层累加
SHORT_OUTER_INCREMENT:
		//重新提取i,避免寄存器污染
		mov eax, dword ptr ds:[ebp - 0x4]
		//i++
		add eax, 0x1
		//写会内存
		mov dword ptr ds:[ebp - 0x4], eax
		//无条件跳转回标签头
		jmp SHORT_OUTER_CHECK

//集合点
SHORT_FOR_END:

		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0xa0
		mov esp, ebp
		pop ebp
		ret
	}
}
}

第七关:提线木偶 (The Puppeteer)

  • 核心知识点: 二级指针 (Pointer to Pointer)。

  • C 语言场景: int **pp = &p;

  • 汇编看点: 双重解引用。

    • mov eax, [ebp-8] (拿 p 的地址)

    • mov eax, [eax] (拿 a 的地址)

    • mov eax, [eax] (拿 a 的值)

  • 目的: 适应"间接寻址"的思维,这是理解复杂数据结构(链表、树)的基础。

具体代码

c 复制代码
//任务:修改a -> 1314
int main(int argc, char* argv[])
{
	int a = 520;
	int *p = &a;
	_asm
	{
		lea eax, p
		push eax
		call level6
		add esp, 0x4
	}
	printf("a -> %d\n", a);
	getchar();
	return 0;
}

反汇编代码

c 复制代码
//作业:初始化int arr[4][8],总字节->4*4*8 = 128,0x80
// + 8,存放两个变量,0x88
__declspec(naked) void level6(int** ppa)
{
	_asm
	{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0x88
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0x88]
		mov ecx, 0x22
		mov eax, 0xcccccccc
		rep stosd

		//ebp + 0x8 - > 存储的是p的地址

		//提取&p
		mov eax, dword ptr ds:[ebp + 0x8]
		//第一次解引用 -> *&p -> &a
		mov ecx, dword ptr ds:[eax]
		//第二次解引用可以修改赋值
		mov DWORD ptr ds:[ecx], 0x522

		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0x88
		mov esp, ebp
		pop ebp
		ret
	}
}

第八关:扫描仪 (The Scanner)

  • 核心知识点: 指针与循环的混合 (Pointer Iteration)。

  • C 语言场景: 不使用 i,直接移动指针遍历数组 while(*p != 0) p++; (模拟 strlen)

  • 汇编看点: 没有 [ebp + ecx*4] 这种结构了,变成了直接对地址寄存器add 4

  • 目的: 这是编译器优化后最常见的形态,也是实战中识别字符串操作的关键。

具体代码

c 复制代码
int level7(char* pa)
{
	int len = 0;
	while(*pa != '\0')
	{
		pa++;
		len++;
	}
	return len;
}

反汇编代码

c 复制代码
__declspec(naked) void level8(char* pa)
{
	_asm
	{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0x88
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0x88]
		mov ecx, 0x22
		mov eax, 0xcccccccc
		rep stosd

		//ebp + 0x8 - > 存储的是pa的地址
		//放入eax寄存器
		mov eax, dword ptr ds:[ebp + 0x8]
		//初始化ecx
		xor ecx, ecx
SHORT_START_LOOP:
		//判断是否遇到'\0'
		cmp byte ptr ds:[eax], 0
		//条件相等则跳转
		je SHORT_LOOP_END
		//地址加1
		add eax, 0x1
		//个数加1
		add ecx, 0x1
		//跳转回标签头
		jmp SHORT_START_LOOP

//集合点
SHORT_LOOP_END:
		mov eax, ecx
		

		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0x88
		mov esp, ebp
		pop ebp
		ret
	}
}

第九关:寻找最大值

具体代码

c 复制代码
// 擂台法逻辑
int FindMax(int *arr, int count)
{
	int max = arr[0];
	int i = 1;
    while (i < count)
    {
        if (max < arr[i])
        {
            max = arr[i]; // 篡位,更新擂主
        }
        i++;
    }
    return max;
}

反汇编代码

c 复制代码
__declspec(naked) int level9(int count, int* pa)
{
	_asm
	{
		//函数头
		push ebp
		mov ebp, esp
		sub esp, 0x88
		push ebx
		push esi
		push edi
		lea edi, dword ptr ds:[ebp - 0x88]
		mov ecx, 0x22
		mov eax, 0xcccccccc
		rep stosd

		//ebp + 0x8 -> count
		//ebp + 0xC -> arr首元素地址
		//ebp - 0x4 -> max
		//ebp - 0x8 -> i
		//核心代码
		//假设arr[0] 是最大值
		//提取arr首元素地址
		mov eax, dword ptr ds:[ebp + 0xC]
		//获取0下标数据
		mov eax, dword ptr ds:[eax]
		//放入ebp - 0x4中
		mov DWORD ptr ds:[ebp - 0x4], eax
		//i为1,从1下标开始遍历
		mov dword ptr ds:[ebp - 0x8], 0x1

//标签头
SHORT_WHILE_START:
		//提取i
		mov eax, dword ptr ds:[ebp - 0x8]
		//跟count做比较
		cmp eax, dword ptr ds:[ebp + 0x8]
		//大于跳走
		jge SHORT_WHILE_END

//循环体
SHORT_WHILE_BODY:
		//提取首元素地址
		mov edx, dword ptr ds:[ebp + 0xc]
		//提取i
		mov ecx, dword ptr ds:[ebp - 0x8]
		//提取max
		mov eax, dword ptr ds:[ebp - 0x4]
		//比较
		cmp eax, dword ptr ds:[edx + ecx * 4]
		//大于等于则跳过
		jge SHORT_WHILE_INCREMENT

//交换
SHORT_WHILE_SWAP:
		//提取这个最大值
		mov eax, dword ptr ds:[edx + ecx * 4]
		//将这个放入max中
		mov DWORD ptr ds:[ebp - 0x4], eax

//累加
SHORT_WHILE_INCREMENT:
		//提取i
		mov eax, dword ptr ds:[ebp - 0x8]
		//i++
		add eax, 0x1
		//回写内存
		mov dword ptr ds:[ebp - 0x8], eax
		//跳转标签头
		jmp SHORT_WHILE_START

//集合点
SHORT_WHILE_END:
		mov eax, dword ptr ds:[ebp - 0x4]
		//函数尾
		pop edi
		pop esi
		pop ebx
		add esp, 0x88
		mov esp, ebp
		pop ebp
		ret
	}
}
	

int main(int argc, char* argv[])
{
	// 定义一个乱序数组,包含负数、大数
	int arr[] = {12, -5, 88, 33, 9, 100, 2}; 
	// 数组长度是 7
	int count = 7;
	int max_val = 0;
	_asm
	{
		lea eax, arr
		push eax
		push count
		call level9
		add esp, 0x8
		mov max_val, eax
	}
	printf("max_val -> %d\n", max_val);
	getchar();
	return 0;
}

第十关:冒泡排序(汇编版本)

具体代码

c 复制代码
//冒泡排序
void Bubble_Sort(int* arr, int len)
{
	int i = 0;
	for(i = 0; i < len - 1; i++)
	{
		int j = 0;
		for(j = 0; j < len - i - 1; j++)
		{
			if(arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

反汇编代码

在这个冒泡排序部分采用了纯手工写汇编的方法,当你尝试去了解底层的时候,而非是会读,更要明白每一行每一段的汇编是如何进行运转的,当你有这样夯实的基础后,逆向的大门才算是正式打开。

核心回顾:淬炼与觉醒

在这个阶段,你完成了三次视角的关键转换:

  • 从"语法"到"算术":

    • 你不再看 arr[i],你看到的是 [ESI + ECX * 4]

    • 你明白了,计算机里没有魔法,一切皆为计算。

  • 从"自动"到"手动":

    • C 语言帮你管理的寄存器分配、堆栈平衡、流程跳转,现在全由你接管。

    • 在"Bug 满天飞"的调试中,你学会了像 CPU 一样思考。你学会了在崩溃中寻找秩序。

  • 从"翻译"到"创造":

    • 你不再是把 C 翻译成汇编,你是在用汇编设计逻辑。

    • 冒泡排序的完成,标志着你已经拥有了用最基础的积木搭建复杂大厦的能力。


结语:剑铸之时 (The Epilogue)

当我们终于看着冒泡排序的汇编代码在调试器中完美运行,看着寄存器里的数据如流水般精准交换,看着内存里的乱序数字最终整齐排列时------我们知道,手中的剑,已然铸成。

我们不再畏惧 Bug,因为调试器是我们透视真相的眼睛;我们不再抱怨寄存器不够用,因为我们懂得了调兵遣将的策略;我们不再被复杂的逻辑吓倒,因为所有的复杂,终将回归到最朴素的'检查、循环、累加'三部曲。

那个曾经看着汇编代码感到头晕目眩的初学者,已经死在了昨天的调试中。 站在这里的,是一个懂内存、懂栈帧、懂逻辑的掌控者。

数组篇章的结束,不是终点,而是跳板。 前方,函数调用的契约法则(Level 10)正在等待着我们。那是通往真实世界的必经之路。

既然已经征服了内存的波涛,又何惧那契约的枷锁? 剑已出鞘,继续前行。

相关推荐
iCxhust17 小时前
如何利用iret修改cs ip
汇编·单片机·嵌入式硬件·微机原理·8088单板机
是星辰吖~2 天前
X86反汇编:透视之眼_反编译特训(1-2)
汇编
是星辰吖~2 天前
X86反汇编:破茧成蝶 —— 赤裸逻辑与机械之心(1-1)
汇编
逆向命运2 天前
PC企微搜索手机号窗口绕过
c语言·汇编·c++·飞书·企业微信
是星辰吖~3 天前
函数战争:内存领地的争夺与撤退
汇编
止观止3 天前
在 WSL2 上从零搭建 ARM 混合编程环境
汇编·arm开发·嵌入式开发·混合编程
say_fall4 天前
8086汇编程序设计_从基础到实战
开发语言·汇编·8086
浩浩测试一下5 天前
LoadPE &&& 原理以及作用 (ASM汇编版本)>>01
汇编·免杀·pe结构·windows编程·二进制逆向·系统loadpe
ThornArmor5 天前
【控制篇】斩断无休止空转:4-bit 指令集里的跳转律令与时序状态机
c语言·汇编·c++·单片机·嵌入式硬件