亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > ASM > 正文

用匯編寫系統(tǒng)服務程序

2019-11-17 05:51:38
字體:
供稿:網(wǎng)友
用匯編寫系統(tǒng)服務程序
本想寫一篇關于服務的文章,結(jié)果搜了一遍以后,發(fā)現(xiàn)發(fā)表于天極網(wǎng)上的一篇文章《Win32程序設計之服務》把要說的大部分東西都說了,為了不做重復勞動,所以在這里首先引用這篇文章來說明服務程序的原理,在后面的補充一節(jié)中再補上一些內(nèi)容和一個用匯編實現(xiàn)服務的源代碼。

======================================================================
第一部分:轉(zhuǎn)載《Win32程序設計之服務》

發(fā)表于:yesky
編譯:QQ新人類
原始位置:http://www.chinabyte.com/20010528/181751.sHtml
======================================================================

每個操作系統(tǒng)都需要有在后臺執(zhí)行任務的方法,無論是誰正在使用這部機器,這些任務都可以繼續(xù)運行,后臺任務可以處理各種重要的服務,包括系統(tǒng)的或者用戶的。例如,一個信使服務可以監(jiān)控網(wǎng)絡,并且在接收到另一臺機子的信息時,可以顯示一個對話框。一個發(fā)送和接收傳真的應用需要在啟動的時候運行,并且不斷地監(jiān)控負責傳真的modem,看有沒有傳真進來。一個家庭的或者辦公室的安全程序,用來控制一件檢測設備時,它需要不時地查詢傳感器,并且在適當?shù)臅r候響應它。所有這些任務都需要CPU時間來執(zhí)行它們,不過由于它們需要的CPU時間很少,因此可以放在后臺而不影響用戶使用系統(tǒng)。

  在MS-DOS中,后臺的任務是通過TSR(Terminate and Stay Resident)程序來處理的。這些程序經(jīng)由autoexec.bat文件開始。在UNIX中,后臺任務是通過Daemons來處理的。在每次啟動UNIX的過程中,你都可以看到操作系統(tǒng)啟動一些任務,例如定時的程序(Cron)和Finger的daemons,然后才可以讓首個用戶登錄。在Windows NT中,后臺的任務被稱為服務。服務可在每次NT啟動的時候運行,并且不管是誰登陸,都會一直運行下去。

  Windows NT的服務都是通過一般的可執(zhí)行程序?qū)崿F(xiàn)的,不同的是,它遵循內(nèi)部的一個特定協(xié)議來設計,以便它們能夠與服務控制治理器(SCM,Service Control Manager)進行正確的交互。在這篇文章中,你將學習到如何在Windows NT中創(chuàng)建和安裝簡單的Win32服務。一旦你懂得了這個簡單的服務,你要建立自己的服務也不難了,因為所有的服務,不論是如何地復雜,都必須包含有同樣基本的SCM接口代碼。只要符合SCM的要求,其實為服務設計的可執(zhí)行文件和一般的程序并沒有多少的區(qū)別。

  無論是對于編程者或者系統(tǒng)治理員,了解NT的服務如何工作都是很重要的。編程者就不必說了,因為他們要創(chuàng)建自己的服務,而對于系統(tǒng)治理員,也是同樣重要的。因為后臺的任務可以是很危險的。MS-DOS和Macintosh系統(tǒng)都是一個病毒的溫床,因為它們在安全性方面先天不足,它們都可以答應任何人或者程序在任何時間創(chuàng)建后臺的任務。Windows NT和UNIX系統(tǒng)是較安全的,因為只有系統(tǒng)治理員才可以為系統(tǒng)增加后臺的任務,不過,假如系統(tǒng)治理員加入了一個破壞性的后臺程序,就它就可以為所欲為了。因此系統(tǒng)治理員要了解Windows NT服務的技巧和權限設置,就可以避免加入有潛在危險的后臺任務。

基本的概念

  服務有兩種不同的形式。驅(qū)動器服務使用驅(qū)動器協(xié)議,讓NT可以與特定的硬件進行通信。另一個是Win32服務,通過一般的Win32 API來實現(xiàn)后臺任務。這篇文章的重點是談Win32服務,因為它們更為常見,而且創(chuàng)建起來也很輕易。任何的NT編程者通過使用一般的NT SDK(或者Visual C++),并且可以用治理員的身份訪問一臺NT機器,都可以實現(xiàn)和安裝自己的Win32服務。假如你想創(chuàng)建一些在Windows NT啟動時就運行的程序,并且要求它會在系統(tǒng)中一直運行,你就要使用Win32的服務。

  在NT中,服務通過控制面板進行治理。在控制面板中,你會發(fā)現(xiàn)有一個服務的圖標,打開它你會看到所有Win32服務的清單。在那里你可以開始、停止、暫停和繼續(xù)某個服務。你按下其中的啟動按鈕后,就會出現(xiàn)一個對話框,你可以修改啟動操作以及服務使用的默認帳號。一個服務可以在系統(tǒng)啟動的時候自動運行,也可以被完全禁止?;蛘咴O置為手動執(zhí)行。在手動的時候,用戶還可以設置啟動的參數(shù)。要對服務中的項目作修改的話,你需要以一個治理員或者超級用戶的身份登錄。

  Windows NT自帶有一些預裝的任務,用來處理諸如網(wǎng)絡信使服務的操作或者使用“at”命令定時執(zhí)行的操作,以及分布的RPC命名。在你創(chuàng)建自己的服務時,你必須執(zhí)行一個獨立的安裝步驟,以將服務的信息插入到服務治理工具的列表中,這些信息包括有新服務的名字、執(zhí)行文件的名字和啟動的類型等,都會寫入到注冊表中,這樣在機器下次啟動的時候,SCM就會得到新服務的相關信息。

