1.win32 程序运行原理

  • CPU的保护模式和windows系统
    80386有3种工作模式:实模式、保护模式、虚拟86模式
    windows系统主要工作在保护模式下
保护模式下32位windows系统可寻址4GB的地址空间。
每个进程都有4GB的内存空间。

在这里插入图片描述

  • 内核模式和用户模式

  • 80386共定义4种特权级别,其中0级是内核模式下,3级是用户模式,俗称0环、3环
    用户模式:用户程序的代码在用户模式下运行
    内核模式:系统程序(服务和硬件程序驱动)在内核模式下运行
    在这里插入图片描述

  • 内核对象
    内核对象是系统提供的用户模式下代码与内核模式下代码进行交互的基本接口。
    为保证系统正常运行,系统维护一些不允许用户程序直接访问的数据,它只能在被内核模式运行的代码访问。内核对象记录的数据被称为系统资源。

  • 计数
    内核对象创建时,系统会初始化计数为1,如果计数为0,系统会自动销毁内核对象,打开内核对象一次加1,关闭内核对象减1。

  • 对象句柄
    内核对象的数据仅能够在内核模式访问,应用程序只能通过API函数访问内核对象。调用函数创建内核对象时,函数会返回标识此内核对象的句柄,该句柄在内存中就是一个地址。

  • 进程和线程
    硬盘上的可执行文件被载入内存就成为进程,生成进程的同时会创建一个线程,线程才是执行代码的独立实体。

  • 应用程序的启动过程
    win32程序的启动过程,操作系统通过调用CreateProcess函数来创建新进程。一般将创建进程称为父进程、被创建的进程称为子进程。
    系统在创建新进程时会传递给子进程一个STARTUPINFO结构体类型的变量,该变量在图形化程序中影响图形的显示,在控制台窗口中影响控制台窗口。
    STARTUPINFO

typedef struct
{
	DWORD cb; //该结构体的长度,总是被设置为sizeof(STARTUPINFO)
	LPSTR lpReserved; //保留字段,即程序不使用这个参数
	LPSTR lpDesktop;  //指定桌面名称
	LPSTR lpTile;     //控制台应用程序使用,指定控制台窗口标题
	DWORD dwX;   //指定创建窗口的位置坐标(dwX,dwY)和大小信息
	DWORD dwY;
	DWORD dwXSize;
	DWORD dwYSize;
	DWORD dwXCountChars; //控制台使用,指定控制台窗口行数
	DWORD dwYCountChars;
	DWORD dwFillAttribut; //指定控制台窗口的背景色
	DWORD dwFlags; //标志位,决定STARTUPINFO结构体哪些成员值有效
	WORD wShowWindow; //窗口的显示方式
	WORD cbReserved2;
	LPBYTE lpReserved2;
	HANDLE hStdInput;  //控制台使用的几个标准句柄
	HANDLE hStdOutput;
	HANDLE hStdError;
}STARTUPINFO,*LPSTARTUPINFO;
  • windows获取当前进程的创建信息
VOID GetStartupInfo(LPSTARTUPINFO lpStartupInfo);
  • CreateProcess函数
CreateProcess(
LPCSTR lpApplicationName, //可执行文件名称
LPSTR lpCommandLine,   //指定要传递给执行模块的参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, //进程安全性,NULL表示默认安全性
LPSECURITY_ATTRIBUTES lpThreadAttribute, //线程安全性,NULL表示默认安全属性
BOOL bInheritHandles, //指定当前进程中的可继承句柄是否可以被新进程继承
DWORD dwCreationFlags, //指定新进程的优先级及其他创建标志
LPVOID lpEnviroment, //指定新进程使用的环境变量
LPCSTR lpCurrentDirectory, //新进程使用的当前目录
LPSTARTUPINFO lpStartupInfo,  //指定新进程中主窗口的位置、大小和标准句柄
LPPROCESS_INFORMATION lpProcessInformation, //[out]返回新建进程的标志信息,如ID号、句柄等
)
  • windows中的变量
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef void far *LPVOID
typedef int INT;
typedef unsigned int UINT;
  • 示例
StARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
char* szCommandLine = "notepad"; //也可以是"notepad.exe"
CreateProcess(NULL,szCommandLine,NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);

CreateProcess函数会按照以下路径去搜索可执行文件

  1. 调用进程的可执行文件所在目录
  2. 调用进程的当前目录
  3. windows的系统目录(system32目录)
  4. windows目录
  5. 在名称为PATH的环境变量中列出的目录
  • lpProcessInformation结构体
