一種躲避運行時代碼校正的方法

來源:互聯網
上載者:User

我們有時候需要對運行中的程式打記憶體補丁,或者對它的代碼掛一些鉤子之類的工作。但是現在相當多軟體進行了運行時的代碼檢測。一旦發現記憶體中的代碼被修改掉,就會進行處理。本文介紹了一種比較特別的辦法,用於通過這些檢測。

首先需要說一下做運行時代碼校正的方法。一般來說,校正者需要取得當前模組的基地址,通過分析PE結構,獲得代碼節的位移和大小,然後對記憶體中的代碼進行CRC或者其他的一些校正。

這其中有個很大的問題,校正者預設了通過這種方式取得的代碼節就是當前被使用到的代碼,但是事實卻不一定如此。一般編譯器正常產生的程式碼,絕大部分跳轉和call語句都使用相對位址,因此,我們完全可以把代碼節或者整個exe檔案映像複製到記憶體其他地方,並操作進程內的所有線程,使得它執行在新複製的那份代碼中。這樣,校正者仍然在掃描舊的地址,新的那份我們就可以隨意修改了。

由於一旦進程開始執行,並且建立其他線程之後,我們通過取得線程Context獲得的EIP,多半在系統代碼中間,所以難以改變。唯一的方法就是,在exe的EntryPoint被執行前,將EntryPoint重新導向到新的代碼,並Hook CreateThread,將新線程也重新置放。可以通過下面幾個步驟來實現:

  1. 如果想處理的進程為a.exe,並且a.exe是由b.exe建立的,那麼我們需要hook掉b.exe的進程建立函數,一般是CreateProcess。
  2. 在Hook的CreateProcess中,以CREATE_SUSPENDED標誌建立a.exe。並向a.exe中注入我們的dll,等待這個dll完成處理之後才ResumeThread a.exe的主線程。
  3. 注入的dll需要完成幾件事。首先要獲得主模組的基地址和大小,並分配足夠的空間,將原始映像複製過去。然後Hook掉EntryPoint,並Hook CreateThread,最後恢複a.exe主線程。
  4. Hook掉的EntryPoint中,恢複被Hook的EntryPoint代碼,防止在後面被檢查出來,然後jmp到新分配的代碼地區即可。
  5. Hook的CreateThread中,重新計算代碼線程函數地址,並修改後建立。這樣,所有線程就都在新分配的代碼中執行了。

下面是注入的dll的實現代碼:

 

pfnCreateThread
g_pCreateThread = ::CreateThread;

PBYTE    g_pbyNewImage = NULL;

 

#pragma
pack(push,1)

typedef
struct
_PUSH_RETN

{

    BYTE
byOpcodePush;//0×68

    DWORD
dwRetnAddr;

    BYTE
byOpcodeRetn;//0xC3

}PUSH_RETN, *PPUSH_RETN;

#pragma
pack(pop)

 

BYTE
g_abyOldEntry[6] = {0};

PBYTE
g_pbyOldEntry = 0;

PBYTE
g_pbyNewEntry = 0;

 

//這裡使用Detours庫Hook掉CreateThread。

BOOL
HookThreadCreate()

{

    DetourTransactionBegin();

    DetourUpdateThread( GetCurrentThread());

 

 

    if( DetourAttach( &(PVOID&)g_pCreateThread, Hook_CreateThread) != NO_ERROR)

    {

        DebugOut( TEXT( “Hook CreateThread failrn”));

    }

 

    if( DetourTransactionCommit() != NO_ERROR)

    {

        DebugOut( TEXT( “Hook failrn”));

        return
FALSE;

    }

    else

    {

        DebugOut( TEXT( “Hook okrn”));

        return
TRUE;

    }

}

 

//Hook的CreateThread裡面,重新計算lpStartAddress地址,並按這個地址來建立

HANDLE
WINAPI
Hook_CreateThread(

                                LPSECURITY_ATTRIBUTES
lpThreadAttributes,

                                SIZE_T
dwStackSize,

                                LPTHREAD_START_ROUTINE
lpStartAddress,

                                LPVOID
lpParameter,

                                DWORD
dwCreationFlags,

                                LPDWORD
lpThreadId

                                )

{

    PBYTE
pfn = (PBYTE)lpStartAddress;

    HMODULE
hMod = ::GetModuleHandle( NULL);

    pfn = g_pbyNewImage + (pfn - (PBYTE)hMod);

    HANDLE
hThread = g_pCreateThread( lpThreadAttributes, dwStackSize, (LPTHREAD_START_ROUTINE)pfn, lpParameter, dwCreationFlags,lpThreadId);

    return
hThread;

}

 

