c 写asm并将生成的16进制代码注入到目标程序的一次"失败"尝试
首先说明一下这是要干啥。
就是想对一个游戏进行代码注入,类似于ce那种代码注入,ce那里你可以直接写汇编,但是如果用c怎么实现写汇编呢?
其中一个思路就是,用c写 asm 的函数,这个函数会被翻译成16进制代码,然后我们找到生成的代码,读取出来,然后注入到目标游戏进程中即可。但是在实现的过程中发现,这个方法不是太方便。具体是在写带有地址的命令时,地址不是正确的地址。
例如:
mov [little_game_for_ce.exe+0x100000],100
这个命令在ce中写没有任何问题,因为ce知道little_game_for_ce.exe这个模块的基地址,直接算出来写进去就好,每次重新打开游戏,他也会自己算,不会出错。
但是如果在c的代码里边写这个,那么由于有这个不确定的 little_game_for_ce.exe 这个模块的地址,那么我们势必要使用一个变量 game_module_base_addr 存储这个地址,然后写出来的asm代码就张下面这个样子:
__asm{
mov [game_module_base_addr+0x100000],100
}
但是由于我们是要将这个生成的16进制代码注入到游戏进程,而不是自己使用,所以这个代码,不会被运行,在编译运行之后,这个命令不会被翻译成正确的地址
会被翻译成 game_module_base_addr 这个变量的地址,再加上偏移 0x100000。
所以这个翻译出来的命令中的地址是不对的。不能直接使用。
你要是不嫌麻烦,再写个函数后处理所有带地址的命令也可以,但感觉还是工作量太大了。
鉴于使用c来写代码注入的初衷是为了,写一些汇编不太好实现的逻辑,并不是说ce的汇编不好使或者想完全替代ce,那么还是建议先使用ce,写好汇编后,将16进制复制过来,标记好那些地址所在的16进制的位置,以字符串的形式存放,然后做后处理。emmm,也挺麻烦的,哈哈。
由于我不想写第一种方案所带来的巨大的代码量,所以我个人认为,是"失败"的。
但是写了一般还是写了一些以后可能用到的代码的,先记录在这里吧。
ce_inject.exe
// ce_inject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "functions.h"
uintptr_t code_address;
uintptr_t new_memory_address;
uintptr_t player_health_address;
extern "C"
{
void inject_code_function()
{
__asm {
mov [player_health_address],20
mov [player_health_address],10
}
}
void jmp_to_new_memory()
{
__asm {
jmp [new_memory_address]
}
}
void jmp_back()
{
__asm {
jmp [code_address]+9
}
}
}
int main()
{
string process_name = "little_game_for_ce.exe";
int pid = FindPID(process_name);
printf("%d\n", pid);
map<string, HMODULE> module_base_address_map;
get_all_module_base_address(pid, module_base_address_map);
string inject_module_name = "little_game_for_ce.exe";
HMODULE inject_module_base_address = NULL;
for (auto it = module_base_address_map.begin(); it != module_base_address_map.end(); it++)
{
int module_name_start = it->first.rfind("\\")+1;
string module_name = it->first.substr(module_name_start, it->first.size() - module_name_start);
if (module_name == inject_module_name)
{
inject_module_base_address = it->second;
break;
}
}
if (inject_module_base_address == NULL)
{
printf("module doesn't exsist!\n");
return 0;
}
player_health_address = (uintptr_t)inject_module_base_address + 0x8028;
// 保存会被影响的代码
code_address = (uintptr_t)inject_module_base_address + 0x14DE;
int old_code_size = 9;
char *old_code_data=NULL;
old_code_data = (char *)read_memory(pid, code_address, old_code_size);
if (old_code_data == NULL)
{
printf("read old_code_data failed!\n");
return -1;
}
// 申请新的内存
unsigned int new_memory_size = 24;
new_memory_address = (uintptr_t)process_alloc(pid, new_memory_size);
printf("new_memory_address is: %x\n", new_memory_address);
// 向新内存区写入新代码
unsigned int asm_code_size = 0;
char* asm_code = NULL;
asm_code = read_asm_function_code(_getpid(), inject_code_function, asm_code_size);
write_memory(pid, new_memory_address, asm_code, asm_code_size);
free(asm_code);
printf("%x\n", player_health_address);
printf("%x\n", &player_health_address);
//// 向新内存区写入保存的旧代码
//printf("old_code_data %x\n", old_code_data);
//write_memory(pid, new_memory_address + 10, old_code_data, 9);
//// 写入最后的jmp到原来的位置
//printf("jmp_back %x\n", (char*)jmp_back + 3);
//write_memory(pid, new_memory_address + 19, ((char*)jmp_back + 3), 5);
//// 注入跳转到新内存的jmp
//write_memory(pid, code_address, ((char*)jmp_to_new_memory + 3), 5);
free(old_code_data);
return 0;
}
functions.h
#pragma once
//
// Created by Amazing on 2021/12/2.
//
#ifndef CE_INJECT_FUNCTIONS_H
#define CE_INJECT_FUNCTIONS_H
#include <iostream>
#include <Windows.h>
#include <Tlhelp32.h>
#include <stdio.h>
#include <time.h>
#include <tchar.h>
#include <psapi.h>
#include <vector>
#include <map>
#include <conio.h>
#include <process.h>
using namespace std;
wstring string2wstring(string str);
string wstring2string(wstring wstr);
HANDLE get_handle(int pid);
unsigned int get_base_address(int pid);
int FindPID(string ProcessName);
uintptr_t calc_offsets(uintptr_t ptr, vector<uintptr_t> offsets);
LPVOID process_alloc(DWORD pid, unsigned int bytes);
bool get_all_module_base_address(DWORD pid, map<string, HMODULE>& module_base_address_map);
int* read_memory(DWORD pid, uintptr_t addr, int bytes);
bool write_memory(DWORD pid, uintptr_t addr, char* data, int bytes);
uintptr_t read_true_process_address(DWORD pid, void* p);
uintptr_t find_asm_code_start_offset(DWORD pid, uintptr_t start_address, uintptr_t max_search_size = 1024);
uintptr_t find_asm_code_end_offset(DWORD pid, uintptr_t start_address, uintptr_t max_search_size = 1024);
char* read_asm_function_code(DWORD pid, void* f, unsigned int& asm_code_size);
#endif //CE_INJECT_FUNCTIONS_H
functions.cpp
//
// Created by Amazing on 2021/12/2.
//
#include "functions.h"
#include <Windows.h>
//将string转换成wstring
wstring string2wstring(string str)
{
wstring result;
//获取缓冲区大小,并申请空间,缓冲区大小按字符计算
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
buffer[len] = '\0'; //添加字符串结尾
//删除缓冲区并返回值
result.append(buffer);
delete[] buffer;
return result;
}
//将wstring转换成string
string wstring2string(wstring wstr)
{
string result;
//获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的
int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
char* buffer = new char[len + 1];
//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);
buffer[len] = '\0';
//删除缓冲区并返回值
result.append(buffer);
delete[] buffer;
return result;
}
int FindPID(string ProcessName)
{
int pid = -1;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
cout << "CreateToolhelp32Snapshot Error!" << endl;;
return false;
}
BOOL bResult = Process32First(hProcessSnap, &pe32);
int num(0);
while (bResult)
{
if (wstring2string(wstring(pe32.szExeFile)) == ProcessName)
{
pid = pe32.th32ProcessID;
break;
}
bResult = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return pid;
}
HANDLE get_handle(int pid)
{
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
return handle;
}
uintptr_t calc_offsets(HANDLE handle, uintptr_t ptr, vector<uintptr_t> offsets)
{
uintptr_t addr = ptr;
uintptr_t t;
for (unsigned int i = 0; i != offsets.size(); i++)
{
// printf("%x %x\n",addr,offsets[i]);
addr += offsets[i];
// printf("%x\n",addr);
if (i < offsets.size() - 1)//最后一次只加偏移量,不用读取了
{
bool state = ReadProcessMemory(handle, (LPVOID)addr, &t, sizeof(t), 0);
if (!state)
{
// cout<<"error in reading memory!"<<endl;
addr = 0;
break;
}
addr = t;
}
}
return addr;
}
LPVOID process_alloc(DWORD pid, unsigned int bytes)
{
LPVOID virAddr = NULL;
HANDLE handle = get_handle(pid);
virAddr = VirtualAllocEx(handle, NULL, bytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
CloseHandle(handle);
return virAddr;
}
bool get_all_module_base_address(DWORD pid, map<string, HMODULE>& module_base_address_map)
{
module_base_address_map.clear();
bool flag = TRUE;
const int max_module_number = 1024;
HMODULE hMods[max_module_number];
HANDLE handle;
DWORD cbNeeded;
unsigned int i;
// Get a handle to the process.
handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (handle == NULL)flag = false;
if (flag != false)
{
// Get a list of all the modules in this process.
//if (EnumProcessModules(handle, hMods, sizeof(hMods), &cbNeeded))
//if (EnumProcessModulesEx(handle, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_32BIT))
//if (EnumProcessModulesEx(handle, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_64BIT))
//if (EnumProcessModulesEx(handle, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL)!=0)
if (EnumProcessModulesEx(handle, hMods, sizeof(hMods), &cbNeeded, (DWORD)(0x01 | 0x02)) != 0)
// if (EnumProcessModulesEx(handle, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_32BIT)!=0)
{
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
{
TCHAR szModName[MAX_PATH];
// Get the full path to the module's file.
if (GetModuleFileNameEx(handle, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
{
// Print the module name and handle value.
module_base_address_map[wstring2string(wstring(szModName))] = hMods[i];
}
}
}
// Release the handle to the process.
CloseHandle(handle);
}
return flag;
}
int* read_memory(DWORD pid, uintptr_t addr, int bytes)
{
HANDLE handle = get_handle(pid);
int* t = (int*)malloc(bytes);
bool state = ReadProcessMemory(handle, (LPVOID)addr, t, bytes, NULL);
if (!state)
{
free(t);
t = NULL;
}
CloseHandle(handle);
return t;
}
bool write_memory(DWORD pid, uintptr_t addr, char* data, int bytes)
{
HANDLE handle = get_handle(pid);
bool flag = WriteProcessMemory(handle, (LPVOID)addr, data, bytes, NULL);
CloseHandle(handle);
return flag;
}
uintptr_t read_true_process_address(DWORD pid, void* p)
{
uint8_t* jmp_content = (uint8_t*)read_memory(pid, (uintptr_t)p, 5);
// 去掉jmp命令的E9
jmp_content += 1;
// 拿到偏移
uint32_t offset = *(uint32_t*)jmp_content;
// 注意回收内存
jmp_content -= 1;
free(jmp_content);
uintptr_t true_address = (uintptr_t)p + 5 + offset;
return true_address;
}
uintptr_t find_asm_code_start_offset(DWORD pid, uintptr_t start_address, uintptr_t max_search_size)
{
uintptr_t offset = -1;
// 特征码
const unsigned int start_feature_size = 7;
uint8_t start_feature_code[start_feature_size] = { 0xb8,0xcc,0xcc,0xcc,0xcc,0xf3,0xab };
for (int i = 0; i != max_search_size; i++)
{
uint8_t* data = (uint8_t*)read_memory(pid, start_address + i, start_feature_size);
bool flag = TRUE;
for (int j = 0; j != start_feature_size; j++)
{
if (start_feature_code[j] != data[j])
{
flag = FALSE;
break;
}
}
free(data);
if (flag == TRUE)
{
offset = i;
break;
}
}
return offset + start_feature_size;
}
uintptr_t find_asm_code_end_offset(DWORD pid, uintptr_t start_address, uintptr_t max_search_size)
{
uintptr_t offset = -1;
// 特征码
const unsigned int end_feature_size = 11;
uint8_t end_feature_code[end_feature_size] = { 0x5f,0x5e,0x5b,0x81,0xc4,0xc0,0x00,0x00,0x00,0x3b,0xec };
for (int i = 0; i != max_search_size; i++)
{
uint8_t* data = (uint8_t*)read_memory(pid, start_address + i, end_feature_size);
bool flag = TRUE;
for (int j = 0; j != end_feature_size; j++)
{
if (end_feature_code[j] != data[j])
{
flag = FALSE;
break;
}
}
free(data);
if (flag == TRUE)
{
offset = i;
break;
}
}
return offset;
}
char* read_asm_function_code(DWORD pid, void* f, unsigned int& asm_code_size)
{
char* code_data = NULL;
uintptr_t true_process_address = read_true_process_address(pid, f);
uintptr_t start_offset = find_asm_code_start_offset(pid, true_process_address);
uintptr_t end_offset = find_asm_code_end_offset(pid, true_process_address);
asm_code_size = end_offset - start_offset;
code_data = (char*)read_memory(pid, true_process_address + start_offset, asm_code_size);
return code_data;
}
little_game_for_ce.exe
#include <iostream>
#include <stdio.h>
class Player
{
public:
int health = 100;
int weapon = 10;
Player();
Player(int health, int weapon);
};
Player::Player()
{
this->health=100;
this->weapon=10;
}
Player::Player(int health, int weapon)
{
this->health = health;
this->weapon = weapon;
}
Player computer(200, 20),player(100, 10);
int is_game_over()
{
int flag = 0;
if(computer.health<=0)flag = 1;
else if(player.health<=0)flag = 2;
return flag;
}
void print_state()
{
printf("computer health: %d, player health: %d\n", computer.health, player.health);
}
void computer_attack()
{
player.health-=computer.weapon;
}
void player_attack()
{
computer.health-=player.weapon;
}
void update_game()
{
computer_attack();
player_attack();
}
int main()
{
int result = is_game_over();
printf("%p\n", &player);
while(result==0)
{
print_state();
printf("enter to attack\n");
while(getchar()!='\n');
update_game();
result = is_game_over();
}
if(result==1)
{
printf("congratulations! you wine!\n");
}
else if(result==2)
{
printf("computer wine!\n");
}
else
{
printf("someone cheating failed?");
}
return 0;
}