代码注入

@author: dlive

0x01 代码注入 VS DLL注入

代码注入相比于DLL注入的有点:

  1. 占用内存少,如果要注入的代码与数据较少,那么就不需要将它们做成DLL的形式注入,此时代码注入的方式占用的内存会更少
  2. 难以查找痕迹,DLL注入的方式会在目标内存中留下相关痕迹,很容易让人判断出目标进程是否被执行过注入操作,代码注入更难以查找痕迹
  3. 其他,不需要另外的DLL文件,只要有代码注入程序即可。

0x02 代码分析

CodeInjection.cpp

typedef struct _THREAD_PARAM 
{
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
    char    szBuf[4][128];          // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;

//声明函数类型
typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
    LPCSTR lpLibFileName
);

//声明函数类型
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
    HMODULE hModule,
    LPCSTR lpProcName
);

//声明函数类型
typedef int (WINAPI *PFMESSAGEBOXA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
);

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    PTHREAD_PARAM   pParam      = (PTHREAD_PARAM)lParam;
    HMODULE         hMod        = NULL;
    FARPROC         pFunc       = NULL;

    // LoadLibrary()
  	// 对函数指针进行强制类型转换
    hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);    // "user32.dll"
    if( !hMod )
        return 1;

    // GetProcAddress()
    pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);  // "MessageBoxA"
    if( !pFunc )
        return 1;

    // MessageBoxA()
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

    return 0;
}

BOOL InjectCode(DWORD dwPID)
{
    HMODULE         hMod            = NULL;
    THREAD_PARAM    param           = {0,};
    HANDLE          hProcess        = NULL;
    HANDLE          hThread         = NULL;
    LPVOID          pRemoteBuf[2]   = {0,};
    DWORD           dwSize          = 0;

    hMod = GetModuleHandleA("kernel32.dll");

    // set THREAD_PARAM
    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
    param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
    strcpy_s(param.szBuf[0], "user32.dll");
    strcpy_s(param.szBuf[1], "MessageBoxA");
    strcpy_s(param.szBuf[2], "www.reversecore.com");
    strcpy_s(param.szBuf[3], "ReverseCore");

    // Open Process
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS,   // dwDesiredAccess
                                  FALSE,                // bInheritHandle
                                  dwPID)) )             // dwProcessId
    {
        printf("OpenProcess() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for THREAD_PARAM
    dwSize = sizeof(THREAD_PARAM);
    if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess,          // hProcess
                                      NULL,                 // lpAddress
                                      dwSize,               // dwSize
                                      MEM_COMMIT,           // flAllocationType
                                      PAGE_READWRITE)) )    // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !WriteProcessMemory(hProcess,                       // hProcess
                            pRemoteBuf[0],                  // lpBaseAddress
                            (LPVOID)&param,                 // lpBuffer
                            dwSize,                         // nSize
                            NULL) )                         // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for ThreadProc()
    dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
    if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess,          // hProcess
                                      NULL,                 // lpAddress
                                      dwSize,               // dwSize
                                      MEM_COMMIT,           // flAllocationType
                                      PAGE_EXECUTE_READWRITE)) )    // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !WriteProcessMemory(hProcess,                       // hProcess
                            pRemoteBuf[1],                  // lpBaseAddress
                            (LPVOID)ThreadProc,             // lpBuffer
                            dwSize,                         // nSize
                            NULL) )                         // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !(hThread = CreateRemoteThread(hProcess,            // hProcess
                                       NULL,                // lpThreadAttributes
                                       0,                   // dwStackSize
                                       (LPTHREAD_START_ROUTINE)pRemoteBuf[1],     // dwStackSize
                                       pRemoteBuf[0],       // lpParameter
                                       0,                   // dwCreationFlags
                                       NULL)) )             // lpThreadId
    {
        printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

​ 代码比较简单易懂,需要注意的一点是,代码注入时需要同时注入代码与代码中引用的数据,并且要保证代码能够正确引用注入的数据。从ThreadProc的代码中也可以看出,函数中并未直接调用相关API,也未直接定义使用字符串,它们都通过THREAD_PARAM结构体以线程参数的形式传递使用。

​ ThreadProc的参数中传递了LoadLibrary和GetProcAddress的地址以及参数,并使用LoadLibrary加载user32.dll,使用GetProcAddress获取MessagaBox的地址。

这里根据代码注入的原理做一个猜想,不传递LoadLibrary和GetProcAddress的地址而是直接在ThreadProc中直接调用这两个函数应该也是可以的,因为注入程序和被注入程序在同一个运行环境下的Loadlibrary和GetProcAddress函数的地址是一样的。问了一下大佬,大佬说这个猜想不对,如果直接调用LoadLibrary或GetProcAddress会经过IAT这个过程,call函数的时候会call 到IAT的空间,然后做一个跳转调到真正的函数地址。所以注入程序里如果ThreadProc直接调用loadlibrary,那ThreadProc的代码注入到其他程序里是不能用的。而通过GetProcAddress获得的是函数真正的地址,所以注入到别的程序中是可以正常运行的。

​ 在函数大小的计算时使用了如下代码

    dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;

​ 能这么计算,是因为MS VC++中使用Release模式编译程序代码后,源代码中函数顺序和二进制代码中的顺序一致。InjectCode函数定义在ThreadProc之后,函数名就是函数地址,所以直接将地址相减即可。

0x03 代码注入调试

首先使用OD打开notepad.exe进行调试

设置调试选项->事件,勾选中断于新线程

运行注入命令,OD在新线程运行时断下

posted @ 2017-01-25 16:58  dlive  阅读(367)  评论(0编辑  收藏  举报