1,119 views
首页 > 技术心得 > 也写了一个自删除程序, 采用远程线程注入技术.

也写了一个自删除程序, 采用远程线程注入技术.

2008年6月21日

有点新意的就是, 在线程函数里调用 VirtualFree 函数时, 不是用 call 指令, 而是 jmp, 这样, 当 VirtualFree 函数返回后,
就会直接进入到 ExitThread 函数, 因为此前已经将这个函数的地址压入栈了.

我们知道, call 指令其实就是两个操作:
1. 将 call 指令返回后的紧接着的下一条指令的地址压栈,
2. 跳转到 call 指令代表的函数的首地址.

函数执行完毕后,
1. ret 指令就将从栈上读取返回地址,
2. 跳转到那个地址继续执行,

我们其实是在这里人为的将其执行流程给改了. 我们这么做的意图很负责任: 释放这块指令所处的内存, 避免内存泄漏,
因为注入代码的进程已经退出, 没有机会清理这块内存, 咱们就自力更生了.
这么干也很安全, 因为 ExitThread 不会返回了, EIP 就不会跑飞了(也就是说, 换成别的函数会造成目标进程崩溃的, 特此说明.).
线程函数的最后的平衡堆栈的指令和返回指令是没有意义的, 放这里是为了让反汇编器不会少见多怪.

接下来废话少说, 贴代码:

#include <windows.h>
#include <tchar.h>
#include <TLHELP32.H>
#include <stddef.h>

void EnablePrivilege(void)
{
    HANDLE           hToken;
    TOKEN_PRIVILEGES tp = { 0 };

    HANDLE hProcess = GetCurrentProcess();

    if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                          &hToken))
        return;

    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid))
    {
        CloseHandle(hToken);
        return;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
                          NULL, NULL);
    CloseHandle(hToken);
}

DWORD FindTarget(LPCTSTR lpszProcess)
{
    DWORD  dwRet     = 0;
    PROCESSENTRY32 pe32 = { sizeof( PROCESSENTRY32 ) };
    HANDLE hSnapshot = NULL;

    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    Process32First(hSnapshot, &pe32);
    do
    {
        if (0 == lstrcmpi(pe32.szExeFile, lpszProcess))
        {
            dwRet = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapshot, &pe32));
    CloseHandle(hSnapshot);
    return dwRet;
}

static DWORD WINAPI DelProc(LPVOID lpParam)
{
    Sleep(50);
    DeleteFileA((LPCSTR)lpParam);
    VirtualFree((PVOID)0x10000000, 0, MEM_RELEASE);
    ExitThread(0);
    return 0;
}

//==============================================================================

PUCHAR FindDWordFromBuffer(PUCHAR lpBuffer, UINT cchMax, DWORD dwValue)
{
    PUCHAR pResult = NULL;
    UINT nIter = 0;
    for (nIter=0; nIter<cchMax; nIter++)
    {
        if ( *(DWORD *)(lpBuffer + nIter) == dwValue ) {
            pResult = lpBuffer + nIter;
            break;
        }
    }
    return pResult;
}

//==============================================================================

#define Sleep_addr          0xBBBBBBBB
#define DeleteFileA_addr    0xDDDDDDDD
#define ExitThread_addr     0xFFFFFFFF
#define VirtualFree_addr    0xEEEEEEEE
#define _DelProc_addr       0xCCCCCCCC

static __declspec(naked) DWORD WINAPI _DelProc(LPVOID lpParam)
{
    __asm {
        ; // __this_addr:
        push    ebp                     ;
        mov     ebp, esp                ;

        push    0x32                    ; // dwMilliseconds
        mov     eax, Sleep_addr         ;
        call    eax                     ; // ds:__imp__Sleep@4 ; Sleep(x)

        mov     eax, [ebp+8]            ;
        push    eax                     ; // lpFileName
        mov     eax, DeleteFileA_addr   ;
        call    eax                     ; // ds:__imp__DeleteFileA@4 ; DeleteFileA(x)

        push    0                       ; // dwExitCode, ExitThread 函数的参数

        push    8000h                   ; // dwFreeType
        push    0                       ; // dwSize
        mov     eax, _DelProc_addr      ; // __this_addr
        push    eax                     ; // lpAddress

        mov     eax, ExitThread_addr    ; // ds:ExitThread
        push    eax                     ; // 将 ExitThread 函数的地址压栈, 这样, VirtualFree 函数返回后就会马上执行 ExitThread 函数

        mov     eax, VirtualFree_addr   ;
        jmp     eax                     ; // ds:__imp__VirtualFree@12 ; VirtualFree(x,x,x)

        mov     esp, ebp                ; // 以下 3 条指令不会被执行的.
        pop     ebp                     ;

        ret     4                       ;
    }
}

