zoukankan      html  css  js  c++  java
  • 代码注入

    @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
    ", 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
    ", GetLastError());
            return FALSE;
        }
    
        if( !WriteProcessMemory(hProcess,                       // hProcess
                                pRemoteBuf[0],                  // lpBaseAddress
                                (LPVOID)&param,                 // lpBuffer
                                dwSize,                         // nSize
                                NULL) )                         // [out] lpNumberOfBytesWritten
        {
            printf("WriteProcessMemory() fail : err_code = %d
    ", 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
    ", GetLastError());
            return FALSE;
        }
    
        if( !WriteProcessMemory(hProcess,                       // hProcess
                                pRemoteBuf[1],                  // lpBaseAddress
                                (LPVOID)ThreadProc,             // lpBuffer
                                dwSize,                         // nSize
                                NULL) )                         // [out] lpNumberOfBytesWritten
        {
            printf("WriteProcessMemory() fail : err_code = %d
    ", 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
    ", 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在新线程运行时断下

  • 相关阅读:
    CodeForces 55D Beautiful numbers(数位dp+数学)
    hdu 2089 不要62(数位dp入门)
    Git版本控制
    Git初始化-添加提交以及查看状态
    linux-高并发与负载均衡-lvs-3种模型推导
    Scrapy中选择器的用法
    Scrapy命令行详解
    Scrapy框架基本用法讲解
    MaxCompute教程
    Scrapy安装报错
  • 原文地址:https://www.cnblogs.com/dliv3/p/6349751.html
Copyright © 2011-2022 走看看