創(chuàng)建一個新的服務

  執(zhí)行服務的程序也是一個EXE文件,不過它必須符合某些特定的規(guī)范,以便可以與SCM進行正確的交互。微軟很細致地設計了函數(shù)調(diào)用的流程,你必須遵循這些流程,否則你的服務就不能工作。具體的規(guī)定如下所列。你可以在Win32編程者的參考指南中找到以下涉及的函數(shù),這些資料在SDK的Win32在線幫助或者Visual C++都有:

   .服務的代碼必須要有一個一般的main或者WinMain函數(shù)。這個函數(shù)應該會馬上調(diào)用StartServiceCrtlDispatcher函數(shù)。通過調(diào)用這個函數(shù),你可以讓SCM得到ServiceMain函數(shù)的指針,這樣在SCM要啟動該服務時,就可以調(diào)用它

   .在SCM要啟動服務的時候,就會調(diào)用ServiceMain函數(shù)。例如,假如治理員在服務治理器中按下啟動的按鈕,SCM就會在一個獨立的線程中執(zhí)行ServiceMain函數(shù)。ServiceMain應該調(diào)用RegisterServiceCtrlHandler函數(shù),這樣可以注冊一個Handler函數(shù),以便SCM對服務進行控制。Handler函數(shù)的名字可以是任意的,不過它會在Handler下的文檔中列出來。RegisterServiceCtrlHandler函數(shù)會返回一個句柄,在服務需要發(fā)送狀態(tài)信息給SCM時,可以通過該句柄進行。

   .ServiceMain函數(shù)也必須啟動做該服務實際工作的線程。在服務停止前,ServiceMain函數(shù)是不應該有返回的。當它返回的時候,服務已經(jīng)停止了。

   .Handler函數(shù)包含了一個switch語句,用來分析由SCM傳送過來的請求。默認的情況,SCM可以發(fā)送以下任何的的控制常數(shù):

     SERVICE_CONTROL_STOP - 要服務停止
     SERVICE_CONTROL_PAUSE - 要服務暫停
     SERVICE_CONTROL_CONTINUE - 要服務繼續(xù)
     SERVICE_CONTROL_INTERROGATE - 要服務馬上報告它的狀態(tài)
     SERVICE_CONTROL_SHUTDOWN - 告訴服務即將關機

  也可以創(chuàng)建自定義的常數(shù)(值在128到255之間),并且通過SCM發(fā)送給服務。

  假如你創(chuàng)建的EXE包括有以上提到的main、ServiceMain和Handler函數(shù),以及執(zhí)行服務自身任務的線程函數(shù),那么你的服務程序設計就完成了。以下的圖總結(jié)了這些不同的函數(shù)和SCM之間的交互:



  在本文的最后還有幾段程序的列表,其中列表一為我們展示了一個可能是最簡單的服務。該服務只發(fā)出"嘟"的響聲。默認的狀態(tài)下,它每兩秒響一次。你可以通過啟動的參數(shù)來修改發(fā)聲的間隔。這個服務挺完整,它可以正確響應SCM傳來的每個控制信號。因此,這個程序可作為你創(chuàng)建自己服務的一個很好的模板。

  main函數(shù)通過調(diào)用StartServiceCtrlDispatcher來注冊ServiceMain函數(shù)。注冊的操作使用了一個SERVICE_TABLE_ENTRY結(jié)構的數(shù)組。在這個例子中,該程序只包含了一個服務,因此在表中只會有一個項目。不過,對于一個EXE文件,可以創(chuàng)建幾個任務,這樣在表中就會有幾項,以識別不同的ServiceMain函數(shù)。在調(diào)用StartServiceCtrlDispatcher之前,可在main函數(shù)中放入初始化的代碼,不過這些代碼必須在少于30秒內(nèi)完成,否則,SCM會認為某些地方出錯而終止服務。

  在自動或者手動啟動服務時,將會調(diào)用ServiceMain函數(shù)。ServiceMain函數(shù)將包含有以下的步驟:

  1.它馬上調(diào)用RegisterServiceCtrlHandler來注冊Handler函數(shù),作為SCM控制該服務的Handler函數(shù)

  2.然后它將調(diào)用SendStatusToSCM函數(shù),將當前的進程通報給SCM。第四個參數(shù)是一個“click count”值,在程序每次更新狀態(tài)時,它的值就會增加。SCM和其它的程序可以根據(jù)click count的值來知道初始化期間進行的處理。最后的參數(shù)是"wait hint",是用來告訴SCM在click count下次更新前,它需要等待的時間(以毫秒計算)。

  3.ServiceMain接著會創(chuàng)建一個事情,該事件在函數(shù)的底部使用,可讓它一直運行,直到SCM發(fā)出一個STOP的請求。

  4.接著,ServiceMain檢查啟動的參數(shù)。參數(shù)可在用戶手動啟動服務時,通過服務治理工具中的啟動參數(shù)傳送過來。這些參數(shù)以一個argv形式的數(shù)組進入ServiceMain函數(shù)。

  5.假如你的服務需要處理其它初始化的任務,它們應該放在這一步,在調(diào)用InitService之前。

  6.ServiceMain函數(shù)接著調(diào)用InitService函數(shù),這將啟動線程并做服務的真正工作。假如該調(diào)用成功,SverviceMain將會通知SCM服務已經(jīng)成功啟動。

  7.ServiceMain將調(diào)用WaitForSingleObject,用來等待terminateEvent事件對象被設置。這個對象通過Handler函數(shù)設置,一旦它被設置,ServiceMain將調(diào)用終止的函數(shù)來做清除的工作,然后就返回并停止服務。

  你從上面可以看到,在這個函數(shù)中并沒有多少靈活的地方。除了第5步外,你必須按步執(zhí)行以上提到的任務,否則服務將不可以正確啟動。

  終止函數(shù)清除所有打開的句柄,并且發(fā)送一個狀態(tài)的信息給SCM,告訴它服務現(xiàn)已停止。

  在SCM要暫停、繼續(xù)、詢問或者停止服務時,它就會調(diào)用Handler函數(shù)。要停止服務,Handler設置terminateEvent,這樣做會導致ServiceMain(它會以一個獨立線程的形式執(zhí)行)終止并且返回。一旦ServiceMain返回,服務就停止了。

  SendStatusToSCM 函數(shù)負責發(fā)送服務的當前狀態(tài)給SCM。

  在需要啟動服務線程時,ServiceMain就會調(diào)用InitService函數(shù)。該函數(shù)調(diào)用CreateThread來為服務創(chuàng)建一個新的線程。

  ServiceThread函數(shù)包含有該服務真正要做的工作。在這個例子中,該線程包含有一個無限的循環(huán),以一個預定義的時間間隔發(fā)出響聲。在你創(chuàng)建自己的服務時,你可以在該線程中放入任何的代碼,調(diào)用Win32函數(shù)或者你自己的函數(shù)。

