社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
最近因为爬虫登录的网站加了密码控件,尝试了很多方法都不能破解(该控件屏蔽了虚拟软键盘),寻找资料中发现了winIo这么工具,十几年前的东西了,它的官网貌似也处于游离状态,使用中也出现了很多问题,便以此mark一下。
最开始从这里获取到了winIo32实现驱动级键盘事件,但使用的是JNative(目前只支持32位),同时winIO32在64位的OS下运行有问题(本人测试过,可能过程中有没考虑到的原因,此文略过),所以采用winIo64+JNA来实现键盘的驱动级模拟。
在开始前,先申明一下所需要的环境和设备(本文主要在window7 64位环境下测试):
1.WinIo64.dll 和 WinIo64.sys(下载请见上面winIo32实现驱动级键盘事件的附件key文件夹)
2.因为WinIo库允许Windows应用程序中直接对I/O端口和物理内存进行存取操作,目前从网上找到的资料winio能模拟PS/2键盘输入(串口键盘),但USB设备的模拟暂时资料不详,所以此次以PS/2键盘模拟为例(必须链接PS/2键盘,否者模拟无效)。
3.WinIo64.sys签名:64位版本的Windows只加载设备驱动程序,这些驱动程序由一个公共CA签发的代码签名证书签署,如Verisign、Thawte等。WinIo64 除非获得了代码签名证书,否则系统不能部署在生产机器上。
a.开启测试模式
1)管理员模式运行cmd
2)在命令行输入:bcdedit /set TESTSIGNING ON
3)重启电脑,桌面右下角会出现测试模式。
b.完成winIO64的签名认证:
1.打开 WinIO64.sys的属性框,翻到“数字签名”选项卡,点击“详细信息”
2.在新出来的对话框中点击“查看证书”
3.在又新出来的对话框中点击“安装证书”
4.点击“下一步”,然后选择“将所有的证书放入下列存储”
5.点击浏览,选择“受信任的根证书发布机构”
4.需要jna相关的jar,自行下载。本示例jdk:1.8 64位。
接下来,贴出代码:
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;
public class Selenium {
private Robot robot = null;//此处用robot去移动鼠标,可忽略
public static final int CONTROL_PORT = 0x64;
public static final int DATA_PORT = 0x60;
public static final Map<String,Integer> map=new HashMap();
public Selenium(){
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
}
static{
map.put("0", KeyEvent.VK_0);
map.put("1", KeyEvent.VK_1);
map.put("2", KeyEvent.VK_2);
map.put("3", KeyEvent.VK_3);
map.put("4", KeyEvent.VK_4);
map.put("5", KeyEvent.VK_5);
map.put("6", KeyEvent.VK_6);
map.put("7", KeyEvent.VK_7);
map.put("8", KeyEvent.VK_8);
map.put("9", KeyEvent.VK_9);
map.put("a", KeyEvent.VK_A);
map.put("b", KeyEvent.VK_B);
map.put("c", KeyEvent.VK_C);
map.put("d", KeyEvent.VK_D);
map.put("e", KeyEvent.VK_E);
map.put("f", KeyEvent.VK_F);
map.put("g", KeyEvent.VK_G);
map.put("h", KeyEvent.VK_H);
map.put("i", KeyEvent.VK_I);
map.put("j", KeyEvent.VK_J);
map.put("k", KeyEvent.VK_K);
map.put("l", KeyEvent.VK_L);
map.put("m", KeyEvent.VK_M);
map.put("n", KeyEvent.VK_N);
map.put("o", KeyEvent.VK_O);
map.put("p", KeyEvent.VK_P);
map.put("q", KeyEvent.VK_Q);
map.put("r", KeyEvent.VK_R);
map.put("s", KeyEvent.VK_S);
map.put("t", KeyEvent.VK_T);
map.put("u", KeyEvent.VK_U);
map.put("v", KeyEvent.VK_V);
map.put("w", KeyEvent.VK_W);
map.put("x", KeyEvent.VK_X);
map.put("y", KeyEvent.VK_Y);
map.put("z", KeyEvent.VK_Z);
map.put("Tab", KeyEvent.VK_TAB);
map.put("Space", KeyEvent.VK_SPACE);
map.put("Shift", KeyEvent.VK_SHIFT);
map.put("Cntl", KeyEvent.VK_CONTROL);
map.put("Alt", KeyEvent.VK_ALT);
map.put("F1",KeyEvent.VK_F1);
map.put("F2",KeyEvent.VK_F2);
map.put("F3",KeyEvent.VK_F3);
map.put("F4",KeyEvent.VK_F4);
map.put("F5",KeyEvent.VK_F5);
map.put("F6",KeyEvent.VK_F6);
map.put("F7",KeyEvent.VK_F7);
map.put("F8",KeyEvent.VK_F8);
map.put("F9",KeyEvent.VK_F9);
map.put("F10",KeyEvent.VK_F10);
map.put("F11",KeyEvent.VK_F11);
map.put("F12",KeyEvent.VK_F12);
}
//使用User32库里面键位值转换
public interface User32 extends StdCallLibrary{
User32 Instance = (User32)Native.loadLibrary("User32",User32.class);
int MapVirtualKeyA(int key, int type);
}
//此处是winIo使用关键
public interface WinIo extends StdCallLibrary{
WinIo Instance = (WinIo)Native.loadLibrary("WinIo64",WinIo.class);
boolean InitializeWinIo();
boolean GetPortVal(int portAddr, int pPortVal, int size);
boolean SetPortVal(int portAddr, int portVal, int size) ;
void ShutdownWinIo();
}
//将虚拟键位值转成扫描码
public static int toScanCode(String key){
try {
return User32.Instance.MapVirtualKeyA(map.get(key).intValue(),0);
} catch (Exception e) {
return 0;
}
}
public static void KBCWait4IBE() throws Exception{
int val=0;
do {
if(!WinIo.Instance.GetPortVal(CONTROL_PORT,val, 1)){
System.err.println("Cannot get the Port");
}
} while ((0x2&val)>0);
}
public static void KeyDown(int key) throws Exception{
KBCWait4IBE();
WinIo.Instance.SetPortVal(WinIOAPI.CONTROL_PORT,0xD2,1);
KBCWait4IBE();
WinIo.Instance.SetPortVal(WinIOAPI.DATA_PORT,key,1);
}
public static void KeyUp(int key) throws Exception{
KBCWait4IBE();
WinIo.Instance.SetPortVal(WinIOAPI.CONTROL_PORT,0xD2,1);
KBCWait4IBE();
WinIo.Instance.SetPortVal(WinIOAPI.DATA_PORT,(key|0x80),1);
}
public void mouseDemo() throws InterruptedException{
robot.mouseMove(500, 290);
Thread.sleep(650);
robot.mousePress(KeyEvent.BUTTON1_MASK);
Thread.sleep(150);
robot.mouseRelease(KeyEvent.BUTTON1_MASK);
Thread.sleep(999);
System.out.println("鼠标点击完毕");
}
public static void main(String[] args) throws Exception{
//System.setProperty("webdriver.ie.driver", "D:/IEDriverServer.exe");
//WebDriver dr = new InternetExplorerDriver();
// 打开网站
//dr.get("https://www.hao123.com");
// 获取源代码
//System.out.println("--"+dr.getPageSource());
//Selenium se = new Selenium();
//se.mouseDemo();
System.out.println("winIO64初始化是否成功:"+WinIo.Instance.InitializeWinIo());//此处应该有判断,只有初始化成功才可继续往下走,否者直接终止
Thread.sleep(1000);
String s="helloworld";
for (int i = 0; i < s.length(); i++) {
KeyDown(toScanCode(""+s.charAt(i)));
Thread.sleep(10);
KeyUp(toScanCode(""+s.charAt(i)));
Thread.sleep(200);
}
WinIo.Instance.ShutdownWinIo();
// 关闭
//System.out.println(dr.getPageSource());
//Thread.sleep(20000);
//dr.quit();
}
}
说明:代码中有使用selenium工具,一种自动化网页测试的工具,可自行注释掉,不影响执行,关于代码中scancode可以自行查找相关资料。
关于代码中如果winIO64初始化失败的问题,首先检查2个winio的文件路径是否正确,我习惯放在jdk的java/bin下,其次执行这段代码必须是管理员的权限,用IDE的童靴请自行用管理员启动IDE。
结语:
本方法仅适用于个人需求,对于上生产环境使用作用不大,而且涉及的问题较多,主要是得有ps/2键盘。
2018-05-16 add:无意中发现笔记本上的键盘就是PS/2,呵呵呵呵呵!!!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!