Chinaunix首页 | 论坛 | 博客
  • 博客访问: 533431
  • 博文数量: 135
  • 博客积分: 3568
  • 博客等级: 中校
  • 技术积分: 1942
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-19 17:52
文章分类

全部博文(135)

文章存档

2012年(29)

2011年(41)

2010年(26)

2009年(12)

2008年(9)

2007年(12)

2006年(6)

分类: Java

2012-03-09 01:57:25

不少商业网站都提供了密码输入框ActiveX控件来保护用户密码在输入过程中不被窃取,并加密。
但是有时可能需要做一些程序自动完成某些功能,比如:抓取网页的一些数据。
使用的账户少还好,一个一个通过嗅探器分析,保存加密后的数据。
若是账户多,就麻烦了。此时最好能够通过程序调用这段加密逻辑。
不过,这些控件不少都只提供用户输入接口,而不提供编程接口,即,只能通过用户界面发送消息。

下面的例子就是以淘宝的密码输入框控件为例,使用Swt和JNA来完成这项任务。
其主要思路是:
1. 通过Swt加载该含窗口界面的ActiveX控件。窗口可以不显示。
   这是由于这种控件是不能独立显示的,必须有一个外部容器,比如IE浏览器。
   (单独的 Swt 加载 ActiveX 的例子请参考 。)
2. 通过JNA调用Windows的相关消息接口模拟键盘发送相关消息。
3. 最后通过Swt的接口调用该控件的方法、属性,获取输出值。

说明:
TestSwtAndJna.java 只是最基本的示例,旨在说明原理。
而附件中的 OleFactory.java 是一个进一步再组织的半成品。
 Java&&ActiveX.zip  


在针对某个特定的ActiveX控件开发时,需要熟练使用 Visual Studio 中的 OLE-COM Object Viewer 和 Spy++ 这两个工具。

(PS:之所以使用 Java,是因为偶更熟悉Java开发。)
(PPS:附件中没有包含第三方jar包,请自行下载: 、、)


