郁金香2021课程

赖卓成2025年4月3日
大约 4 分钟

01-函数入口

#include <stdio.h>
void main() {
	printf("我的第一个程序");
	// 等待键盘输入
	getchar();
}

02-函数和转义字符

#include <stdio.h>

void func() {
	printf("自定义函数\n");
}

int main() {
	func();

	// \n换行
	printf("我的第一个程序\n");
	// 等待键盘输入
	getchar();
	return 0;
}


03-调用window系统api

#include <iostream>
#include <Windows.h>

int main()
{
	MessageBoxA(0, "我的内容", "标题", 1);
	/* MessageBoxA(
		_In_opt_ HWND hWnd, 窗口句柄
		_In_opt_ LPCSTR lpText, 内容
		_In_opt_ LPCSTR lpCaption,  标题
		_In_ UINT uType); 按钮类型
		*/
}


04-数据类型


#include <iostream>
#include <limits.h>
int main()
{
	//char 1字节 能表示256个整数 -127到128
	// unsigned char 也是能表示256个,但是是0到256
	char a = 'a';   
	char b = 'b';  
	// %zd是大于0的  %d带符号整数  %u无符号整数  %llu 无符号整数(8字节)
	printf("char变量所占内存空间:%d,%zd", sizeof(a), sizeof(b));
	printf("\n===============================================\n");


	char c = 123;
	char d = -123;
	char e = 3333;
	// 打印结果e=5 因为存不下,被截取了
	printf("c = %d, d = %d, e = %d",c,d,e);
	printf("\n===============================================\n");

	// short为2字节
	short i, j, k;
	i = 3333;
	j = -3333;
	k = 33336;
	// k被截取
	printf("i = %d, j = %d, k = %d", i, j, k);
	printf("\n===============================================\n");

	// int为4字节
	int a1, a2;
	a1 = 33336;
	a2 = -33336;
	// 正常显示
	printf("a1 = %d, a2 = %d", a1, a2 );
	printf("\n===============================================\n");

	// 范围在limits.h中有定义常量,可以打印出来看看
	printf("char可以表示的范围是:%d到%d\n", SCHAR_MIN, SCHAR_MAX);
	printf("unsigned char可以表示的范围是:%d到%d\n", 0, UCHAR_MAX);
	printf("short可以表示的范围是:%d到%d\n", SHRT_MIN, SHRT_MAX);
	printf("unsigned short可以表示的范围是:%d到%d\n", 0, USHRT_MAX);
	printf("int可以表示的范围是:%d到%d\n", INT_MIN, INT_MAX);
	printf("unsigned int可以表示的范围是:%d到%d\n", 0, UINT_MAX);
	
	return 0;
}


05-window api操作窗口

vscode工具中的spy++可以获取窗口标题、类名和句柄

image-20250403122707735

#include <iostream>
#include <Windows.h>
int main()
{
	// HWND为指针类型
	HWND hwd = (HWND)FindWindowA("ConsoleWindowClass",NULL);   //0x00261394;
	printf("%p\r\n", hwd);
	// 改变标题
	SetWindowTextA(hwd,"新标题");
	// eax是4字节,rax是8字节
	printf("窗口句柄占字节数:%zd", sizeof(hwd));
	return 0;
}

image-20250403124119562

06-利用window api获取pid


#include <iostream>
#include <Windows.h>

int main()
{
	// 获取窗口句柄
	HWND hwd = (HWND)FindWindowA("MainWindow", "植物大战僵尸中文版"); 
	printf("%p\r\n", hwd);
	/*
	GetWindowThreadProcessId(
	_In_ HWND hWnd, // 窗口句柄
	_Out_opt_ LPDWORD lpdwProcessId); // 变量,函数内会通过指针修改内存值
	*/
	DWORD pid,tid;
	tid = GetWindowThreadProcessId(hwd, &pid);
	printf("pid = %d,tid = %d", pid,tid);
	getchar();
}

image-20250403130700008

07-读取目标进程内存数据

