标 题:连连看修改(golang)
作 者:dinger
时 间:2020-06-10
链 接:https://blog.csdn.net/man_45_start/article/details/106764981

背景

学习golang有一段时间了,找个事情来练一下,于是针对老婆常玩的连连看游戏做一个修改器。
游戏详情是:宠物连连看可爱版,图片是这样:

其中最重要的是洗牌次数,没有洗牌次数就game over了。因此针对洗牌次数做一个修改器。
说明下:如果实现这个目的,使用golang并不是最佳选择,更好的选择比如用VC,因此这个程序主要用于golang练手。

原理

利用数值及变化找出变量所在的内存地址,修改之。
约束1:变量的地址不变,(像VC编写的程序可以,像golang这种语言编写的程序,不一定行,因为有垃圾收集,会移动变量)
约束2:不加密,(意思是,你在界面上看到的就是内部的值)
约束3:无校验,(意思是,你可以修改,如果有校验,你必须同时修改校验值,但这很难办到)

代码

//修改连连看的洗牌次数
//博客展示版本package mainimport ("fmt""unsafe""golang.org/x/sys/windows""syscall""strings"
)func main() {fmt.Println("----start----")defer func() {fmt.Println("====end----")}()//打开连连看进程,获得进程句柄//建议使用IE浏览器,有些浏览器启动进程较多,不易判别是哪个进程//需要输入浏览器进程的PID(在任务管理器中查看)hp := inputPidOpenProcess()//输入PID,打开进程,得到进程句柄defer windows.CloseHandle(windows.Handle(hp))//多次输入数值,查找之,修改之var result []uintptr//保存含有指定数值的内存地址for {var val byteinputVal(&val, "input val:")//在进行内存中进行数值查找old := resultresult = memFindByte(hp, val)fmt.Printf("    find ok, count=%d\n", len(result))if len(old) > 0 {//不是第一次查找,进行相同地址的匹配result = getSame(old, result)//返回的result在执行后是一个新的地址,调用没问题fmt.Printf("    do getSame, count=%d\n", len(result))}if 1 == len(result) {//只有一个地址,修改一个字节的数值为99fmt.Printf("    only one addr:%X, %d\n", result[0], result[0])if !myWritePMOneByte(hp, result[0], 99) {myMsgBox("myWritePMOneByte fail")break}myMsgBox("change val to 99, ok")break}if 0 == len(result) {//结果为空,表示没找到,(可能是进程不对,或哪个数值输入的不对,或其他原因)myMsgBox("no addr, end")break}}
}//输入一个整数值
//prompt为输入前的提示
func inputVal(v interface{}, prompt string) {for {fmt.Println(prompt)_, err := fmt.Scanf("%d", v)//消除unexpected newline等错误,win10平台for {var s stringif n, _ := fmt.Scanf("%s", &s); n>0 {continue}break}if err != nil {fmt.Println("intput error, please input again")continue}break}
}//输入PID,打开进程,返回句柄
func inputPidOpenProcess() uintptr {for {var pid uint32inputVal(&pid, "input pid of ie:")hp, err := windows.OpenProcess(PROCESS_ALL_ACCESS, false, pid)if err != nil {fmt.Println("pid ok, but OpenProcess fail, please input again")continue}return uintptr(hp)}
}//在内存中查找指定的字节值,返回地址集合
func memFindByte(hp uintptr, v byte) (result []uintptr) {var addr uintptrfor {var mbi MBI64if !myVQuery(hp, addr, &mbi) {break}addr += uintptr(mbi.RegionSize)//过滤mbi,关注的数不可能出现的内存块不进行数值查找if MEM_COMMIT!=mbi.State || PAGE_READWRITE!=mbi.Protect || MEM_PRIVATE!=mbi.Type{continue}//测试过,性能影响很小,因此每次都分配释放,否则只分配一次mem, err := windows.VirtualAlloc(0, uintptr(mbi.RegionSize), MEM_COMMIT, PAGE_READWRITE)if err != nil {fmt.Printf("in memFindByte, call VirtualAlloc fail\n")break}defer func () {if err := windows.VirtualFree(mem, 0, MEM_RELEASE); err != nil {fmt.Printf("in memFindByte, call VirtualFree fail\n")}}()//fmt.Printf("do VirtualAlloc, mem=%X\n", mem)if !myReadPM(hp, mbi.BaseAddress, mbi.RegionSize, mem) {fmt.Printf("in memFindByte, call myReadPM fail\n")break}//经测试,关注的数在内存中按4个字节存储,且都是小头(意思是字节序相同)for i:=0; i<int(mbi.RegionSize)/4; i++ {if uint32(v) != *(*uint32)(unsafe.Pointer(mem + uintptr(i)*4)) {continue}result = append(result, mbi.BaseAddress + uintptr(i)*4)}}return
}//在两个地址列表中查找相同的项目,返回之
func getSame(a []uintptr, b []uintptr) (result []uintptr) {var i, j intfor {if a[i] == b[j] {result = append(result, a[i])i++j++} else if a[i] < b[j] {i++} else {j++}if i>=len(a) || j>=len(b) {return}}
}//----win32函数相关const (MB_OK = 0PROCESS_ALL_ACCESS = 0x1F0FFFPAGE_NOACCESS = 0x1PAGE_READWRITE = 0x04MEM_COMMIT = 0x1000MEM_RELEASE = 0x8000MEM_PRIVATE = 0x20000
)//_MEMORY_BASIC_INFORMATION64
type MBI64 struct {BaseAddress uintptrAllocationBase uintptrAllocationProtect uint32__alignment1 uint32RegionSize uint64State uint32Protect uint32Type uint32__alignment2 uint32
}//size=48var win32Funcs = make(map[string]*windows.LazyProc)func init() {// Librarylibkernel32 := windows.NewLazySystemDLL("kernel32.dll")libuser32 := windows.NewLazySystemDLL("user32.dll")win32Funcs["VirtualQueryEx"] = libkernel32.NewProc("VirtualQueryEx")win32Funcs["ReadProcessMemory"] = libkernel32.NewProc("ReadProcessMemory")win32Funcs["WriteProcessMemory"] = libkernel32.NewProc("WriteProcessMemory")win32Funcs["MessageBox"] = libuser32.NewProc("MessageBoxW")
}//win32函数VirtualQueryEx的封装,(只支持64位,无需输入参数dwLength,返回成功与否)
func myVQuery(hp uintptr, addr uintptr, mbi *MBI64) bool {n, _, _ := win32Funcs["VirtualQueryEx"].Call(hp, addr, uintptr(unsafe.Pointer(mbi)), 48)return 48 == n
}//win32函数ReadProcessMemory的封装,(无需输入参数nRead,返回成功与否)
func myReadPM(hp uintptr, addr uintptr, size uint64, buf uintptr) bool {var n uint64ret, _, _ := win32Funcs["ReadProcessMemory"].Call(hp, addr, buf, uintptr(size), uintptr(unsafe.Pointer(&n)))return 0!=ret && n==size
}//调用win32函数WriteProcessMemory,写一个字节
//输入字节值,输出成功与否
func myWritePMOneByte(hp uintptr, addr uintptr, v byte) bool {ret, _, _ := win32Funcs["WriteProcessMemory"].Call(hp, addr, uintptr(unsafe.Pointer(&v)), 1)return 0 != ret
}//win32函数MessageBox的封装,简化输入参数
//使用myMsgBox的目的是为了消除闪退
func myMsgBox(s string) {var lpText *uint16 = syscall.StringToUTF16Ptr(strings.ReplaceAll(s, "\x00", "␀"))win32Funcs["MessageBox"].Call(0,uintptr(unsafe.Pointer(lpText)),uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("information"))),MB_OK,)
}