typedef struct{
HANDLE hProcess; //新创建进程的内核句柄
HANDLE hThread;  //新创建进程中主线程的内核句柄
HANDLE dwProcessId; //新创建进程的ID
HANDLE dwThreadId; //新创建进程的主线程ID 
}PROCESS_INFORMATION,*LPPROCESS_INFORMATION;
  • 示例: 创建进程
// 02CreateProcess.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

int main(int argc,char* argv[])
{
	char szCommandLine[] = "cmd";
	STARTUPINFO si = {sizeof(si)};
	PROCESS_INFORMATION pi;

	si.dwFlags = STARTF_USESHOWWINDOW; //指定wShowWindow成员有效
	si.wShowWindow = TRUE; //成员为TRUE的话显示新建进程的主窗口,FALSE不显示

	BOOL bRet = CreateProcess(
		NULL,            //不指定可执行文件的文件名
		szCommandLine,   //命令行参数
		NULL,            //默认进程安全性
		NULL,            // 默认线程安全性
		FALSE,          //指定当前进程内的句柄不可以被子进程继承
		CREATE_NEW_CONSOLE,  //新创建一个控制台窗口
		NULL,   //使用本进程的环境变量
		NULL,   //使用本进程的驱动器和目录
		&si,      
		&pi);
	if(bRet)
	{
	//不使用两个句柄,就立刻关闭它们
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
        printf("新进程的进程ID号:%d\n",pi.dwProcessId);
		printf("新进程的主线程ID号: %d\n",pi.dwThreadId);

	}

	return 0;
}

在这里插入图片描述

  • CreateToolhelp32Snapshot
    用于获取系统内指定进程的快照
HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, //用来指定“快照”返回对象,可以是TH32CS_SNAPPROCESS等
DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,获取系统进程列表可以设置为0
);
#dwFlags:
1. TH32CS_SNAPHEAPLIST //枚举th32ProcessID参数指定的进程中的堆
2. TH32CS_SNAPMOUDLE //枚举th32ProcessID参数指定的进程中的模块 
3. TH32CS_SNAPPROCESS //枚举系统范围内的进程,此时th32ProcessID参数被忽略
4. TH32CS_SNAPTHREAD  //枚举系统范围内的线程,此时th32ProcessID参数被忽略
函数执行成功将返回一个快照句柄,否则返回INVALID_HANDLE_VALUE(-1)
  • Process32First和Process32Next
    从快照列表中获取进程信息需要使用Process32First和Process32Next函数,首次调用使用Process32First,以后调用使用Process32Next
Process32First和Process32Next第一个参数是快照句柄,第二个参数是一个指向PROCESSENTRY32结构的指针,进程信息将会被返回到这个结构中
typedef struct{
DWORD dwSize;  //结构体的长度,必须预先设置
DWORD cntUsage;  //进程的引用计数
DWORD th32ProcessID; //进程ID
DWORD th32DefaultHeapID; //进程默认堆的ID
DWORD th32ModuleID;  //进程模块的ID
DWORD cntThreads;   //进程创建的线程数
DWORD th32ParentProcessID; //进程的父线程ID
LONG pcPriClassBase;  //进程创建的线程的基本优先级
DWORD dwFlags;  //内部使用
CHAR  szExeFile[MAX_PATH]; //进程对应的可执行文件名
}PROCESSENTRY32;
EnumProcess也可以完成系统进程列表枚举功能
  • 示例: 遍历系统进程
// 02ProcesssList.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include <tlhelp32.h> //声明快照函数的头文件
#include "stdio.h"

int main(int argc, char* argv[])
{
	PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(pe32);

	HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
	if(hProcessSnap == INVALID_HANDLE_VALUE)
	{
		printf("CreateToolhelp32Snapshot 调用失败! \n");
		return -1;
	}
	BOOL bReturn = Process32First(hProcessSnap,&pe32);
	while(bReturn)
	{
		printf("进程名称: %s\n",pe32.szExeFile);
		printf("进程ID: %u\n\n",pe32.th32ProcessID);

		bReturn = Process32Next(hProcessSnap,&pe32);
	}

	CloseHandle(hProcessSnap); //关闭Snapshot对象

	return 0;
}

在这里插入图片描述

  • ExitProcess函数
    只能用来终止当前进程
