从零开始学习破解(第一天)

发布于 2022-04-25  580 次阅读 百度未收录


最近闲来无事,饭也吃饱了。

ps:我一般就是记个随笔,不要当作教程来看,我会比较详细的记录研究过程以及遇到的问题,所以,可能中间有些东西会写错了,后面修改,也可能写错了我自己也不知道,欢迎指正。如果要动手,尽量看完再动手,默认所有人都有最基本的判断力。

准备搞搞研究研究破解相关的东西,首先声明,只做研究,不做坏坏的事情。

首先呢,了解到,破解和注入啥的有很大的相关性。

经过查资料,发现只要b把自己注入到其他进程中,就能做很多的事情。

那么下面,就尝试着写个dll,注入一下试试看。

vs新建工程,选择动态库,写上以下代码,没几行就不贴代码了。

然后尝试一下,怎么注入呢,下载个工具试试效果。工具直接搜索注入工具就有了。试试注入到vs中看看效果。

从下面的提示图标可以看出来,这个提示框已经是vs进程的了,图标都变了。

但是这个工具感觉不好用,我dll修改东西的时候老是提示我,文件被占用,又无法卸载dll。

那个注出按钮一点就崩溃。所以咱们自己写个注入工具吧。

经过百度和到处ctrl+c以及之前研究键盘hook新建一个控制台程序,代码如下。

// mxInject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
#include"tlhelp32.h"
#include<tchar.h>
#define DLLPath "DLL的存放位置"


int Inject(int pid)
{
	HANDLE hprocess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
	if (!hprocess)
	{
		std::cout << "can not get handle" << std::endl;
		return 1;
	}
	SIZE_T PathSize = (strlen(DLLPath) + 1) * sizeof(TCHAR);
	LPVOID StartAddress = VirtualAllocEx(hprocess, NULL, PathSize, MEM_COMMIT, PAGE_READWRITE);
	if (!StartAddress)
	{
		std::cout << "开辟内存失败" << std::endl;
		return 1;
	}
	if (!WriteProcessMemory(hprocess, StartAddress, DLLPath, PathSize, NULL))
	{
		std::cout << "无法写入DLL路径" << std::endl;
		return 1;
	}
	PTHREAD_START_ROUTINE pfnStartAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
	if (!pfnStartAddress)
	{
		std::cout << "无法获取函数地址" << std::endl;
		return 1;
	}
	HANDLE hThread = CreateRemoteThreadEx(hprocess, NULL, NULL, pfnStartAddress, StartAddress, NULL, NULL, NULL);

	if (!hThread)
	{
		std::cout << "创建线程失败" << std::endl;
		return 1;
	}
	//WaitForSingleObject(hThread, INFINITE);//等待DLL结束
	std::cout << "注入成功!\n";
	CloseHandle(hThread);
	CloseHandle(hprocess);
	return 0;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
	BOOL bMore = FALSE, bFound = FALSE;
	HANDLE hSnapshot, hProcess, hThread;
	HMODULE hModule = NULL;
	MODULEENTRY32 me = { sizeof(me) };
	LPTHREAD_START_ROUTINE pThreadProc;
	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
	bMore = Module32First(hSnapshot, &me);
	for (; bMore; bMore = Module32Next(hSnapshot, &me))
	{
		if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName))
		{
			bFound = TRUE;
			break;
		}
	}
	if (!bFound)
	{
		CloseHandle(hSnapshot);
		return FALSE;
	}
	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
	{
		_tprintf("OpenProcess(%d) failed!!! [%d]\n,", dwPID, GetLastError());
		return FALSE;
	}
	hModule = GetModuleHandle("Kernel32.dll");
	pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
	hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
	CloseHandle(hProcess);
	CloseHandle(hSnapshot);
	return TRUE;
}


