目录
第一步:Windows系统开启测试模式(针对64位系统,32系统不需要)
第二步:WinIo64.sys安装签名(针对64位系统,32系统不需要)
开发环境:winxp、win7、win10 32位及64位
开发语言:Java和c++
必要文件:WinIo32.dll、WinIo64.dll、WinIo32.sys、WinIo64.sys
(注:有些步骤的详细做法非常简单,如果不明白请问度哥)
开启测试模式:cmd执行命令:bcdedit /set testsigning on 然后重启,电脑右下角出现测试模式......
关闭测试模式:cmd执行命令:bcdedit /set testsigning off 然后重启,电脑右下角测试模式消失
参考链接如下:
win10:https://jingyan.baidu.com/article/72ee561a724b74e16138df1c.html
win7、winxp:https://jingyan.baidu.com/article/acf728fd21c3e7f8e510a3ef.html
1、打开 WinIO64.sys的属性框,翻到“数字签名”选项卡,点击“详细信息”
2、在新出来的对话框中点击“查看证书”
3、在又新出来的对话框中点击“安装证书”
4、点击“下一步”,然后选择“将所有的证书放入下列存储”
5、点击浏览,选择“受信任的根证书发布机构”
6、点击“下一步”,然后点击“完成”
7、在弹出的“安全性警告”对话框中选择“是”,才能导入成功
8、重启电脑
网上有很多人使用jnative实现的,这里使用jna实现。jnative是在jna的基础上封装的有局限性。以下是maven依赖:
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
winio代码实现
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.win32.W32APIOptions;
/**
* author songxq
* date 2020/6/18
* description
*/
public interface WinIo extends Library {
/**
* 系统架构32位或者64位
*/
String ARCH = System.getProperty("os.arch");
/**
* 使用的winio版本
*/
String WIN_IO_LIB_NAME = "x86".equals(ARCH) ? "WinIo32" : "WinIo64";
WinIo INSTANCE = (WinIo) Native.loadLibrary(WIN_IO_LIB_NAME, WinIo.class, W32APIOptions.DEFAULT_OPTIONS);
/**
* PS/2键盘的命令端口
*/
int CONTROL_PORT = 0x64;
/**
* PS/2键盘的数据端口
*/
int DATA_PORT = 0x60;
/**
* 初始化winiolib库
* @param path 驱动文件绝对路径
* @return
*/
boolean InitializeWinIo(WString path);
/**
* 释放资源及开辟的内存空间
*/
void ShutdownWinIo();
/**
* 读数据
* 98/ME系列使用GetPortVal NT/2000/XP系列可以使用_inp/_inpw/_inpd
* @param portAddr io地址
* @param pPortVal 指针变量
* @param size 数据大小
* @return
*/
boolean GetPortVal(int portAddr, Pointer pPortVal, int size);
/**
* 写数据
* @param portAddr io地址
* @param portVal 指针变量
* @param size 数据大小
* @return
*/
boolean SetPortVal(int portAddr, int portVal, int size);
}
键盘调用代码
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
/**
* author songxq
* date 2020/6/18
* description
*/
public class VirtualKB {
private static final WinIo WIN_IO = WinIo.INSTANCE;
private static final String WIN_BASE_SYSTEM32 = "C:\\Windows\\System32";
private static final String WINIO_INSTALL_PATH = "C:\\INSTALLWINIO";
private static final String WINIO_INSTALL_EXE = "C:\\INSTALLWINIO\\InstallWinIo.exe";
private static final String WIN_32_SYS = "WinIo32.sys";
private static final String WIN_64_SYS = "WinIo64.sys";
//keyame
public static final String Tab = "Tab";
public static final String Esc = "Esc";
public static final String Backspace = "Backspace";
public static final String Enter = "Enter";
public static final String LCtrl = "LCtrl";
public static final String LShift = "LShift";
public static final String RShift = "RShift";
public static final String LAlt = "LAlt";
public static final String Space = "Space";
public static final String CapsLock = "CapsLock";
public static final String F1 = "F1";
public static final String F2 = "F2";
public static final String F3 = "F3";
public static final String F4 = "F4";
public static final String F5 = "F5";
public static final String F6 = "F6";
public static final String F7 = "F7";
public static final String F8 = "F8";
public static final String F9 = "F9";
public static final String F10 = "F10";
public static final String F11 = "F11";
public static final String F12 = "F12";
public static final String NumLock = "NumLock";
public static final String ScrollLock = "ScrollLock";
static{
String x86 = "x86";
String driverName;
if (x86.equals(WinIo.ARCH)) {
driverName = WIN_BASE_SYSTEM32 + File.separator + WIN_32_SYS;
} else {
driverName = WIN_BASE_SYSTEM32 + File.separator + WIN_64_SYS;
}
if(!WIN_IO.InitializeWinIo(new WString(driverName))){
installSys();
}
}
/**
* 等待缓冲区置空
*/
private static void KBCWait4IBE() {
int val;
do {
Pointer p = new Memory(8);
if (!WIN_IO.GetPortVal(WinIo.CONTROL_PORT, p, 1)) {
throw new RuntimeException("Cannot Get The Port!");
}
val = p.getInt(0);
} while ((0x2 & val) > 0);
}
private static void down(int key) {
KBCWait4IBE();
WIN_IO.SetPortVal(WinIo.CONTROL_PORT, 0xd2, 1);
KBCWait4IBE();
WIN_IO.SetPortVal(WinIo.DATA_PORT, key, 1);
}
private static void up(int key) {
KBCWait4IBE();
WIN_IO.SetPortVal(WinIo.CONTROL_PORT, 0xd2, 1);
KBCWait4IBE();
WIN_IO.SetPortVal(WinIo.DATA_PORT, (key | 0x80), 1);
}
private static void press(String[] words, long firstSleepTime, long sleepTime) throws Exception{
if (null == words || words.length == 0) {
throw new IllegalArgumentException("words Is NULL Or Empty");
}
int minSleepTime = 50;
if (sleepTime < minSleepTime) {
throw new IllegalArgumentException("SleepTime Less Than 50 ms");
}
if (firstSleepTime > 0) {
Thread.sleep(firstSleepTime);
}
for (String word : words) {
Integer vk = VirtualKBMapping.VK_MAP.get(word);
if (null == vk) {
vk = VirtualKBMapping.NEED_SHIFT_VK.get(word);
}
if (null == vk) {
throw new RuntimeException(word + " Not Support");
}
boolean needShift = word.length() == 1 && null != VirtualKBMapping.NEED_SHIFT_VK.get(word);
if (needShift) {
down(VirtualKBMapping.VK_MAP.get("LShift"));
}
down(vk);
Thread.sleep(sleepTime);
up(vk);
if (needShift) {
up(VirtualKBMapping.VK_MAP.get("LShift"));
}
}
}
/**
* 键盘输入文本
* @param content 文本
* @param sleepTime 等待时间间隔
*/
public static void inputStr(String content,long sleepTime) throws Exception {
if (null == content || content.length() == 0) {
throw new IllegalArgumentException("content Is NULL Or Empty");
}
char[] chars = content.toCharArray();
for (int i=0;i<chars.length;i++){
press(new String[]{String.valueOf(chars[i])},sleepTime,50);
}
}
/**
* 键盘按键
* @param keyname 按键名称
* @param sleepTime 等待时间间隔
*/
public static void pressKey(String keyname,long sleepTime) throws Exception {
if (null == keyname || keyname.length() == 0) {
throw new IllegalArgumentException("keyname Is NULL Or Empty");
}
press(new String[]{keyname},sleepTime,50);
}
private static void extract(final String fileName, final String name) {
if (new File(fileName).exists()) {
return;
}
try(InputStream inputStream = VirtualKB.class.getResourceAsStream("/" + name)) {
Files.copy(inputStream, Paths.get(fileName), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 执行sys驱动安装
*/
public static void installSys() {
if (!new File(WINIO_INSTALL_EXE).exists()) {
if(!new File(WINIO_INSTALL_PATH).exists()){
new File(WINIO_INSTALL_PATH).mkdirs();
}
extract(WINIO_INSTALL_EXE,"InstallWinIo.exe");
}
Runtime rn = Runtime.getRuntime();
Process p = null;
try {
p = rn.exec(WINIO_INSTALL_EXE);
} catch (Exception e) {
e.printStackTrace();
}
}
}
虚拟键盘映射代码
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* author songxq
* date 2020/6/18
* description
*/
public class VirtualKBMapping {
/**
* 映射基本键map
*/
static final Map<String, Integer> VK_MAP = new HashMap<>();
/**
* 映射shift转换键map
*/
static final Map<String, Integer> NEED_SHIFT_VK = new HashMap<>();
static {
VK_MAP.put("Esc", 0x01);
VK_MAP.put("1", 0x02);
VK_MAP.put("2", 0x03);
VK_MAP.put("3", 0x04);
VK_MAP.put("4", 0x05);
VK_MAP.put("5", 0x06);
VK_MAP.put("6", 0x07);
VK_MAP.put("7", 0x08);
VK_MAP.put("8", 0x09);
VK_MAP.put("9", 0x0a);
VK_MAP.put("0", 0x0b);
VK_MAP.put("-", 0x0c);
VK_MAP.put("=", 0x0d);
VK_MAP.put("Backspace", 0x0e);
VK_MAP.put("Tab", 0x0f);
VK_MAP.put("q", 0x10);
VK_MAP.put("w", 0x11);
VK_MAP.put("e", 0x12);
VK_MAP.put("r", 0x13);
VK_MAP.put("t", 0x14);
VK_MAP.put("y", 0x15);
VK_MAP.put("u", 0x16);
VK_MAP.put("i", 0x17);
VK_MAP.put("o", 0x18);
VK_MAP.put("p", 0x19);
VK_MAP.put("[", 0x1a);
VK_MAP.put("]", 0x1b);
VK_MAP.put("Enter", 0x1c);
VK_MAP.put("LCtrl", 0x1d);
VK_MAP.put("a", 0x1e);
VK_MAP.put("s", 0x1f);
VK_MAP.put("d", 0x20);
VK_MAP.put("f", 0x21);
VK_MAP.put("g", 0x22);
VK_MAP.put("h", 0x23);
VK_MAP.put("j", 0x24);
VK_MAP.put("k", 0x25);
VK_MAP.put("l", 0x26);
VK_MAP.put(";", 0x27);
VK_MAP.put("'", 0x28);
VK_MAP.put("`", 0x29);
VK_MAP.put("LShift", 0x2a);
VK_MAP.put("\\", 0x2b);
VK_MAP.put("z", 0x2c);
VK_MAP.put("x", 0x2d);
VK_MAP.put("c", 0x2e);
VK_MAP.put("v", 0x2f);
VK_MAP.put("b", 0x30);
VK_MAP.put("n", 0x31);
VK_MAP.put("m", 0x32);
VK_MAP.put(",", 0x33);
VK_MAP.put(".", 0x34);
VK_MAP.put("/", 0x35);
VK_MAP.put("RShift", 0x36);
VK_MAP.put("LAlt", 0x38);
VK_MAP.put("Space", 0x39);
VK_MAP.put("CapsLock", 0x3a);
VK_MAP.put("F1", 0x3b);
VK_MAP.put("F2", 0x3c);
VK_MAP.put("F3", 0x3d);
VK_MAP.put("F4", 0x3e);
VK_MAP.put("F5", 0x3f);
VK_MAP.put("F6", 0x40);
VK_MAP.put("F7", 0x41);
VK_MAP.put("F8", 0x42);
VK_MAP.put("F9", 0x43);
VK_MAP.put("F10", 0x44);
VK_MAP.put("NumLock", 0x45);
VK_MAP.put("ScrollLock", 0x46);
VK_MAP.put("F11", 0x57);
VK_MAP.put("F12", 0x58);
}
static {
NEED_SHIFT_VK.put("!", 0x02);
NEED_SHIFT_VK.put("@", 0x03);
NEED_SHIFT_VK.put("#", 0x04);
NEED_SHIFT_VK.put("$", 0x05);
NEED_SHIFT_VK.put("%", 0x06);
NEED_SHIFT_VK.put("^", 0x07);
NEED_SHIFT_VK.put("&", 0x08);
NEED_SHIFT_VK.put("*", 0x09);
NEED_SHIFT_VK.put("(", 0x0a);
NEED_SHIFT_VK.put(")", 0x0b);
NEED_SHIFT_VK.put("_", 0x0c);
NEED_SHIFT_VK.put("+", 0x0d);
NEED_SHIFT_VK.put("{", 0x1a);
NEED_SHIFT_VK.put("}", 0x1b);
NEED_SHIFT_VK.put(":", 0x27);
NEED_SHIFT_VK.put("\"", 0x28);
NEED_SHIFT_VK.put("~", 0x29);
NEED_SHIFT_VK.put("|", 0x2b);
NEED_SHIFT_VK.put("<", 0x33);
NEED_SHIFT_VK.put(">", 0x34);
NEED_SHIFT_VK.put("?", 0x35);
NEED_SHIFT_VK.put("Q", 0x10);
NEED_SHIFT_VK.put("W", 0x11);
NEED_SHIFT_VK.put("E", 0x12);
NEED_SHIFT_VK.put("R", 0x13);
NEED_SHIFT_VK.put("T", 0x14);
NEED_SHIFT_VK.put("Y", 0x15);
NEED_SHIFT_VK.put("U", 0x16);
NEED_SHIFT_VK.put("I", 0x17);
NEED_SHIFT_VK.put("O", 0x18);
NEED_SHIFT_VK.put("P", 0x19);
NEED_SHIFT_VK.put("A", 0x1e);
NEED_SHIFT_VK.put("S", 0x1f);
NEED_SHIFT_VK.put("D", 0x20);
NEED_SHIFT_VK.put("F", 0x21);
NEED_SHIFT_VK.put("G", 0x22);
NEED_SHIFT_VK.put("H", 0x23);
NEED_SHIFT_VK.put("J", 0x24);
NEED_SHIFT_VK.put("K", 0x25);
NEED_SHIFT_VK.put("L", 0x26);
NEED_SHIFT_VK.put("Z", 0x2c);
NEED_SHIFT_VK.put("X", 0x2d);
NEED_SHIFT_VK.put("C", 0x2e);
NEED_SHIFT_VK.put("V", 0x2f);
NEED_SHIFT_VK.put("B", 0x30);
NEED_SHIFT_VK.put("N", 0x31);
NEED_SHIFT_VK.put("M", 0x32);
}
/**
* 返回所有支持的按键
*/
public static String[] getSupportKeys() {
String[] vkArray = VK_MAP.keySet().toArray(new String[0]);
String[] needShiftVKArray = NEED_SHIFT_VK.keySet().toArray(new String[0]);
String[] supportKeys = new String[vkArray.length + needShiftVKArray.length];
System.arraycopy(vkArray, 0, supportKeys, 0, vkArray.length);
System.arraycopy(needShiftVKArray, 0, supportKeys, vkArray.length, needShiftVKArray.length);
return supportKeys;
}
/**
* 返回是否支持按键
*/
public static boolean isSupportKey(String key) {
return StringUtils.join(Arrays.asList(VirtualKBMapping.getSupportKeys()),"").contains(key);
}
}
c++装载驱动代码可以生成为dll文件调用,也可生成为exe文件调用。这里编译为exe。
注:生成dll文件及exe文件的时候一定生成64位的文件(32位系统不需要这个)。
#include "stdafx.h"
#include <windows.h>
#include <winsvc.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <tchar.h>
#pragma comment (lib, "Advapi32.lib")
#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
#define DRIVER_NAME "WinIo64"
#define DRIVER_PATH "C:\\Windows\\System32\\WinIo64.sys"
//装载NT驱动程序
BOOL LoadNTDriver(char* lpszDriverName, char* lpszDriverPath)
{
char szDriverImagePath[256];
//得到完整的驱动路径
GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄
//打开服务控制管理器
hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hServiceMgr == NULL)
{
//OpenSCManager失败
printf("OpenSCManager() Faild ! \n");
//printf(GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
OpenSCManager成功
printf("OpenSCManager() ok ! \n");
}
//创建驱动所对应的服务
hServiceDDK = CreateService(hServiceMgr,
lpszDriverName, //驱动程序的在注册表中的名字
lpszDriverName, // 注册表驱动程序的 DisplayName 值
SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序
SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
szDriverImagePath, // 注册表驱动程序的 ImagePath 值
NULL,
NULL,
NULL,
NULL,
NULL);
DWORD dwRtn;
//判断服务是否失败
if (hServiceDDK == NULL)
{
dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
{
//由于其他原因创建服务失败
printf("CrateService() Faild ! \n");
///printf(dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务创建失败,是由于服务已经创立过
printf("CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n");
}
// 驱动程序已经加载,只需要打开
hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
if (hServiceDDK == NULL)
{
//如果打开服务也失败,则意味错误
dwRtn = GetLastError();
printf("OpenService() Faild ! \n");
//printf(dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
printf("OpenService() ok ! \n");
}
}
else
{
printf("CrateService() ok ! \n");
}
//开启此项服务
bRet = StartService(hServiceDDK, NULL, NULL);
if (!bRet)
{
DWORD dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING)
{
printf("StartService() Faild ! \n" );
//printf(dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
if (dwRtn == ERROR_IO_PENDING)
{
//设备被挂住
printf("StartService() Faild ERROR_IO_PENDING ! \n");
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务已经开启
printf("StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! \n");
bRet = TRUE;
goto BeforeLeave;
}
}
}
bRet = TRUE;
//离开前关闭句柄
BeforeLeave:
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
//卸载驱动程序
BOOL UnloadNTDriver(char * szSvrName)
{
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄
SERVICE_STATUS SvrSta;
//打开SCM管理器
hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hServiceMgr == NULL)
{
//带开SCM管理器失败
printf("OpenSCManager() Faild ! \n");
//printf(GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
//带开SCM管理器失败成功
printf("OpenSCManager() ok ! \n");
}
//打开驱动所对应的服务
hServiceDDK = OpenService(hServiceMgr, szSvrName, SERVICE_ALL_ACCESS);
if (hServiceDDK == NULL)
{
//打开驱动所对应的服务失败
printf("OpenService() Faild ! \n");
//printf(GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
printf("OpenService() ok ! \n");
}
//停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。
if (!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &SvrSta))
{
printf("ControlService() Faild %d !\n", GetLastError());
}
else
{
//打开驱动所对应的失败
printf("ControlService() ok !\n");
}
//动态卸载驱动程序。
if (!DeleteService(hServiceDDK))
{
//卸载失败
printf("DeleteSrevice() Faild %d !\n", GetLastError());
}
else
{
//卸载成功
printf("DelServer:eleteSrevice() ok !\n");
}
bRet = TRUE;
BeforeLeave:
//离开前关闭打开的句柄
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
int main(int argc, char* argv[])
{
//加载驱动
BOOL bRet = LoadNTDriver(DRIVER_NAME, DRIVER_PATH);
if (!bRet)
{
printf("install winio error\n");
return 0;
}
//加载成功
//printf("install winio successed!\n");
//getch();
//这时候你可以通过注册表,或其他查看符号连接的软件验证。
//printf("press any to unload the driver!\n");
//getch();
//卸载驱动
//BOOL bRet = UnloadNTDriver(DRIVER_NAME);
//if (!bRet)
//{
// printf("unload winio error!\n");
// return 0;
//}
//printf("unload winio successed!\n");
//return 0;
}
大家按照上述代码都写好后就是执行了。有人可能成功了,有人可能还不行。所以下面介绍下这个代码基本的运行逻辑。供大家自行调整bug。
windows系统开启测试模式后,winio64.sys安装签名后。
执行代码:
1、***首先执行装载winio64.sys驱动代码,就是c++生成的dll或者exe文件里的LoadNTDriver方法。(这一步必须有,网上大部分没有这一步,所以大部分你试了都不会成功。)
2、执行winio的初始化方法 InitializeWinIo
3、KBCWait4IBE方法等待0x64控制端口空闲
4、0x64控制端口空闲时SetPortVal发送命令到0x64端口,在发送按键对应的虚拟码到0x60端口即可
5、winio.ShutdownWinIo释放资源
6、winio64.sys驱动加载后,系统重启后需要重新加载(注意)
以上即是本人实践总结,欢迎留言交流。