安裝和移除服務

  為了使用上面提到的發(fā)響聲服務,你必須安裝它。安裝可讓SCM知道這個服務,并且讓SCM將它加入到控制面板中的服務列表中。表單二的代碼展示了如何安裝一個服務。

  表單2先通過OpenSCManager函數(shù)打開一個到SCM的連接。在OpenSCManager的調(diào)用中,你必須指定你要做的東西,以便SCM可以驗證這個行為。假如你登錄的帳號沒有足夠的權限,該調(diào)用將返回NULL。

  CreateService的調(diào)用是真正用來裝入新服務的。它使用OpenSCManager返回至SCM的指針、名字、標簽和命令行中的EXE文件,還有一些標準的參數(shù)值。使用SERVICE_WIN32_OWN_PROCESS表明該服務的EXE文件只包含有一個服務,而SERVICE_DEMAND_START則表明該服務是手動而不是自動啟動的。使用命令行安裝程序的一個典型格式如下:

   install BeepService "Beeper" c:/winnt/beep.exe

  第一個參數(shù)是SCM內(nèi)部使用的服務名字。這個名字也用在以后移除服務。第二個參數(shù)是一個標識符,即服務治理中顯示的名字。第三個參數(shù)指出該服務的執(zhí)行文件的路徑。在你安裝服務后,可在控制面板的服務治理中啟動它。假如有錯,可在Win32 API的在線文檔中查出錯誤代碼的含義。

  要移除服務,你要按列表3的步驟進行。它首先打開一個到SCM的連接,然后使用OpenService函數(shù)打開一個與服務的連接。列表3中接著查詢服務來看它是否現(xiàn)已停止了。假如不是的話,就會停止它。DeleteService 用來從控制面板中移除服務,移除操作的典型格式如下:

  remove BeepService

  需要的話,你也可以在馬上重新安裝服務。

結(jié)論

  服務對于Windows NT是很重要的一部分,因為它可讓你對操作系統(tǒng)進行擴展。使用列表1的代碼作為一個模板,你將會發(fā)現(xiàn)要建立一個自己的新服務是非常簡單的。

  列表1

  實現(xiàn)可能是最簡單NT服務的代碼


//***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// This code implements the simplest possible service.
// It beeps every 2 seconds, or at a user specified interval.
file://***************************************************************

// beepserv.cpp

#include
#include
#include
#include

#define DEFAULT_BEEP_DELAY 2000

// Global variables

// The name of the service
char *SERVICE_NAME = "BeepService";
// Event used to hold ServiceMain from completing
HANDLE terminateEvent = NULL;
// Handle used to communicate status info with
// the SCM. Created by RegisterServiceCtrlHandler
SERVICE_STATUS_HANDLE serviceStatusHandle;
// The beep interval in ms.
int beepDelay = DEFAULT_BEEP_DELAY;
// Flags holding current state of service
BOOL pauseService = FALSE;
BOOL runningService = FALSE;
// Thread for the actual work
HANDLE threadHandle = 0;

void ErrorHandler(char *s, DWord err)
{
cout << s << endl;
cout << "Error number: " << err << endl;
ExitProcess(err);
}

// This function consolidates the activities of
// updating the service status with
// SetServiceStatus
BOOL SendStatusToSCM (DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode,
DWORD dwCheckPoint,
DWORD dwWaitHint)
{
BOOL sUCcess;
SERVICE_STATUS serviceStatus;

// Fill in all of the SERVICE_STATUS fields
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = dwCurrentState;

// If in the process of doing something, then accept
// no control events, else accept anything
if (dwCurrentState == SERVICE_START_PENDING)
serviceStatus.dwControlsAccepted = 0;
else
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;

// if a specific exit code is defined, set up
// the win32 exit code properly
if (dwServiceSpecificExitCode == 0)
serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
serviceStatus.dwServiceSpecificExitCode =dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint = dwCheckPoint;
serviceStatus.dwWaitHint = dwWaitHint;

// Pass the status record to the SCM
success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
return success;
}

DWORD ServiceThread(LPDWORD param)
{
while (1)
{
Beep(200,200);
Sleep(beepDelay);
}
return 0;
}

// Initializes the service by starting its thread
BOOL InitService()
{
DWORD id;

// Start the service's thread
threadHandle = CreateThread(0, 0,(LPTHREAD_START_ROUTINE) ServiceThread,0, 0, &id);

if (threadHandle==0)
return FALSE;
else
{
runningService = TRUE;
return TRUE;
}
}

