用Go语言写一个Windows的外挂(上)

作者: 乱入的Zeal 链接:https://www.jianshu.com/p/1b8efb1bc3c0 來源:简书

本人在一家互联网金融公司上班,对于一家互联网金融公司,最基本的功能就是客户入金和出金,而出金的稳定性是很重要的,出金不畅容易导致投资人恐慌,本文讲的是出金,出金接口我们对接的是招商银行的银企直联系统,那么银企直连系统是一个什么样的程序呢?

image.png

没错,这个程序是运行在Windows上的,并且需要插入USBKey才能正常工作,这就意味着,不能简单的使用命令行进行运维管理。

看到这里,做运维的同学的内心应该和我一样是崩溃的。。

image.png

跟大家解释一下,这个服务是做什么的,大家可以把这个程序当成是我们的业务系统和招商银行沟通的信使,所有出金操作、查询操作都是通过这个信使来完成。

由于各种未知的原因,比如网络不稳定,或者USBKey插入时间过长产生了一些莫名其妙的错误,那么就需要人工去重启一下服务或重新登录一下账号,而且,这个工作有时候是在夜间操作的,这相当于要24小时待命啊,虽然故障频率不高,但这根弦始终是崩着的,这简直就是在破坏我的幸福美好生活啊。

image.png

这种体力活的事情,我坚决不能干,所以一定要交给别人干。

image.png

别想多了,【别人】也只能是个外挂而已,谁都不喜欢干这种人肉体力活。

所以凭借着我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 来调用 windowsDLL(动态链接库),所以果断使用 golang, 因为这个外挂大部分的WinAPI都在 user32.dllkernel32.dll 里,我们只需要能加载这几个DLL 就可以调用强大的 WinAPI

image.png

大家可以使用 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++

image.png

将瞄准器拖拽到具体的窗口上,就会得到窗口的句柄,我们可以通过 FindWindowW 或 EnumChildWindows 来实现相同的功能

银企直连正常工作需要两个步骤

  1. 启动HTTP服务监听
  2. 登录

我们先看看启动HTTP监听按钮

image.png

我们使用spy++抓到了这个ToolBar的句柄

image.png

然后用 spy++ 向第一个按钮发送鼠标点击事件,那么就可以开启监听了

image.png

点击动作在Windows消息来看,是分为两个动作,一个是 WM_LBUTTONDOWN 而另一个是 WM_LBUTTONUP ,所以我们需要发送两次事件,当完成这两次发送后,我们可以看到下面的界面

image.png

没错,其实这里是一个坑,启动监听还不好好启动,非得弹出一个消息框,同时伴随着的是spy++卡死了,为什么呢? 因为我们使用的是SendMessage,这是一个同步的过程,因为出现了消息框,所以spy++还未收到返回消息,所以就卡死了。当我们点击完 确认 按钮后就可以恢复了,当然我们也可以使用 PostMessage ,不过这个接口只适合不在乎执行结果的情况下执行。

好了,这里我们出现了第一个坑:有弹窗,我们的外挂需要自动识别,并且能够自动关闭弹窗。

image.png

OK, 我们继续,我们该开始登陆了

image.png

刚才我们 SendMessage 里的WPARAM是1,那么,这个按钮是4

image.png

继续使用 spy++ 发送消息

image.png

模拟完发送,整个人一下子就不好了,因为这个按钮根本就没有反应,后面的两个参数你也不知道到底传什么好,就在陷入了整个困局的时候,发现我们其实可以通过快捷键 ctrl+b 完成监听, ctrl+i 进入登录界面

image.png

此时未插入USBKey

所以,我们需要使用另外一个API: SendInput, 包括后面的密码输入,也一样要使用这个API

我们看一下这个API的定义