static __declspec(naked) DWORD WINAPI _DelProc_end(LPVOID lpParam)
{
    __asm {
        ret     4                       ;
    }
}

BOOL BuildRemoteThreadCode(OUT PUCHAR lpCode, UINT cchMax, DWORD dwRemoteBegin)
{
    UINT nCodeLen = 0;
    PUCHAR pIter = NULL;
    DWORD dwFnAddr = 0;

    if (NULL==lpCode || 0==cchMax) {
        return FALSE;
    }

    nCodeLen = (PUCHAR) &_DelProc_end - (PUCHAR) &_DelProc;
    if (nCodeLen > cchMax) {
        return FALSE;
    }

    memcpy((void *)lpCode, (void *) &_DelProc, nCodeLen);

    {
        pIter = FindDWordFromBuffer(lpCode, nCodeLen, Sleep_addr);
        if (NULL == pIter) {
            return FALSE;
        }

        dwFnAddr = (DWORD) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "Sleep");

        if (0 == dwFnAddr) {
            return FALSE;
        }
        *(DWORD *)pIter = dwFnAddr;
    }

    {
        pIter = FindDWordFromBuffer(lpCode, nCodeLen, DeleteFileA_addr);
        if (NULL == pIter) {
            return FALSE;
        }

        dwFnAddr = (DWORD) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "DeleteFileA");
        if (0 == dwFnAddr) {
            return FALSE;
        }
        *(DWORD *)pIter = dwFnAddr;
    }

    {
        pIter = FindDWordFromBuffer(lpCode, nCodeLen, ExitThread_addr);
        if (NULL == pIter) {
            return FALSE;
        }

        dwFnAddr = (DWORD) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "ExitThread");
        if (0 == dwFnAddr) {
            return FALSE;
        }
        *(DWORD *)pIter = dwFnAddr;
    }

    {
        pIter = FindDWordFromBuffer(lpCode, nCodeLen, VirtualFree_addr);
        if (NULL == pIter) {
            return FALSE;
        }

        dwFnAddr = (DWORD) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "VirtualFree");
        if (0 == dwFnAddr) {
            return FALSE;
        }
        *(DWORD *)pIter = dwFnAddr;
    }

    {
        pIter = FindDWordFromBuffer(lpCode, nCodeLen, _DelProc_addr);
        if (NULL == pIter) {
            return FALSE;
        }

        dwFnAddr = (DWORD) dwRemoteBegin;
        if (0 == dwFnAddr) {
            return FALSE;
        }
        *(DWORD *)pIter = dwFnAddr;
    }

    return TRUE;
}

BOOL RemoteDel(DWORD dwProcessID, LPCSTR lpszFileName, DWORD dwTime)
{
    CHAR szFileName[MAX_PATH] = { 0 };
    HANDLE hProcess = NULL;
    DWORD dwCodeLen = 0;
    DWORD dwSize = 0;
    LPVOID lpRemoteBuf = NULL;
    PUCHAR pBuff = NULL;
    DWORD dwWritten = 0;
    DWORD dwID = 0;
    HANDLE hThread = NULL;

    // 打开目标进程
    hProcess = OpenProcess(
                   PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE,
                   dwProcessID);
    if (NULL == hProcess)
        return FALSE;

    GetModuleFileNameA(NULL, szFileName, MAX_PATH);
    dwCodeLen = (DWORD)&_DelProc_end - (DWORD)&_DelProc;

    dwSize = dwCodeLen + sizeof(szFileName);

    lpRemoteBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (NULL == lpRemoteBuf)
    {
        CloseHandle(hProcess);
        return FALSE;
    }

    pBuff = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(pBuff, &_DelProc, dwSize);

    BuildRemoteThreadCode(pBuff, dwCodeLen, (DWORD)lpRemoteBuf);
    memcpy(pBuff+dwCodeLen, szFileName, strlen(szFileName) + 1);

    WriteProcessMemory(hProcess, lpRemoteBuf, (LPVOID)pBuff, dwSize, &dwWritten);

    hThread = CreateRemoteThread(hProcess, NULL, 0,
                                 (LPTHREAD_START_ROUTINE)lpRemoteBuf,
                                 (LPVOID)((DWORD)lpRemoteBuf + dwCodeLen), 0, &dwID);

    CloseHandle(hThread);
    CloseHandle(hProcess);

    VirtualFree(pBuff, 0, MEM_RELEASE);

    return TRUE;
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR lpCmdLine, int nShowCmd)
{
    CHAR szMe[MAX_PATH] = { 0 };
    DWORD dwId = 0;

    EnablePrivilege();

    GetModuleFileNameA(NULL, szMe, MAX_PATH);

    dwId = FindTarget(_T("explorer.exe"));
    RemoteDel(dwId, szMe, 50);

    return 0;
}

技术心得

  1. 目前还没有任何评论.
  1. 目前还没有任何 trackbacks 和 pingbacks.