代码说明

正常运行的流程
输入连连看进程的PID,打开进程
输入数值,在内存中查找,记录地址,
再次输入数值,在内存中查找,暂存地址,
找出两个地址序列中相同项,记录之
当只有一个地址时,这个地址就找到了,修改地址处的值

存在的问题

利用Scanf来进行数值输入,需要面对一次输入,多次读取都有内容的情况(一般情况都不是所要的),本程序利用“按字符串识别直到没有内容”的方法不一定适用所有情况(常见的情况,测试没发现问题)

连连看修改(golang)相关推荐

  1. Golang中的自动伸缩和自防御设计

    Raygun服务由许多活动组件构成,每个组件用于特定的任务.其中一个模块是用Golang编写的,负责对iOS崩溃报告进行处理.简而言之,它接受本机iOS崩溃报告,查找相关的dSYM文件,并生成开发者可 ...

  2. gophp解释器_【干货】Gisp 解释器 Golang 辅助开发工具

    Gisp 是一个提供给 golang 使用的 Lisp 类 DSL 解释器.在 Lisp 的基本语法基础上,针对 go 环境稍作了一点语法糖.主要目标是提供一个尽可能便于与 golang 互操作的微型 ...

  3. golang操作mongodb的驱动mongo-go-driver的事务支持和访问控制(mongodb4.0)

    关注公众号 风色年代(itfantasycc) 300G微服务资料等你拿! 作者: sdghchj 本文链接:golang操作mongodb的驱动mongo-go-driver的事务支持和访问控制_s ...

  4. golang 官方依赖管理工具 dep 使用和持续集成

    介绍 go dep 依赖管理工具是为应用管理代码的,go get是为GOPATH管理代码的 官方地址 官方说明为啥要统一依赖管理 dep 需要在Go 1.7及更高的版本中使用 安装 本文使用 gola ...

  5. Golang Iris Websocket 跨域问题

    问题描述 在尝试使用iris中的websocket搭建一个实时通讯聊天的demo时,出现一个Upgrade Error错误,一个跨域问题,网上说要修改golang中的websocket upgrade ...

  6. GoLang笔记—基础语法篇

    GoLang笔记 Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态类型.编译型语言.Go 语言语法与 C ...

  7. 为什么 Kubernetes 变得如此流行(2020版)

    在写本文的时候,Kubernetes已经有6年左右的历史了,在过去两年中,Kubernetes的知名度不断上升,成为工程师最爱的平台之一.今年,它被列为最受欢迎的平台的第三位.如果你还没听说过Kube ...

  8. notepad++ smali语法高亮模板分享

    某论坛也有,但是太难看了, 前面介绍了一些工具可以反编译dex文件为smali文件,在Android程序逆向分析中,阅读smali代码已然是十分重要的,但各种代码编辑器都无法较好的支持smali文件的 ...

  9. golang 代码实现 修改配置文件

    我们在使用golang的场景中,经常会有修改某个配置文件的需求,那么怎么处理呢? 下面我用一个例子给大家演示一下: 现在,我有一个连接数据库的配置文件如下(配置文件路径为ConfigPath): [C ...

最新文章

  1. WIF(Windows Identity Foundation) 被动联合身份验证过程详解
  2. 怎么快速了解自己的MySQL服务器?
  3. 虚拟桌面的备份恢复最佳实践 第二部分
  4. mysql where varchar_MySQL数据库之MySQL索引使用:字段为varchar类型时,条件要使用''包起来...
  5. Java内存管理(一)--内存分区
  6. 2008.05.21 下午茶in萨贝尔
  7. 该虚拟机似乎正在使用中。 如果该虚拟机未在使用,请按“获取所有权(T)”按钮获取它的所有权。否则,请按“取消(C)”按钮以防损坏。 配置文件: D:\instractPath\Developmen
  8. C++笔记---函数声明(prototype)
  9. 不等式解集怎么取_6.初中数学:一个不等式的解集,都是另一个不等式的解,求a的取值范围?...
  10. numpy 全部笔记的思维导图精简记忆版
  11. 可视化建站cms_帝国CMS教程 | 01.系统运行环境及简介
  12. 标准差 php,标准偏差怎么算
  13. python pip安装包导入导出及下载包(只下载不安装)
  14. Spring batch批量处理框架最佳实践
  15. 版本管理工具-Git
  16. 你知道吗?除了迅雷,这几款下载神器也不错!
  17. 上台阶问题:一个人上台阶,一次可以走1、2、3步,问n个台阶有多少种走法?
  18. 光学指纹模组解锁方案设计指纹锁方案
  19. java + nginx + ffmpeg + vue实现摄像头,rtmp、rtsp直播流协议的实时播放
  20. 求1到50中7的倍数之和

热门文章

  1. 程序人生-hello`s P2P
  2. 苹果手机10秒解除锁屏_忘记苹果锁屏密码10秒解决 音量键选择wipedata/
  3. C语言关于输入某天日期求是本年第几天(计算天数)
  4. Java反射获取类,方法
  5. 理解假设检验: 统计学意义上的显著性水平 (Alpha) 和P值
  6. 怎样写出优秀的的研究计划 (Research Proposal) ?
  7. 基于Token的WEB后台认证机制
  8. 数据库系统原理实验一:关系数据库标准语言SQL
  9. 多个lmg在盒子里在左浮动( float: left)时候出现横向图片中间有缝隙
  10. 年仅28岁的程序员郭宇,宣布从字节跳动辞职,实现财富自由!