void ExitProcess(UNIN uExitCode); //参数uExitCode为此程序的退出代码
  • TerminateProcess函数
    用来终止其他进程
BOOL TerminateProcess(
	HANDLE hProcess,  //要结束的进程(目标进程)的句柄
	UINT uExitCode   //指定目标进程的退出代码,GetExitCodeProcess获得目标进程的退出代码
);
  • OpenProcess
HANDLE OpenProcess(
	DWORD dwDesiredAccess, //想得到的访问权限,可以是PROCESS_ALL_ACCESS等
	BOOL bInheritHandle, //指定返回的句柄是否可以被继承
	DWORD dwProcessId //指定要打开的进程ID号
);
  • 示例:关闭一个进程
BOOL TerminateProcessFromId(DWORD dwId)
{
	BOOL bRet = FALSE;
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwId);
	if(hProcess != NULL)
	{
	bRet = TerminateProcess(hProcess,0);
	}
	CloseHandle(hProcess);
	return bRet;
}

进程一旦终止,就会有下列事件发生

1. 所有被这个进程创建或打开的对象句柄就会关闭
2. 此进程内所有的线程将终止执行
3. 进程内核对象变成受信状态,所有等待在此对象上的线程开始运行,即WaitForSingleObject函数返回
4. 系统将进程对象中退出代码的值由STILL_ACTIVE改为指定的退出码
  • 关闭notpad进程
// 02TerminateNotepadProcess.cpp : Defines the entry point for the console application.
//

	
#include "stdafx.h"
#include "windows.h"
#include <tlhelp32.h> //声明快照函数的头文件
#include "stdio.h"

int main(int argc, char* argv[])
{
	PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(pe32);

	HANDLE hProcess;
	HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
	if(hProcessSnap == INVALID_HANDLE_VALUE)
	{
		printf("CreateToolhelp32Snapshot 调用失败! \n");
		return -1;
	}
	BOOL bReturn = Process32First(hProcessSnap,&pe32);
	while(bReturn)
	{
		
		if(!strcmp("notepad.exe",pe32.szExeFile))
		{
			printf("notepad进程名称: %s\n",pe32.szExeFile);
		    printf("notepad进程ID: %u\n\n",pe32.th32ProcessID);
			hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);
			if(hProcess != NULL)
			{
			TerminateProcess(hProcess,0);
			printf("notepad程序关闭\n");
			}
			CloseHandle(hProcess);
		}
	
		
		bReturn = Process32Next(hProcessSnap,&pe32);
		
	}

	CloseHandle(hProcessSnap); //关闭Snapshot对象

	return 0;
}

在这里插入图片描述

  • 保护进程
    指的是保护进程不被其他进程非法关闭。

  • ReadProcessMemory和WriteProcessMemory函数
    读取指定进程的内存

BOOL ReadProcessMemory(
	HANDLE hProcess,  //待读取进程的句柄
	LPCVOID lpBaseAddress, //目标进程中待读内存的起始地址
	LPVOID lpBuffer, //用来接受读取数据的缓冲区
	DWORD nSize, //要读取的字节数
	LPDWORD lpNumberOfBytesRead //用来供函数返回实际读取的字节数
);
WriteProcessMemory(hProcess,lpBaseAddress,lpBuffer,nSize,lpNumberOfBytesRead);//参数含义同上
  • GetVersionEx函数
BOOL GetVersionex(LPOSVERSIONINFO lpVersionInfo);
系统会将操作系统的版本返回到参数lpVersionInfo指向的OSVERSIONINFO结构中
typedef struct _OSVERSIONINFO{
	DWORD dwOSVersionInfoSiz; //本结构体大小,必须在调用之前设置
	DWORD dwMajorVersion; //操作系统的主版本号
	DWORD dwMinorVersion; //操作系统的次版本号
	DWORD dwBulidNumber;  //操作系统的编译版本号
	DWORD dwPlatformId;  //操作系统平台,可以是VER_PLATFORM_WIN32_NT(2000系列)等
	TCHAR szCSDVersion[128]; //指定安装在系统上的最新服务包,例如Service Pack3等
}OSVERSIONINFO;
  • 读写修改内存注意
    win2000系列中的应用程序内存使用是从64KB开始,到2GB结束
    win98: 4MB开始,到2GB结束
  • 示例程序:02Testor.exe
    将02Testor当作游戏程序,找到游戏程序中的一个值然后修改它的值
