可移植 JRE API 挂钩 (IAT)






4.45/5 (6投票s)
通过API挂钩使JRE真正便携。
引言
这是关于通过API挂钩使Java运行时环境真正便携的一种方法的说明。桌面开发中最热门和最令人兴奋的事情是桌面虚拟化。我们一直需要将我们喜欢的软件放在USB驱动器上,以便使其便携。但这时会出现一个主要障碍,即在最终用户桌面安装应用程序。换句话说,应用程序需要进行注册表项。进行注册表项本身不是问题。真正的问题在于向HKEY_CLASSES_ROOT进行注册表项,因为所有Windows机器都需要管理员权限才能在此节点下进行条目。
背景
现在,让我们考虑一种使应用程序真正便携的简单方法,即减少其对最终桌面(虚拟注册表)的实际注册表项的依赖。
我个人需要将JRE随身携带在USB上,以便无论最终机器是否安装了JRE,我都可以使用JRE,并且Internet Explorer能够检测到它。这是通过API挂钩(IAT补丁)实现的。
IAT补丁是一种改变可执行文件函数指针的概念,这样,与其调用实际的底层函数,不如调用DLL中存在的替换函数。Windows中的每个应用程序都采用PE(Portable EXE)格式。可以通过PE Viewer查看。CodeProject上提供了关于IAT的优秀文章。
Internet Explorer使用注册表项来确定要加载的Java插件以及查找Java DLL的位置。任何Windows应用程序都会使用注册表API来读取和设置注册表值。因此,我们需要挂钩Advapi32.dll中存在的这些注册表函数。
我们可以伪造Internet Explorer正在查看的关键值,并让它加载我们随身携带的USB驱动器中的JRE DLL,从而使JRE真正便携。
本文档就像一个概念验证,说明我们可以拥有虚拟化的JRE。至少目前而言,它不适用于实时场景。
如何实现
我发现IE 6.0使用了以下注册表函数:
RegOpenKeyEx()
RegOpenKey()
除了advapi32.dll中发现的大多数注册表函数外,Internet Explorer还使用以下键:
- HKEY_CLASSES_ROOT\CLSID\{8AD9C840-044E-11D1-B3E9-00805F499D93}
- HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Plug-in\1.6.0
来确定机器上可用的Java插件以及加载Java DLL的Java主目录。
我们在HKEY_CURRENT_USER下创建一个名为PortableJre的节点,这样即使在非管理员模式下也不会有问题,并添加IE关注的节点,其中键的值指向USB上的Java主目录以及USB上的ssv.dll。
然后,我们挂钩函数RegOpenKeyEx
和RegOpenKey
。
LONG WINAPI RegOpenKey( __in HKEY hKey, __in_opt LPCTSTR lpSubKey, __out PHKEY phkResult ); LONG WINAPI RegOpenKeyEx( __in HKEY hKey, __in_opt LPCTSTR lpSubKey, __reserved DWORD ulOptions, __in REGSAM samDesired, __out PHKEY phkResult );
创建键的代码片段
//Portable JRE and Keys under them are created by the below code Snippet if(RegCreateKey(HKEY_CURRENT_USER,"PortableJRE",&hPortableJRE)==ERROR_SUCCESS) { HKEY hJavaCLISD; std::string strJavaPlugin("Java Plug-in 1.6.0"); if(RegCreateKey(hPortableJRE,"{8AD9C840-044E-11D1-B3E9-00805F499D93}", &hJavaCLISD)==ERROR_SUCCESS) { RegSetValueEx(hJavaCLISD,"",0,REG_SZ, (unsigned char *)strJavaPlugin.c_str(),strJavaPlugin.length()); HKEY hInproc; std::string strDefault(JREPath+"\\bin\\ssv.dll"); std::string strThreadModel("Apartment"); if(RegCreateKey(hJavaCLISD,"InprocServer32",&hInproc)==ERROR_SUCCESS) { RegSetValueEx(hInproc,"",0,REG_SZ, (unsigned char *)strDefault.c_str(),strDefault.length()); RegSetValueEx(hInproc,"ThreadingModel",0,REG_SZ, (unsigned char *)strThreadModel.c_str(),strThreadModel.length()); } } HKEY hSoftware; if(RegCreateKey(hPortableJRE,"SOFTWARE",&hSoftware)==ERROR_SUCCESS) { HKEY hJavaSoft; if(RegCreateKey(hSoftware,"JavaSoft",&hJavaSoft)==ERROR_SUCCESS) { HKEY hJavaPlugin,hJRE; if(RegCreateKey(hJavaSoft,"Java Plug-in",&hJavaPlugin)==ERROR_SUCCESS) { HKEY hplugin160; if(RegCreateKey(hJavaPlugin,"1.6.0",&hplugin160)==ERROR_SUCCESS) { MessageBox(NULL,"plgun1.6.0success","PortableJRE",NULL); RegSetValueEx(hplugin160,"JavaHome",0,REG_SZ, (unsigned char *)JREPath.c_str(),JREPath.length()); } } if(RegCreateKey(hJavaSoft,"Java Runtime Environment",&hJRE)==ERROR_SUCCESS) { HKEY hplugin160; if(RegCreateKey(hJRE,"1.6.0",&hplugin160)==ERROR_SUCCESS) { MessageBox(NULL,"JRE1.6.0success","PortableJRE",NULL); RegSetValueEx(hplugin160,"JavaHome",0,REG_SZ, (unsigned char *)JREPath.c_str(),JREPath.length()); } } } } }
正如你所见,RegOpenKey
和RegOpenKeyEx
使用lpSubKey
参数打开相应的子键。因此,每当我们收到打开8AD9C840-044E-11D1-B3E9-00805F499D93或JavaSoft的请求时,我们都会调用我们为在HKEY_CURRENT_USER下创建的PortableJRE节点调用的OpenKey,然后返回phkResult
,它将指向我们在PortableJRE下的JRE条目。从那时起,IE可能会使用RegEnumKey
、RegEnumKeyEx
、RegQueryValue
、RegQueryValueEx
,这些都将指向USB驱动器上的JRE DLL。
以下是我使用的挂钩函数:
LONG WINAPI MyREGOpenKeyW(HKEY hKey,LPCWSTR lpSubKey,PHKEY phkResult) { REGOpenKeyW_Type oldFn = (REGOpenKeyW_Type)TextHook.Functions[ADVAPI32_REGOpenKeyW].OrigFn; std::wstring strJavasoft(L"JavaSoft"); std::wstring strJRE(L"Java Runtime Environment"); std::wstring strJRECLSID(L""); REGOpenKeyW_Type OpenKeyfun = (REGOpenKeyW_Type)TextHook.Functions[ADVAPI32_REGOpenKeyW].OrigFn; OutputDebugStringA("MyREGOpenKeyW"); if(lpSubKey!=NULL) { std::wstring strSubKey(lpSubKey); OutputDebugString("inside lpsubkey check"); TCHAR test1=(TCHAR)lpSubKey; OutputDebugString(&test1); if(strSubKey.compare(L"JavaSoft")==0) { OutputDebugStringA("In Jaavasoft\n"); HKEY hPortableJRE; LONG lret = OpenKeyfun(HKEY_CURRENT_USER, L"PortableJRE\\SOFTWARE",&hPortableJRE); if(lret==ERROR_SUCCESS) { OutputDebugString("Changing the Key\n"); LONG retVal = oldFn(hPortableJRE,lpSubKey,phkResult); OutputDebugString("RegOpenKeyExA = "); OutputDebugStringW(lpSubKey); OutputDebugString("\n"); return retVal; } } else if(strSubKey.find(L"{8AD9C840-044E-11D1-B3E9-00805F499D93}")!=wstring::npos ) { OutputDebugString("founda a request for ssv.dll"); HKEY hPortableJRE; LONG lret = OpenKeyfun(HKEY_CURRENT_USER,L"PortableJRE",&hPortableJRE); if(lret==ERROR_SUCCESS) { OutputDebugString("Changing the Key for 8ad9c8\n"); LONG retVal = oldFn(hPortableJRE, L"{8AD9C840-044E-11D1-B3E9-00805F499D93}",phkResult); OutputDebugString("RegOpenKeyExA = "); OutputDebugStringW(lpSubKey); OutputDebugString("\n"); return retVal; } } else if(strSubKey.find(L"SOFTWARE\\JavaSoft\\Java Plug-in\\1.6.0")!=wstring::npos) { OutputDebugString("In Java-plug-in\n"); HKEY hPortableJRE; LONG lret = OpenKeyfun(HKEY_CURRENT_USER,L"PortableJRE",&hPortableJRE); if(lret==ERROR_SUCCESS) { OutputDebugString("Changing the Key\n"); LONG retVal = oldFn(hPortableJRE,lpSubKey,phkResult); OutputDebugString("RegOpenKeyExA = "); OutputDebugStringW(lpSubKey); OutputDebugString("\n"); return retVal; } } OutputDebugString("after lpsubkey check"); OutputDebugString("No Match for Hooking registry"); } LONG retVal = oldFn(hKey,lpSubKey,phkResult); OutputDebugString("RegOpenKeyW = "); OutputDebugStringW(lpSubKey); OutputDebugString("\n"); return retVal; }
我实现的挂钩函数将查看我们创建的PortbaledJRE下的键,并最终从USB驱动器加载JRE DLL。
测试PortableJRE
- 将包含JRE 1.6.0及其子目录的Java目录复制到已安装JRE 1.6的机器的USB驱动器上。
- 在Win2K机器上启动PortableJRE.EXE。你应该会看到一个消息框,提示钩子已安装。
- 你可以启动Internet Explorer并查看一个嵌入了applet的页面。
你应该会看到从USB加载了JRE,并且从Java控制台中,你应该会看到正在使用JRE 1.6。
USB闪存驱动器上的文件应该如下所示:
- /PortableJRE.Exe
- /TestDll.dll
- /Java/jre1.6.0/
依赖项
- 仅测试了JRE 1.6。
- 仅测试了Win2K。
- 已在IE 6.0上进行测试。
另外,请确保你使用的页面具有用于加载Java applet的<object>
标签,如下所示:
<OBJECT
classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
width="200" height="200">
<PARAM name="code" value="Applet1.class">
</OBJECT>
结论
本文档展示了API挂钩的力量以及虚拟化应用程序的优势。如果您需要更多关于此的信息,请告诉我。
参考文献
历史
初始版本仅适用于Win2K和Internet Explorer 6。稍后将推出适用于WinXP和IE 7的版本。