1. 程式人生 > >利用注入程序shellcode實現程式自刪除

利用注入程序shellcode實現程式自刪除

某些情況下需要exe執行結束刪除自身,而自刪除程式有好幾種方法,如下面的兩方法:
a.先使自己快速退出,然後再在命令列中刪除自己;或是先使自己快速退出,然後再在bat檔案或指令碼檔案中刪除自己

b. 非常經典的,關閉硬編碼為4的控制代碼,撤銷自己在記憶體的對映,再刪除自己,再結束自己,也就是程序在未真正退出的時候就已經被刪除了(只能在win2k或98使用)
                    int main(int argc, char *argv[])
                 {
                 HMODULE module = GetModuleHandle(0);
                 CHAR buf[MAX_PATH];
                 GetModuleFileName(module, buf, sizeof buf);
                 CloseHandle(HANDLE(4));
                 __asm {
                 lea    eax, buf
                 push    0
                 push    0
                 push    eax
                 push    ExitProcess
                 push    module
                 push    DeleteFile
                 push    UnmapViewOfFile//FreeLibrary;98
                 ret
                 }
                 return 0;
                 }

本文所使用的是另外一種方法:使用注入其他程序的shellcode來實現自刪除,其基本原理就是:讓shellcode先終止自己,然後再讓shellcode刪除自己。執行shellcode完畢之後,僅在記憶體保留一段地址,而不會產生磁碟檔案,利用這個特點可以用來做'乾淨的'自刪除程式,但會讓被注入的程序造成記憶體洩漏,理由是注入的程式碼不能被釋放,否則會造成呼叫方的訪問違規。

要點: 1.本地修改API函式在遠端程序的實際地址,有效的減少shellcode的程式碼長度,利用預定義的巨集作為可能被呼叫的API函式的值,巨集  的一個特點是,在編譯的時候,只會編譯成巨集定義的值,而不是像變數一樣被優化成地址(因為本地變數的地址在其他程序是無效的)
            2.在執行時修改暫存器指向的地址,在程序virtualallocex分配一塊記憶體,然後把得到的地址由esi指向它,
            然後writeprocessmemory,esi持有的地址就會發生變化,這樣就可以修改預先編碼的資料,就像使用函式引數一樣來傳遞。

 基本實現: 程式自殺的原理,注入遠執行緒到其他程序,讓執行緒的shellcode終止掉自己,然後再呼叫deletefile刪除自己(有點笨挫,也許可以有更好的方法),(當然也可以用來幹掉其他程序,但前提是可以被結束和刪除的。。)

限制:可能被殺軟報警,至少讀寫其他程序的資料是違規的吧?

下面是程式碼:
//h檔案//////////////////////////

//查詢指標內的一個無符號的32位整數值,這個可以是個標籤
void* __cdecl find_ptrvalue(__in  void* pfun,__in unsigned  int findvalue,__out unsigned int *ioffset );
//更完善的
void* __cdecl FindCodeValue(__in void* pcode,__in size_t codelen,__in unsigned int findvalue,__out unsigned int *ioffset );
//查詢全部匹配的,並替換為指定的,並返回最後一個找到的偏移量
unsigned int __cdecl FindCodeReplaceAll(__in void* pcode,__in size_t codelen,
                                        __in unsigned int findvalue,__in unsigned int replacevalue);

/*獲得指定模組路徑的在某個程序中的基地址*/
DWORD __stdcall GetModulebaseProcessW(__in DWORD dwPid,__in  LPWSTR lpszDllFile);
DWORD __stdcall GetModulebaseProcessA(__in DWORD dwPid,__in   LPSTR    lpszDllFile);


#ifdef UNICODE
#define  GetModulebaseProcess GetModulebaseProcessW
#else
#define  GetModulebaseProcess GetModulebaseProcessA
#endif

