Author:linshao

V公众

目录

            一.目的

            二. 相关要求

            三.具体方法

一、目的:

        通过开发简单游戏辅助来加深对windows api操作,以及程序内存的理解

二、相关要求:

        》》1.golang编程基础/C++基础

        》》2.简单理解PE在内存中的知识,基址(BaseAddress),偏移(RVA)

        》》3.windows Api

        》》4.一颗好用的脑子

三、具体方法

0x01.准备工作:

工具:

         》》go

         》》Cheat Engine 7.4(CE神器)

         》》idea++(写程序用)

游戏:单机游戏:Dissonance(steam上下载)

0x02.查找偏移

进入游戏

ce打开进程

进行扫描,我是知道值是4字节所以为了快速记录直接搜索了

查找游戏基质

多捡几个塑料瓶子,方便没血了喝。(诺,就这个玩意):

去找这个漩涡,手电照一下它就有伤害性了,别问我为什么不去找怪,那玩意把握不住死了就白忙活了

像这样

对着血条搜索,不行了就嗑药,

先首次扫描,再掉血,去搜索

最终找到少量数据,右键添加到地址栏

选中几个右键锁定,再次尝试发现不掉血了就对了,那血量的地址就在这几个之中,

分别锁定几个地址后找到唯一能有效锁定血量的值

对唯一地址进行指针扫描,找到偏移路径

扫描结果。这些都是基址经过偏移量到达血量的路径

这个地址可能会有重启游戏就不能用的,所以需要筛选,操作如下:

重启游戏后再次找到血量地址,使用这个指针文件去扫描,然后筛选血量值

如果有一样的,那说明这个链路可以使用。

重启游戏,ce打开该进程,点击查看内存->工具->指针扫描

打开之前保存的文件进行扫描

扫描后可以看到最后一列有些是空值,那就是失效的指针

像红框的那种有数值且很多是一样的,那就是血量的地址的值,选择一条单击后添加到地址栏

可以修改为其他值看血量有无变化,有变化说明对了

然后双击值复制它,便于后面过滤无效指针

点击工具栏的Point Scanner->Rescan memory->value=1来过滤所有值为272549262的指针,这6604条指针偏移路径都是可以使用

回到主界面,记录一下偏移(RVA相对虚拟地址),

获取到有效的地址偏移后就可以开始写程序了

C++会更简单,但是我有写过go的枚举pe模块基址的程序,代码拿来就用 ,多好哈哈哈

0x03.实现思路:

1.先通过执行wmic命令获取游戏的pid(ProcessId)

2.调用windows的OpenProcess函数打开目标进程,获取返回到的句柄

3.调用EnumProcessModules函数枚举目标进程的模块,查找ResonanceEAE-Win64-Shipping.exe并返回它的地址

4.获取到基址后,使用它加上之前得到的偏移路径,一步一步相加获取到最终角色的血量的真实地址

5.调用ReadProcessMemory函数读取该地址就可以获取到角色血量

6.调用WriteProcessMemory函数修改该地址

7.在程序实现中可以:开子线程来循环读取血量的值有无改变。有变化就修改回来,就达到了锁血的功能

主要windows Api

码子在文末,注意编译最好不要命令行go build main.go,尽量使用ide工具编译,

不然就会这样:

看一下效果,其他功能什么飞天遁地呀可以自己花时间去找偏移(我找的时候从第3关,穿越到了第6关哈哈哈)

0x04.代码:

package main

import (
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"
	"unsafe"
)

type obj struct {
	address int
}

var (
	flag1 int
	flag2=0

	kernel32=syscall.MustLoadDLL("kernel32.dll")
	OpenProcess=kernel32.MustFindProc("OpenProcess")
	ReadProcessMemory=kernel32.MustFindProc("ReadProcessMemory")
	WriteProcessMemory=kernel32.MustFindProc("WriteProcessMemory") //写入内存
	Psapi=syscall.MustLoadDLL("Psapi.dll")
	EnumProcessModules =Psapi.MustFindProc("EnumProcessModules")
	GetModuleBaseNameA=Psapi.MustFindProc("GetModuleBaseNameA")
)

func main(){

	var pid int64
	var modelName string
	modelName="ResonanceEAE-Win64-Shipping.exe"

	pid=getProcPid(modelName)
	fmt.Println(pid)
	//打开进程句柄
	handle:=getModelHandle(pid)
	//fmt.Println("句柄",handle)
	if handle<=0{fmt.Printf(" 打开句柄失败",handle)
	return}

	//获取模块基址
	BaseAddress:=GetProcessMoudleBase(handle,modelName)
	//fmt.Printf("[+] 基址%X\n",BaseAddress)

	fmt.Printf("\n************************************************\n")
	fmt.Printf("\n\t\t==linshao--功能面板==\t\t")

	RVA2:=[...]int64{0x04D98A40,0x118,0xA0,0x2B0,0x20,0x1A0,0x90,0xB8}
	//RVA2:=[...]int64{0x04CF3798,0x68,0x110,0xE0,0x260,0x1A0,0x90,0xB8}
	//其他偏移路径
	// 04CF3798 120 110 F8 20 1A0 90 B8
	// 04DB5400 400 8 78 A0 1A0 90 B8
	xueAddr,xueValue:=readXue(handle,BaseAddress,RVA2)
	fmt.Printf("[!]当前游戏角色血量--->%d(%f%%)\n",xueValue,(float32(xueValue)/1120403456)*100)

	for true{
		fmt.Printf("1开启/0关闭锁血/2退出->")
		var gn int
		fmt.Scanln(&gn)
		if gn==1{
			flag2=1
			go suoxie(handle,xueAddr,xueValue)
			if flag1==1{
				fmt.Printf("----------》锁满血成功\n")
			}
		}
		if gn==0 {
			flag2=0
		}
		if gn==2{
			return
		}
		fmt.Printf("----------------------\n")
		fmt.Printf("当前信息:\n")
		if flag2==0{
			fmt.Printf("锁血[关闭]\n")
		}
		if flag2==1{
			fmt.Printf("锁血[开启]\n")
		}
		fmt.Printf("----------------------\n")
	}


	}

