WINDOWS95 及WINDOWS98 的 后 台 进 程

黄 飞


后 台 进 程

---- 在WINDOWS NT 中 有 一 个 功 能 强 大 的SERVICE 管 理 器, 它 管 理 着 一 部 分 实 现 重 要 功 能 的 后 台 进 程, 例 如FTP.HTTP.RAS. 网 络Message 等 等, 这 些 后 台 进 程 被 称 之 为Service, 他 们 可 以 在 系 统 启 动 时 就 加 载, 可 以 运 行 在 较 高 的 优 先 级, 可 以 说 是 非 常 靠 近 系 统 核 心 的 设 备 驱 动 程 序 中 的 一 种. 在WINDOWS95 下,Microsoft 没 有 提 供 这 样 一 个 高 度 集 中 化 了 的 管 理 器, 不 过 我 们 通 过VC 自 带 的PVIEW 可 以 看 到, 在 桌 面 背 后 同 样 有 秘 密 的 后 台 进 程 存 在, 例 如:SysTray, 电 源 管 理 等. 其 实, 这 些 就 是WINDOWS95 管 理 的 后 台 进 程,WINDOWS95 没 有 提 供SERVICE 管 理 器, 取 而 代 之 的 是 一 个 简 单 的 登 记 接 口, 可 以 类 似 的 称 之 为WINDOWS95 下 的Service( 不 过 严 格 的 讲,WINDOWS95 下 是 没 有Service 的), 同 样 的, 通 过 这 个 登 记 接 口, 我 们 可 以 使 自 己 的 程 序 随 系 统 启 动 而 最 先 运 行, 随 系 统 关 闭 而 最 后 停 止, 和 操 作 系 统 结 合 在 一 起, 实 现 许 多 独 特 的 功 能. 我 在 实 际 工 作 中, 仔 细 的 分 析 了 这 个Windows95 的 接 口, 并 且 测 试 后 发 现, 在WINDOWS97 和 最 新 的WINDOWS98 中 它 一 样 有 效. 并 通 过 这 个 机 制, 成 功 的 实 现 了WINDOWS95.98 下 的 无 人 职 守 监 控. 下 面 是 关 于 这 个 接 口 的 分 析 结 果 和 一 些 准 备 知 识.

进 程 数 据 库(PDB) 介 绍

---- 在Windows 的 核 心 数 据 结 构 中, 有 一 个 重 要 的 进 程 管 理 结 构 叫 进 程 数 据 库, 它 位 于Kernel32 的 公 用 内 存 堆 中, 可 以 通 过GetCurrentProcessID(...) 得 到 指 向 该 结 构 的 指 针, 以 下 是 部 分PDB 的 组 成, 与 本 文 直 接 相 关 的 是PDB 偏 移21h 处 的Service 标 志 字 节, 通 过 后 面 的 伪 码 分 析, 我 们 可 以 清 楚 的 看 到 所 谓 登 记 为Windows95 或Windows98 下 的Service 进 程, 只 不 过 是 把 它 相 应 的PDB 中 该 标 志 字 节 置 为1 而 已.

 偏 移 量 长 度             说 明
============================================
+00h   DWORD      Type // Kernel32 对 象 的 类 型
+04h   DWORD      CReference // 参 考 计 数
+08h   DWORD      Un1 // 未 知
+0ch   DWORD      pSomeEvent // 指 向K32OBJ_EVENT 指 针
+10h   DWORD      TerminationStatus // 活 动 标 志 或 返 回 值
+14h   DWORD      Un2 // 未 知
...
+21h   BYTE       Flags1 // Service 标 记,
                         // "1" 是Service 进 程,
                         // "0" 普 通 进 程
...
+24h   DWORD      pPSP  // DOS PSP 指 针
...
============================================

实 现 接 口

---- (1) Windows95 中 提 供 的 简 单 的Service 接 口 是 一 个32 位 的API: RegisterServiceProcess, 由 于 在VC++ 的Online help 中 得 不 到 关 于 这 个API 的 确 切 解 释, 笔 者 不 得 不 针 对 此API 进 行 了 逆 向 分 析, 以 下 是 在Windows95 的Kernel32.dll 中 该API 的 伪 码. 我 们 可 以 清 楚 的 看 到Window95 内 部 到 底 是 怎 样 做 的, 其 实 处 理 的 非 常 简 单.