/*計算遠端程序呼叫載入模組匯出函式的地址,原理:利用DLL匯出函式的RVA+模組基地址*/
DWORD __stdcall GetRemoteProcAddress(
                                     __in   DWORD dwRemoteModule,
                                     __in   DWORD  dwLocalModule,
                                     __in   LPCSTR   lpszProcName);


/*自刪除的簡單實現*/
BOOL __stdcall SeltKill(__in  DWORD dwInjectPid/*被注入的程序PID*/);

//下面是cpp檔案///////////////////////////////////////////////////////////////////////////////////

//查詢指標內的一個無符號的32位整數值,這個可以是個標籤
void* __cdecl find_ptrvalue(__in  void* pfun,__in unsigned  int findvalue,__out unsigned int *ioffset ){
    void *pret;
    int   i=0;
    __asm{
        mov eax,pfun
            mov ecx,0
            jmp __loop
__goon:
        inc   eax
            inc   ecx
__loop:
        mov ebx,[eax]
        cmp ebx,findvalue
            jnz __goon
            mov pret,eax
            mov i,ecx
        }
    if(ioffset) *ioffset = i;
    return pret;/*這個函式沒有使用,而是使用FindCodeReplaceAll*/
    }

//更完善的
void* __cdecl FindCodeValue(__in void* pcode,__in size_t codelen,__in unsigned int findvalue,__out unsigned int *ioffset ){
    if(!pcode || !codelen ) return NULL;
    LPDWORD dwfind =NULL;
    void* presult=NULL;
    PBYTE pb=(PBYTE)pcode;
    for(int i =0;i<codelen-3;i++,pb++){
        dwfind=(LPDWORD)pb;
        if(*dwfind==findvalue){
            presult=pb;
            if(ioffset) *ioffset=i;
            return presult;
            }
        }
    return NULL;/*這個函式沒有使用,而使用FindCodeReplaceAll*/
    }

//查詢全部匹配的,並替換為指定的,並返回最後一個找到的偏移量
unsigned int __cdecl FindCodeReplaceAll(__in void* pcode,__in size_t codelen,    __in unsigned int findvalue,__in unsigned int replacevalue){

    if(!pcode || !codelen) return 0;
unsigned int iret=0;
unsigned int *pui=0;
unsigned char *puc=(unsigned char *)pcode;
for(int i =0;i<codelen-3;i++,puc++){
pui=(unsigned int*)puc;
if(*pui==findvalue){
*pui=replacevalue;
iret=i;
}
    }
return iret;
}

//獲得指定程序的模組的控制代碼並轉換為DWORD,引數lpszDllFile指定的是完全路徑
DWORD __stdcall GetModulebaseProcessW(__in DWORD dwPid,__in  LPWSTR lpszDllFile){
    if(!dwPid || !lpszDllFile) return 0;
    HANDLE  hSnap=NULL;
    MODULEENTRY32W me32={sizeof(me32)};
    __try{
        hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwPid);
        if(hSnap==INVALID_HANDLE_VALUE) __leave;
        if(!Module32FirstW(hSnap,&me32)) __leave;
        do{
            if(lstrcmpiW(me32.szExePath,lpszDllFile)==0)
                return (DWORD)me32.modBaseAddr;

            }while(Module32NextW(hSnap,&me32));

        }__finally{
            if(hSnap!=INVALID_HANDLE_VALUE &&hSnap!=NULL) CloseHandle(hSnap);


        }
    return 0;
    }
DWORD __stdcall GetModulebaseProcessA(__in DWORD dwPid,__in   LPSTR    lpszDllFile){
    WCHAR szFile[MAX_PATH];
    wsprintfW(szFile,L"%S/0",lpszDllFile);
    return GetModulebaseProcessW(dwPid,szFile);
    }