func suoxie(handle uintptr,xueAddr int64,xueValue int){
	defer func() {
		err:=recover()
		if err!=nil{
			fmt.Println("成功捕获一个异常")
		}
	}()
	for flag2!=0{
		value:=0
		ReadProcessMemory.Call(handle, uintptr(xueAddr),uintptr(unsafe.Pointer(&value)),unsafe.Sizeof(value),0)
		if value!=1272549262{
			xueValue=1272549262
			a,_,_:=WriteProcessMemory.Call(handle,uintptr(xueAddr),uintptr(unsafe.Pointer(&xueValue)),unsafe.Sizeof(xueValue))
			if a>0{
				flag1=1
			}
		}
		time.Sleep(100*time.Millisecond)
	}

}
func readXue(handle uintptr,BaseAddress int64,RVA2 [8]int64) (int64,int){


	fmt.Printf("\n************************************************\n")
	var CurrentAddress int64
	//fmt.Println("模块名",modelName)
	fmt.Printf("[*] 使用的RVA偏移量链 -》%X\n",RVA2)
	CurrentAddress=BaseAddress
	var value int
	var tmp64 int64
	for _,x:=range RVA2{
		value=0
		tmp64=CurrentAddress+x
		ReadProcessMemory.Call(handle, uintptr(tmp64),uintptr(unsafe.Pointer(&value)),unsafe.Sizeof(value),0)
		fmt.Printf("访问0x%X >> (RVA) %X -> (0x%X) %X\n",CurrentAddress,x,tmp64,value)
		CurrentAddress=int64(value) //把偏移后获取到的实际地址值放入下一次循环计算的基地址
	}
	return tmp64,value
}
func GetProcessMoudleBase( hProcess uintptr, moduleName string)int64{
	//异常处理
	defer func() {
		//捕获异常
		err := recover()
		if err != nil {
			fmt.Println("[!]发生异常")
		}
	}()
	// 遍历进程模块,
	var hModel =[10000]int64{0}
	var lpcbNeeded int=0 //将所有模块句柄存储在 lphModule 数组中所需的字节数
	var cb= int(unsafe.Sizeof(hModel))  //lphModule 数组的大小,以字节为单位。
	isok,_,_:=EnumProcessModules .Call(hProcess, uintptr(unsafe.Pointer(&hModel)), uintptr(cb), uintptr(unsafe.Pointer(&lpcbNeeded)))
	num:=lpcbNeeded/int(unsafe.Sizeof(hModel[0]))
	//fmt.Println("----------lpcbNeeded所需字节",lpcbNeeded,"当前",cb,"需要长度",num)
	if isok<=0 {
		fmt.Println("[!] 枚举模块失败")
	}
	fmt.Printf("[+] 枚举进程模块成功,共%d个\n",num)//,hModel)
	tmp:=[50]byte{}
	a:=""
	for i:=0;i<num;i++{
		GetModuleBaseNameA.Call(hProcess, uintptr(hModel[i]),uintptr(unsafe.Pointer(&tmp)),50)
		for _,v:=range tmp{
			if(v==0){continue}else {a+=string(v)}
		}
		if(strings.EqualFold(moduleName, a)) {
			fmt.Printf("[+] find! 模块名字%s  地址:0x%X\n", a, hModel[i])
			return int64(hModel[i])
		}
		fmt.Printf(" > %s  \t--->: 0x%X",a, hModel[i])
		fmt.Printf(" \n ")
		a=""
		tmp=[50]byte{}
	}


	return 0
}

func getProcPid(PROCESS string)int64{
	task:=exec.Command("cmd","/c","wmic", "process", "get", "name,","ProcessId","|","findstr",PROCESS)
	data2, _ := task.CombinedOutput()
	res:=strings.Split(string(data2),"\n")[0]//取第一行程序结果
	re := regexp.MustCompile("[0-9]+")
	pid,_:=strconv.ParseInt(re.FindAllString(res,-1)[1],10,64)
	return pid
}

func getModelHandle(_pid int64)uintptr{
	hand,_, err :=OpenProcess.Call(2097151, uintptr(0), uintptr(_pid))
	fmt.Println("[+] 打开目标进程成功",hand)
	checkErr(err)
	return hand
}
func checkErr(err error){
	if err!=nil {
		if err.Error() != "The operation completed successfully." {
			fmt.Println("报错:",err.Error())
			syscall.Exit(1)
			return
		}
	}
}

不来一下子??

 V