用Go语言写个外挂(上)
用Go语言写一个Windows的外挂
转 http://www.jianshu.com/p/1b8efb1bc3c0?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#
本人在一家互联网金融公司上班,对于一家互联网金融公司,最基本的功能就是客户入金和出金,而出金的稳定性是很重要的,出金不畅容易导致投资人恐慌,本文讲的是出金,出金接口我们对接的是招商银行的银企直联系统,那么银企直连系统是一个什么样的程序呢?
![](http://upload-images.jianshu.io/upload_images/1077703-e19df7a0b3093922.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
没错,这个程序是运行在Windows上的,并且需要插入USBKey才能正常工作,这就意味着,不能简单的使用命令行进行运维管理。
看到这里,做运维的同学的内心应该和我一样是崩溃的。。
![](http://upload-images.jianshu.io/upload_images/1077703-a67a70eccfaef9c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
跟大家解释一下,这个服务是做什么的,大家可以把这个程序当成是我们的业务系统和招商银行沟通的信使,所有出金操作、查询操作都是通过这个信使来完成。
由于各种未知的原因,比如网络不稳定,或者USBKey插入时间过长产生了一些莫名其妙的错误,那么就需要人工去重启一下服务或重新登录一下账号,而且,这个工作有时候是在夜间操作的,这相当于要24小时待命啊,虽然故障频率不高,但这根弦始终是崩着的,这简直就是在破坏我的幸福美好生活啊。
![](http://upload-images.jianshu.io/upload_images/1077703-f5316433336d8863.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这种体力活的事情,我坚决不能干,所以一定要交给别人干。
![](http://upload-images.jianshu.io/upload_images/1077703-0b33a887b35f3918.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
别想多了,【别人】也只能是个外挂而已,谁都不喜欢干这种人肉体力活。
所以凭借着我18岁那年的开发经验,脑子里想到了 Windows 的消息模型,使用 SendMessage
给对应的窗体控件句柄发送特定的事件不就搞定了么,异常自动重启使用 CreateProcess
不就行了吗?
天真的我脑子里已经充满了 SendMessage
的语句
LRESULT WINAPI SendMessage(_In_ HWND hWnd,_In_ UINT Msg,_In_ WPARAM wParam,_In_ LPARAM lParam );
有木有很熟悉的样子,惊不惊喜,开不开心?是不是感觉发送键盘点击事件、鼠标点击事件就OK了?
后面会讲到,其实还需要很多工作才能完成一个比较完善可用的外挂软件,
SendMessage
基本上只能解决一部分问题
然而当我想完这些代码后,感觉还是太麻烦,因为按键精灵这类软件就能解决,为什么还要自己亲自操刀?不过最终放弃了这种念头,因为这是一个很重要的服务,说不定在未来会掌握好 几千个亿
的资金命运,如果安装了不明软件,资金安全如何得以保障???绝对不能这么草草的做这种决定,所以还是决定老老实实的撸代码了。。。
用什么语言是个问题,在Windows上可以使用 C++
, C#
系列,而且C#
我记得有一个automation框架可以完成类似的操作,不过本人最近这3年一直在使用 golang
,前两种语言目前也只是偶尔用用的节奏,所以基本处于手生的状态,而 golang
本身也支持使用 syscall
来调用 windows
的 DLL
(动态链接库),所以果断使用 golang
, 因为这个外挂大部分的WinAPI都在 user32.dll
和 kernel32.dll
里,我们只需要能加载这几个DLL
就可以调用强大的 WinAPI
了
![](http://upload-images.jianshu.io/upload_images/1077703-cdac407079beaa2c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
大家可以使用 PE Explorer 查看一个DLL有哪些输出函数
var (moduser32 = syscall.NewLazyDLL("user32.dll")procSendMessage = moduser32.NewProc("SendMessageW")procPostMessage = moduser32.NewProc("PostMessageW")
)func SendMessage(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { ret, _, _ := procSendMessage.Call( uintptr(hwnd), uintptr(msg), wParam, lParam) return ret } ...
大家可以看到,在这里我们使用的是SendMessageW
,而不是SendMessageA
,因为go
语言底层调用DLL
接口时,传入的是utf16
,看看下面的代码就明白了
func SetWindowText(hwnd HWND, text string) { procSetWindowText.Call( uintptr(hwnd), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text)))) }
这是一个设置窗体标题的API,第一个参数是窗体句柄,第二个参数大家可以看到,是将go语言的字符串转换成UTF16格式,并获取其指针。
另外值得注意的是,如果我们编译出来的程序是32位
的,那么尽量不要用来作为64位
程序的外挂,因为有很多复杂一点的功能无法实现,后续会提到这个部分,银企直连
这个服务是32位的,因此我们的go语言也是安装的32位的,同时为了更好的编译测试,我的虚拟机装的是 Win2008 R2 32位
操作系统
那么我们应该如何向一个窗体发送消息呢?能不能先做实验,不写代码呢?答案是肯定的,我们先请出我们的神器,Spy++
![](http://upload-images.jianshu.io/upload_images/1077703-50061283ce289cc4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
将瞄准器拖拽到具体的窗口上,就会得到窗口的句柄,我们可以通过 FindWindowW 或 EnumChildWindows 来实现相同的功能
银企直连正常工作需要两个步骤
- 启动HTTP服务监听
- 登录
我们先看看启动HTTP监听按钮
![](http://upload-images.jianshu.io/upload_images/1077703-05d7ee220e054d9d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我们使用spy++
抓到了这个ToolBar的句柄
![](http://upload-images.jianshu.io/upload_images/1077703-6ba4a722ef4e2ca3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后用 spy++
向第一个按钮发送鼠标点击事件,那么就可以开启监听了
![](http://upload-images.jianshu.io/upload_images/1077703-162bda9c694d359a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
点击动作在Windows消息来看,是分为两个动作,一个是 WM_LBUTTONDOWN
而另一个是 WM_LBUTTONUP
,所以我们需要发送两次事件,当完成这两次发送后,我们可以看到下面的界面
![](http://upload-images.jianshu.io/upload_images/1077703-17615fd510f0b17a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
没错,其实这里是一个坑,启动监听还不好好启动,非得弹出一个消息框,同时伴随着的是spy++
卡死了,为什么呢? 因为我们使用的是SendMessage
,这是一个同步的过程,因为出现了消息框,所以spy++
还未收到返回消息,所以就卡死了。当我们点击完 确认 按钮后就可以恢复了,当然我们也可以使用 PostMessage
,不过这个接口只适合不在乎执行结果的情况下执行。
好了,这里我们出现了第一个坑:有弹窗,我们的外挂需要自动识别,并且能够自动关闭弹窗。
![](http://upload-images.jianshu.io/upload_images/1077703-b8957f4a6cb14af8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
OK, 我们继续,我们该开始登陆了
![](http://upload-images.jianshu.io/upload_images/1077703-9922e88bbfa4a81a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
刚才我们 SendMessage
里的WPARAM是1,那么,这个按钮是4
![](http://upload-images.jianshu.io/upload_images/1077703-4ad2f673f1064127.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
继续使用 spy++
发送消息
![](http://upload-images.jianshu.io/upload_images/1077703-0d196331eec8fabe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
模拟完发送,整个人一下子就不好了,因为这个按钮根本就没有反应,后面的两个参数你也不知道到底传什么好,就在陷入了整个困局的时候,发现我们其实可以通过快捷键 ctrl+b
完成监听, ctrl+i
进入登录界面
![](http://upload-images.jianshu.io/upload_images/1077703-de305df2595148d9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
此时未插入USBKey
所以,我们需要使用另外一个API: SendInput
, 包括后面的密码输入,也一样要使用这个API
我们看一下这个API的定义
UINT WINAPI SendInput(_In_ UINT nInputs, // 按键数量_In_ LPINPUT pInputs, // 按键内容数组_In_ int cbSize // 数组内容结构体的尺寸 );
看上去很心塞,一堆参数。
![](http://upload-images.jianshu.io/upload_images/1077703-1cd1ee3b766d2569.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
由于本文讲解的是调研篇,我们此处假设SendInput
可以完成快捷键的按键模拟,密码输入的按键模拟,实际上这个API确实是可以工作的,因为这个接口是真实的模拟键盘输入,不针对某个窗口句柄。
接下来我们会迎来第二个坑,如果USBKey正常工作,那么用户名里的的内容是自动填写好的,如图:
![](http://upload-images.jianshu.io/upload_images/1077703-a9b50eaab9d001c0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这个用户名是从USBKey里读出来的,读取是需要时间的,因此我们可以在这里不停的向这个文本框发送WM_GETTEXT
消息,拿到用户名,如果用户名是预期的数据,我们就认为此时USBKey是正常工作的,否则如果长时间用户名未成功加载,则说明USBKey工作异常,应该发送报警信息。
![](http://upload-images.jianshu.io/upload_images/1077703-0c04668d80666541.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/1077703-8ad4efa11f52ab75.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/1077703-8992e5b8ac00f2be.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我们大概会得到如下几类错误
- 密码错误
- 通讯故障
- USBKey有问题
对于密码错误这个问题,我们的外挂应该立即停止工作,因为密码输入次数超过限制,USBKey将会锁定,公司出金服务就挂了。。。。
![](http://upload-images.jianshu.io/upload_images/1077703-33a119b1c19d717b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
为什么会密码输入错误呢?因为很有可能在自动输入时,被其他程序干扰了一下
我们在代码中会尽量用SetForegroundWindow
让窗口保持在最前面,成为激活状态
那么对于通讯故障,解决的办法就只能是重新尝试了
剩下的问题,我个人认为发出报警,人工处理一下会比较合适。
此时迎来两个新问题,
- 我们如何知道消息框里的内容是什么
- 我们如何知道外挂登录成功了呢?
对于第一个问题,我们可以通过 EnumChildWindows
来遍历这个消息框的孩子句柄,然后通过 GetWindowText
就可以知道是什么内容了。
我们重点来讨论第二个问题
此处有两种解法:
- 向招行发起查询请求,如果能查询到数据,说明登录成功
- 检查登陆信息里的内容
![](http://upload-images.jianshu.io/upload_images/1077703-20e94c8dc8c9614d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
登陆信息列表
为了提升难度,我们选择方案2
![](http://upload-images.jianshu.io/upload_images/1077703-8c83483d6a8ceb97.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这种方法是比较困难的,有困难,我们要解决,没有困难我们也要创造困难来解决。。。。
为什么难呢?
因为我们没办法通过SendMessage
发送 WM_GETTEXT
事件获取内容,但是我们可以通过 LVM_GETITEMTEXT
来获取 listview 的列表内容
BUT..... 跨进程这么拿是拿不到的,同时,不同位数的进程,也是拿不到数据的。
如何解决?
我们需要使用API VirtualAllocEx
向银企直联进程申请一块内存空间,用于我们的外挂进程和银企直联进行数据沟通,当我们发送 LVM_GETITEMTEXT
消息之前,我们需要把参数信息写到这个内存块里,然后再使用SendMessage
,ListView的数据会写到这个内存块,最后我们通过 ReadProcessMemory
来读取获取到列表的数据
这里就是为什么32位
不能读64位
程序的内容的原因了,虽然我们可以使用WriteProcessMemory
和 ReadProcessMemory
来写入和读取进程内存里的数据,但是由于通过这种机制进行交互,指针大小是不同的,通过SendMessage
指令虽然能执行成功,但是回写的数据内容会跑飞。
![](http://upload-images.jianshu.io/upload_images/1077703-5f804241f6774085.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
箭头代表数据流向,所有的API调用都是在外挂这边完成的
整个流程大概就是这样的,我们需要借助远程进程的内存块来做数据交互,但最后切记一定要使用VirtualFreeEx
释放掉不用的内存块。
此处应该有总结:
- 使用模拟键盘的方法开启监听和进入到登录界面而非
SendMessage
- 通过远程申请内存块的方式获取登录结果内容
- 需要判断弹出消息框的内容,用以判断是否有异常,同时需要关闭这些消息窗口
到此为止,关键的技术内容我们已经调研完了,下一篇内容我们会讲如何使用go语言实现一个真正可用的外挂。
我们先来预览几个外挂的截图吧:
外挂工作中.....
![](http://upload-images.jianshu.io/upload_images/1077703-a3bc75d3b3eed0d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
当发生稳定性异常时,会通过bearychat的Incoming服务发送报警
![](http://upload-images.jianshu.io/upload_images/1077703-e3262e300f334819.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/1077703-7e7ffb34fb3bd5b9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
用Go语言写个外挂(上)相关推荐
- python可以写dnf外挂么_易语言写DNF外挂各种功能(很详细适合新手)
3S==================== 写内存字节集 (进程ID, 十六到十 ("0177E5C2"), { 216, 60, 131 }) ================ ...
- 看看现在大型网站都是用什么语言写的 ?
看看现在大型网站都是用什么语言写的 ? 不排除一个网站用多种技术!如淘宝是Java + php,底层是java,表现层是php.新浪,网易,腾讯应该也是用了多种技术. 据说是这样的:php,新浪,雅虎 ...
- 会写代码的AI开源了!C语言写得比Codex还要好,掌握12种编程语言丨CMU
点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 萧箫 发自 凹非寺 量子位 | 公众号 QbitAI 比Codex还 ...
- 大学生只会用C语言写简单的计算怎么办?
大学生会用C语言写几个简单的计算太正常了,有些人直到毕业了都写不出什么程序. 这个也是大多数应届生的一个现状. 在学校的时候不知道要好好学习,等马上会接受社会毒打时才发现自己一无是处,然后开始临时抱佛 ...
- c++ 小游戏_C/C++编程笔记:C语言写推箱子小游戏,大一学习C语言练手项目
C语言,作为大多数人的第一门编程语言,重要性不言而喻,很多编程习惯,逻辑方式在此时就已经形成了.这个是我在大一学习 C语言 后写的推箱子小游戏,自己的逻辑能力得到了提升,在这里同大家分享这个推箱子小游 ...
- Go语言写的解析器(支持json,linq,sql,net,http等)
Monkey程序语言 Monkey v2.0版本已发布. monkey v2.0 增加了如下内容: 新增 short arrow(->)支持(类似C#的lambda表达式) 增加 列表推导和哈希 ...
- mapreduce编程实例python-使用Python语言写Hadoop MapReduce程序
原标题:使用Python语言写Hadoop MapReduce程序 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 在本教程中,我将描述如何使用Pytho ...
- python是c语言写的吗-C语言和python的区别
Python可以说是目前最火的语言之一了,人工智能的兴起让Python一夜之间变得家喻户晓,Python号称目前最最简单易学的语言,现在有不少高校开始将Python作为大一新生的入门语言.本萌新也刚开 ...
- C语言写的俄罗斯方块
源:C语言写的俄罗斯方块 2014年最后一天, 任天堂将风靡全球30年的经典游戏<<俄罗斯方块>>下架. 作为全球最畅销的游戏, 其移植版本遍布各个平台. 下面这个是我去年在5 ...
最新文章
- RAC起单实例make
- dos显示磁盘剩余空间
- 【渝粤教育】国家开放大学2018年秋季 0043-22T计算机文化 参考试题
- 前端学习(2954):vue文件的三大组成部分
- 【OpenCV 例程200篇】07. 图像的创建(np.zeros)
- IEnumerable和IEnumerator详解
- glusterfs java_GlusterFS分布式文件系统使用简介
- 表达式求值(nyoj305)
- STM8学习笔记---Modbus通信协议简单移植
- buf.indexOf()
- android a20 i2c 通信,Android程序运行分析——中等复杂程度的NTAG I2C Demo为例(二)...
- python生成序列_python如何生成随机序列?
- 惠普p1106打印机安装步骤_hplaserjetp1106打印机驱动安装说明
- 计算机论文档案初探,[电子档案管理论文:档案计算机管理技术人才培训工作初探.doc...
- 医疗知识图谱_寻医问药
- 互联网 必须有免费的精神
- 西瓜书读书笔记(一)-绪论
- Linux 安装locust
- brew | brew cask | yum | apt-get
- PG内核分析 Question and Answer
热门文章
- SpringBoot入门学习(一)
- 花了一天,解决java调用matlab
- java框架外文翻译_Spring框架-毕业论文外文文献翻译.doc
- 在项目中调ODI接口
- VUE前后分离调起微信支付
- 评测i5 1240p和i5 11320h 选哪个
- 实战Spring Boot构建多租户SaaS平台分享(含源代码)
- MATLAB图像处理基于发票号码识别
- 如何保证战略落地_战略到底如何“落地”?
- 前端里的button怎么去除点击自带边框_这款系统自带的视频剪辑软件,了解一下...