/*計算遠端程序呼叫載入模組匯出函式的地址,原理:利用DLL匯出函式的RVA+模組基地址*/
DWORD __stdcall GetRemoteProcAddress(
                                     __in   DWORD dwRemoteModule,
                                     __in   DWORD  dwLocalModule,
                                     __in   LPCSTR   lpszProcName){
DWORD dwfun=(DWORD)GetProcAddress((HMODULE)dwLocalModule,lpszProcName);
dwfun-=dwLocalModule;/*從當前程序載入模組獲取的函式(即便匯入表中不存在的,只要模組被對映到程序的記憶體地址空間,整個模組的匯出函式都可以使用GetProcAddress來獲取),得到函式的地址後,減去模組基地址得到函式在模組的RVA,利用這個RVA+其他程序模組的基地址=該函式在其他程序的真實地址*/
return dwfun+dwRemoteModule;
    }

/*預定義隨機值的巨集,每個巨集代表一個函式的地址,這些僅作為“佔位”的方式存在於shellcode,是無法呼叫,只有替換為真實的API地址後才能呼叫,否則呼叫異常*/
#define   _PRE_PID                                   0x88aa77cc //隨機數..
#define   _PRE_OpenProcess                      0x99bb1234
#define   _PRE_GetExitCodeProcess            0x99aa4321
#define   _PRE_TerminateProcess               0xFaFaFaFa
#define   _PRE_MessageBoxA                    0xFA971215
#define   _PRE_szFile                               0xAF792151
#define   _PRE_CloseHandle                      0x88aa77bc
#define   _PRE_DeleteFileA                       0xAF782161
#define   _PRE_Sleep                               0xBf782115
#define   _PRE_GetCurrentThreadId           0xFb564646
#define   _PRE_OpenThread                      0xFb564791

//////////////////////////////自刪除程式//////////////////////////////////////////


BOOL __stdcall SeltKill(__in  DWORD dwInjectPid){
goto _l2;
_l1:
__asm{
pushad
mov ebp,esp
sub  esp,4

mov   eax,_PRE_PID
push  eax
push  FALSE
push  PROCESS_QUERY_INFORMATION|PROCESS_TERMINATE
mov   edx,_PRE_OpenProcess
call    edx
test   eax,eax
jz      _end
mov   ebx,eax

lea     ecx,[ebp-4]
mov    edx,_PRE_GetExitCodeProcess
push   ecx
push   ebx
call     edx

push  ecx
push  ebx
mov   edx,_PRE_TerminateProcess
call    edx

push   ebx
mov    edx,_PRE_CloseHandle
call     edx

push   500 ;等待0.5秒再刪除,或許可以更短
mov    edx,_PRE_Sleep
call     edx

_end:
mov   edx,_PRE_DeleteFileA
mov   ecx,_PRE_szFile
lea     esi,[ecx]
push   esi
call     edx
add   esp,4
popad
ret
    }
_l2:
PSTR   pPageCode=NULL;
ULONG ulPageCodeLen=0;
__asm{
    mov  eax,_l1
    mov  ebx,_l2
    mov  pPageCode,eax
    sub   ebx,eax
    mov  ulPageCodeLen,ebx
    }
DWORD  dwCurPid=GetCurrentProcessId();
if(dwCurPid==dwInjectPid) return FALSE;
HANDLE hprocess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwInjectPid);
if(!hprocess) return FALSE;
LPVOID  lpRmCode=NULL;
LPVOID  lpRmSzFile=NULL;
HANDLE  hRmthd=NULL;
PSTR pMemCode=(PSTR)GlobalAlloc(GPTR,ulPageCodeLen);
if(!pMemCode) return FALSE;
CopyMemory(pMemCode,pPageCode,ulPageCodeLen);
WCHAR  szkerneldir[MAX_PATH];
WCHAR   sysdir[MAX_PATH];
GetSystemDirectoryW(sysdir,(ULONG)(MAX_PATH)*sizeof(WCHAR));
lstrcpynW(szkerneldir,sysdir,(lstrlenW(sysdir)+1)*sizeof(WCHAR));
StrCatW(szkerneldir,L"//kernel32.dll/0");
char szThisFile[260];
GetModuleFileNameA(NULL,szThisFile,260);