LRESULT CALLBACK CallBackProc_keyboard(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode != HC_ACTION)
	{
		return CallNextHookEx(0, nCode, wParam, lParam);
	}
	if (wParam == WM_KEYDOWN || wParam == WM_KEYUP)
	{
		PKBDLLHOOKSTRUCT hookStruct = (PKBDLLHOOKSTRUCT)lParam;
		if (hookStruct->vkCode == VK_F3)
		{
			if (wParam == WM_KEYDOWN)
			{
				HWND hwnd = GetForegroundWindow();
				DWORD dwPID,dwTid;
				dwTid = GetWindowThreadProcessId(hwnd, &dwPID);
				printf("开始注入%d\n", dwPID);
				Inject(dwPID);
				printf("注入%d结束\n", dwPID);
			}
			return 1;
		}
		if (hookStruct->vkCode == VK_F4)
		{
			if (wParam == WM_KEYDOWN)
			{
				HWND hwnd = GetForegroundWindow();
				DWORD dwPID, dwTid;
				dwTid = GetWindowThreadProcessId(hwnd, &dwPID);
				printf("开始卸载dll%d\n", dwPID);
				EjectDll(dwPID,"remotecall.dll");
				printf("dll卸载%d结束\n", dwPID);
			}
			return 1;
		}
	}
	return CallNextHookEx(0, nCode, wParam, lParam);
}

int main() 
{
	SetWindowsHookEx(WH_KEYBOARD_LL, CallBackProc_keyboard, GetModuleHandle(nullptr), 0);
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}

F3注入 F4卸载。测试一下。

完全没有问题。既然已经可以打入敌人内部了,那么应该可以修改点内存啥的,就是怎么找内存呢。

从网上找资料,发现可以根据偏移来查找。

为了测试方便,咱们写个程序用来破解吧。

vs新建MFC工程,基于对话框,拖个控件。

写个onok

void CloginDlg::OnBnClickedOk()
{
	CString str;
	GetDlgItemText(IDC_EDIT1, str);
	if (str != _T("123"))
	{
		MessageBox("密码错误");
		return ;
	}
	// TODO: 在此添加控件通知处理程序代码
	CDialogEx::OnOK();
}

运行效果如下

然后试试能不能破解。下载个ce工具,这个全名叫Cheat Engine 直接百度就可以了。

扫描一下,发现不是绿色的,人家说绿色的才行。而且这个好像也不准。忽然发现代码中用的是字符串,这里用的是数值,修改成字符串再试试,

搜索出来两个,确认框点掉,发现就剩下一个了。
说明第二个就是咱们申请的临时变量。CString str;

第一个应该是控件中保存数据的变量,测试一下。

修改输入框内容,发现这个就跟着变化,这么就说明,这个变量是控件的变量,用来保存控件数据的,不过这好像也没啥用啊。需要有固定偏移,咱们用的都是动态申请的,所以搞个全局变量应该就好了,试试看。增加全局变量int m_nCount;并修改代码,记录密码错误的次数。

点击几次确定,然后搜索(别忘记了,修改成4字节的,因为这次咱们用的是int)

太多了,再点两下试试看。

这次就只剩下1个了,并且是绿色的

再点击两下,发现确实变化了。

那么记录下这个地址login.exe+0xBB3B58下面去修改dll代码

int OnInit()
{
    MessageBox(0, "成功潜入敌人内部!", "报告主人", NULL);
    HANDLE hand = GetModuleHandle("login.exe");
    if (!hand)
    {
        MessageBox(0, "查找主句柄失败!", "报告主人", NULL);
        return 0;
    }
    int noffset = 0XBB3B58;
    int* pCount = (int*)((DWORD)hand + noffset);
    *pCount = 15;
    return 0;
}

然后再F3注入试试看。可以看到这个值变成15了。

添加图片注释,不超

点击确定,也提示的16次

到这里第一步算是完成了,我们实现了进入敌人内部,并修改敌人的内存数据。

参考链接:残阳血:C++ DLL注入微信实现自动接收、发送消息


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。