// Dispatches events received from the SCM
VOID Handler (DWORD controlCode)
{
DWORD currentState = 0;
BOOL success;

switch(controlCode)
{
// There is no START option because
// ServiceMain gets called on a start

// Stop the service
case SERVICE_CONTROL_STOP:
// Tell the SCM what's happening
success = SendStatusToSCM(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 5000);
runningService=FALSE;
// Set the event that is holding ServiceMain
// so that ServiceMain can return
SetEvent(terminateEvent);
return;
// Pause the service
case SERVICE_CONTROL_PAUSE:
if (runningService && !pauseService)
{
// Tell the SCM what's happening
success = SendStatusToSCM(SERVICE_PAUSE_PENDING,
NO_ERROR, 0, 1, 1000);
pauseService = TRUE;
SuspendThread(threadHandle);
currentState = SERVICE_PAUSED;
}
break;
// Resume from a pause
case SERVICE_CONTROL_CONTINUE:
if (runningService && pauseService)
{
// Tell the SCM what's happening
success = SendStatusToSCM(SERVICE_CONTINUE_PENDING,
NO_ERROR, 0, 1, 1000);
pauseService=FALSE;
ResumeThread(threadHandle);
currentState = SERVICE_RUNNING;
}
break;
// Update current status
case SERVICE_CONTROL_INTERROGATE:
// it will fall to bottom and send status
break;
// Do nothing in a shutdown. Could do cleanup
// here but it must be very quick.
case SERVICE_CONTROL_SHUTDOWN:
return;
default:
break;
}
SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}

// Handle an error from ServiceMain by cleaning up
// and telling SCM that the service didn't start.
VOID terminate(DWORD error)
{
// if terminateEvent has been created, close it.
if (terminateEvent) CloseHandle(terminateEvent);

// Send a message to the scm to tell about stopage
if (serviceStatusHandle)
SendStatusToSCM(SERVICE_STOPPED, error,0, 0, 0);

// If the thread has started, kill it off
if (threadHandle) CloseHandle(threadHandle);

// Do not need to close serviceStatusHandle
}

// ServiceMain is called when the SCM wants to
// start the service. When it returns, the service
// has stopped. It therefore waits on an event
// just before the end of the function, and
// that event gets set when it is time to stop.
// It also returns on any error because the
// service cannot start if there is an eror.
VOID ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;

// immediately call Registration function
serviceStatusHandle =
RegisterServiceCtrlHandler(
SERVICE_NAME, (LPHANDLER_FUNCTION)Handler);
if (!serviceStatusHandle) {terminate(GetLastError()); return;}

// Notify SCM of progress
success = SendStatusToSCM(SERVICE_START_PENDING,
NO_ERROR, 0, 1, 5000);
if (!success) {terminate(GetLastError()); return;}

// create the termination event
terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
if (!terminateEvent) {terminate(GetLastError()); return;}

// Notify SCM of progress
success = SendStatusToSCM(SERVICE_START_PENDING,
NO_ERROR, 0, 2, 1000);
if (!success) {terminate(GetLastError()); return;}

// Check for startup params
if (argc == 2)
{
int temp = atoi(argv[1]);
if (temp < 1000)
beepDelay = DEFAULT_BEEP_DELAY;
else
beepDelay = temp;
}

// Notify SCM of progress
success = SendStatusToSCM(SERVICE_START_PENDING,
NO_ERROR, 0, 3, 5000);
if (!success) {terminate(GetLastError()); return;}

// Start the service itself
success = InitService();
if (!success) {terminate(GetLastError()); return;}

// The service is now running.
// Notify SCM of progress
success = SendStatusToSCM(SERVICE_RUNNING,
NO_ERROR, 0, 0, 0);
if (!success) {terminate(GetLastError()); return;}

// Wait for stop signal, and then terminate
WaitForSingleObject (terminateEvent, INFINITE);

terminate(0);
}

VOID main(VOID)
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ SERVICE_NAME,
(LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
BOOL success;

// Register with the SCM
success =
StartServiceCtrlDispatcher(serviceTable);
if (!success)
ErrorHandler("In StartServiceCtrlDispatcher",
GetLastError());
}

列表2
安裝NT服務的代碼


file://***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// This code installs a service.
file://***************************************************************

// install.cpp

#include
#include

void ErrorHandler(char *s, DWORD err)
{
cout << s << endl;
cout << "Error number: " << err << endl;
ExitProcess(err);
}

void main(int argc, char *argv[])
{
SC_HANDLE newService, scm;

if (argc != 4)
{
cout << "Usage:/n";
cout << " install service_name /
service_label executable/n";
cout << " service_name is the /
name used internally by the SCM/n";
cout << " service_label is the /
name that appears in the Services applet/n";
cout << " (for multiple /
words, put them in double quotes)/n";
cout << " executable is the /
full path to the EXE/n/n";
return;
}

// open a connection to the SCM
scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
if (!scm) ErrorHandler("In OpenScManager",
GetLastError());

// Install the new service
newService = CreateService(
scm, argv[1], // eg "beep_srv"
argv[2], // eg "Beep Service"
SERVICE_ALL_access, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
argv[3], // eg "c:/winnt/xxx.exe"
0, 0, 0, 0, 0);
if (!newService) ErrorHandler("In CreateService",
GetLastError());
else cout << "Service installed/n";

// clean up
CloseServiceHandle(newService);
CloseServiceHandle(scm);
}

列表3
移除NT服務的代碼
file://***************************************************************
// From the book "Win32 System Services: The Heart of Windows NT"
// by Marshall Brain
// Published by Prentice Hall
file://
// This code removes a service from the Services applet in the
// Control Panel.
file://***************************************************************

// remove.cpp

#include
#include

void ErrorHandler(char *s, DWORD err)
{
cout << s << endl;
cout << "Error number: " << err << endl;
ExitProcess(err);
}

void main(int argc, char *argv[])
{
SC_HANDLE service, scm;
BOOL success;
SERVICE_STATUS status;

if (argc != 2)
{
cout << "Usage:/n remove service_name/n";
return;
}

// Open a connection to the SCM
scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
if (!scm) ErrorHandler("In OpenScManager",
GetLastError());

// Get the service's handle
service = OpenService(scm, argv[1],
SERVICE_ALL_ACCESS | DELETE);
if (!service) ErrorHandler("In OpenService",
GetLastError());

// Stop the service if necessary
success = QueryServiceStatus(service, &status);
if (!success) ErrorHandler("In QueryServiceStatus",
GetLastError());
if (status.dwCurrentState != SERVICE_STOPPED)
{
cout << "Stopping service.../n";
success = ControlService(service,
SERVICE_CONTROL_STOP, &status);
if (!success) ErrorHandler("In ControlService",
GetLastError());
}

// Remove the service
success = DeleteService(service);
if (success) cout << "Service removed/n";
else ErrorHandler("In DeleteService",
GetLastError());

// Clean up
CloseServiceHandle(service);
CloseServiceHandle(scm);
}


