实现WINDOWS平台下帮助文件的反编译

摘要:本文论述了CHM及HLP类型的帮助文件反编译的意义及其反编译工具的实现。介绍了WIN32程序中进程的创建,C++Builder中资源的创建及调用与VC中的资源创建及管理的区别。

关键词:hlp,chm,API,反编译,进程,C++Builder,TresourceStream
中图分类号:TP3     文献标志识码 B

软件的易用性很大程度上受其帮助系统的好坏的影响,优秀的软件必须有优秀的帮助系统。作为一个程序员不但要能够制作自己的帮助系统,而且还要能够反编译这些帮助系统的文件,从而对这些文件中的资源进行重新编辑和修改,于是如何提取帮助文件中的资源便成为一个必须完成的任务。

目前WINDOWS系统中使用的帮助文件主要有两种格式,扩展名为hlp的文件和chm的文件。Hlp文件是较早的一种帮助文件,目前使用最多的是chm文件。现在国内外反编译帮助文件的工具很少,,作者曾在网络上发现过这样的工具,可是都是DOS下的程序,很难操作,界面也不太友好,于是制作一个WINDOWS下的程序就是相当必要的了。制作这个工具的方案有两种,一种是代码移植,一种是给DOS程序加上WINDOWS外壳。第一种方式比较繁琐,我选用第二种方式。下面对反编译HLP文件的DOS程序进行移植,制作一个WINDOWS中使用的工具。

这个工具的实现有两个技术要点:

  1. 后台进程的创建
  2. 在C++Builder 中如何实现资源的分离

1. 后台进程的创建

在Win32中,为保证多任务之间的独立性,当各个模块可以分开独立执行时,可以使用多进程。创建进程可以使用CreateProcess函数。

该函数的原形为

BOOL CreateProcess(
    LPCTSTR lpApplicationName,
    LPCTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)

CreateProcess函数是一个Windows API函数,它的含义包含在Windows.h头文件中。它的参数多而繁杂,下面介绍它的各个参数的含义:

参数说明
lpApplicationName指向可执行文件名的字符串,注意必须为路径全名。
LpCommandLine指向可执行的应用程序的命令行的字符串。若lpApplicationName和lpCommandLine都不为空,则lpApplicationName指定执行模块,lpCommandLine指定命令行,如果任何一个为空,WINDOWS使用不空的那一个来调用可执行程序。
lpProcessAttributes指向一个安全的结构,用以说明创建进程的安全属性。
lpThreadAttributes用以指定创建进程的主线程的安全属性。
bInheritHandles决定新进程是否从调用进程中继承句柄。如为真,则调用进程中每个可继承的打开句柄。
dwCreationFlags为新进程的附加标志。
lpEnvironment指向一个用于新进程的环境块。如果此参数为空,则新进程使用调用进程的环境。
lpCurrentDirectory指向一个字符串,该字符串将创建进程指定当前工作驱动器和目录,必须为一全路径名。如果此字符串为空,则新进程使用调用进程一样的当前工作驱动器和工作目录。
lpStartupInfo指向STARTUPINFO 结构,该结构用于指定新进程的主窗口启动时如何显示。
lpProcessInformation参数指向一个PROCESS_INFORMATION 结构,该结构用于接受有关新进程的标识信息。

除了用CreateProcess函数创建进程外,还可以使用WinExec函数和LoadModule函数。WinExec源于Windows3.1操作系统,但在Win32中仍然可用。WinExec函数较CreateProcess函数简单的多,但是与之相应的是少了许多控制或安全的功能。此函数原形如下:

UINT WinExec(LPCSTR lpCmdLine,UINT uCmdShow);

参数说明
lpCmdLine是命令行
uCmdShow是应用程序窗口显示方式

LoadModule函数的使用也比CreateProcess函数简单,其原形如下:

DWORD LoadModule(
    LPCSTR lpModuleName,      // 指向可执行文件名的字符串
    LPVOID lpParameterBlock   // 指向LOADPARMS32 结构,LOADPARMS32 结构定义了程序参数,包括如何显示,命令参数等。
);

如果想结束一个进程,可以调用ExitProgress 函数或TerminateProgress 函数,若使用ExitProgress,则此进程的所有附属的DLL和线程都将被终止。

2.在 C++Builder 中如何实现资源的分离

C++Builder在资源的创建和引用中与VC不同。在VC中有专门的资源编辑器,只要在资源编辑器中创建了资源并赋予ID,然后引用ID就可以了。虽然感觉VC这种可视化的资源管理方式很方便,可是感觉总是不如C++Builder对资源管理和引用灵活。在C++Builder中只要用记事本按照“资源名资源类型 文件名(包括路径)”的格式书写,然后保存为扩展名为rc的文件,将这个文件加入项目中。完成以上步骤后就可以用TresourceStream类中的方法处理定义的这些资源。

具体实现请看如下代码。

GetCurrentDirectory(sizeof(currentpath),currentpath);

if(FileListBox1->FileName!="")
{
    tmppath=FileListBox1->FileName.Delete(FileListBox1->FileName.Length()-3,4);
    CreateDirectory(tmppath.c_str(),NULL); 
    strcpy(exefile,(AnsiString(tmppath)+AnsiString("\\Helpdeco.exe")).c_str());
    ret=GetFileAttributes(exefile);
    CopyFile(FileListBox1->FileName.c_str(),(AnsiString(tmppath)+AnsiString("\\tmp.hlp")).c_str(),FALSE);

    if(ret=0xffffffff)
    {
        TResourceStream &rs = *new TResourceStream((int)HInstance,AnsiString("helpfile"),"HELPFILE");
        rs.SaveToFile(AnsiString(exefile));
        delete &rs;
        cmd=AnsiString(tmppath)+"\\helpdeco.exe"+" "+"tmp.hlp";
        ShowMessage(cmd);  
        proc_create_state=CreateProcess(NULL,cmd.c_str(),NULL,NULL,false,NULL,NULL,tmppath.c_str(),&siStartInfo,&piProcInfo);

        if(proc_create_state) ShowMessage("反编译成功");

        TerminateThread(piProcInfo.hProcess,0);//终止进程    DeleteFile((AnsiString(tmppath)+"\\Helpdeco.exe").c_str());
        DeleteFile((AnsiString(tmppath)+"\\tmp.hlp").c_str());
    }
}
else
{
    ShowMessage("请选择要反编译的文件");
}

结论:本文的目的在于推荐一种DOS程序向WINDOWS程序移植的方式。用这种方式制作的工具可以实现:选择需要编译的文件即可在和这个文件相同的目录里创建一个文件夹,存放反编译提取的资源。此工具经过作者的多次测试,运行稳定,占用系统资源较少,不产生临时的垃圾文件,基本能够满足用户的要求。

参考文献

[1] 梁肇新 编著,编程高手箴言,电子工业出版社,2003年10月第一版

Contributors: FHL