在WINDOWS NT 中有一個功能強大的SERVICE 管理器, 它管理著一部分實現重要功能的後台進程, 例如FTP.HTTP.RAS. 網路Message 等等, 這些後台進程被稱之為Service, 他們可以在系統啟動時就載入, 可以運行在較高的優先順序, 可以說是非常靠近系統核心的裝置驅動程式中的一種. WINDOWS95 沒有提供SERVICE 管理器, 取而代之的是一個簡單的登記介面, 可以類似的稱之為WINDOWS95 下的Service( 不過嚴格的講,WINDOWS95 下是沒有Service 的), 同樣的, 通過這個登記介面, 我們可以使自己的程式隨系統啟動而最先運行, 隨系統關閉而最後停止, 和作業系統結合在一起, 實現許多獨特的功能. 首先我們可以先來看看一些相關的知識。
進程資料庫(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, 自己建一個也是可以的.
常式
---- 下面是實現常式, 所有代碼經過了測試, 可以方便的加入到自己的專案檔中.
---- 標頭檔:
// 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 一直保持開啟狀態直至受到響應或系統關機.