======================================================================
第二部分:用Win32匯編實現(xiàn)系統(tǒng)服務
by:羅云彬

好了,經(jīng)過了上面這篇文章,大家對服務應該有個初步的了解了,但是要注重分辨文章中提及的 API 名稱和程序自己的模塊名稱,比如CreateService是一個 API,而 ServiceMain 是程序中一個子程序的名稱,千萬不要把子程序名當系統(tǒng)的 API 去使用了 :)
在這部分的補充中,我再歸納一下實現(xiàn)各種功能所使用的 API 函數(shù)名和使用的步驟,并提供一個匯編源程序例子來供大家參考,大家也可以下載整個源代碼包并修改使用。
一般來說,整個服務程序系統(tǒng)由兩個單獨的程序組成:服務程序和服務控制程序,經(jīng)過了上面的介紹,大家一定明白這兩個程序應該由哪些部分組成了:

服務程序的結(jié)構

服務程序是被用來實現(xiàn)服務功能的部分,它由相對獨立的3個部分組成:

1. 程序入口 -> 調(diào)用StartServiceCtrlDispatcher(注冊服務主程序)
2. 服務主程序(被SCM調(diào)用)-> 調(diào)用RegisterServiceCtrlHandler(注冊控制handler)-> 調(diào)用 SetServiceStatus 設置服務的正確狀態(tài) -> 執(zhí)行服務的功能代碼 -> 需要結(jié)束服務的時候則返回。
3. 控制Handler -> 設置內(nèi)部標志以控制第2部分服務主程序中的代碼走向,并調(diào)用 SetServiceStatus 更新服務的狀態(tài)。

可見,服務程序必須用到的 API 只有 StartServiceCtrlDispatcher、RegisterServiceCtrlHandler和SetServiceStatus ,其余的大部分代碼是執(zhí)行本身的功能模塊。這里有一個服務程序的簡單例子,實現(xiàn)的是每秒種讓喇叭發(fā)聲一次的功能。

下面的源代碼是 Service.asm 程序:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Windows NT 服務程序模板
; by 羅云彬, http://asm.yeah.net
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 服務端程序 Ver 1.0
;
; 2002.06.20 ----- 第1版
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定義
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include AdvApi32.inc
includelib AdvApi32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數(shù)據(jù)段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?

stSS SERVICE_STATUS <> ;服務的狀態(tài)
hSS dd ? ;服務的狀態(tài)句柄
dwOption dd ?
F_STOP equ 0001h ;停止服務

include
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 服務控制程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcHandler proc _dwControl

pushad
mov eax,_dwControl
.if eax == SERVICE_CONTROL_STOP
or dwOption,F_STOP
mov stSS.dwCurrentState,SERVICE_STOPPED
invoke SetServiceStatus,hSS,addr stSS
.elseif eax == SERVICE_CONTROL_INTERROGATE
invoke SetServiceStatus,hSS,addr stSS
.endif
popad
ret

_ProcHandler endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 服務主程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ServiceMain proc _dwArgc,_lpszArgv

pushad
invoke RegisterServiceCtrlHandler,addr szServiceName,offset _ProcHandler
mov hSS,eax
mov stSS.dwServiceType,SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS
mov stSS.dwCurrentState,SERVICE_START_PENDING
mov stSS.dwControlsAccepted,SERVICE_ACCEPT_STOP
mov stSS.dwWin32ExitCode,NO_ERROR
invoke SetServiceStatus,hSS,addr stSS
;********************************************************************
; 假如初始化代碼比較多,那么需要首先把狀態(tài)設置為 pending,等完成以后
; 再設置為 Running。(在這里加入初始化代碼)
;********************************************************************
mov stSS.dwCurrentState,SERVICE_RUNNING
invoke SetServiceStatus,hSS,addr stSS
;********************************************************************
; 服務的具體執(zhí)行代碼
; 在這里是每隔1秒種讓喇叭發(fā)聲
;********************************************************************
.repeat
invoke MessageBeep,-1
invoke Sleep,1000
.until dwOption & F_STOP
popad
ret

_ServiceMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 主程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stSTE[2]:SERVICE_TABLE_ENTRY

invoke RtlZeroMemory,addr @stSTE,sizeof @stSTE
mov @stSTE[0].lpServiceName,offset szServiceName
mov @stSTE[0].lpServiceProc,offset _ServiceMain
invoke StartServiceCtrlDispatcher,addr @stSTE
ret

_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
invoke _WinMain
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start

這里是服務名稱的定義文件 Define.inc,這個文件已經(jīng)在前面用include語句包含進來:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 公用文本信息
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.const

szServiceEXE db 'Service.exe',0 ;在這里定義運行服務的 exe 文件名
szServiceName db 'ServiceTemplate',0 ;在這里定義服務的名稱
szDisplayName db 'Service Template for test',0 ;在這里定義服務顯示的名稱
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


要注重的是,服務程序部分的代碼并沒有什么靈活性可言,它必須按照這樣的步驟來實現(xiàn),否則Windows就不認為它是一個服務程序了,另外,需要補充說明的是:

