Introduction
This is an illustration of an approach to make the Java Runtime Environment truly portable by API Hooking. The most hot and happening stuff in Desktop development is Desktop Virtualization. We always had a need for carrying out our favorite software on a USB drive to make it portable. But there comes a major hindrance, the installation of the application on to the end Desktop. In other words, the application needs to make Registry entries. Making a Registry entry is not a problem though. The actual problem is making Registry entries on HKEY_CLASSES_ROOT as all Windows machines need administrative privileges to make an entry under this node.
Background
Now, let us consider a simple way of making an application truly portable, by making it less dependant on the actual Registry entries of the end Desktop (virtualized Registry).
Personally, I had a need to carry JRE along with me on USB, so that irrespective of whether the end machine has JRE or not, I wanted JRE which could be detected by Internet Explorer. This is done with the help of API Hooking (IAT patching).
IAT patching is a concept of changing the function pointers of an executable so that instead of calling the actual underlying function, the replacement function that we have in a DLL is called. Every application in Windows is in the form of a PE (Portable EXE). This can be viewed through the PE Viewer. Good articles about IAT are available here in CodeProject.
Internet Explorer uses Registry entries to determine the Java plug-in to be loaded and the location to find the Java DLLs. Any Windows application would use Registry APIs to read and set Registry values. So, we need to hook these Registry functions available in Advapi32.dll.
We can fake Internet Explorer on the key values it's looking at, and make it to load the JRE DLLs in the USBs that we carry, thus making JRE truly portable.
This article is like a proof of concept that we can have virtualized JRE .It’s not in anyway suitable for a real time scenario, at least for now.
How it's done
I found out that IE 6.0 uses the following Registry functions:
RegOpenKeyEx()
RegOpenKey()
In addition to most of the Registry functions found in advapi32.dll, Internet Explorer also uses the following keys:
- HKEY_CLASSES_ROOT\CLSID\{8AD9C840-044E-11D1-B3E9-00805F499D93}
- HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Plug-in\1.6.0
to determine the Java plug-in available on the machine and the Java home directory from where the Java DLLs are loaded.
We create a node PortableJre under HKEY_CURRENT_USER, which will not have a problem even in non-Admin mode, and add the nodes looked upon by IE with values for the keys pointing to the Java home in the USB as well as to the ssv.dll in the USB.
Then, we hook the functions RegOpenKeyEx
and 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
);
Code snippet to create keys
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());
}
}
}
}
}
As you can see, the RegOpenKey
and RegOpenKeyEx
use the lpSubKey
parameter to open the corresponding subkey. So, whenever we get a request to open either 8AD9C840-044E-11D1-B3E9-00805F499D93 or JavaSoft, we call the OpenKey for the PortableJRE node that we created under HKEY_CURRENT_USER, and then return the phkResult
which will point to our JRE entries under PortableJRE. From then on, IE might use RegEnumKey
, RegEnumKeyEx
, RegQueryValue
, RegQueryValueEx
which will all result in pointing to JRE DLLs on the USB drive.
Below is the hook function that was used by me:
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;
}
The hooked function that was implemented by me will look at the keys under PortbaledJRE that was created by us and will eventually load the JRE DLLs from a USB Drive.
Testing the PortbaleJRE
- Copy the Java directory containing JRE 1.6.0 and the subdirectories on to a USB drive from a machine which has JRE 1.6 installed.
- Start the PortableJRE.EXE on a Win2K machine. You should be seeing a message box saying that the hook is installed.
- You can start Internet Explorer and look at a page which has an applet embedded in it.
You should be seeing that the JRE from the USB is loaded, and from the Java console, you should be seeing that JRE 1.6 is used.
The files should be like below on the USB pen drive:
- /PortableJRE.Exe
- /TestDll.dll
- /Java/jre1.6.0/
Dependencies
- Have tested it with JRE 1.6 only.
- Have tested it for Win2K only.
- Have tested it on IE 6.0.
Also, please make sure that the page that you use has the <object>
to load Java applets, like below:
<OBJECT
classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
width="200" height="200">
<PARAM name="code" value="Applet1.class">
</OBJECT>
Conclusion
This article was an illustration of the power of API hooking and the advantages of virtualized applications. Please let me know if you need any more info on this.
References
History
Initial version which will run only on Win2K and Internet Explorer 6. Will come up with one which works for WinXP and IE 7 as well.