C语言枚举进程,实现一个简单的内存补丁
直接进入正题,本文使用c语言对一款小软件实现内存补丁,今天下午才自学过,便想在博客中记录下来,分析过程仅供初学者学习,大神可以绕过。
大概思路为: 1、枚举进程获得系统进程列表
2、遍历进程列表,根据进程名得到进程的PID
3、根据PID获取进程句柄
4、根据进程句柄读写内存操作
先介绍要用到的几个API函数:
一、CreateToolhelp32Snapshot
该函数用于获取进程信息或模块信息快照,函数的原型如下:
HANDLE WINAPI CreateToolhelp32Snapshot(_In_ DWORD dwFlags, //指定快照中返回的对象,为TH32CS_SNAPPROCESS表示系统中的所有进程_In_ DWORD th32ProcessID //进程PID,用于指定进程,当为0时表示获取系统所有进程
);
函数成功返回快照句柄,失败返回INVALID_HANDLE_VALUE。
二、Process32First与Process32Next
这两个函数都是从指定的进程快照句柄中获取一个进程,从名字就可以看出Process32First用于获取第一个进程,后面的函数用于获取下一个进程,第一个函数的原型如下:
BOOL WINAPI Process32First(_In_ HANDLE hSnapshot, //快照句柄_Inout_ LPPROCESSENTRY32 lppe //LPPROCESSENTRY32进程结构体
);
函数成功返回true,并将一个进程信息存储到第二个参数中,失败返回false,后面的函数参数一样。
三、OpenProcess
该函数用户打开一个进程,可以指定打开后的权限,函数原型如下:
HANDLE WINAPI OpenProcess(_In_ DWORD dwDesiredAccess, //访问权限,PROCESS_ALL_ACCESS指定所有权限_In_ BOOL bInheritHandle, //是否继承句柄_In_ DWORD dwProcessId //要打开进程的PID
);
函数成功则返回进程的句柄,失败则返回NULL。
四、ReadProcessMemory
该函数用于向指定的进程的指定地址处读取数据,数据长度自定义,函数原型如下:
BOOL WINAPI ReadProcessMemory(_In_ HANDLE hProcess, //进程句柄_In_ LPCVOID lpBaseAddress, //要读取的地址_Out_ LPVOID lpBuffer, //要读取的数据缓存区_In_ SIZE_T nSize, //指定读取的字节大小_Out_ SIZE_T *lpNumberOfBytesRead //实际读取的字节大小
);
函数失败返回0,否则读取成功。
五、WriteProcessMemory
该函数用于向指定的进程的指定地址处写数据,数据长度自定义,函数原型如下:
BOOL WINAPI WriteProcessMemory(_In_ HANDLE hProcess, //进程句柄_In_ LPVOID lpBaseAddress, //要写入的地址_In_ LPCVOID lpBuffer, //要写入的数据缓存区_In_ SIZE_T nSize, //指定写入的大小 _Out_ SIZE_T *lpNumberOfBytesWritten //实际写入的大小
);
函数失败返回0,否则写入成功。
这里还需要介绍下进程结构体 PROCESSENTRY32,该结构体的定义如下:
typedef struct tagPROCESSENTRY32 {DWORD dwSize; // 结构大小;DWORD cntUsage; // 此进程的引用计数;DWORD th32ProcessID; // 进程ID;DWORD th32DefaultHeapID; // 进程默认堆ID;DWORD th32ModuleID; // 进程模块ID;DWORD cntThreads; // 此进程开启的线程计数;DWORD th32ParentProcessID;// 父进程ID;LONG pcPriClassBase; // 线程优先权;DWORD dwFlags; // 保留;WCHAR szExeFile[MAX_PATH]; // 进程全名;
} PROCESSENTRY32;
这里我们只需要用到th32ProcessID和szExeFile这两个属性。
现在介绍我们需要打补丁的小软件,软件截图如下:
该软件是一个注册码验证,需要输入用户名和正确的序列号,输入正确时弹出正确的提示信息,否则弹出错误的提示信息,如下图:
我们现在要做的就是,不管输入是否正确,总是弹出正确的提示,用OD打开软件,输入提示字符串,定位到如下地方:
00402588 . 8945 B4 mov dword ptr ss:[ebp-0x4C],eax
0040258B 74 58 je short Afkayas.004025E5 ; 关键跳
0040258D . 68 801B4000 push Afkayas.00401B80 ; You Get It
00402592 . 68 9C1B4000 push Afkayas.00401B9C ; \r\n
00402597 . FFD7 call edi ; msvbvm50.__vbaStrCat
00402599 . 8BD0 mov edx,eax
0040259B . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18]
0040259E . FFD3 call ebx ; msvbvm50.__vbaStrMove
004025A0 . 50 push eax
004025A1 . 68 A81B4000 push Afkayas.00401BA8 ; KeyGen It Now
004025A6 . FFD7 call edi ; msvbvm50.__vbaStrCat
004025A8 . 8D4D 94 lea ecx,dword ptr ss:[ebp-0x6C]
004025AB . 8945 CC mov dword ptr ss:[ebp-0x34],eax
004025AE . 8D55 A4 lea edx,dword ptr ss:[ebp-0x5C]
004025B1 . 51 push ecx
004025B2 . 8D45 B4 lea eax,dword ptr ss:[ebp-0x4C]
004025B5 . 52 push edx
004025B6 . 50 push eax
004025B7 . 8D4D C4 lea ecx,dword ptr ss:[ebp-0x3C]
004025BA . 6A 00 push 0x0
004025BC . 51 push ecx
004025BD . C745 C4 08000>mov dword ptr ss:[ebp-0x3C],0x8
004025C4 . FF15 10414000 call dword ptr ds:[<&MSVBVM50.#rtcMsgBox>; 正确的提示
004025CA . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18]
004025CD . FF15 80414000 call dword ptr ds:[<&MSVBVM50.__vbaFreeS>; msvbvm50.__vbaFreeStr
004025D3 . 8D55 94 lea edx,dword ptr ss:[ebp-0x6C]
004025D6 . 8D45 A4 lea eax,dword ptr ss:[ebp-0x5C]
004025D9 . 52 push edx
004025DA . 8D4D B4 lea ecx,dword ptr ss:[ebp-0x4C]
004025DD . 50 push eax
004025DE . 8D55 C4 lea edx,dword ptr ss:[ebp-0x3C]
004025E1 . 51 push ecx
004025E2 . 52 push edx
004025E3 . EB 56 jmp short Afkayas.0040263B ; 跳过错误提示
004025E5 > 68 C81B4000 push Afkayas.00401BC8 ; You Get Wrong
004025EA . 68 9C1B4000 push Afkayas.00401B9C ; \r\n
004025EF . FFD7 call edi ; msvbvm50.__vbaStrCat
004025F1 . 8BD0 mov edx,eax
004025F3 . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18]
004025F6 . FFD3 call ebx ; msvbvm50.__vbaStrMove
004025F8 . 50 push eax
004025F9 . 68 E81B4000 push Afkayas.00401BE8 ; Try Again
004025FE . FFD7 call edi ; msvbvm50.__vbaStrCat
00402600 . 8945 CC mov dword ptr ss:[ebp-0x34],eax
00402603 . 8D45 94 lea eax,dword ptr ss:[ebp-0x6C]
00402606 . 8D4D A4 lea ecx,dword ptr ss:[ebp-0x5C]
00402609 . 50 push eax
0040260A . 8D55 B4 lea edx,dword ptr ss:[ebp-0x4C]
0040260D . 51 push ecx
0040260E . 52 push edx
0040260F . 8D45 C4 lea eax,dword ptr ss:[ebp-0x3C]
00402612 . 6A 00 push 0x0
00402614 . 50 push eax
00402615 . C745 C4 08000>mov dword ptr ss:[ebp-0x3C],0x8
0040261C . FF15 10414000 call dword ptr ds:[<&MSVBVM50.#rtcMsgBox>; 错误的提示
00402622 . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18]
地址0040258B处的跳转跳过了正确的提示,直接到达了错误提示,如果此处不跳转,就会执行正确的提示,而且在下方的jmp可以跳过错误的提示,因此,只需要将此地址处的指令进行NOP,即写入两字节的0x90,即可以破解,源码如下:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <tlhelp32.h>
int main()
{//首先获取进程PIDint pid=0;PROCESSENTRY32 processentry={0}; //创建一个进程结构体processentry.dwSize=sizeof(PROCESSENTRY32);HANDLE hprocessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); //获取进程快照if(hprocessSnap==INVALID_HANDLE_VALUE){return -1;}int flag=Process32First(hprocessSnap,&processentry); //获取第一个进程while(flag){if(lstrcmpi(processentry.szExeFile,"Afkayas.exe")==0){ //lstrcmpi函数用于比较两个字符串,相同时返回0pid=processentry.th32ProcessID;}// printf("%d----%s\n",processentry.th32ProcessID,processentry.szExeFile);flag=Process32Next(hprocessSnap,&processentry);}CloseHandle(hprocessSnap);if(pid==0){printf("请先打开进程");return 0;}//获取进程句柄HANDLE procHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);if(procHandle==NULL){printf("打开进程失败!");return 0;}//测试读进程数据int tmp;DWORD buffer;if(ReadProcessMemory(procHandle,0x400000,&tmp,4,&buffer)){printf("读取成功,读取内容为:%#X\n",tmp);}else{printf("读取进程内容失败!");}tmp=0x9090;if(WriteProcessMemory(procHandle,0x40258B,&tmp,2,&buffer)){printf("写入进程内容成功\n");}else{printf("写入进程内容失败!\n");return 0;}system("pause");return 0;
}
打开程序后,再执行该补丁程序,提示注册成功:
本文只是记录了自己的学习过程,新手可以用来参考学习,大神发现问题欢迎指出。
C语言枚举进程,实现一个简单的内存补丁相关推荐
- C语言游戏脚本:一个简单的内存脚本!
通过 C 语言编写一个简单的外挂,通过 API 函数修改游戏数据,从而实现作弊功能 ● 对象分析 ● 要用的 API 函数简单介绍 ● 编写测试效果 ● 总体评价 对象分析 本次游戏对象为 Super ...
- glibc版本查看_[译] 写一个简单的内存分配器(替换glibc中的malloc函数)
本文介绍如何用c语言实现一个简单的内存分配器,可替换glibc中的 malloc(), calloc(), realloc(), free(). 这是一篇入门级别的文章,所以不会介绍所有的细节. 代码 ...
- 编程题目:使用C++语言模拟完成一个简单的计算机系统
编程题目:使用C++语言模拟完成一个简单的计算机系统(举一个管理人员的例子,其他的例子与下述方法一致即可) #include<iostream.h> #include<math.h& ...
- c语言设计程序计算器,C语言程序设计,做一个简单计算器
题目: C语言程序设计,做一个简单计算器 程序内容有:加减乘除和平方,立方,开方等.用if或者swith结构来编程. 算加法时只输出加法的结果,算减法时只出减法的结果,如此类推. 解答: 已发送, # ...
- python 消息队列 go_gmq: gmq是基于redis提供的特性,使用go语言开发的一个简单易用的消息队列;支持延迟任务,异步任务,超时任务,优先级任务...
1. 概述 gmq是基于redis提供的特性,使用go语言开发的一个简单易用的队列;关于redis使用特性可以参考之前本人写过一篇很简陋的文章Redis 实现队列; gmq的灵感和设计是基于有赞延迟队 ...
- 实现自己的operator new和operator delete以及实现一个简单的内存池管理类
为什么有必要写自己的operator new和operator delete? 为了效率.缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特 ...
- C/C++编程日记:制作一个简单的内存外挂
简单外挂 通过 C 语言编写一个简单的外挂,通过api函数修改游戏数据,从而实现作弊功能 对象分析 要用的 API 函数简单介绍 编写测试效果 本次游戏对象为 Super Mario XP 没有更新所 ...
- 【Go语言】实现一个简单的纯后端学员管理系统,入门级别练手项目,练习结构体,接口,构造函数
GO语言实现一个简单的纯后端学员管理系统 项目总览: 一.项目开发日志 二.前情知识补充 1.构造函数:用来在创建对象时初始化对象 2.方法和接收者 三.主函数中的内容 四.逐个自定义函数拆解分析 1 ...
- java虚拟机运行C语言_用C语言来实现一个简单的虚拟机
必要的准备工作及注意事项: 在开始之前需要做以下工作: 一个C编译器--我使用了 clang 3.4,也可以用其它支持 c99/c11 的编译器: 文本编辑器--我建议使用基于IDE的文本编辑器,我使 ...
- C语言实现的一个简单的HTTP程序
以下是参考<winsock网络编程经络>中讲解web应用http协议的时候,实现的一个简单的http程序,包含一个服务器和一个客户端. 先贴上客户端的程序: /************ ...
最新文章
- 为Nginx制作404 403 500等错误页面
- linux VFS 虚拟文件系统 简介 super_block inode dentry file
- “影响100活动”答记者问(二)
- 【机器学习】异常检测算法之(HBOS)-Histogram-based Outlier Score
- 节约内存:Instagram的Redis实践(转)
- 前端学习(1414):ajax基础
- 推荐系统——协同过滤
- JAVA入门级教学之(char类型)
- (28)FPGA计数器设计(软核实现)
- python开发好吗_用Python开发应用好用吗?
- Graph——bfs,dfs
- 约瑟夫问题的数学解法
- CGAL几何库配置教程
- 郭依婷—大学生的创业故事
- 编解码学习笔记(七):微软Windows Media系列
- 基于JAVA EE的临床科室管理系统
- HTTPS TSL/SSL详解
- 【译】JavaScript中的Promises
- 14个PPT资源素材网站,再也不怕做PPT了
- linux 将sda分区扩容