1. StartServiceCtrlDispatcher 函數(shù)被調(diào)用后并不馬上返回,這時系統(tǒng)會去調(diào)用服務主程序(上面的_ServiceMain子程序),只有當服務主程序返回的時候,函數(shù)才會返回,這時主程序繼續(xù)執(zhí)行并執(zhí)行 ExitProcess 終止執(zhí)行。
2. 從第1點可以發(fā)現(xiàn):在服務程序中,服務主程序的退出就意味著服務自行終止了,所以假如程序創(chuàng)建了多個線程來執(zhí)行功能代碼的話,在線程被創(chuàng)建以后,主程序還需要在這里等待,否則子程序返回以后服務進程就結(jié)束了,這些線程又怎么能繼續(xù)執(zhí)行呢?

服務控制程序的結(jié)構

好了,有了服務程序以后,還需要把服務正確安裝才能讓它運行,這個工作就是服務控制程序的事情了,一般來說,服務控制程序的結(jié)構并沒有什么非凡的要求,它只是一個普通的Win32可執(zhí)行文件而已,在服務控制程序中可以使用下面的 API 來進行各種控制,這些 API的具體語法請查看 Win32 API 手冊:

1. 在進行和服務相關的各種操作之前,必須使用OpenSCManager來聯(lián)系服務治理器,并從這個 API 得到一個
服務治理器的句柄 hSCM。
2. 操作某個具體的服務前,需要使用OpenService來打開服務,并得到服務的句柄hService,接下來就可以使用這個句柄來控制服務(當然,安裝服務的時候并不需要此步驟,這時服務還不存在呢,又如何打開?)
3. 安裝服務:使用 CreateService 函數(shù),這個函數(shù)將服務程序的可執(zhí)行文件在SCM中注冊,注重:“注冊”并不意味著執(zhí)行,它只是在SCM的數(shù)據(jù)庫中登記一條包含可執(zhí)行文件名、啟動方式、服務名稱等信息的數(shù)據(jù)而已,執(zhí)行這個函數(shù)以后,在控制面板的服務一欄中就可以看到服務列表中已經(jīng)出現(xiàn)這個服務了,由于這僅僅是“注冊”,所以系統(tǒng)并不檢查可執(zhí)行文件的合法性,也就是說,即使注冊一個不存在的可執(zhí)行文件,注冊操作仍然會成功,但這樣以后要啟動服務的時候就會失敗。注冊過一次以后,SCM數(shù)據(jù)庫中的信息會被自動保存,所以“注冊”操作是一次性的過程,并不需要在每次啟動服務的時候都去注冊一遍。
4. 啟動服務:當安裝了服務以后,可以用StartService函數(shù)來啟動服務,這時系統(tǒng)才真正根據(jù)注冊的文件名去執(zhí)行服務程序。
5. 刪除服務:刪除服務是安裝服務的逆過程,也就是在SCM的數(shù)據(jù)庫中將服務程序的信息刪除,刪除服務使用DeleteService函數(shù)。
6. 服務的控制:服務的暫停、停止等工作都可以由服務控制函數(shù)ControlService來完成,需要的僅僅是指定不同的參數(shù)而已。
7. 服務狀態(tài)的查詢可以由QueryServiceStatus函數(shù)來完成。
8. 句柄的關閉:和上面的操作相關的句柄(包括SCM句柄和服務句柄)在不再使用以后需要用CloseServiceHandle函數(shù)關閉。

下面的幾個子程序舉例說明了安裝服務、刪除服務、啟動服務和停止服務的操作方法,注重子程序中用到的hSCM已經(jīng)在其他地方首先調(diào)用OpenSCManager函數(shù)獲取了。更具體的源代碼請查看文章附帶的源代碼包中的Control.asm文件。


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 安裝服務
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_InstallService proc
local @hService,@dwReturn

mov @dwReturn,FALSE
;********************************************************************
; 創(chuàng)建服務
;********************************************************************
invoke CreateService,hSCM,addr szServiceName,addr szDisplayName,/
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS,/
SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,addr szServiceFile,/
NULL,NULL,NULL,NULL,NULL
.if ! eax
invoke GetLastError
.if eax == ERROR_DUP_NAME || eax == ERROR_SERVICE_EXISTS
mov @dwReturn,TRUE
.else
mov @dwReturn,FALSE
.endif
jmp @F
.endif
mov @hService,eax
mov @dwReturn,TRUE
;********************************************************************
invoke CloseServiceHandle,@hService
@@:
mov eax,@dwReturn
ret

_InstallService endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 刪除服務
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DeleteService proc
local @hService,@dwReturn
local @stStatus:SERVICE_STATUS

mov @dwReturn,FALSE
;********************************************************************
; 打開服務
;********************************************************************
invoke OpenService,hSCM,addr szServiceName,SERVICE_ALL_ACCESS
.if ! eax
jmp @F
.endif
mov @hService,eax
;********************************************************************
; 停止服務并刪除服務
;********************************************************************
call _StopService
invoke DeleteService,@hService
.if eax
mov @dwReturn,TRUE
.endif
invoke CloseServiceHandle,@hService
@@:
mov eax,@dwReturn
ret

_DeleteService endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 啟動服務
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_StartService proc
local @hService

invoke OpenService,hSCM,addr szServiceName,SERVICE_START
.if eax
mov @hService,eax
invoke StartService,@hService,0,NULL
.if eax
mov eax,TRUE
.else
mov eax,FALSE
.endif
push eax
invoke CloseServiceHandle,@hService
pop eax
.else
mov eax,FALSE
.endif
ret

_StartService endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 停止服務
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_StopService proc
local @hService
local @stStatus:SERVICE_STATUS

invoke OpenService,hSCM,addr szServiceName,SERVICE_ALL_ACCESS
.if eax
mov @hService,eax
invoke QueryServiceStatus,@hService,addr @stStatus
invoke ControlService,@hService,SERVICE_CONTROL_STOP,addr @stStatus
.if eax
mov eax,TRUE
.else
mov eax,FALSE
.endif
push eax
invoke CloseServiceHandle,@hService
pop eax
.else
mov eax,FALSE
.endif
ret

_StopService endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


合并服務程序和服務控制程序