UINT WINAPI SendInput(
  _In_ UINT    nInputs, // 按键数量
  _In_ LPINPUT pInputs, // 按键内容数组
  _In_ int     cbSize // 数组内容结构体的尺寸);

看上去很心塞,一堆参数。

image.png

由于本文讲解的是调研篇,我们此处假设SendInput可以完成快捷键的按键模拟,密码输入的按键模拟,实际上这个API确实是可以工作的,因为这个接口是真实的模拟键盘输入,不针对某个窗口句柄。

接下来我们会迎来第二个坑,如果USBKey正常工作,那么用户名里的的内容是自动填写好的,如图:

image.png

这个用户名是从USBKey里读出来的,读取是需要时间的,因此我们可以在这里不停的向这个文本框发送WM_GETTEXT 消息,拿到用户名,如果用户名是预期的数据,我们就认为此时USBKey是正常工作的,否则如果长时间用户名未成功加载,则说明USBKey工作异常,应该发送报警信息。

image.png

image.png

image.png

我们大概会得到如下几类错误

  • 密码错误
  • 通讯故障
  • USBKey有问题

对于密码错误这个问题,我们的外挂应该立即停止工作,因为密码输入次数超过限制,USBKey将会锁定,公司出金服务就挂了。。。。

image.png

为什么会密码输入错误呢?因为很有可能在自动输入时,被其他程序干扰了一下 我们在代码中会尽量用 SetForegroundWindow 让窗口保持在最前面,成为激活状态

那么对于通讯故障,解决的办法就只能是重新尝试了

剩下的问题,我个人认为发出报警,人工处理一下会比较合适。

此时迎来两个新问题,

  1. 我们如何知道消息框里的内容是什么
  2. 我们如何知道外挂登录成功了呢?

对于第一个问题,我们可以通过 EnumChildWindows 来遍历这个消息框的孩子句柄,然后通过 GetWindowText 就可以知道是什么内容了。

我们重点来讨论第二个问题

此处有两种解法:

  1. 向招行发起查询请求,如果能查询到数据,说明登录成功
  2. 检查登陆信息里的内容

image.png

登陆信息列表

为了提升难度,我们选择方案2

image.png

这种方法是比较困难的,有困难,我们要解决,没有困难我们也要创造困难来解决。。。。

为什么难呢?

因为我们没办法通过SendMessage 发送 WM_GETTEXT 事件获取内容,但是我们可以通过 LVM_GETITEMTEXT 来获取 listview 的列表内容

BUT..... 跨进程这么拿是拿不到的,同时,不同位数的进程,也是拿不到数据的。

如何解决?

我们需要使用API VirtualAllocEx 向银企直联进程申请一块内存空间,用于我们的外挂进程和银企直联进行数据沟通,当我们发送 LVM_GETITEMTEXT 消息之前,我们需要把参数信息写到这个内存块里,然后再使用SendMessage,ListView的数据会写到这个内存块,最后我们通过 ReadProcessMemory 来读取获取到列表的数据

这里就是为什么32位不能读64位程序的内容的原因了,虽然我们可以使用WriteProcessMemoryReadProcessMemory 来写入和读取进程内存里的数据,但是由于通过这种机制进行交互,指针大小是不同的,通过SendMessage指令虽然能执行成功,但是回写的数据内容会跑飞。

image.png

箭头代表数据流向,所有的API调用都是在外挂这边完成的

整个流程大概就是这样的,我们需要借助远程进程的内存块来做数据交互,但最后切记一定要使用VirtualFreeEx 释放掉不用的内存块。

此处应该有总结:
  1. 使用模拟键盘的方法开启监听和进入到登录界面而非SendMessage
  2. 通过远程申请内存块的方式获取登录结果内容
  3. 需要判断弹出消息框的内容,用以判断是否有异常,同时需要关闭这些消息窗口

到此为止,关键的技术内容我们已经调研完了,下一篇内容我们会讲如何使用go语言实现一个真正可用的外挂。 我们先来预览几个外挂的截图吧:

外挂工作中.....

image.png

当发生稳定性异常时,会通过bearychat的Incoming服务发送报警

image.png

image.png


版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2018-08-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Youngxj

WFPHP订单系统纯WAP手机版 v2.0

4242
来自专栏Debian社区

Qt 5.9.4 正式发布:包含近 200 项 Bug 修复

Qt 5.9.4 已正式发布,这是继 5.9正式版 之后的第四个维护版本,未添加任何新功能,但提供了大量的 Bug 修复和性能改进。

1582
来自专栏互联网杂技

在网页里点击链接,直接打开app的方法

通俗点说,就是url地址栏; 输入 http:…. 打开的是网页; 输入 qqdl:…… 如果你安装了腾讯的旋风下载工具,系统会自动启动旋风下载; 输入 thu...

6377
来自专栏我和未来有约会

Silverlight 2.0细节

平台/浏览器/设备: --支持Linux(将由Novell提供) --支持FireFox --支持移动设备 兼容性: --Silverlight 2...

1959
来自专栏跟着阿笨一起玩NET

C# Xamarin For Android自动升级项目实战

1953
来自专栏腾讯大讲堂的专栏

客户端检查篇

作者:互娱iOS预审团队,隶属于互娱研发部品质管理中心,致力于互娱产品的iOS审核前的验收工作。 通过细分将iOS预审工作划为3大块:客户端资源检查、应用内容检...

2968
来自专栏魏琼东

基于DotNet构件技术的企业级敏捷软件开发平台 - AgileEAS.NET平台开发指南 - 分布式应用

分布式系统         分布式系统(distributed system)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透...

18610
来自专栏13blog.site

Spring+SpringMVC+MyBatis+easyUI整合基础篇(二)牛刀小试

前言 承接上文,该篇即为项目整合的介绍了。 废话不多说,先把源码和项目地址放上来,重点要写在前面。 项目展示地址,点这里http://ssm-demo.hans...

2593
来自专栏我有一个梦想

Python 项目实践一(外星人入侵)第一篇

python断断续续的学了一段实践,基础课程终于看完了,现在跟着做三个小项目,第一个是外星人入侵的小游戏: 一 Pygame pygame 是一组功能强大而有趣...

44310
来自专栏安恒网络空间安全讲武堂

WIFI干扰器制作

WIFI干扰器制作 ? emmmm 不能瞎玩啊 被隔壁邻居举报了我不负责的 Esp8266的工作原理 Esp8266的工作原理,知乎有位大佬的回答: 解析一键配...

3.1K9

扫码关注云+社区

领取腾讯云代金券