#include <iostream>
#include <Windows.h>

int main()
{
	// 获取窗口句柄
	HWND hwd = (HWND)FindWindowA("MainWindow", "植物大战僵尸中文版");
	printf("%p\r\n", hwd);
	DWORD pid, tid;
	tid = GetWindowThreadProcessId(hwd, &pid);
	printf("pid = %d,tid = %d\r\n", pid, tid);

	// 操作进程需要获取进程句柄
/*
OpenProcess(
	_In_ DWORD dwDesiredAccess,  渴望得到的权限
	_In_ BOOL bInheritHandle, 是否继承句柄
	_In_ DWORD dwProcessId  进程pid
	);

*/

	HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
	printf("进程句柄:%d\r\n", h);

	/*
	ReadProcessMemory(
    _In_ HANDLE hProcess,  进程句柄
    _In_ LPCVOID lpBaseAddress, 目标进程中的内存起始地址
    _Out_writes_bytes_to_(nSize,*lpNumberOfBytesRead) LPVOID lpBuffer,用于存放数据的地址,先传入后,会被修改
    _In_ SIZE_T nSize, 读取的字节数
    _Out_opt_ SIZE_T* lpNumberOfBytesRead 返回的实际读取字节数
    );

	*/
	unsigned int data;
	ReadProcessMemory(h, (LPCVOID)0x77961000, &data, 4, 0);
	printf("读取到的值:%x", data);
	getchar();
}

image-20250403143816256

08-封装读取4字节


#include <iostream>
#include <Windows.h>

unsigned int r4(UINT_PTR addr)
{
	HWND hwd = (HWND)FindWindowA("MainWindow", "植物大战僵尸中文版");
	DWORD pid, tid;
	tid = GetWindowThreadProcessId(hwd, &pid);
	HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
	unsigned int data;
	ReadProcessMemory(h, (LPCVOID)addr, &data, 4, 0);
	return data;
}

int main() {
	unsigned int sun = r4(r4(r4(0x6a9ec0) + 0x768) + 0x5560);
	printf("阳光值:%d", sun);
	return 0;
}

image-20250403145521784

09-CE分析基址偏移-精确数值

通过搜索找到记录,添加:

image-20250403150836798

按快捷键f5找出是什么访问了这个地址,可能会有多条:

image-20250403151024000

复制下来:

0041BAAF - CC - int 3 
0041BAB0 - E8 CBFEFFFF - call PlantsVsZombie.exe+1B980
0041BAB5 - 03 82 60550000  - add eax,[edx+00005560] <<
0041BABB - 39 44 24 04  - cmp [esp+04],eax
0041BABF - 0F9E C0  - setle al

EAX=00000032
EBX=00000001
ECX=FFFFFFFF
EDX=1D98D050
ESI=000000FF
EDI=027B9D40
ESP=001996D8
EBP=1B01C970
EIP=0041BABB



0048981D - 84 C0  - test al,al
0048981F - 0F85 0E010000 - jne PlantsVsZombie.exe+89933
00489825 - 8B 86 60550000  - mov eax,[esi+00005560] <<
0048982B - 33 C9  - xor ecx,ecx
0048982D - 85 C0  - test eax,eax

EAX=00000032
EBX=00000002
ECX=00000000
EDX=0C8B4178
ESI=1D98D050
EDI=1B01C948
ESP=00199790
EBP=0019FA98
EIP=0048982B



可以先尝试从mov指令的这一条分析,mov eax,[esi+00005560],去搜索esi的值(1D98D050),把红色的过滤掉,多搜几次,会有很多条:

image-20250403152358394

随便找一条,f5继续看是什么访问了(截图是找对了的,实际需要多次尝试):

image-20250403153247454

继续f5:

image-20250403170025604

随便找一条,看看:

image-20250403170112816

再去搜eax的值:

image-20250403170156029

出现了绿色的,就可以找到基址了,随便取一条都是基址。

10-CE分析基址偏移-未知初始值

11111

Loading...