在上面的例子中,將服務程序和服務控制程序分開是為了更好地從概念上說明兩者的區(qū)別,但在實際應用中,還是有很多的程序?qū)蓚€部分合在同一個可執(zhí)行文件中實現(xiàn),這時兩部分僅僅是在物理上被放在同一個exe文件中,在結(jié)構上還是無不相干,完全獨立的。
雖然兩個部分的實現(xiàn)方式是完全不同的,但只要在程序的入口處就按照不同的情況去執(zhí)行不同部分的代碼,將它們合在一個文件中還是可行的,其要害就是分辨程序被執(zhí)行的時候是作為服務程序執(zhí)行還是被作為服務控制程序執(zhí)行的,常見的方法有:

1. 用命令行參數(shù)來分辨,使用CreateService函數(shù)安裝服務的時候遞交文件名字符串時可以在文件名后面跟參數(shù),這樣程序入口處用GetCommandLine函數(shù)獲取命令行參數(shù),檢測到特定參數(shù)則按照服務方式執(zhí)行,否則按照服務控制程序方式運行。
2. 用運行路徑分辨,一些病毒程序初始化的時候?qū)⒆约旱母北究截惖絎indows目錄下并將這個副本注冊為服務,所以到它檢測到當前路徑是Windows目錄的時候,就按照服務方式運行。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表

圖片精選