DWORD  dwRemoteKernel=GetModulebaseProcessW(dwInjectPid,szkerneldir);/* kernel32在任何程序的地址都是一樣的*/
DWORD     dwLocalKernel=(DWORD)GetModuleHandleW(L"kernel32");
DWORD  dwExportFunc=0;

__try{
dwExportFunc=GetRemoteProcAddress(dwRemoteKernel,dwLocalKernel,"OpenProcess");
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_OpenProcess,dwExportFunc))
__leave;
dwExportFunc=GetRemoteProcAddress(dwRemoteKernel,dwLocalKernel,"GetExitCodeProcess");
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_GetExitCodeProcess,dwExportFunc))
__leave;
dwExportFunc=GetRemoteProcAddress(dwRemoteKernel,dwLocalKernel,"TerminateProcess");
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_TerminateProcess,dwExportFunc))
__leave;
dwExportFunc=GetRemoteProcAddress(dwRemoteKernel,dwLocalKernel,"CloseHandle");
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_CloseHandle,dwExportFunc))
__leave;
dwExportFunc=GetRemoteProcAddress(dwRemoteKernel,dwLocalKernel,"DeleteFileA");
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_DeleteFileA,dwExportFunc))
__leave;
dwExportFunc=GetRemoteProcAddress(dwRemoteKernel,dwLocalKernel,"Sleep");
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_Sleep,dwExportFunc))
__leave;
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_PID,dwCurPid))
__leave;
DWORD dwszlen=lstrlenA(szThisFile)+1;
lpRmSzFile=VirtualAllocEx(hprocess,NULL,dwszlen,MEM_COMMIT,PAGE_READWRITE);
if(!lpRmSzFile) __leave;//不能釋放,否則遠端崩潰
if(!WriteProcessMemory(hprocess,lpRmSzFile,szThisFile,dwszlen,NULL)){
    VirtualFreeEx(hprocess,lpRmSzFile,0,MEM_RELEASE);
    __leave;
    }
if(!FindCodeReplaceAll(pMemCode,ulPageCodeLen,_PRE_szFile,(DWORD)lpRmSzFile)){
  VirtualFreeEx(hprocess,lpRmSzFile,0,MEM_RELEASE);
  __leave;
    }
//寫程式碼
lpRmCode=VirtualAllocEx(hprocess,0,ulPageCodeLen,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if(!lpRmCode){
    VirtualFreeEx(hprocess,lpRmSzFile,0,MEM_RELEASE);
    __leave;
    }
if(!WriteProcessMemory(hprocess,lpRmCode,pMemCode,ulPageCodeLen,NULL)){
VirtualFreeEx(hprocess,lpRmCode,0,MEM_RELEASE);
VirtualFreeEx(hprocess,lpRmSzFile,0,MEM_RELEASE);
__leave;
    }
hRmthd=CreateRemoteThread(hprocess,NULL,0,
                          (LPTHREAD_START_ROUTINE)lpRmCode,NULL,NULL,NULL);
if(!hRmthd){
    VirtualFreeEx(hprocess,lpRmCode,0,MEM_RELEASE);
    VirtualFreeEx(hprocess,lpRmSzFile,0,MEM_RELEASE);
    __leave;
    }
return TRUE;
    }__finally{
        if(pMemCode) GlobalFree(pMemCode);
        if(hprocess) CloseHandle(hprocess);
    }
return FALSE;
    }

//程式碼完畢

上面的程式碼可以實現程式的自刪除,其基本原理就是終止程序,等待,再刪除。或許可以有更好的演算法。。。

