随笔-31  评论-92  文章-0  trackbacks-0
有点新意的就是, 在线程函数里调用 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)
0x100000000, 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;
}

posted on 2008-06-21 22:24 free2000fly 阅读(1864) 评论(11)  编辑 收藏 引用

评论:
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-06-22 09:30 | 饭中淹
方法很妙
不过查找地址这个方法,我觉得可以稍微替换下。

以前,我做远程注入的时候,都是把API的地址做成一个结构体,通过WriteProcessMemory写到远程进程里。
在远程线程直接使用,或者对于不是kernel32的api进行初始化。

  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-06-22 09:37 | free2000fly
@饭中淹
能详细一点吗,哥们
  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-06-22 09:51 | free2000fly
@饭中淹
明白了, 将所有用到的地址组织成一个数组, 直接写到目标进程, 远程线程通过查表调用需要的 API. 本质相同, 但对于要调用 kernel32.dll 以外的函数在初始化时有重要意义. 但我没有调用那, 远程代码尽量保持小巧.
  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-06-22 22:57 | 影视剧
很厉害的一个程序  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-06-24 19:54 | guest
Sleep(x)
是干嘛用的..  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-06-24 21:17 | free2000fly
@guest
等一小会, 让注入代码的进程退出以后, 才执行下面的删除操作, 不然十之八九会失败.
  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-07-01 12:33 | lll
什么玩意?有什么用啊?  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. [未登录] 2008-07-01 13:49 | free2000fly
@lll
什么玩意?你说什么玩意?  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-07-09 08:52 | wwwww
现在远程线程会被杀毒报危险操作,会给用户不好的感觉  回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. [未登录] 2008-07-09 14:27 | free2000fly
@wwwww
有道理, 但好歹这是公开的合法的 API, 是受操作系统支持的.   回复  更多评论
  
# re: 也写了一个自删除程序, 采用远程线程注入技术. 2008-09-20 17:37 | vfdff
代码好长,厉害  回复  更多评论
  

标题  
姓名  
主页
验证码 *
内容(提交失败后,可以通过“恢复上次提交”恢复刚刚提交的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
[使用Ctrl+Enter键可以直接提交]
相关链接:
网站导航: