c 代码注入

说白了就还是readmemory和writememory,之前改数据是去改堆栈区,现在只不过是去改代码区。

用来测试的小游戏

一个用来测试代码注入的小游戏:

little_game_for_ce:

#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;
}

编译运行起来

然后使用ce找到player,computer的血量。

然后使用 找出是什么改写了这个地址,可以找到减少玩家血量的这个代码位置

可知sub edx,eax 这段代码对应的16进制是 0x29C2.

最简单的方法,我们把sub改成add

可以发现代码add,edx,eax对应的16进制是 0x01C2

最简单的直接修改代码

那么我们使用c进行代码注入最简单的思路就是在 little_game_for_ce.exe+14DE 这个位置,写入两个字节 0x01C2

简单版本的代码注入:

注意下面的代码要使用visual studio编译运行。

ce_inject.cpp

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

#include "functions.h"

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;
    }

    // little_game_for_ce.exe+14DE
    uintptr_t code_address = (uintptr_t)inject_module_base_address + 0x14DE;
    int inject_bytes = 2;
    uint8_t inject_code[2] = {0x01, 0xC2};

    bool flag = write_memory(pid, code_address, (char *)inject_code, inject_bytes);
    printf("%d\n", flag);

    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>


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(HANDLE handle, 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);



#endif //CE_INJECT_FUNCTIONS_H

function.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(HANDLE handle, unsigned int bytes)
{
    LPVOID virAddr = NULL;
    virAddr = VirtualAllocEx(handle, NULL, bytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    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;
}

但是注入代码往往不止这么简单,需要注入好几行,那么我们就需要使用jmp命令了,jmp命令需要占用5个字节,那么就需要在注入代码的那个位置先保存好会被jmp命令影响到的代码,然后去在进程中申请一块内存,jmp到新申请的内存,写入注入的代码,再写入之前保存的那些被jmp影响了的代码,然后再jmp回去。

示意图:

那么现在假设,我们不将sub edx,eax改成add edx, eax

而是给自己奶两次:

mov [00408028],#100
mov [00408028],#200

可知,这次就需要用jmp了,两次奶需要10个字节,旧的代码需要9个字节,跳转回去的jmp需要5个字节,那么一共需要10+9+5=24个字节。

我们申请24个字节的新内存。

汇编代码对应的16进制代码,可以使用c写asm代码,然后读取生成的代码,然后对其中涉及到的地址做后处理,但是比较麻烦。我有一篇博客尝试过:"c 写asm并将生成的16进制代码注入到目标程序的一次"失败"尝试"

ce写好然后复制16进制代码仅后处理地址

由于也不打算完全脱离ce,仅仅是实现ce的代码注入部分,那么其实可以先使用ce写好汇编代码,然后把他的16进制当作字符串直接复制过来,然后仅仅做地址的后处理。

下面是一个简单的例子,其中该的代码是把sub改成add,但是也是先申请新内存,把新代码搞过去,然后跳转回来

ce_inject.cpp

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

#include "functions.h"


uintptr_t code_address;
uintptr_t new_memory_address;
uintptr_t player_health_address;

inline int Hexchar2int(char c)
{
    int ans = 0;
    if (c >= '0' && c <= '9')ans = c - '0';
    else ans = c - 'A' + 10;
    if (ans > 32)ans -= 32;
    return ans;
}

inline char Hexint2char(int t)
{
    char c = '0';
    if (t >= 0 && t <= 9)c = t+'0';
    else c = t - 10 + 'A';
    return c;
}

uint8_t* HexCodeString2uint8t(string HexCodeString)
{
    int t = 0;
    int cnt = 0;
    int size = HexCodeString.size() / 3 + !(HexCodeString.size() % 3 == 0);
    uint8_t* data = (uint8_t*)malloc(size);
    for (int i = 0; i != HexCodeString.size(); i++)
    {
        if (HexCodeString[i] == ' ')
        {
            data[cnt] = t; t = 0; cnt++;
        }
        else
        {
            t <<= 4;
            t += Hexchar2int(HexCodeString[i]);
        }
    }
    data[cnt] = t;
    return data;
}

string offset2String(uintptr_t offset)
{
    string ans = "";
    for (int i = 0; i != 4; i++)
    {
        int t = offset & 0xFF;
        if (i != 0)ans += " ";
        ans += Hexint2char(t >> 4);
        ans += Hexint2char(t & 0xF);
        offset >>= 8;
    }
    return ans;
}

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;

    // 注入跳转到新内存的jmp
    uintptr_t offset = 0;
    string offset_string = "";
    string new_code_string = "01 C2 8B C2 A3 28 80 40 00 E9 00 00 00 00";
    unsigned int new_code_size = new_code_string.size() / 3 + !(new_code_string.size() % 3 == 0);
    printf("%d\n", new_code_size);
    string inject_code_string = "E9 00 00 00 00 0F 1F 40 00";
    unsigned int inject_code_size = inject_code_string.size() / 3 + !(inject_code_string.size() % 3 == 0);

    // 申请新的内存
    new_memory_address = (uintptr_t)process_alloc(pid, new_code_size);
    printf("new_memory_address is: %x\n", new_memory_address);


    // 向新内存区写入新代码
    offset = (code_address + inject_code_size) - (new_memory_address + new_code_size);
    offset_string = offset2String(offset);

    new_code_string = "01 C2 8B C2 A3 28 80 40 00 E9 "+ offset_string;
    uint8_t* new_code_data = HexCodeString2uint8t(new_code_string);
    write_memory(pid, new_memory_address, (char*)new_code_data, new_code_size);
    free(new_code_data);

    // 注入跳转到新内存的jmp
    offset = new_memory_address - (code_address + 5);
    offset_string = offset2String(offset);

    inject_code_string = "E9 "+ offset_string +" 0F 1F 40 00";
    uint8_t* inject_code_data = HexCodeString2uint8t(inject_code_string);
    write_memory(pid, code_address, (char*)inject_code_data, inject_code_size);
    free(inject_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, unsigned int bytes);
bool write_memory(DWORD pid, uintptr_t addr, char* data, int bytes);


#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, unsigned 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;
}

文章目录