18以下岁禁止1000部免费| 国产欧美亚洲精品a| 青青草国产精品视频| 成人性生交xxxxx网站| 天堂在线观看av| 国产日韩在线观看一区| 欧美电影在线观看完整版| 欧美疯狂party性派对| 户外极限露出调教在线视频| 免费国产精品视频| 国产69精品久久久久9999小说| 最近中文字幕在线中文高清版| 久久精品一区二区三| 欧美专区一二三| 国产中文在线| 欧美视频中文一区二区三区在线观看| 日韩视频不卡| 91精品国产乱码久久久| 国产精品久久久久久影院8一贰佰| 成人观看视频| 国产免费xxx| 欧美日韩国产小视频在线观看| 日韩免费观看高清完整版在线观看| 国模精品一区二区| 欧美极品美女视频| 欧洲grand老妇人| 综合久久给合久久狠狠狠97色| 毛片在线播放视频| 91黑人精品一区二区三区| 草民午夜欧美限制a级福利片| 久久精品不卡| 日韩制服一区| 久久精品视频免费看| 欧美成人亚洲成人| 天天弄天天操| 国产盗摄——sm在线视频| 欧美老少做受xxxx高潮| 成人在线观看黄色| 范冰冰一级做a爰片久久毛片| japan高清日本乱xxxxx| 51漫画成人app入口| 国产精品成人69xxx免费视频| 一二三四在线观看视频| 欧美虐宫另类残忍视频| 性一交一乱一色一视频麻豆| 欧美乱大交做爰xxxⅹ性3| 日本24小时在线高清视频| 亚洲xxxx3d| 国产高清亚洲一区| 亚洲v国产v在线观看| 蜜桃传媒视频麻豆一区| 1024手机在线视频| 欧美一级片网址| 欧美xxxxxx| 国产精品美腿一区在线看| 国产精品久久7| 比比资源-先锋影音资源站| 日韩精品成人一区二区在线观看| 日本韩国一区二区三区| 欧美夫妻性生活| 亚洲一区二区三区精品动漫| 一区二区三区四区视频| 亚洲欧美色图小说| 欧美在线视频第一页| 无码日韩人妻精品久久蜜桃| 国产精品久久久久久久蜜臀| 宅男宅女性影片资源在线1| 国产精品7777777| 久久亚洲成人精品| 宇都宫紫苑在线播放| 日韩一级裸体免费视频| 国产精品视频福利一区二区| 无码少妇一区二区三区| 欧美一区二区综合| 青青视频在线观| 91沈先生播放一区二区| 在线欧美一级视频| 免费看的www视频网站视频| 亚洲一二三区在线观看| 国产精品午夜av| 亚洲第一影院| 久久黄色免费网站| 超碰在线免费播放| 国产成人自拍视频在线观看| 日本www高清| 欧美激情 国产精品| 中国女人一级一次看片| 色欲av伊人久久大香线蕉影院| 中文字幕男人天堂| 日韩不卡手机在线v区| 欧洲一区二区av| 国产日韩亚洲精品| 最近2019年中文视频免费在线观看| 国产欧美日韩综合精品一区二区三区| av在线播放一区| 怡红院av久久久久久久| 精品久久久久久久久久久久久久| 福利成人导航| 欧美视频在线播放一区| 久久中文字幕电影| 国产a国产a国产a| 春暖花开亚洲一区二区三区| 日韩av男人的天堂| 国产精品99久久久久久久久| av一区二区在线观看| 国产精品久久久久国产a级| 91久久久久久国产精品| 国产成人涩涩涩视频在线观看| 国产精品嫩草影院桃色| 亚洲另类一区二区| 一色屋色费精品视频在线看| 久久久久久久久艹| 色欧美乱欧美15图片| 波多野结衣在线观看一区| 中文综合在线观看| 欧美日韩国产欧美日美国产精品| 人人干人人插| 国产精品久久久久久久久借妻| 免费成人深夜夜行视频| 日韩精品亚洲人成在线观看| 国产真人真事毛片| 网站黄在线观看| 亚洲成a人片77777kkkk| 欧美日本韩国国产| 亚洲高清视频在线| 亚洲香蕉伊在人在线观| 成人免费在线小视频| 88av.com| 日本va欧美va国产激情| 18久久久久久| 日韩激情电影免费看| 性综艺节目av在线播放| 国产精品久久久久高潮| 日本黄视频在线观看| 午夜精品久久久久久久| 超碰在线免费看| 欧美在线播放| 国产a级片免费观看| 四虎5151久久欧美毛片| 伊人婷婷欧美激情| a级毛片免费观看在线| 亚洲一区亚洲| 九色在线91| 精品无码国产一区二区三区av| 三级成人黄色影院| 国产欧美日韩免费| 婷婷色中文字幕| 热久久免费国产视频| 青青草国产在线观看| 天堂av免费在线观看| 国产乱子伦精品无码码专区| 欧美日韩国产亚洲沙发| 色视频免费在线观看| 岛国精品资源网站| 深夜精品寂寞黄网站在线观看| 国产在线视频一区| 狂野欧美激情性xxxx欧美| 懂色av一区二区三区| 三级黄色在线视频| 无码人妻一区二区三区线| 丁香花在线电影| 欧美激情亚洲自拍| 91精品国产91久久久久游泳池| 国产精品露出视频| 希岛爱理中文字幕| 男生女生差差差的视频在线观看| 91精品国产福利在线观看麻豆| jizzjizz欧美69巨大| japanese色国产在线看视频| 亚洲国产精品999| 99热精品在线播放| 日韩理论电影中文字幕| 欧美中日韩在线| 日韩一区二区三区电影在线观看| 日韩免费看网站| 九色蝌蚪在线观看| 国产精品久久久乱弄| 涩涩漫画在线观看| 欧美在线日韩| 91精品国产综合久久久蜜臀九色| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 亚洲免费在线播放视频| 国产日产欧美精品| 欧美三级视频在线观看| 日韩欧美国产综合在线一区二区三区| 日韩美一区二区三区| 日本一本草久在线中文| 国产精品一区二区不卡| 国产亚洲欧美日韩一区二区| 国产成人精品三级麻豆| 国产精品成人国产乱| 欧美成人精品激情在线观看| 国产精品爱啪在线线免费观看| 污网站在线免费看| 亚洲91精品在线观看| 奇米色一区二区三区四区| 亚洲精品国产系列| 大桥未久av一区二区三区中文| 欧洲美女精品免费观看视频| 国产日韩欧美视频在线观看| 久久在线视频| 国产视频久久久久久久| 亚洲欧美中文字幕在线一区| 亚洲国产高清aⅴ视频| 色婷婷av一区二区三区在线观看| 91视频久色| 免费看一级一片| 91美女免费看| 绯色av一区二区| 日韩久久久久久久久久久久| 欧美疯狂爱爱xxxxbbbb| 欧美视频在线观看视频| 亚洲成av人片一区二区三区| 91视频观看| 强开小嫩苞一区二区三区视频| 成人sese在线| 亚洲午夜av久久乱码| 亚洲私人黄色宅男| 在线一区视频| 四虎成人永久免费视频| 91免费视频观看| 日韩视频一区二区三区在线播放| 红桃成人av在线播放| 日本国产一区二区三区| 天天撸夜夜操| 精品国偷自产一区二区三区| 欧美性生交大片免费| 成人免费看片视频在线观看| 日韩欧美不卡在线观看视频| 欧美在线一区二区三区四区| 欧美性受ⅹ╳╳╳黑人a性爽| 你懂的av在线| 国产91精品一区二区绿帽| 日本欧美在线视频免费观看| aaa国产视频| 免费日本一区二区三区视频| 国产一级在线| 又长又粗又大又爽| 在线一区二区观看| 91亚洲国产成人精品一区二三| 成人精品视频网站| 亚洲天天做日日做天天谢日日欢| 性猛交xxxx| 欧美精品三级| 最近中文字幕免费mv视频多少集| 久久久精品视频在线| 久久久精品人妻一区二区三区| 久久久亚洲影院| 日本成人中文字幕在线| 看片网址国产福利av中文字幕| 欧美精品 - 色网| 自拍偷拍亚洲色图欧美| 欧区一欧区二欧区三| 久久精品国产久精国产思思| 国产美女搞久久| 99久久国产精| 欧美日韩高清免费| 久久久久成人网| 亚洲激情播播| 国产成人自拍网| 青青精品视频播放| 芒果视频成人app| 亚洲精品成av人片天堂无码| 国产精品自拍视频| 成人亚洲欧美一区二区三区| 天天操天天摸天天舔| 最新精品视频在线| 久久久国产视频91| 成人在线视频中文字幕| 毛片av免费在线观看| 亚州一区二区三区| 美女扒开大腿让男人桶| 亚洲一区二区成人| 综合久久精品| 欧美日韩国产三级| 欧美日韩亚洲高清| 欧美尤物一区| 久久久久久久久久久久电影| 欧美日韩综合一区| 国产福利一区二区在线精品| 韩国v欧美v亚洲v日本v| 91专区在线观看| 国产成人亚洲精品自产在线| 日本青青草视频| 国产红桃视频| 日韩视频在线一区二区三区| 国产成人三级在线观看| 伊人成综合网yiren22| 亚洲色图综合| 欧美日韩不卡在线| 懂色av蜜臀av粉嫩av永久| 大桥未久女教师av一区二区| 亚洲 欧美 另类人妖| 国产真实乱子伦精品视频| 妺妺窝人体色www在线下载| 影音先锋男人资源在线观看| 国产又大又黑又粗免费视频| 模特精品在线| 91激情视频| 亚洲欧美高清| av噜噜在线观看| 成人性生生活性生交12| 国产夫妻在线播放| 久草在线视频福利| 国产精品久久久久9999| 成人做爰69片免网站| 国产一级特黄毛片| 91小视频在线播放| 成年美女黄网站色大片不卡| 成年人在线看片| 国产日韩欧美久久| 国产精品美女www爽爽爽| 日日骚欧美日韩| 制服诱惑一区二区| 青青草原免费观看| www.欧美日韩国产在线| 一二美女精品欧洲| 亚洲xxx在线观看| 日本私人影院在线观看| 99免费在线视频观看| 日韩三级不卡| 91亚洲精选| 久久久久久久久久久免费视频| 伊人久久大香线蕉av超碰| 一本一道久久a久久综合精品| 岛国精品一区二区| 日韩av免费看| 国产福利影院在线观看|