TestSwtAndJna.java  (这个只是最基本的示例,)
  1. import org.eclipse.swt.SWT;
  2. import org.eclipse.swt.SWTError;
  3. import org.eclipse.swt.events.SelectionAdapter;
  4. import org.eclipse.swt.events.SelectionEvent;
  5. import org.eclipse.swt.layout.FillLayout;
  6. import org.eclipse.swt.ole.win32.OLE;
  7. import org.eclipse.swt.ole.win32.OleAutomation;
  8. import org.eclipse.swt.ole.win32.OleClientSite;
  9. import org.eclipse.swt.ole.win32.OleFrame;
  10. import org.eclipse.swt.ole.win32.Variant;
  11. import org.eclipse.swt.widgets.Display;
  12. import org.eclipse.swt.widgets.Menu;
  13. import org.eclipse.swt.widgets.MenuItem;
  14. import org.eclipse.swt.widgets.MessageBox;
  15. import org.eclipse.swt.widgets.Shell;

  16. import com.sun.jna.Pointer;
  17. import com.sun.jna.platform.win32.Kernel32;
  18. import com.sun.jna.platform.win32.User32;
  19. import com.sun.jna.platform.win32.WinDef.DWORD;
  20. import com.sun.jna.platform.win32.WinDef.HWND;
  21. import com.sun.jna.platform.win32.WinDef.LPARAM;
  22. import com.sun.jna.platform.win32.WinDef.WPARAM;

  23. public class TestSwtAndJna {
  24.     static OleClientSite clientSite;

  25.     public static void main(String[] args) {
  26.         Display display = new Display();

  27.         Shell shell = new Shell(display);
  28.         shell.setText("Test Password ActiveX Control");
  29.         shell.setLayout(new FillLayout());
  30.         try {
  31.             OleFrame frame = new OleFrame(shell, SWT.NONE);
  32.             clientSite = new OleClientSite(frame, SWT.NONE,
  33.                     "{488A4255-3236-44B3-8F27-FA1AECAA8844}");
  34.             clientSite.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
  35.             addFileMenu(frame);

  36.         } catch (SWTError e) {
  37.             System.out.println("Unable to open activeX control");
  38.             display.dispose();
  39.             return;
  40.         }
  41.         shell.setSize(400, 300);

  42.         shell.open();

  43.         while (!shell.isDisposed()) {
  44.             if (!display.readAndDispatch()) {
  45.                 display.sleep();
  46.             }
  47.         }

  48.         display.dispose();
  49.     }

  50.     static void addFileMenu(final OleFrame frame) {
  51.         final Shell shell = frame.getShell();
  52.         Menu menuBar = shell.getMenuBar();
  53.         if (menuBar == null) {
  54.             menuBar = new Menu(shell, SWT.BAR);
  55.             shell.setMenuBar(menuBar);
  56.         }
  57.         MenuItem fileMenu = new MenuItem(menuBar, SWT.CASCADE);
  58.         fileMenu.setText("&Action");
  59.         Menu menuFile = new Menu(fileMenu);
  60.         fileMenu.setMenu(menuFile);
  61.         frame.setFileMenus(new MenuItem[] { fileMenu });

  62.         MenuItem menuHandle = new MenuItem(menuFile, SWT.CASCADE);
  63.         menuHandle.setText("View Activex Container's window handle");
  64.         menuHandle.addSelectionListener(new SelectionAdapter() {
  65.             public void widgetSelected(SelectionEvent e) {
  66.                 MessageBox messageBox = new MessageBox(shell,
  67.                         SWT.ICON_INFORMATION | SWT.OK);
  68.                 messageBox.setText("Info");

  69.                 // useful with SpyXX.exe
  70.                 messageBox.setMessage("handle = ["
  71.                         + Long.toHexString(clientSite.handle).toUpperCase()
  72.                         + "]");
  73.                 messageBox.open();
  74.             }
  75.         });

  76.         MenuItem menuTextData = new MenuItem(menuFile, SWT.CASCADE);
  77.         menuTextData.setText("View encrypted password");
  78.         menuTextData.addSelectionListener(new SelectionAdapter() {
  79.             public void widgetSelected(SelectionEvent e) {
  80.                 OleAutomation ole = new OleAutomation(clientSite);
  81.                 int[] rgdispid = ole.getIDsOfNames(new String[] { "TextData" });
  82.                 Variant var = ole.getProperty(rgdispid[0]);
  83.                 MessageBox messageBox = new MessageBox(shell,
  84.                         SWT.ICON_INFORMATION | SWT.OK);
  85.                 messageBox.setText("Info");
  86.                 String str = null;
  87.                 if (OLE.VT_NULL == var.getType()) {
  88.                     str = null;
  89.                 } else if (OLE.VT_EMPTY == var.getType()) {
  90.                     str = "";
  91.                 } else {
  92.                     str = var.getString();
  93.                 }
  94.                 messageBox.setMessage("encrypted password = [" + str + "]");
  95.                 messageBox.open();
  96.             }
  97.         });

  98.         MenuItem menuDelete = new MenuItem(menuFile, SWT.CASCADE);
  99.         menuDelete.setText("Delete last input char");
  100.         menuDelete.addSelectionListener(new SelectionAdapter() {
  101.             public void widgetSelected(SelectionEvent e) {
  102.                 Kernel32 kernel32 = Kernel32.INSTANCE;
  103.                 kernel32.SetLastError(0);
  104.                 User32 user32 = User32.INSTANCE;

  105.                 // 找到包含密码框的java窗体的handle
  106.                 HWND handle = new HWND(new Pointer(clientSite.handle));

  107.                 // 找到ActiveX的handle
  108.                 handle = user32.GetWindow(handle, new DWORD(User32.GW_CHILD));

  109.                 // 找到ActiveX中密码输入框的handle
  110.                 handle = user32.GetWindow(handle, new DWORD(User32.GW_CHILD));

  111.                 // 发送消息
  112.                 final WPARAM CHAR_BS = new WPARAM(0x08);
  113.                 final int WM_CHAR = 0x0102;
  114.                 LPARAM l = new LPARAM(0);
  115.                 user32.PostMessage(handle, WM_CHAR, CHAR_BS, l);
  116.                 int lastError = kernel32.GetLastError();
  117.                 if (lastError != 0) {
  118.                     System.out.println("last error = " + lastError);
  119.                 }
  120.             }
  121.         });

  122.         MenuItem menuPostMessage = new MenuItem(menuFile, SWT.CASCADE);
  123.         menuPostMessage.setText("Append a char '1'");
  124.         menuPostMessage.addSelectionListener(new SelectionAdapter() {
  125.             public void widgetSelected(SelectionEvent e) {
  126.                 Kernel32 kernel32 = Kernel32.INSTANCE;
  127.                 kernel32.SetLastError(0);
  128.                 User32 user32 = User32.INSTANCE;

  129.                 // 找到包含密码框的java窗体的handle
  130.                 HWND handle = new HWND(new Pointer(clientSite.handle));

  131.                 // 找到ActiveX的handle
  132.                 handle = user32.GetWindow(handle, new DWORD(User32.GW_CHILD));

  133.                 // 找到ActiveX中密码输入框的handle
  134.                 handle = user32.GetWindow(handle, new DWORD(User32.GW_CHILD));

  135.                 // 发送消息
  136.                 final int WM_CHAR = 0x0102;
  137.                 WPARAM w = new WPARAM('1');
  138.                 LPARAM l = new LPARAM(0);
  139.                 user32.PostMessage(handle, WM_CHAR, w, l);
  140.                 int lastError = kernel32.GetLastError();
  141.                 if (lastError != 0) {
  142.                     System.out.println("last error = " + lastError);
  143.                 }
  144.             }
  145.         });
  146.     }
  147. }