補1:

  上述程式碼是通過遠執行緒強制終止呼叫方的程序的,可能出現不確定性,例如呼叫方程序並未做好退出程序的準備就退出。某些情況下需要呼叫方程序處理完畢之後方可退出並刪除,因此遠執行緒需要執行等待,直到呼叫方程序自身退出程序後再刪除它,利用WaitForSingleObject函式即可等待呼叫方程序的結束,而作為等待狀態的核心物件可以是呼叫方的的程序控制代碼,因為一旦程序退出後,該控制代碼就會無狀態,那麼WaitForSingleObject就不再等待,繼而執行之後的刪除函式。

即把上面的程式碼改這個,注意查詢WaitForSingleObject的地址!

__asm{
pushad
mov   eax,_PRE_PID
push  eax
push  FALSE
push  SYNCHRONIZE
mov   edx,_PRE_OpenProcess
call    edx
test   eax,eax
jz      _end
mov   ebx,eax
mov   edx,_PRE_WaitForSingleObject
push  INFINITE  ;無限等待,也可另設時間
push   ebx
call    edx
;waiting...process exit

mov   edx,_PRE_CloseHandle
push  ebx
call    edx

_end: ;假設程序控制代碼打不開就刪除
mov  ecx,_PRE_szFile
lea  esi,[ecx]
mov  edx,_PRE_DeleteFileA
push esi
call   edx

popad
ret
    }

也就是呼叫WaitForSingleObject等待程序,如果程序退出了,WaitForSingleObject就沒狀態了,就不會掛起執行緒了,那麼之後的語句就會執行。

這樣做有個前提,就是被注入的程序必須長時間駐機執行,如果先退出,就沒有作用了。

相關推薦

利用注入程序shellcode實現程式刪除

某些情況下需要exe執行結束刪除自身,而自刪除程式有好幾種方法,如下面的兩方法:a.先使自己快速退出,然後再在命令列中刪除自己;或是先使自己快速退出,然後再在bat檔案或指令碼檔案中刪除自己b. 非常經典的,關閉硬編碼為4的控制代碼,撤銷自己在記憶體的對映,再刪除自己,再結束

[原始碼和文件分享]3種方式實現程式刪除

背景 寫了很多小程式,也有很多小程式需要用到程式自刪除的功能。所謂的自刪除,就是程式能夠自己刪除自己。常見的自刪除實現方式就有批處理方式還有使用MoveFileEx函式重啟刪除的方式。 現在,我就對自己掌握的自刪除方式進行總結。給出 3 種程式自刪除的實現方式。其中,兩種是批處理方式實現的,

c++實現程式刪除

