65.9K
CodeProject 正在变化。 阅读更多。
Home

可移植 JRE API 挂钩 (IAT)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (6投票s)

2008年11月22日

CPOL

4分钟阅读

viewsIcon

40235

downloadIcon

385

通过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

然后,我们挂钩函数RegOpenKeyExRegOpenKey

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());
                }

            }

        }
    }
}

正如你所见,RegOpenKeyRegOpenKeyEx使用lpSubKey参数打开相应的子键。因此,每当我们收到打开8AD9C840-044E-11D1-B3E9-00805F499D93或JavaSoft的请求时,我们都会调用我们为在HKEY_CURRENT_USER下创建的PortableJRE节点调用的OpenKey,然后返回phkResult,它将指向我们在PortableJRE下的JRE条目。从那时起,IE可能会使用RegEnumKeyRegEnumKeyExRegQueryValueRegQueryValueEx,这些都将指向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

  1. 将包含JRE 1.6.0及其子目录的Java目录复制到已安装JRE 1.6的机器的USB驱动器上。
  2. 在Win2K机器上启动PortableJRE.EXE。你应该会看到一个消息框,提示钩子已安装。
  3. 你可以启动Internet Explorer并查看一个嵌入了applet的页面。

你应该会看到从USB加载了JRE,并且从Java控制台中,你应该会看到正在使用JRE 1.6。

USB闪存驱动器上的文件应该如下所示:

  • /PortableJRE.Exe
  • /TestDll.dll
  • /Java/jre1.6.0/

依赖项

  1. 仅测试了JRE 1.6。
  2. 仅测试了Win2K。
  3. 已在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的版本。

© . All rights reserved.