如题所示,一般的桌面程序,用户登录很简单,就是找到用户名和密码输入框,输入相应的用户名和密码,然后点击“登录”按钮,完成登录操作。这是人为操作的步骤,如果这些动作通过程序来完成,比如调用系统输入和点击事件,同样可以达到登录的效果。
我们可以利用user32提供的SendMessageW函数,发送设置文本指令和鼠标点击指令,让程序完成上述人为操作。
难点就是找到窗口,然后找到窗口里面的组件,然后给对应的“用户名”组件设置用户名文本,给“密码”组件设置密码文本,点击“登录”提交按钮。
按照惯例,我们需要安装对应的依赖库:
npm install ffi ref --save
安装过程中需要源码编译,如果不能正常安装,需要全局安装winows-build-tools:
npm install -g --production windows-build-tools
按照我们分析的思路,我们给出如下代码:
autologin.js
var ffi = require('ffi');
var ref = require("ref");
function strBuf(str) {
//return Buffer.from(`${text}\0`, "ucs2");
return Buffer.from(str+'\0','ucs2');
}
var user32 = new ffi.Library("user32", {
FindWindowW:["int32",["string","string"]],
GetWindow: ["int32",["int32","int32"]],
SendMessageW: ["int32",["int32","uint","uint64","int64"]]
});
function getControls(hwnd, indices) {
const GW_CHILD = 5;
const GW_HWNDNEXT = 2;
const controls = {};
const map = [];
for (let key in indices) {
map[indices[key]] = key;
}
let childhwnd = user32.GetWindow(hwnd, GW_CHILD);
let index = 0;
while (isValidHandle(childhwnd)) {
if (map[index]) {
controls[map[index]] = childhwnd;
}
childhwnd = user32.GetWindow(childhwnd, GW_HWNDNEXT);
index++;
}
return controls;
}
function isValidHandle(hwnd) {
return typeof hwnd === 'number' && hwnd > 0
|| typeof hwnd === 'bigint' && hwnd > 0
|| typeof hwnd === 'string' && hwnd.length > 0;
}
function click(hwnd){
const BM_CLICK = 0xF5;
user32.SendMessageW(hwnd, BM_CLICK, 0, 0);
}
function input(hwnd,str){
const WM_SETTEXT = 0x000C;
const addr = ref.address(strBuf(str));
console.log(addr);
user32.SendMessageW(hwnd,WM_SETTEXT,0,addr);
}
async function login(){
var hwnd = user32.FindWindowW(null,strBuf("loginapp"));
var indices = {accoutlabel:0,passwordlabel:1,account:2,password:3,submit:4,cancel:5};
var controls = getControls(hwnd,indices);
console.log(controls);
input(controls.account,"admin");
await delay(100);
input(controls.password,"123456");
await delay(100);
click(controls.submit);
}
login();
async function delay(milliseconds){
return new Promise(resolve=>setTimeout(resolve,milliseconds));
}
核心操作就是:
1、找到“loginapp”的这个桌面程序窗口句柄,根据句柄,我们可以找到界面的控件,分别是用户名标签,密码标签,用户名输入框,密码输入框,提交按钮,取消按钮。
2、找到了控件,接下来 就是输入用户名和密码。
3、用户名和密码输入无误,点击“登录”提交按钮。
现在给出,手动登录的截图:

这个登录程序是我自己制作的,就是利用vs2019做的一个windows desktop application,界面很简单,点击登录,判断用户名和密码是否为admin与123456,如果是提示登录成功,否则,提示登录失败。
我们运行自动登录程序,看看效果:
这里需要注意的是,SendMessageW()函数的参数,按照官方文档定义如下:
最后两个参数一个是unit_ptr,一个是long_ptr,这里我刚开始都设置默认的int或者int32都不对,计算ref.address(strBuf(str))的时候。总提示超出范围:-2147483648 ~ 2147483647,后来改为long,还是不对,最后根据ref文档提示,改为uint64和int64,问题顺利解决。
数据类型设置错误,报错信息如下:
(node:16748) UnhandledPromiseRejectionWarning: RangeError [ERR_OUT_OF_RANGE]: error setting argument 3 - The value of "value" is out of range. It must be >= -2147483648 and <= 2147483647. Received 3089491790416
今天的这个博客很有意义,帮助我们学习了user32系统函数调用,通过SendMessageW函数可以模拟设置文本,模拟鼠标点击事件,简直不要太刺激,其实按照这个思路,做一些简单的自动登录都是可以的,稍微简单一点的验证码也许也是可以攻破的,但是复杂的验证码可能就够呛了,比如倒立的汉字,图片等等就无法解决了。
这篇博客是参考了这篇文章而来,感觉很有价值。
5039

被折叠的 条评论
为什么被折叠?