程式自刪除的2鍾方法 第一種 /********************************************************************************** * 程式描述: 本程式為Windows7環境下,程式自刪除的一種實現方

VC實現程式刪除(三種方法)

1、Gary Nebbett的方法 Gary Nebbett乃是WINDOWS NT/2000 NATIVE API REFERENCE的作者。乃NT系統一等一的高手。 下面就分析一些他的這段程式碼,這段程式碼在PROCESS沒有結束前就將啟動PROCESS的EXE檔案刪除了

C# 應用程式刪除

示例應用:ClearDir.exe 可用於清理其他檔案 和 自刪除 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text;

Delphi實現程式銷燬(自殺)

unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton;

利用MoveFileEx實現程式的隱藏、啟動與刪除

摘要:程式的自啟動與自刪除幾乎是所有後門或者木馬所必須具備的功能。程式實現自啟動的方法有很多種,最常見的方法莫過於寫登錄檔,新增服務,或者將自身複製到啟動目錄。自刪除通常的辦法是寫批處理。但是這些方法都已經眾所周知,均被各防毒軟體、防護軟體,主動防禦軟體以及經驗豐富的管理員

微信小程式定義欄位實現選項的動態新增和刪除

問題描述: 在自定義選項中,點選新增選項按鈕,會出現一個選項的輸入框,輸入的選項可以通過點選左側的減號刪除 效果如圖: 解決過程: 1.首先寫出大體的框架。在pages下,建立了一個selfdefine的資料夾,在wxml中寫出靜態結構 selfdefine.wxml 說明

C語言禁區之程序自身刪除自身?是時候展現真正的技術之刪除

微信 top ans alt 答疑 便是 很多 one 批處理文件 前言自刪除: 顧名思義,這個程序功能很簡單,就是實現程序的自刪除功能。是不是感到很困惑,一個程序自己運行著,怎樣把自己關掉後,再把自己刪除呢?結束自己進程的時候,自己也就結束了,不會再繼續執行任何代碼了。在

微信小程序如何實現定義tabBar

type pos use 如何 轉載 url avi float 工作 小程序開發現在非常火,但是對於後臺來說如何做到自定義tabBar呢?下面就來講解下,如何實現微信小程序登錄後根據用戶身份權限不同跳轉到不同的頁面問題。首先需要解決的是:你要把底部導航做成一個公共模板te

安全之路 —— 利用SVCHost.exe系統服務實現後門啟動

cas default wait cal tis lis success 放置 簡介 簡介 在Windows系統中有一個系統服務控制器,叫做SVCHost.exe,它可以用來管理系統的多組服務。它與普通的服務控制不同的是它采用dll導出的ServiceMain主函數實現服

Django中利用filter與simple_tag為前端定義函數的實現方法

但是 col filter 成了 應用程序 註冊 number 獲取 except 前言 Django的模板引擎提供了一般性的功能函數,通過前端可以實現多數的代碼邏輯功能,這裏稱之為一般性,是因為它僅支持大多數常見情況下的函數功能,例如if判斷,ifequal對比返回值等

如何實現程式開機啟動

分為三步: 在AndroidManifest.xml檔案中定義廣播和宣告許可權 實現自定義廣播類 接收到廣播後,設定Activity的啟動模式 在AndroidManifest.xml檔案中定義廣播和宣告許可權 <uses-permission and

修改登錄檔實現程式開機啟動

修改登錄檔實現程式開機自啟動 2017年09月27日 10:55:37 閱讀數:335 登錄檔簡介 何為登錄檔 登錄檔相當於Windows下的一個龐大的層次性資料庫.  基本上有責系統所有的配置資訊 登錄檔是windows作業系統中的一個核心資料庫,其中存放

檔案刪除的一些資料與實現

原文連結 https://blogs.msdn.microsoft.com/oldnewthing/20160108-00/?p=92821 MSDN 上關於 FILE_FLAG_DELETE_ON_CLOSE 的描述     &nbs

css利用padding百分比實現圖片適應高度

應用場景 寬高比率,實現圖片自適應高度,防止圖片載入過程高度為0,載入完圖片高度撐起,它下面的div抖動問題 重點:CSS百分比padding都是相對寬度計算的 <div class="works-item-t"> <img src="./150x200.png">

利用協處理器endpoint實現批量刪除功能

最近因為工作需要,用到了hbase的協處理器endpoint,遇到了一些坑。以批量刪除功能為例記錄一些endpoint的使用方法。至於hbase只是以及協處理器coprocessor的知識在此不做過多介紹。 1.安裝protocbuf直譯器安裝 下載protobuf-2.5.0.ta

程式實現左滑刪除功能

小程式中實現左滑刪除 效果預覽: // wxml <view class="delete_list"> <view class="list" wx:for="{{listData}}" wx:key="{{index}}}" bindtouchstart=

程式頁面跳轉傳參-this和that的區別-登入流程-下拉選單-實現畫布適應各種手機尺寸

小程式頁面跳轉傳參 根目錄下的 app.json 檔案 頁面檔案的路徑、視窗表現、設定網路超時時間、設定多 tab { "pages": [ "pages/index/index", "pages/logs/index" ], "window":

微信小程式 定義單選複選按鈕組的實現(用於實現購物車產品列表功能)

  (一)單選按鈕組 模型圖如下:  index.js Page({ data: { parameter: [{ id: 1, name: '銀色' }, { id: 2, name: '白色' },{ id: 3, name: '黑色' }],//模擬商