• 郁金香2021年游戏辅助技术(初级班)(上)


    %p、size_t、%zd、%llu、FindWindow、GetWindowText、SetWindowText

    HMODULE hModule = GetModuleHandle(nullptr);
    // %p 表示格式化指针
    printf("ddd %p\n", hModule);
    // sizeof输出的类型为size_t,%zd 表示格式化size_t类型大小
    // 在x64系统下sizeof运算符 返回值是size_t 占用空间8字节 要使用%llu格式化输出
    printf("sizeof(HMODULE)=%zd 字节\n", sizeof(hModule));	// sizeof(HMODULE)
    //HWND hWnd = reinterpret_cast(0x001A03DE);
    printf("sizeof(DWORD)=%zd sizeof(LONG)=%zd\n", sizeof(DWORD), sizeof(LONG));
    
    	// char short int的表示范围
    	// 头文件 #include
    	printf("char类型表示范围 %d至%d\r\n", SCHAR_MIN, SCHAR_MAX);
    	printf("unsigned char类型表示范围 %u至%u\r\n", 0, UCHAR_MAX); 
    	// short 
    	printf("short类型表示范围 %d至%d\r\n", SHRT_MIN, SHRT_MAX);
    	printf("unsigned short类型表示范围 %u至%u\r\n", 0, USHRT_MAX);
    	// int  -21亿 +21亿
    	printf("int类型表示范围 %d至%d\r\n", INT_MIN, INT_MAX);
    	printf("unsigned int类型表示范围 %u至%u\r\n", 0, UINT_MAX);
    
    HWND calcHwnd = FindWindow(L"CalcFrame", nullptr);
    printf("窗口句柄=%p\r\n", calcHwnd);
    TCHAR buf[MAX_PATH] = { 0 };
    wchar_t outBuf[MAX_PATH] = { 0 };
    GetWindowText(calcHwnd, buf, MAX_PATH);
    // %ws 表示格式化宽字符串
    swprintf_s(outBuf, L"calc %ws\n", buf);	
    // 宽字符串也可以使用大写的%S格式化输出
    //_stprintf_s(outBuf, _T("calc caption %S\n"), buf);
    MessageBox(nullptr, outBuf, buf, MB_OK);
    SetWindowText(calcHwnd, L"我的计算器zzz");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    GetWindowThreadProcessId、OpenProcess、ReadProcessMemory

    GetWindowThreadProcessId //返回线程TID和进程PID

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    #include 	// 包含了头文件
    #include
    int main()
    {
         // 1 通过窗口标题或者类名 获取目标窗口句柄
    	 // 2 通过窗口句柄获取进程的PID,TID
    	HWND 窗口句柄 = FindWindowA("MainWindow", "植物大战僵尸中文版");
    	printf("窗口句柄 h=%p\r\n", h);
    	DWORD pid = 0, tid = 0;
    	printf("&pid=%p\r\n", &pid);
    	// int 和 long是等价
    	// DWORD  等价于 unsigned long 
    	// DWORD* 等价于 LPDWORD
    	tid = GetWindowThreadProcessId(窗口句柄, &pid);
    	printf("tid=%d pid=%d  16进制tid=%X 16进制pid=%X\r\n", tid, pid, tid, pid);
    
    	// 获取 进程 权限 句柄
    	// 通过pid获取进程句柄,同时要指定渴望得到的访问权限
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); // PROCESS_VM_READ
    	printf("进程句柄=%p \r\n", 进程句柄);
    	unsigned int 返回值 = 0;
    	//跨进程读取目标进程指定地址处的4字节数据
    	//ReadProcessMemory(进程句柄, (LPCVOID)0x0000000077B2C934/*要读取的地址*/, &返回值/*存放数据的地址*/, 4, 0);
    	ReadProcessMemory(进程句柄, reinterpret_cast<LPCVOID>(0x77B2C934)/*要读取的地址*/, &返回值/*存放数据的地址*/, 4, 0);
    	printf("返回值=%X 返回值=%d 返回值=%u\r\n", 返回值, 返回值, 返回值);// GetLastError();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述


    封接读内存接口函数 int R4(void* 地址)

    通过接口函数读取基址偏移公式里的数据。

    在这里插入图片描述

    DWORD R4(UINT_PTR 内存地址)
    { 
    	HWND 窗口句柄 = FindWindowA("MainWindow", "植物大战僵尸中文版"); 
    	DWORD pid = 0, tid = 0;  
    	tid = GetWindowThreadProcessId(窗口句柄, &pid);
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);	// PROCESS_VM_READ
    	DWORD 返回值 = 0;
    	ReadProcessMemory(进程句柄, (LPCVOID)内存地址/*要读取的地址*/, &返回值/*存放数据的地址*/, 4, 0);
    	return 返回值;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    跨进程向目标进程内存地址写入数值 WriteProcessMemory

    在这里插入图片描述

    #include 
    #include
    
    HWND 获取游戏窗口句柄()
    {
    	return FindWindowA("MainWindow", "植物大战僵尸中文版");
    }
    
    //unsigned
    DWORD R4(UINT_PTR 内存地址)
    {
    	HWND 窗口句柄 = 获取游戏窗口句柄();	//FindWindowA("MainWindow", "植物大战僵尸中文版");
    	DWORD pid = 0, tid = 0;
    	tid = GetWindowThreadProcessId(窗口句柄, &pid);
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    	DWORD 返回值 = 0;
    	ReadProcessMemory(进程句柄, (LPCVOID)内存地址/*要读取的地址*/, &返回值/*存放数据的地址*/, 4, nullptr);
    	CloseHandle(进程句柄);//释放掉句柄资源
    	return 返回值;
    }
    
    //写入成功 W4返回 非0的数值
    //写入失败 W4返回 0
    int W4(UINT_PTR 内存地址/*要写入目标进程的内存地址*/, DWORD 要写入的值)
    {
    	HWND 窗口句柄 = 获取游戏窗口句柄();	//FindWindowA("MainWindow", "植物大战僵尸中文版");
    	DWORD pid = 0, tid = 0;
    	tid = GetWindowThreadProcessId(窗口句柄, &pid); 
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    	unsigned int 返回值 = WriteProcessMemory(进程句柄, (LPCVOID)内存地址/*要写的目标进程地址*/, &要写入的值/*存放待写入数据的地址*/, 4, nullptr);
    	CloseHandle(进程句柄);//释放掉句柄资源
    	return 返回值;
    }
    
    int main()
    {
    	//[[6A9EC0]+768]+5560 =14F2A3D8
    	// [[[6A9EC0]+768]+5560]=75
    	int 阳光 = R4(R4(R4(0x6A9EC0) + 0x768) + 0x5560);
    	UINT_PTR 阳光地址 = R4(R4(0x6A9EC0) + 0x768) + 0x5560;
    	printf("阳光=%d\r\n", 阳光);
    	printf("阳光地址=%X\r\n", 阳光地址);
    	
    	W4(阳光地址, 567);
    	
    	getchar();//等待键盘输入
    }
    
    int main_0()
    {
    	//[[6A9EC0]+768]+5560 =14F2A3D8
    	// [[[6A9EC0]+768]+5560]=75
    	int 阳光 = R4(R4(R4(0x6A9EC0) + 0x768) + 0x5560);
    	UINT_PTR 阳光地址 = R4(R4(0x6A9EC0) + 0x768) + 0x5560;
    	printf("阳光=%d\r\n", 阳光);
    	printf("阳光地址=%X\r\n", 阳光地址);
    
    	HWND 窗口句柄 = 获取游戏窗口句柄();	//FindWindowA("MainWindow", "植物大战僵尸中文版");
    	DWORD pid = 0, tid = 0;
    	tid = GetWindowThreadProcessId(窗口句柄, &pid);
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    	int 新阳光的值 = 66666;
    	WriteProcessMemory(进程句柄, (LPVOID)阳光地址, &新阳光的值, 4, nullptr);
    	CloseHandle(进程句柄);//释放掉句柄资源
    
    	getchar();//等待键盘输入
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    C,C++32位和64位进程内存数据读写函数接口、通过头文件进行项目管理

    在这里插入图片描述

    // 内存读写.h
    #pragma once
    #include 
    #include
    
    HWND 获取游戏窗口句柄();
    DWORD R4(UINT_PTR 内存地址/*从这个地址开始读取4字节*/);
    int W4(UINT_PTR 内存地址/*从这个地址开始写入4字节*/, DWORD 要写入的值);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // 内存读写.cpp
    #include "内存读写.h"
    
    HWND 获取游戏窗口句柄()
    {
    	return FindWindowA("MainWindow", "植物大战僵尸中文版");
    }
    
    // 读取4字节数据
    DWORD R4(UINT_PTR 内存地址)
    {
    	HWND 窗口句柄 = 获取游戏窗口句柄();	//FindWindowA("MainWindow", "植物大战僵尸中文版");
    	DWORD pid = 0, tid = 0;
    	tid = GetWindowThreadProcessId(窗口句柄, &pid);
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    	DWORD 返回值 = 0;
    	ReadProcessMemory(进程句柄, (LPCVOID)内存地址/*要读取的地址*/, &返回值/*存放数据的地址*/, 4, nullptr);
    	CloseHandle(进程句柄);//释放掉句柄资源
    	return 返回值;
    }
    
    //写入成功 W4返回 非0的数值
    //写入失败 W4返回 0
    int W4(UINT_PTR 内存地址/*要写入目标进程的内存地址*/, DWORD 要写入的值)
    {
    	HWND 窗口句柄 = 获取游戏窗口句柄();	//FindWindowA("MainWindow", "植物大战僵尸中文版");
    	DWORD pid = 0, tid = 0;
    	tid = GetWindowThreadProcessId(窗口句柄, &pid); 
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    	unsigned int 返回值 = WriteProcessMemory(进程句柄, (LPCVOID)内存地址/*要写的目标进程地址*/, &要写入的值/*存放待写入数据的地址*/, 4, nullptr);
    	CloseHandle(进程句柄);//释放掉句柄资源
    	return 返回值;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    // main程序入口点.cpp
    #include "内存读写.h"
    
    	//[[6A9EC0]+768]+5560 =14F2A3D8
    	// [[[6A9EC0]+768]+5560]=75
    	int 阳光 = R4(R4(R4(0x6A9EC0) + 0x768) + 0x5560);
    	UINT_PTR 阳光地址 = R4(R4(0x6A9EC0) + 0x768) + 0x5560;
    	printf("阳光=%d\r\n", 阳光);
    	printf("阳光地址=%X\r\n", 阳光地址);
    	
    	DWORD 新的阳光值 = 5567;
    	W4(阳光地址, 新的阳光值);
    	
    	getchar();//等待键盘输入
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    X64环境masm汇编asm文件

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    当我们在x32dbg调试器中找不到main入口点的时候,可以通过在main函数主体最开始位置那里,添加MessageBoxA或者MessageBeep函数这种方法来定位入口点:

    在这里插入图片描述

    在这里插入图片描述

    如上图所示,通过跳转到MessageBoxA函数这里并下断点,运行到这里断下后,在右下角栈那里可以看到_main返回地址。

    在我们自己编写代码调试测试的时候,最好把随机基址给禁用掉,这样就可以变成固定地址,方便我们测试:

    在这里插入图片描述

      call指令
      77256C78 | E8 EF02FBFF    | call ntdll.77206F6C       
      EIP=77206F6C
      push 77256C78+5 //esp=esp-4	//64 rsp=rsp-8
      
      ret指令
      77256CAF | C3             | ret    
      跳转到 [esp]
      esp=esp+4  //64 rsp=rsp+8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    常见的六种参数调用约定传递与平栈

    在这里插入图片描述


    x86环境

    #include
    
    //VS环境默认的调用约定
    //add esp,参数数量*4
    //add rsp,参数数量*8
    int _cdecl call_cdecl(int a, int b)
    {
    	return a + b;
    }
    
    //ret 参数数量*4  //x86   //ret 8
    //ret 参数数量*8  //x64   //ret 10
    int _stdcall call_std(int a, int b)
    {
    	return a + b;
    }
    //快速
    int _fastcall call_fast(int a, int b)
    {
    	return a + b;
    }
    // thiscall 类成员函数独有
    /*
    int __thiscall call_vector(int a, int b)
    {
    	return a + b;
    }
    */
    // __vectorcall (/ Gv)
    int __vectorcall call_vector(int a, int b)
    {
    	return a + b;
    }
    
    //int __clrcall test()
    //{
    //	return 1;
    //}
    
    int abc(int a, int b, int c)
    {
    	printf("技能释放 a=%d,b=%d c=%d\r\n", a, b, c);
    	return a + b + c;
    }
    int main()
    {
    	int a = 333;
    	int b = 123;
    	printf(" call_cdecl()=%X行号=%d\r\n",  call_cdecl(0x11A, 0xA11), __LINE__);
    
    	printf(" call_std()=%X行号=%d\r\n",    call_std(0x11B, 0xB11), __LINE__);
    
    	printf(" call_fast()=%X行号=%d\r\n",   call_fast(0x11C, 0xC11), __LINE__);
    
    	printf(" call_vector()=%X行号=%d\r\n", call_vector(0x11D, 0xD11), __LINE__);
    
    	printf(" 释放技能 abc=%d\r\n", abc(0x123, 2, 6));
    	getchar();
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    在这里插入图片描述

    在这里插入图片描述

    可以按冒号给函数添加标签,按分号添加注释。

    在这里插入图片描述

    在x86环境下,_fastcall调用约定,传递参数:ecx参数1、edx参数2、剩下的参数通过堆栈(push)来传递,并在函数内进行平栈(ret n)。

    在这里插入图片描述

    在x86环境下,_vectorcall调用约定,传递参数:ecx参数1、edx参数2、剩下的参数通过堆栈(push)来传递,也是在函数内进行平栈(ret 数值)。
    由于现在传的参数都是整数,所以_fastcall和_vectorcall我们通过反汇编看到参数传递、堆栈平衡都一样;其实它们是有区别的,区别应该就在于参数是浮点数的情况下。

    在x86环境下,_cdecl和_stacall调用约定,全部是用push传递参数。


    x64环境

    #include
    
    //VS环境默认的调用约定
    //add esp,参数数量*4
    //add rsp,参数数量*8
    int _cdecl call_cdecl(int a, int b,int a3,int a4,int a5,int a6)
    {
    	return a + b+a3+a4+a5+a6;
    }
    
    //ret 参数数量*4  //x86   //ret 8
    //ret 参数数量*8  //x64   //ret 10
    int _stdcall call_std(int a, int b, int a3, int a4, int a5, int a6)
    {
    	return a + b + a3 + a4 + a5 + a6;
    }
    //快速
    int _fastcall call_fast(int a, int b, int a3, int a4, int a5, int a6)
    {
    	return a + b + a3 + a4 + a5 + a6;
    }
    // thiscall 类成员函数独有
    /*
    int __thiscall call_vector(int a, int b)
    {
    	return a + b;
    }
    */
    // __vectorcall (/ Gv)
    int __vectorcall call_vector(int a, int b, int a3, int a4, int a5, int a6)
    {
    	return a + b + a3 + a4 + a5 + a6;
    }
    
    //int __clrcall test()
    //{
    //	return 1;
    //}
    
    int abc(int a, int b, int c)
    {
    	printf("技能释放 a=%d,b=%d c=%d\r\n", a, b, c);
    	return a + b + c;
    }
    int main()
    {
    	int a = 333;
    	int b = 123;
    	printf(" call_cdecl()=%X行号=%d\r\n", call_cdecl(0x11A, 0xA11,3,4,5,6), __LINE__);
    
    	printf(" call_std()=%X行号=%d\r\n", call_std(0x11B, 0xB11,3,4,5,6), __LINE__);
    
    	printf(" call_fast()=%X行号=%d\r\n", call_fast(0x11C, 0xC11,3,4,5,6), __LINE__);
    
    	printf(" call_vector()=%X行号=%d\r\n", call_vector(0x11D, 0xD11,3,4,5,6), __LINE__);
    
    	printf(" 释放技能 abc=%d\r\n", abc(0x123, 2, 6));
    	getchar();
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    在x64dbg调试器中,转到main函数的话,ctrl+G,输入main确定即可,main前面不需要加下划线(x86)。

    在这里插入图片描述

    在x64环境下,_cdecl、_stacall和_fastcall、_vectorcall这4种调用约定,我们发现都一样,都是用rcx、rdx、r8、r9来传递参数,超出4个的参数用堆栈(rsp+20)来传递。

    我们看到第5个参数是rsp+20,前4个参数占0x20大小的空间,但是在x64环境下,前4个参数分别通过rcx、rdx、r8、r9来传递,为了对齐(也是为了兼容x86汇编),第5个参数通过rsp+20来传递,第6个参数通过rsp+28来传递;
    而且在函数内部最后一条指令就是一条ret,后面不跟数值。

    经过测试我们发现,在x64环境下,这4种调用约定是没有区别的,参数传递和平栈方式都是相同的,所以暂时可以认为这4种调用约定是相同的。

    在这里插入图片描述

    由于call该函数的指令执行后,rsp=rsp-8,所以上图mov ss:[rsp+20],r9d并没有覆盖call指令执行前的第5个参数,mov ss:[rsp+20],r9d对于call指令执行前的环境来说,相当于mov ss:[rsp+18],r9d,即第4个参数给了ss:[rsp+18],在函数里面的这种堆栈传参方式也是为了兼容x86汇编。
    即堆栈变化示意图如下:

    在这里插入图片描述

    在这里插入图片描述

    x64寄存器低4字节低2字节低1字节
    rcxecxcxcl
    rdxedxdxdl
    r8r8dr8wr8b
    r9r9dr9wr9b

    再看一个函数,堆栈变化同理:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    可以针对所有配置、所有平台进行设置:

    在这里插入图片描述


    汇编条件转移指令与循环

    在这里插入图片描述

    在这里插入图片描述

    int main_while01()
    {
    	/* 局部变量定义 */
    	int a = 10;
    
    	/* while 循环执行 */
    	// SF(符号标志位)
    	// OF(溢出标志位)
    	//cmp a-20 从而影响到 符号标志位
    	while (a < 20) // SF=OF  
    	{
    		printf("a 的值: %d  行号=%d \n", a, __LINE__);
    		a++; //a=a+1; add [esp+??],1// inc [esp+??]
    	}
    	return 0;
    }
    
    int main()
    {
    	printf("main start 行号=%d \n", __LINE__);
    	main_while01();
    	printf("main end   行号=%d \n", __LINE__);
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    我们看到上图OF不等于SF,SF为1,这是有符号跳转指令JL的条件,所以jge条件不成立,不跳转,进入循环。
    (汇编指令cmp就是做了一个减法操作,即a - 20,从而影响到符号标志位SF)

    在这里插入图片描述

    如上图所示,当ss:[ebp-8]等于14的时候,执行cmp指令,可以看到ZF等于1,OF等于SF都为0:

    在这里插入图片描述

    当OF等于0,SF等于0,即OF==SF,那么有符号跳转指令jge跳转条件成立,循环终止。

    减法的话对SF和ZF标志位有影响;
    加法的话对CF和OF标志位有影响。

    我们再来看看包含continue语句的while循环代码:

    int main_continue()
    {
    	/* 局部变量定义 */
    	int a = 0;
    
    	/* while 循环执行 */
    	while (a < 10) //0x0A=10
    	{
    		a++;
    		if (a == 5)
    		{
    			/* 使用 continue 进入下一次循环 */
    			continue;
    		}
    		printf("for循环测试:a 的值: %d 行号=%d \n", a, __LINE__);
    	}
    	return 0;
    }
    
    int main()
    {
    	printf("main start 行号=%d \n", __LINE__);
    	main_continue();
    	printf("main end   行号=%d \n", __LINE__);
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    我们从上图可以看到,判断小于的话用jge指令,判断等于的话用jne指令。


    C,C++用代码,跨进程调用CALL

    用代码注入器调用CALL

    #include 
    #include 
    int call00()
    {
    	printf("call00 无参数 call0_test  行号=%d\r\n",  __LINE__);
    	return 0x123;
    }
    
    int call01(int a)
    {
    	printf("call01 参数1=%d   行号=%d\r\n", a,  __LINE__);
    	return a + 2;
    }
    
    int call02(int a, int b)
    {
    	printf("call02 参数1=%d 参数2=%d 行号=%d\r\n", a, b,__LINE__);
    	return a + b+3;
    }
    
    int main()
    {
    //	MessageBoxA(0, 0, 0, 0);//ctrl+G 转到 MessageBoxA 下断
    	printf("MessageBoxA=%p 行号=%d \r\n", MessageBoxA, __LINE__);
    	int 计数 = 1;
    	while (1)
    	{
    		printf("call00=%p call01=%p,call02=%p \n", call00, call01, call02);
    		getchar();
    		printf("计数=%d,行号=%d \r\n", 计数++, __LINE__);
    	}
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    注意,x64dbg标题栏上的线程号5236是十进制的,左下角的1474是十六进制的,这两个数是一样的


    C,C++创建远程线程调用CALL

    • 远程调用CALL的流程
      1. 用spy++获取目标窗口句柄,FindWindow
      2. 有了窗口句柄的情况 用GetWindowThreadProcessID获取目标进程PID
      3. 通过OpenProcess获取目标进程的线程创建权限 获取相应的句柄
      4. 用CreateRemoteThread调用远程的CALL
    #include 
    #include
    //unsigned
    //int R4(UINT_PTR 内存地址)
    //{
    //	HWND 窗口句柄 = FindWindowA("MainWindow", "植物大战僵尸中文版");
    //	DWORD pid = 0, tid = 0;
    //	tid = GetWindowThreadProcessId(窗口句柄, &pid); // a&b 
    //	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);//HWND
    //	unsigned int 返回值 = 0;
    //	ReadProcessMemory(进程句柄, (LPCVOID)内存地址/*要读取的地址*/, &返回值/*存放数据的地址*/, 4, 0);
    //	return 返回值;
    //}
    
    // \n
    // \ \\
    // 窗口标题= "C:\\Users\\Administrator\\source\\repos\\A001\\Debug\\A023_测试CALL.exe"
    //#define 窗口标题 "C:/moshou/A001-游戏辅助技术(初级班)/代码\A001-024/Debug"
    //#define 窗口标题 "C:\moshou\A001-游戏辅助技术(初级班)\代码\A001-024\Debug"
    #define 窗口标题 R"(C:\moshou\A001-游戏辅助技术(初级班)\代码\A001-024\Debug\A023_测试CALL.exe)"
    
    DWORD 获取PID()
    {
     	HWND 窗口句柄 = FindWindowA(0, 窗口标题);
     	DWORD pid = 0, tid = 0;
     	tid = GetWindowThreadProcessId(窗口句柄, &pid); // a&b 
    	return pid;
    }
    
    void 调用call01测试()
    {
    	DWORD pid = 4180; //直接在任务管理器里 取得PID
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);//HWND
    	//1,4,5 重视这3个参数
    	CreateRemoteThread(
    		进程句柄,//1
    		0, //2
    		0, //3
    		(LPTHREAD_START_ROUTINE)0x411370,//4 CALL地址
    		(LPVOID)123,//5 CALL的参数
    		0,
    		0);
    }
    
    void 调用call01测试2(int 参数1)
    {
    	DWORD pid = 4180;//任务管理器取的pid
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);//HWND
    	//1,4,5 重视这3个参数
    	CreateRemoteThread(
    		进程句柄,//1
    		0, //2
    		0, //3
    		(LPTHREAD_START_ROUTINE)0x411370,//4 CALL地址
    		(LPVOID)参数1,//5 CALL的参数
    		0,
    		0);
    	CloseHandle(进程句柄);
    }
    
    void 调用call01测试3(int 参数1)
    {
    	DWORD pid = 获取PID();//任务管理器取的pid
    	printf("pid=%d \r\n", pid);
    	HANDLE 进程句柄 = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);//HWND
    	//1,4,5 重视这3个参数
    	CreateRemoteThread(
    		进程句柄,//1
    		0, //2
    		0, //3
    		(LPTHREAD_START_ROUTINE)0x4113CA,//4 call01地址
    		(LPVOID)参数1,//5 CALL的参数
    		0,
    		0);
    	CloseHandle(进程句柄);
    }
    
    int main()
    {
    	调用call01测试3(12666);
    	调用call01测试3(12667);
    	调用call01测试3(12668);
    	//R忽略 转义字符
    	char test[]= R"(C:\Users\Administrator\source\repos\A001\Debug\A023_测试CALL.exe)";
    	//char test2[] = "C:\Users\Administrator\source\repos\A001\Debug\A023_测试CALL.exe";
    	printf("%s\r\n",test);
        std::cout << "Hello World!\n";
    }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    杂项

    常用软件游戏

    在这里插入图片描述

    这个代码注入器只能用于32位程序的测试,64位进程能用的这类代码注入工具目前还没有发现免费的,我们只能自己写代码来注入。

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    免签名加载驱动:
    https://github.com/TheCruZ/kdmapper

    win10过签名:
    https://github.com/HyperSine/Windows10-CustomKernelSigners

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    vs平台工具集如果装了Inter C++ Compiler编译器的话,Intel的编译器是支持__asm int 3这些内联汇编的:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述


    定位特征码

    在这里插入图片描述
    编辑好特征码之后,移动到函数开头、或者更上面一些,Ctrl+B打开输入要查找的二进制字串对话框,粘贴特征码搜索,能找到的话说明我们编写的特征码没问题;
    还应该重启游戏,打开OD附加游戏,按照上面所述搜索特征码进行验证。


    d3d8thk劫持注入

    在这里插入图片描述

    在这里插入图片描述


    PE查看分析工具

    PE查看器(PE_Study) https://www.cr173.com/soft/47081.html
    PE文件查看器(PETool) https://www.cr173.com/soft/89855.html
    逆缘PE文件提取 https://www.cr173.com/soft/85171.html

    PE分析工具(PE_Hacker) https://www.cr173.com/soft/1438600.html
    https://blog.csdn.net/PE_Hacker/article/details/115766034

    PE-Analysis:

    在这里插入图片描述


    驱动开发调试工具 DriverTool

    驱动开发调试工具,比如测试模式下的数字签名Windows 64Signer V1.2,又比如DebugView等一些工具。

    在这里插入图片描述

    /

    在这里插入图片描述

  • 相关阅读:
    Js 返回当前时间,上一天的字符串格式yyyy-mm-dd或者yyyy-MM-dd hh:mm:ss
    前端面试题:基础理论整理(篇1)
    phaser3 创建多个按钮
    组合数的计算
    虚拟音频设备软件 Loopback mac中文版软件介绍
    1_SQL
    Linux开发工具---->yum/gcc/g++/gdb/makefile
    JAVA并发编程--6 理解分布式锁
    数据结构刷题:第十一天
    《PolarDB-X学习》第一讲
  • 原文地址:https://blog.csdn.net/zhaopeng01zp/article/details/128086031