OleFactory.java (附件里忘了+了)

点击(此处)折叠或打开

  1. import java.util.HashMap;
  2. import java.util.Map;

  3. import org.eclipse.swt.SWT;
  4. import org.eclipse.swt.SWTError;
  5. import org.eclipse.swt.layout.FillLayout;
  6. import org.eclipse.swt.ole.win32.OLE;
  7. import org.eclipse.swt.ole.win32.OleAutomation;
  8. import org.eclipse.swt.ole.win32.OleClientSite;
  9. import org.eclipse.swt.ole.win32.OleFrame;
  10. import org.eclipse.swt.ole.win32.Variant;
  11. import org.eclipse.swt.widgets.Display;
  12. import org.eclipse.swt.widgets.Shell;

  13. import com.sun.jna.Native;
  14. import com.sun.jna.Pointer;
  15. import com.sun.jna.platform.win32.Kernel32;
  16. import com.sun.jna.platform.win32.User32;
  17. import com.sun.jna.platform.win32.WinDef.DWORD;
  18. import com.sun.jna.platform.win32.WinDef.HWND;
  19. import com.sun.jna.platform.win32.WinDef.LPARAM;
  20. import com.sun.jna.platform.win32.WinDef.WPARAM;
  21. import com.sun.jna.platform.win32.WinUser;
  22. import com.sun.jna.win32.StdCallLibrary;
  23. import com.sun.jna.win32.W32APIOptions;

  24. public class OleFactory {

  25.     public static void main(String[] args) {

  26.         PasswordEncrypter encrypter = new PasswordEncrypter();
  27.         encrypter.setOleFactory(OleFactory.getInstance());

  28.         String clearPassword = "111111";
  29.         String encryptedPassword = encrypter.encrypt(clearPassword);
  30.         System.out.println("-------------------------------");
  31.         System.out.println("clear password = [" + clearPassword + "]");
  32.         System.out.println("encrypted password = [" + encryptedPassword + "]");

  33.         clearPassword = "222222";
  34.         encryptedPassword = encrypter.encrypt(clearPassword);
  35.         System.out.println("-------------------------------");
  36.         System.out.println("clear password = [" + clearPassword + "]");
  37.         System.out.println("encrypted password = [" + encryptedPassword + "]");

  38.         OleFactory.getInstance().unregistAll();

  39.     }

  40.     private Map<String, ComContainer> oleContainerMap = new HashMap<String, ComContainer>();
  41.     private static OleFactory instance = null;

  42.     private OleFactory() {
  43.     };

  44.     public final static OleFactory getInstance() {
  45.         if (instance == null) {
  46.             instance = new OleFactory();
  47.         }
  48.         return instance;
  49.     }

  50.     /**
  51.      * NOTICE : when using the return object, pay attention about
  52.      * synchonization.
  53.      *
  54.      * @param clsId
  55.      * e.g. "Word.Document", "{11111111-2222-3333-4444-555555555555}"
  56.      */
  57.     public synchronized OleClientSite registAndGetOle(String progId) {
  58.         ComContainer container = oleContainerMap.get(progId);
  59.         if (container == null) {
  60.             container = new ComContainer(progId);
  61.             oleContainerMap.put(progId, container);
  62.         }
  63.         if (!container.isAlive()) {
  64.             container.start();
  65.             while (!container.isReady()) {
  66.                 try {
  67.                     container.join(1000);
  68.                 } catch (InterruptedException e) {
  69.                     throw new RuntimeException(e);
  70.                 }
  71.             }
  72.         }
  73.         return container.getOleClientSite();
  74.     }

  75.     @SuppressWarnings("deprecation")
  76.     public synchronized void unregistOle(String progId) {
  77.         ComContainer container = oleContainerMap.get(progId);
  78.         if (container != null) {
  79.             if (container.isAlive()) {
  80.                 container.notifyStop();
  81.                 try {
  82.                     container.join(1000);
  83.                 } catch (InterruptedException e) {
  84.                 }
  85.                 if (container.isAlive()) {
  86.                     container.stop();
  87.                 }
  88.             }
  89.             oleContainerMap.remove(progId);
  90.         }
  91.     }

  92.     public synchronized void unregistAll() {
  93.         for (String progId : oleContainerMap.keySet()) {
  94.             unregistOle(progId);
  95.         }
  96.     }

  97.     public static class PasswordEncrypter {
  98.         private final String progId = "{488A4255-3236-44B3-8F27-FA1AECAA8844}";
  99.         private OleFactory oleFactory = null;

  100.         public synchronized String encrypt(String password) {
  101.             Kernel32 kernel32 = Kernel32.INSTANCE;
  102.             kernel32.SetLastError(0);
  103.             User32 user32 = User32.INSTANCE;

  104.             OleClientSite clientSite = oleFactory.registAndGetOle(progId);
  105.             // find the window handle of OleFrame
  106.             HWND h = new HWND(new Pointer(clientSite.handle));

  107.             // find the window handle of the ActiveX
  108.             h = user32.GetWindow(h, new DWORD(User32.GW_CHILD));

  109.             // find the window handle of the password input control
  110.             h = user32.GetWindow(h, new DWORD(User32.GW_CHILD));

  111.             // clear previous input first
  112.             final char CHAR_BS = 0x08;
  113.             WPARAM wParam = new WPARAM(CHAR_BS);
  114.             LPARAM lParam = new LPARAM(1);

  115.             OleAutomation ole = new OleAutomation(clientSite);
  116.             int[] rgdispid = ole.getIDsOfNames(new String[] { "TextData" });
  117.             Variant var = ole.getProperty(rgdispid[0]);
  118.             while (OLE.VT_EMPTY != var.getType()) {
  119.                 MyUser32.INSTANCE
  120.                         .SendMessage(h, User32.WM_CHAR, wParam, lParam);
  121.                 var = ole.getProperty(rgdispid[0]);
  122.             }

  123.             // send the new password string
  124.             for (char c : password.toCharArray()) {
  125.                 wParam = new WPARAM(c);
  126.                 int errorNo = 0;
  127.                 int tryTimes = 0;
  128.                 kernel32.SetLastError(0);
  129.                 do {
  130.                     MyUser32.INSTANCE.SendMessage(h, User32.WM_CHAR, wParam,
  131.                             lParam);
  132.                     tryTimes++;
  133.                     errorNo = kernel32.GetLastError();
  134.                 } while (errorNo != 0 && tryTimes < 3);
  135.                 if (errorNo != 0) {
  136.                     throw new RuntimeException("Could not complete the "
  137.                             + "password encoding. error code = " + errorNo);
  138.                 }
  139.             }

  140.             // get the encrypted password
  141.             var = ole.getProperty(rgdispid[0]);
  142.             if (OLE.VT_NULL == var.getType()) {
  143.                 return null;
  144.             } else if (OLE.VT_EMPTY == var.getType()) {
  145.                 return "";
  146.             }
  147.             return var.getString();
  148.         }

  149.         public void setOleFactory(OleFactory oleFactory) {
  150.             this.oleFactory = oleFactory;
  151.         }
  152.     }
  153. }

  154. /**
  155.  * An container for ActiveX/COM components with windows UI.

  156.  * For those without UI, U can also consider using Jacob etc.

  157.  */
  158. class ComContainer extends Thread {
  159.     private Display display = null;
  160.     private Shell shell = null;
  161.     private OleClientSite clientSite = null;
  162.     private boolean stop = false;
  163.     private String progId = null;
  164.     private boolean ready = false;

  165.     /**
  166.      * @param clsId
  167.      * e.g. "Word.Document", "{11111111-2222-3333-4444-555555555555}"
  168.      */
  169.     public ComContainer(String progId) {
  170.         this.progId = progId;
  171.         ready = false;
  172.         stop = false;
  173.     }

  174.     public void run() {
  175.         ready = false;
  176.         stop = false;
  177.         display = new Display();
  178.         shell = new Shell(display);
  179.         shell.setLayout(new FillLayout());
  180.         try {
  181.             OleFrame frame = new OleFrame(shell, SWT.NONE);
  182.             clientSite = new OleClientSite(frame, SWT.NONE, progId);
  183.             clientSite.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
  184.         } catch (SWTError e) {
  185.             e.printStackTrace();
  186.             display.dispose();
  187.             throw e;
  188.         }

  189.         stop = false;
  190.         ready = true;
  191.         shell.setSize(400, 300);
  192.         // shell.open(); // You can uncomment this for an direct view

  193.         // start to dispatch event
  194.         while (!stop && !shell.isDisposed()) {
  195.             if (!display.readAndDispatch()) {
  196.                 display.sleep();
  197.             }
  198.         }

  199.         display.dispose();
  200.     }

  201.     public void notifyStop() {
  202.         stop = true;
  203.     }

  204.     public OleClientSite getOleClientSite() {
  205.         return clientSite;
  206.     }

  207.     public boolean isReady() {
  208.         return ready;
  209.     }
  210. }

  211. interface MyUser32 extends StdCallLibrary, WinUser {
  212.     static MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("user32",
  213.             MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);

  214.     LRESULT SendMessage(HWND hWnd, int Msg, WPARAM wParam, LPARAM lParam);
  215. }


阅读(6239) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~