//Hook掉的進入點,恢複舊代碼並跳轉到新分配的代碼空間

__declspec( naked ) VOID
Hook_EntryPoint()

{

    //_asm int 3

    for ( DWORD
i = 0; i < sizeof(g_abyOldEntry); i++)

    {//恢複舊的代碼

        g_pbyOldEntry[i] = g_abyOldEntry[i];

    }

    _asm
jmp
g_pbyNewEntry;

}

 

//Hook掉進入點

VOID
HookEntryPoint()

{

    DebugOut( _T( “In HookEntryPointrn”));

 

    HMODULE
hMod = ::GetModuleHandle( NULL);

    PIMAGE_DOS_HEADER
pstDosHeader = (PIMAGE_DOS_HEADER)hMod;

    PIMAGE_NT_HEADERS
pstHeader = (PIMAGE_NT_HEADERS)((PBYTE)hMod + pstDosHeader->e_lfanew);

    DWORD
dwEntryRVA = pstHeader->OptionalHeader.AddressOfEntryPoint;

    PBYTE
pbyRVA = (PBYTE)(hMod) + dwEntryRVA;

    PPUSH_RETN
pstHook = (PPUSH_RETN)pbyRVA;

    memcpy( g_abyOldEntry, pbyRVA, sizeof(PUSH_RETN));

    pstHook->byOpcodePush = 0×68;

    pstHook->dwRetnAddr = (DWORD)Hook_EntryPoint;

    pstHook->byOpcodeRetn = 0xC3;

 

    g_pbyOldEntry = pbyRVA;

    g_pbyNewEntry = g_pbyNewImage + dwEntryRVA;

    DebugOut( _T(“New image base = 0x%X, New EntryPoint = 0x%Xrn

                Old image base = 0x%X, Old EntryPoint = 0x%Xrn”), g_pbyNewImage, g_pbyNewEntry, hMod, g_pbyOldEntry);

    DebugOut( _T( “HookEntryPoint OKrn”));

}

 

//在DllMain裡面Process Attach的時候調用

VOID
OnAttachProcess()

{

    HMODULE
hMod = ::GetModuleHandle( NULL);

    DWORD
dwSize = GetImageSize();

    g_pbyNewImage = new
BYTE[dwSize];

 

    DWORD
dwOldProtect = 0;

    VirtualProtect( g_pbyNewImage, dwSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    VirtualProtect( (LPVOID)hMod, dwSize, PAGE_READWRITE, &dwOldProtect);

    memcpy( g_pbyNewImage, (LPVOID)hMod, dwSize);

 

    HookEntryPoint();

    HookThreadCreate();

}

 

//獲得主模組映像大小

DWORD
GetImageSize()

{

    HANDLE
hShot = NULL;

    BOOL
blResult = FALSE;

    MODULEENTRY32
stInfo = {0};

    stInfo.dwSize = sizeof( MODULEENTRY32);

    TCHAR
tszTmp[MAX_PATH] = {0};

 

    ::GetModuleFileName( GetModuleHandle(NULL), tszTmp, MAX_PATH);

    _tcslwr( tszTmp);

    size_t
s = _tcslen(tszTmp);

    for ( DWORD
i = 0; i < s; i++)

    {

        if ( tszTmp[s - i] == ”)

        {

            s = s - i + 1;

            break;

        }

    }

    hShot = ::CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, ::GetCurrentProcessId());

    if ( INVALID_HANDLE_VALUE == hShot)

    {

        DebugOut( _T( “CreateToolhelp32Snapshot fail.Err=%drn”), GetLastError());

        return 0;

    }

 

    blResult = ::Module32First( hShot, &stInfo);

    while ( blResult)

    {

        _tcslwr( stInfo.szModule);

        if ( _tcscmp( stInfo.szModule, tszTmp + s) == 0)

        {

            CloseHandle( hShot);

            return
stInfo.modBaseSize;

        }

        blResult = ::Module32Next( hShot, &stInfo);

    }

    CloseHandle( hShot);

    return 0;

}

這種方法對加殼之後的exe作用有限,因為Hook的並不是真實的OEP。這樣做常常會造成殼執行過程中,或者跳轉到OEP之後迅速崩潰。本文僅僅是介紹一種思想,有時候巧妙的構思可以使得複雜問題很快得到解決。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.