// 02Testor.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "stdio.h"

int g_nNum;     //全局变量测试

int main(int argc, char* argv[])
{
	int i = 198; //局部变量测试
	g_nNum = 1003;

	while(1)
	{
		//输出个变量的值和地址
		printf("i = %d,addr = 0x%X;  g_nNum = %d,addr = 0x%X\n",++i,&i,--g_nNum,&g_nNum);
		getchar();
	
	}
	
	return 0;
}

  • 修改游戏程序中内存的值02MemRepair.exe
// 02MemRepair.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"

BOOL FindFirst(DWORD dwValue); //在目标进程空间进行第一次查找
BOOL FindNext(DWORD dwValue);  //在目标进程地址空间进行第2、3、4...次查找
void ShowList();
BOOL WriteMemory(DWORD dwAddr,DWORD dwValue);



DWORD g_arList[1024];    //地址列表
int g_nListCnt;          //有效地址的个数
HANDLE g_hProcess;       //目标进程的句柄

BOOL CompareAPage(DWORD dwBaseAddr,DWORD dwValue)
{
	//读取1页内存,即4KB大小
	BYTE arBytes[4096];
	if(!ReadProcessMemory(g_hProcess,(LPVOID)dwBaseAddr,arBytes,4096,NULL))
		return FALSE; //此页不可读
	
	//在这1页内存中查找
	DWORD* pdw;
	for(int i=0;i<(int)4*1024-3;i++)
	{
		pdw = (DWORD*)&arBytes[i];
		if(pdw[0] == dwValue) //等于要查找的值
		{
			if(g_nListCnt>=1024)
				return FALSE;
			g_arList[g_nListCnt++] = dwBaseAddr+i;
		}
	}

    return TRUE;
}



int main(int argc, char* argv[])
{
	//启动02testor进程
	char szFileName[] = "..\\02Testor\\Debug\\02Testor.exe";
	STARTUPINFO si ={sizeof(si)};
	PROCESS_INFORMATION pi;
	CreateProcess(NULL,szFileName,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);

	//关闭线程句柄,既然我们不使用它
	CloseHandle(pi.hThread);
	g_hProcess = pi.hProcess;

	//输入要修改的值
	int iVal;
	printf("Input val=");
	scanf("%d",&iVal);

	//进行第一次查找
	FindFirst(iVal);
	//打印出搜索的结果
	ShowList();

	while(g_nListCnt>1)
	{
		printf("Input val=");
		scanf("%d",&iVal);
		//进行下次搜索
		FindNext(iVal);

		//显示搜索结果
		ShowList();
	}

	//取得新值
	printf("New Value=");
	scanf("%d",&iVal);

	//写入新值到内存中
	if(WriteMemory(g_arList[0],iVal))
		printf("Write data success\n");
	

	CloseHandle(g_hProcess);


	return 0;
}

BOOL FindFirst(DWORD dwValue)
{
	const DWORD dwOneGB = 1024*1024*1024; //1GB
	const DWORD dwOnePage = 4*1024; //4KB

	if(g_hProcess == NULL)
		return FALSE;

	//查看操作系统类型,以决定开始地址
	DWORD dwBase;
	OSVERSIONINFO vi = {sizeof(vi)};
	GetVersionEx(&vi);
	if(vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
		dwBase = 4*1024*1024; //win98,4MB
	else
		dwBase = 640*1024;    //windows NT,64KB

	//从开始地址到2GB的地址空间进行查找
	for(;dwBase<2*dwOneGB;dwBase+=dwOnePage)
	{
		CompareAPage(dwBase,dwValue);
	}
	return TRUE;

}

void ShowList()
{
	for(int i=0;i<g_nListCnt;i++)
	{
		printf("0x%X\n",g_arList[i]);
	}
}

BOOL FindNext(DWORD dwValue)
{
	//保存m_arList数组中有效地址的个数,初始化新的m_nListCnt值
	int nOrgCnt = g_nListCnt;
	g_nListCnt = 0;

    BOOL bRet = FALSE; //假设失败
	DWORD dwReadValue;
	for(int i=0;i<nOrgCnt;i++)
	{
		if(ReadProcessMemory(g_hProcess,(LPVOID)g_arList[i],&dwReadValue,sizeof(DWORD),NULL))
		{
			if(dwReadValue == dwValue)
			{
				g_arList[g_nListCnt++] = g_arList[i];
				bRet = TRUE;
			}
		
		}
	}
	return bRet;

}

BOOL WriteMemory(DWORD dwAddr,DWORD dwValue)
{
	return WriteProcessMemory(g_hProcess,(LPVOID)dwAddr,&dwValue,sizeof(DWORD),NULL);
}

在这里插入图片描述

  • 将上述修改程序封装为类程序
#ifndef __MEMFINDER_H__
#define __MEMFINDER_H__

#include <windows.h>

class CMemFinder
{
public:
	CMemFinder(DWORD dwProcessId);
	virtual ~CMemFinder();

// 属性
public:
	BOOL IsFirst() const { return m_bFirst; }
	BOOL IsValid() const { return m_hProcess != NULL; }
	int GetListCount() const { return m_nListCnt; }
	DWORD operator [](int nIndex) { return m_arList[nIndex]; }

// 操作
	virtual BOOL FindFirst(DWORD dwValue);
	virtual BOOL FindNext(DWORD dwValue);
	virtual BOOL WriteMemory(DWORD dwAddr, DWORD dwValue);

// 实现
protected:
	virtual BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue);

	DWORD m_arList[1024];	// 地址列表
	int m_nListCnt;		// 有效地址的个数
	HANDLE m_hProcess;	// 目标进程句柄
	BOOL m_bFirst;		// 是不是第一次搜索
};


