? ? 最近因為爬蟲登錄的網站加了密碼控件,嘗試了很多方法都不能破解(該控件屏蔽了虛擬軟鍵盤),尋找資料中發現了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,呵呵呵呵呵!!!
總結
以上是生活随笔為你收集整理的WinIo64驱动级别的键盘模拟(java)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。