BOOL RegisterServiceProcess( DWORD dwProcessID, DWORD dwType )
{
  HANDLE dwPID;
  if( dwProcessID == NULL )
     dwPID = dwCurrentProcessID; // Get global kernel32 variable
  else
      // Call some kernel functions
     if( ( dwPID = CheckPID( dwProcessID ) == NULL )
         return FALSE;
  if( dwType == 1 )
  {
     *(BYTE *)( dwPID + 0x21 ) | = 0x01;
     return TRUE;
  }
  if( dwType == 0 )
  {
     *(BYTE *)( dwPID + 0x21 ) & = 0xFE;
     return TRUE;
  }
  return FALSE;
}
    以 下 为 函 数 原 形:
BOOL    RegisterServiceProcess( DWORD dwPID, DWORD dwType )
 参 数:   dwPID: 进 程ID, NULL 代 表 当 前 进 程
        dwType: RSP_SIMPLE_SERVICE 为 登 记
                RSP_UNREGISTER_SERVICE 为 取 消 登 记
 返 回 值: TRUE:   调 用 成 功
        FALSE:  调 用 失 败

---- (2) 另 外, 为 了 让Service 进 程 有 机 会 在BOOT 后 就 启 动,Windows95 的Registry 中 提 供 了 加 载 方 法: 在KEY " MyComputer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices " 加 入 自 己 的 应 用 程 序 命 令 行, 即 可 实 现 开 机 自 动 加 载. 当 然, 如 果 你 得 机 器 中 没 有 这 个Key, 自 己 建 一 个 也 是 可 以 的.

例 程

---- 下 面 是 实 现 例 程, 所 有 代 码 经 过 了WINDOWS95. WINDOWS98 BETA3 的 测 试, 可 以 方 便 的 加 入 到 自 己 的 项 目 文 件 中.

---- 头 文 件:

// File:           service.h
// The head file of "service.cpp"
// Note: 1. You must use C++ compiler
//     2. The platform is WIN32 (WINNT & WIN95)

#ifndef _SERVICE_H
#define _SERVICE_H

/////////////////////////////////////////////////// USED FOR WIN95 SERVICE
// Micros
#define RSP_SIMPLE_SERVICE                      1
#define RSP_UNREGISTER_SERVICE          0

// Function types for GetProcAddress
#define RegisterServiceProcess_PROFILE  (DWORD (__stdcall *) (DWORD, DWORD))

// Service Fuctions in Win95
BOOL    W95ServiceRegister(DWORD dwType);
BOOL    W95StartService( DWORD dwType );

#endif

CPP 文 件:
// File:                service.cpp --- implement the service

#include "service.h"
/////////////////////////////////////////////////// USED FOR WIN95 SERVICE
 登 记 为Service 子 程 序:
/////////////////////////////////////////////////////////////////////////////////
// Define:              BOOL    W95ServiceRegister(DWORD dwType)
// Parameters:  dwType  --- Flag to register or unregister the service
//            RSP_SIMPLE_SERVICE      means register
//            RSP_UNREGISTER_SERVICE  means unregister
// Return:  TRUE --- call success; FALSE --- call failer

BOOL    W95ServiceRegister( DWORD dwType )
{
        // Function address defination
        DWORD   (__stdcall * hookRegisterServiceProcess)
                   ( DWORD dwProcessId, DWORD dwType ); 

        // Get address of function
        hookRegisterServiceProcess = RegisterServiceProcess_PROFILE
                                                 GetProcAddress
   (GetModuleHandle("KERNEL32"),
  TEXT("RegisterServiceProcess"));

        // Register the WIN95 service
        if(hookRegisterServiceProcess(NULL,dwType)==0)
                return FALSE;
        return TRUE;
}

 加 入 注 册 表 子 程 序:

#define SERVICE_NAME    TEXT("SERVICE")
// Define:              BOOL    W95StartService( DWORD dwType )
// Parameters:  dwType  --- Flag to register or unregister the service
//            RSP_SIMPLE_SERVICE      means register
//            RSP_UNREGISTER_SERVICE  means unregister
// Return: TRUE --- call success; FALSE --- call failer

BOOL W95StartService( DWORD dwType )
{
        // Local Variables
        TCHAR   lpszBuff[256];
        LPTSTR  lpszStr = lpszBuff +128;
        LPTSTR  lpszName        = lpszBuff;
        HANDLE  hKey            = NULL;
        DWORD   dwStrCb         = 0;
        DWORD   dwValueType     = 0;

        // Get service name currently
        lpszName = GetCommandLine();
        for( int i = _tcslen(lpszName)-1; i>=0; i-- )
        {
                if( ( lpszName[i] != '"' )&&( lpszName[i]!=' ') )
                        break;
                else if( lpszName[i] == '"' )
                        lpszName[i] = '\0';
        }
        if( lpszName[0] == '"' )
                lpszName = lpszName +1;

        // Registe as start up service
        if( RegOpenKeyEx (HKEY_LOCAL_MACHINE,
TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunServices"),
                     0,
                     KEY_QUERY_VALUE | KEY_SET_VALUE,
                     &hKey ) != ERROR_SUCCESS )
        {
                if( RegCreateKey(       HKEY_LOCAL_MACHINE,
TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunServices"),
                                        &hKey ) != ERROR_SUCCESS )
                {
                        //DebugOut( "RegCreateKey() error!");
                        return FALSE;
                }
        }

        dwValueType     = REG_SZ;
        dwStrCb         = 128;

        // Take value
        if( RegQueryValueEx(hKey, 
                                SERVICE_NAME,
                                0,
                                &dwValueType,
                                (LPBYTE)lpszStr,
                                &dwStrCb ) == ERROR_SUCCESS )
        
        {
                // Find this key value
                if( _tcscmp( lpszStr, lpszName )==0 )
                {
                        // Remove the service
                        if( dwType == RSP_UNREGISTER_SERVICE )
                        {
if( RegDeleteValue( hKey, SERVICE_NAME ) == ERROR_SUCCESS )
                                {
                                        RegCloseKey ( hKey );
                                        return TRUE;
                                }
                                RegCloseKey( hKey );
                                return FALSE;
                        }
                        // Already exist service
                        if( dwType == RSP_SIMPLE_SERVICE )
                        {
                                //DebugOut("Already registed!");
                                RegCloseKey( hKey );
                                return TRUE;
                        }
                }
                // Not find it
        } // No this value

        // Unregiste return
        if( dwType == RSP_UNREGISTER_SERVICE )
        {
                RegCloseKey( hKey );
                return TRUE;
        }

        // No this value then create it
        if( dwType == RSP_SIMPLE_SERVICE )
        {
                dwStrCb = 128;

                // Set value
                if( RegSetValueEx(hKey,
                                        SERVICE_NAME,
                                        0,
                                        REG_SZ,
                                        (CONST BYTE *)lpszName,
                                        dwStrCb ) != ERROR_SUCCESS )
                {
                        //DebugOut("RegSetValueEx() error!");
                        RegCloseKey( hKey );

                        return FALSE;
                }
                RegCloseKey( hKey );
                return TRUE;
        }

        // Unknow type
        RegCloseKey( hKey );
        return FALSE;
}

 主 程 序:
// WinMain function is the entry of the this program
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
        if( W95ServiceRegister( RSP_SIMPLE_SERVICE ) )
        {
                W95StartService( RSP_SIMPLE_SERVICE );
        }

        MessageBox(NULL, "Sample service", "SERVICE", MB_OK );
        UNREFERENCED_PARAMETER( hInstance );
        UNREFERENCED_PARAMETER( lpCmdLine );
        UNREFERENCED_PARAMETER( nCmdShow );
        UNREFERENCED_PARAMETER( hPrevInstance );
        return 0;
}

---- 运 行 这 个 程 序, 等 到MessageBox 弹 出 后, 从WINDOWS 中 退 出 到LOG ON 状 态, 你 会 看 见MessageBox 一 直 保 持 打 开 状 态 直 至 受 到 响 应 或 系 统 关 机. 所 以 要 做WINDOWS95 下 系 统 级 的 后 台 进 程, 并 不 一 定 非 要 去 编 写 容 易 引 起 系 统 混 乱 的VXD 程 序, 在 硬 件 部 分 允 许 的 情 况 下, 我 认 为 本 文 介 绍 的 方 法 更 加 方 便 有 效.