CMemFinder::CMemFinder(DWORD dwProcessId)
{
	m_nListCnt = 0;
	m_bFirst = TRUE;
	m_hProcess = ::OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, dwProcessId);
}

CMemFinder::~CMemFinder()
{
	if(m_hProcess != NULL)
		::CloseHandle(m_hProcess);
}

BOOL CMemFinder::FindFirst(DWORD dwValue)
{
	const DWORD dwOneGB = 1024*1024*1024;	// 1GB
	const DWORD dwOnePage = 4*1024;		// 4KB

	if(m_hProcess == NULL)
		return FALSE;
	
	// 查看操作系统类型,以决定开始地址
	DWORD dwBase;
	OSVERSIONINFO vi = { sizeof(vi) };
	::GetVersionEx(&vi);
	if (vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
		dwBase = 4*1024*1024;		// Windows 98系列,4MB	
	else
		dwBase = 640*1024;		// Windows NT系列,64KB

	// 在开始地址到2GB的地址空间进行查找
	for(; dwBase < 2*dwOneGB; dwBase += dwOnePage)
	{
		// 比较1页大小的内存
		CompareAPage(dwBase, dwValue);
	}

	m_bFirst = FALSE;

	return TRUE;
}

BOOL CMemFinder::CompareAPage(DWORD dwBaseAddr, DWORD dwValue)
{
	// 读取1页内存
	BYTE arBytes[4096];
	if(!::ReadProcessMemory(m_hProcess, (LPVOID)dwBaseAddr, arBytes, 4096, NULL))
		return FALSE;	// 此页不可读

	// 在这1页内存中查找
	DWORD* pdw;
	for(int i=0; i<(int)4*1024-3; i++)
	{
		pdw = (DWORD*)&arBytes[i];
		if(pdw[0] == dwValue)	// 等于要查找的值?
		{
			if(m_nListCnt >= 1024)
				return FALSE;
			// 添加到全局变量中
			m_arList[m_nListCnt++] = dwBaseAddr + i;
		}
	}

	return TRUE;
}

BOOL CMemFinder::FindNext(DWORD dwValue)
{
	// 保存m_arList数组中有效地址的个数,初始化新的m_nListCnt值
	int nOrgCnt = m_nListCnt;
	m_nListCnt = 0;	

	// 在m_arList数组记录的地址处查找
	BOOL bRet = FALSE;	// 假设失败	
	DWORD dwReadValue;
	for(int i=0; i<nOrgCnt; i++)
	{
		if(::ReadProcessMemory(m_hProcess, (LPVOID)m_arList[i], &dwReadValue, sizeof(DWORD), NULL))
		{
			if(dwReadValue == dwValue)
			{
				m_arList[m_nListCnt++] = m_arList[i];
				bRet = TRUE;
			}
		}
	}
	
	return bRet;
}

BOOL CMemFinder::WriteMemory(DWORD dwAddr, DWORD dwValue)
{
	return ::WriteProcessMemory(m_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);
}


#endif // __MEMFINDER_H__

调用的时候引用头文件即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值