前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DLL注入

DLL注入

作者头像
红客突击队
发布2022-09-29 21:23:56
1.7K0
发布2022-09-29 21:23:56
举报
文章被收录于专栏:kaydenkayden

DLL注入

前言

继续学习《逆向工程核心原理》,本篇笔记是第三部分:DLL注入,主要包括三种DLL注入、DLL卸载、修改PE、代码注入等内容

一、windows消息钩取

1、钩子

钩子(Hook):截取信息时所用的手段

以键盘消息为例,常规流程如下:

发生键盘输入事件时,WM_KEYDOWN消息被添加到[OS message queue]

OS判断哪个应用发生了事件,然后从[OS message queue]取出消息,添加到相应应用的[application message queue]

应用监视自己的[application message queue],发现新的WM_KEYDOWN消息后,调用相应的时间处理程序

在此过程中,消息钩子可以截取消息,修改消息,如下图所示:

2、SetWindowsHookEx()

SetWindowsHookEx() API可以实现消息钩子,定义如下:

钩子过程(hook procedure)是系统调用的回调函数

安装钩子时,钩子过程需要在DLL内部,该DLL的示例句柄(instance handle)即hMod

线程ID如果为0,则钩子为“全局钩子”

用SetWindowsHookEx()设置好钩子后,在某个进程中生成指定消息时,操作系统会将相关DLL文件强制注入相应进程

3、键盘消息钩取

如下图所示:

KeyHook.dll是个含有钩子过程的DLL文件

HookMain.exe是个加载KeyHook.dll,并使用SetWindowsHookEx()安装键盘钩子的程序

一个钩子HookMain.exe的源码

代码语言:javascript
复制
//HookMain.exe

#include "stdio.h"
#include "conio.h"
#include "windows.h"

#define  DEF_DLL_NAME    "KeyHook.dll"
#define  DEF_HOOKSTART    "HookStart"
#define  DEF_HOOKSTOP    "HookStop"

typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();

void main()
{
  HMODULE      hDll = NULL;
  PFN_HOOKSTART  HookStart = NULL;
  PFN_HOOKSTOP  HookStop = NULL;
  char      ch = 0;

    // 加载KeyHook.dll
  hDll = LoadLibraryA(DEF_DLL_NAME);
    if( hDll == NULL )
    {
        printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
        return;
    }

    // 获取导出函数地址
  HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
  HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);

    // 开始
  HookStart();

    // “q”退出
  printf("press 'q' to quit!\n");
  while( _getch() != 'q' )  ;

    // 结束
  HookStop();

    // 卸载 KeyHook.dll 
  FreeLibrary(hDll);
}

其中KeyHook.dll的源码

代码语言:javascript
复制
// KeyHook.dll

#include "stdio.h"
#include "windows.h"

#define DEF_PROCESS_NAME    "notepad.exe"

HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
  switch( dwReason )
  {
        case DLL_PROCESS_ATTACH:
      g_hInstance = hinstDLL;
      break;

        case DLL_PROCESS_DETACH:
      break;  
  }

  return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  char szPath[MAX_PATH] = {0,};
  char *p = NULL;

  if( nCode >= 0 )
  {
    // bit 31 : 0 => press, 1 => release
    if( !(lParam & 0x80000000) ) //释放键盘按键时
    {
      GetModuleFileNameA(NULL, szPath, MAX_PATH);
      p = strrchr(szPath, '\\');

            // 比较当前进程名称,如果是 notepad.exe 则消息不会传给应用程序
      if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
        return 1;
    }
  }

    //反之,调用 CallNextHookEx() 消息传给应用程序
  return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

#ifdef __cplusplus
extern "C" {
#endif
  __declspec(dllexport) void HookStart()
{
    g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
  }

  __declspec(dllexport) void HookStop()
{
    if( g_hHook )
    {
      UnhookWindowsHookEx(g_hHook);
      g_hHook = NULL;
    }
  }
#ifdef __cplusplus
}
#endif

调用导出函数HookStart()时,SetWindowsHookEx()会将KeyboadProc()添加到键盘钩链

4、调试练习

(1)调试HookMain.exe

OD打开

由于我们知道运行之后,会出现press 'q' to quit!字符串 所以可以 右键-Search for-All referenced text strings

可以看到地址0040104D处应该就是Main函数

在地址00401000处设置断点,然后开始调试

  • 在地址00401006处调用了LoadLibrary(KeyHook.dll)
  • 在地址0040104B处调用了KeyHook.HookStart(),跟踪如下

这是Hookstart()函数

  • 在地址100010EF可以看到 CALL SetWindowsHookExW()
  • SetWindowsHookExW() 的第二个参数lpfn是10001020

(2)调试KeyHook.dll

开启如图所示这项,有DLL装载时,会自动暂停调试

后面不多说,简单讲就是

OD打开notepad.exe

运行HookMain.exe

OD跳出 Executable modules 窗口

根据上一小节的地址10001020找到钩子

二、DLL注入

DLL注入:向运行中的其他进程强制插入特定的DLL文件,如下图所示

原理:从外部促使目标进程调用LoadLibrary() API,从而强制调用DLL的DllMain()

注入的DLL拥有目标进程内存的访问权限

一些用处

修复Bug

改善功能

消息钩取

API钩取

监视进程

恶意代码

……

注入方法

创建远程线程 CreateRemoteThread() API

使用注册表 AppInit_DLLs 值

消息钩取 SetWindowsHookEx() API (就是上面消息钩取所用的)

下面记下前两种方法

1、CreateRemoteThread()

书中给了个例子:用InjectDll.exe把myhack.dll注入notepad.exe

两个文件源码如下:

代码语言:javascript
复制
// myhack.dll

#include "windows.h"
#include "tchar.h"

#pragma comment(lib, "urlmon.lib")

#define DEF_URL       (L"http://www.naver.com/index.html")
#define DEF_FILE_NAME   (L"index.html")

HMODULE g_hMod = NULL;

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    TCHAR szPath[_MAX_PATH] = {0,};

    if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) )
        return FALSE;

    TCHAR *p = _tcsrchr( szPath, '\\' );
    if( !p )
        return FALSE;
  //下载指定网站的index.html文件
    _tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME);

    URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL); 

    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    HANDLE hThread = NULL;

    g_hMod = (HMODULE)hinstDLL;

    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH :  //加载时
        OutputDebugString(L"<myhack.dll> Injection!!!"); //输出调试字符串
        hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); //创建线程
        CloseHandle(hThread);
        break;
    }

    return TRUE;
}

2、AppInit_DLLs

将要注入的DLL路径写入AppInit_DLLs,并把LoadAppInit_DLLs设置为1 重启后,指定的DLL会注入所有的进程

代码语言:javascript
复制
// myhack2.cpp

#include "windows.h"
#include "tchar.h"

#define DEF_CMD  L"c:\\Program Files\\Internet Explorer\\iexplore.exe" 
#define DEF_ADDR L"http://www.naver.com"
#define DEF_DST_PROC L"notepad.exe"

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    TCHAR szCmd[MAX_PATH]  = {0,};
    TCHAR szPath[MAX_PATH] = {0,};
    TCHAR *p = NULL;
    STARTUPINFO si = {0,};
    PROCESS_INFORMATION pi = {0,};

    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH : 
        if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
            break;

        if( !(p = _tcsrchr(szPath, '\\')) )
            break;

        if( _tcsicmp(p+1, DEF_DST_PROC) )
            break;

        wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR);
        if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, 
                            NULL, NULL, FALSE, 
                            NORMAL_PRIORITY_CLASS, 
                            NULL, NULL, &si, &pi) )
            break;

        if( pi.hProcess != NULL )
            CloseHandle(pi.hProcess);

        break;
    }

    return TRUE;
}

三、DLL卸载

DLL卸载(DLL Ejection):将强制插入进程的DLL弹出的技术

原理:驱使目标进程调用FreeLibrary() API

例子:EjectDll.exe卸载上面加载到notepad.exe的myhack.dll,代码如下:

代码语言:javascript
复制
// EjectDll.exe

#include "windows.h"
#include "tlhelp32.h"
#include "tchar.h"

#define DEF_PROC_NAME  (L"notepad.exe")
#define DEF_DLL_NAME  (L"myhack.dll")

DWORD FindProcessID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;

    // Get the snapshot of the system
    pe.dwSize = sizeof( PROCESSENTRY32 );
    hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

    // find process
    Process32First(hSnapShot, &pe);
    do
    {
        if(!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    }
    while(Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                    &hToken) )
    {
        _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        _tprintf(L"The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    BOOL bMore = FALSE, bFound = FALSE;
    HANDLE hSnapshot, hProcess, hThread;
    HMODULE hModule = NULL;
    MODULEENTRY32 me = { sizeof(me) };
    LPTHREAD_START_ROUTINE pThreadProc;

    // dwPID = notepad 进程 ID
    // 使用 TH32CS_SNAPMODULE 参数,获取加载到 notepad 进程的 DLL名称
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);

    bMore = Module32First(hSnapshot, &me);
    for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
    {
        if( !_tcsicmp((LPCTSTR)me.szModule, szDllName) || 
            !_tcsicmp((LPCTSTR)me.szExePath, szDllName) ) 
        {
            bFound = TRUE;
            break;
        }
    }

    if( !bFound )
    {
        CloseHandle(hSnapshot);
        return FALSE;
    }

    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) //使用进程ID来获取目标进程的进程句柄
    {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }

    //获取加载到EjectDll.exe进程的kernel32.FreeLibrary地址(这个地址在所有进程中是一样的)
    hModule = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");

    //在目标进程中运行线程,pThreadProc是FreeLibrary地址,me.modBaseAddr是要卸载的DLL的加载地址
    hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 pThreadProc, me.modBaseAddr, 
                                 0, NULL);
    WaitForSingleObject(hThread, INFINITE);  

    CloseHandle(hThread);
    CloseHandle(hProcess);
    CloseHandle(hSnapshot);

    return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
    DWORD dwPID = 0xFFFFFFFF;

    // find process
    dwPID = FindProcessID(DEF_PROC_NAME);
    if( dwPID == 0xFFFFFFFF )
    {
        _tprintf(L"There is no <%s> process!\n", DEF_PROC_NAME);
        return 1;
    }

    _tprintf(L"PID of \"%s\" is %d\n", DEF_PROC_NAME, dwPID);

    // change privilege
    if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // eject dll
    if( EjectDll(dwPID, DEF_DLL_NAME) )
        _tprintf(L"EjectDll(%d, \"%s\") success!!!\n", dwPID, DEF_DLL_NAME);
    else
        _tprintf(L"EjectDll(%d, \"%s\") failed!!!\n", dwPID, DEF_DLL_NAME);

    return 0;
}

四、通过修改PE加载DLL

上面是在运行的进程中注入DLL

本节直接修改目标程序的可执行文件,使其在运行时强制加载DLL

目标:修改TextView.exe,使其运行时自动加载myhack3.dll

1、TextView.exe

这是个简单的文本查看程序

用PEView查看,可以看到4个本身就已经加载的DLL文件

2、myhack3.dll源码

源码如下

代码语言:javascript
复制
#include "stdio.h"
#include "windows.h"
#include "shlobj.h"
#include "Wininet.h"
#include "tchar.h"

#pragma comment(lib, "Wininet.lib")

#define DEF_BUF_SIZE            (4096)
#define DEF_URL                 L"http://www.baidu.com/index.html"
#define DEF_INDEX_FILE          L"index.html"

HWND g_hWnd = NULL;

#ifdef __cplusplus
extern "C" {
#endif
// 出现在 IDT 中的 dummy export function...
// 主要是为了保持形式的完整
__declspec(dllexport) void dummy()
{
    return;
}
#ifdef __cplusplus
}
#endif

//DownloadURL下载指定szURL的网页,保存到szFile目录
BOOL DownloadURL(LPCTSTR szURL, LPCTSTR szFile)
{
    BOOL            bRet = FALSE;
    HINTERNET      hInternet = NULL, hURL = NULL;
    BYTE            pBuf[DEF_BUF_SIZE] = {0,};
    DWORD           dwBytesRead = 0;
    FILE            *pFile = NULL;
    errno_t         err = 0;

    hInternet = InternetOpen(L"ReverseCore", 
                             INTERNET_OPEN_TYPE_PRECONFIG, 
                             NULL, 
                             NULL, 
                             0);
    if( NULL == hInternet )
    {
        OutputDebugString(L"InternetOpen() failed!");
        return FALSE;
    }

    hURL = InternetOpenUrl(hInternet,
                           szURL,
                           NULL,
                           0,
                           INTERNET_FLAG_RELOAD,
                           0);
    if( NULL == hURL )
    {
        OutputDebugString(L"InternetOpenUrl() failed!");
        goto _DownloadURL_EXIT;
    }

    if( err = _tfopen_s(&pFile, szFile, L"wt") )
    {
        OutputDebugString(L"fopen() failed!");
        goto _DownloadURL_EXIT;
    }

    while( InternetReadFile(hURL, pBuf, DEF_BUF_SIZE, &dwBytesRead) )
    {
        if( !dwBytesRead )
            break;

        fwrite(pBuf, dwBytesRead, 1, pFile);
    }

    bRet = TRUE;

_DownloadURL_EXIT:
    if( pFile )
        fclose(pFile);

    if( hURL )
        InternetCloseHandle(hURL);

    if( hInternet )
        InternetCloseHandle(hInternet);

    return bRet;
}

BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
    DWORD dwPID = 0;

    GetWindowThreadProcessId(hWnd, &dwPID);

    if( dwPID == (DWORD)lParam )
    {
        g_hWnd = hWnd;
        return FALSE;
    }

    return TRUE;
}

HWND GetWindowHandleFromPID(DWORD dwPID)
{
    EnumWindows(EnumWindowsProc, dwPID);

    return g_hWnd;
}

//DropFile将下载的index.html拖入TextView中
BOOL DropFile(LPCTSTR wcsFile)
{
    HWND            hWnd = NULL;
    DWORD           dwBufSize = 0;
    BYTE            *pBuf = NULL; 
  DROPFILES    *pDrop = NULL;
    char            szFile[MAX_PATH] = {0,};
    HANDLE          hMem = 0;

    WideCharToMultiByte(CP_ACP, 0, wcsFile, -1,
                        szFile, MAX_PATH, NULL, NULL);

    dwBufSize = sizeof(DROPFILES) + strlen(szFile) + 1;

    if( !(hMem = GlobalAlloc(GMEM_ZEROINIT, dwBufSize)) )
    {
        OutputDebugString(L"GlobalAlloc() failed!!!");
        return FALSE;
    }

    pBuf = (LPBYTE)GlobalLock(hMem);

    pDrop = (DROPFILES*)pBuf; 
    pDrop->pFiles = sizeof(DROPFILES);
    strcpy_s((char*)(pBuf + sizeof(DROPFILES)), strlen(szFile)+1, szFile);

    GlobalUnlock(hMem);

    if( !(hWnd = GetWindowHandleFromPID(GetCurrentProcessId())) )
    {
        OutputDebugString(L"GetWndHandleFromPID() failed!!!");
        return FALSE;
    }

    PostMessage(hWnd, WM_DROPFILES, (WPARAM)pBuf, NULL);

    return TRUE;
}

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    TCHAR szPath[MAX_PATH] = {0,};
    TCHAR *p = NULL;

    OutputDebugString(L"ThreadProc() start...");

    GetModuleFileName(NULL, szPath, sizeof(szPath));

    if( p = _tcsrchr(szPath, L'\\') )
    {
        _tcscpy_s(p+1, wcslen(DEF_INDEX_FILE)+1, DEF_INDEX_FILE);

        OutputDebugString(L"DownloadURL()");
        //线程中下载指定的网页并放入文本查看程序
        if( DownloadURL(DEF_URL, szPath) )
        {
            OutputDebugString(L"DropFlie()");
            DropFile(szPath);
        }
    }

    OutputDebugString(L"ThreadProc() end...");

    return 0;
}

//DllMain创建线程并运行
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH : 
            CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL));
            break;
    }

    return TRUE;
}

3、修改TextView.exe

思路:将myhack3.dll加入TextView.exe的IDT的末尾

(1)查看IDT是否有足够空间

PEView查看IDT地址(RVC)是000084CC

找到位置如下图所示,整个IID区域的RVA是000084CC-0000852F

修改视图,发现文件偏移是000076CC

winhex打开,找到地址,发现无足够空间给myhack3.dll

(2)移动IDT

查找空白区域,移动整个IDT以获取足够的空间 在rdata的尾部有大量空白(RVA:00008C60-00008DFF

但是要注意,不是所有文件区域都映射到内存中 查看节区头,可以看到.rdata节区的大小为00002E00,实际使用大小为00002C56,还剩下000001AA的空间,够用了

下面开始修改 首先修改导入表的RVA值为8C80,如下

然后删除绑定导入表(全修改为0)

剪切导入表到8C80(RAW:7E80),并在尾部添加与myhack3.dll对应的IID

这几个修改的值如下

在对应地址处修改如下

然后修改IAT节区的属性值

向属性值40000040添加IMAGE_SCN_MEM_WRITE(80000000),进行OR运算,得到C0000040,如图所示

至此完成所有修改,验证如下

五、代码注入

代码注入(Code Injection):向目标进程插入独立运行代码并使之运行,一般调用CreateRemoteThread() API

比起DLL注入

  • 内存占用更少
  • 难以查找痕迹
  • 不需要另外的DLL文件

1、示例:CodeInjection

向notepad.exe进行注入,使其弹出消息框,源码如下:

代码语言:javascript
复制
// CodeInjection.cpp
// reversecore@gmail.com
// http://www.reversecore.com

#include "windows.h"
#include "stdio.h"

// 下面一大串其实就是MessageBoxA(NULL, "www.reversecore.com", "ReverseCore", MB_OK)
// 为了同时注入代码和数据,并保证代码能准确引用注入的数据,通过_THREAD_PARAM 结构体以线程参数传递
typedef struct _THREAD_PARAM 
{
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
    char    szBuf[4][128];          // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;

// LoadLibraryA()
typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
    LPCSTR lpLibFileName
);

// GetProcAddress()
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
    HMODULE hModule,
    LPCSTR lpProcName
);

// MessageBoxA
typedef int (WINAPI *PFMESSAGEBOXA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
);

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

    // LoadLibrary("user32.dll")
    // pParam->pFunc[0] -> kernel32!LoadLibraryA()
    // pParam->szBuf[0] -> "user32.dll"
    hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);  
    if( !hMod )
        return 1;

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

    // MessageBoxA(NULL, "www.reversecore.com", "ReverseCore", MB_OK)
    // pParam->szBuf[2] -> "www.reversecore.com"
    // pParam->szBuf[3] -> "ReverseCore"
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

    return 0;
}

// 主体,与DLL注入很类似
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;
    }

    // data: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;
    }

    // code: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;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                    &hToken) )
    {
        printf("OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

//main函数调用InjectCode,传入的dwPID是目标进程的PID
int main(int argc, char *argv[])
{
    DWORD dwPID     = 0;

  if( argc != 2 )
  {
      printf("\n USAGE  : %s <pid>\n", argv[0]);
    return 1;
  }

  // change privilege
  if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // code injection
    dwPID = (DWORD)atol(argv[1]);
    InjectCode(dwPID);

  return 0;
}

六、使用汇编语言编写注入代码

用OD进行汇编

一个简单没啥用的程序asmtest.exe,源码如下:

代码语言:javascript
复制
// asmtest.exe

#include "stdio.h"

int test()
{
    return 0;
}

int main(int argc, char* argv[])
{
    return test();
}

1、OD编写汇编代码

用OD打开asmtest.exe,编写ThreadProc()代码

00401000右键-New origin here,然后EIP就会变成00401000,如下图所示

然后按空格键进行编写,如下图所示

55 8BEC是生成栈帧指令,可以在函数终止时清理干净栈

8B75 08是THREAD_PARAM结构体指针

接着三行是压入user32.dll字符串的ASCII码,小端标记故逆向

push esp是将user32.dll字符串的起始地址压入栈

然后是调用LoadLibraryA() API

  • 接着三行是MessageBoxA字符串
  • push esp是将MessageBoxA字符串的起始地址压入栈
  • push eaxuser32.dll模块的起始地址hMod压入栈
  • 然后调用GetProcAddress() API
  • push 0MessageBoxA API的第4个参数,表示弹出的消息框是MB_OK

上图最后一步CALL是“使用CALL指令将包含在代码间的字符串数据地址压入栈” 因为CALL可以认为是PUSH+JMP,但这里JMP的地址不是函数不具有RETN 然后将00401033处的内容Ctrl+E进行编辑如下,注意最后要以NULL结尾00

这里的显示有点奇怪,是反汇编将字符串误认为是IA-32指令 执行Analysis(Ctrl+A),如下图所示

再进行一些编辑

  • www.reversecore.com字符串
  • call eax调用MessageBoxA() API
  • xor eax,eaxThreadProc()函数的返回值设置为0
  • 最后三行是删除栈帧和函数返回

保存右键-Copy to executable-All modifications

然后右键-save file

2、编写代码注入程序

提取上一小节编写的代码的Hex代码(401000-401061),如下

然后类似上一节代码注入的CodeInjection,给出代码

代码语言:javascript
复制
// CodeInjection2.cpp
// reversecore@gmail.com
// http://www.reversecore.com

#include "windows.h"
#include "stdio.h"

typedef struct _THREAD_PARAM 
{
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
} THREAD_PARAM, *PTHREAD_PARAM;

BYTE g_InjectionCode[] = 
{
    0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00,
    0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 0x68, 0x75, 0x73, 0x65,
    0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68,
    0x61, 0x67, 0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54,
    0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0C, 0x00, 0x00,
    0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,
    0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,
    0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,
    0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x6A, 0x00,
    0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
};

/*
004010ED    55               PUSH EBP
004010EE    8BEC             MOV EBP,ESP
004010F0    8B75 08          MOV ESI,DWORD PTR SS:[EBP+8]       ; ESI = pParam           
004010F3    68 6C6C0000      PUSH 6C6C                      
004010F8    68 33322E64      PUSH 642E3233
004010FD    68 75736572      PUSH 72657375
00401102    54               PUSH ESP                           ; - "user32.dll"
00401103    FF16             CALL DWORD PTR DS:[ESI]            ; LoadLibraryA("user32.dll")
00401105    68 6F784100      PUSH 41786F
0040110A    68 61676542      PUSH 42656761
0040110F    68 4D657373      PUSH 7373654D
00401114    54               PUSH ESP                           ; - "MessageBoxA"
00401115    50               PUSH EAX                           ; - hMod
00401116    FF56 04          CALL DWORD PTR DS:[ESI+4]          ; GetProcAddress(hMod, "MessageBoxA")
00401119    6A 00            PUSH 0                             ; - MB_OK (0)
0040111B    E8 0C000000      CALL 0040112C
00401120                     <ASCII>                            ; - "ReverseCore", 0
0040112C    E8 14000000      CALL 00401145
00401131                     <ASCII>                            ; - "www.reversecore.com", 0
00401145    6A 00            PUSH 0                             ; - hWnd (0)
00401147    FFD0             CALL EAX                           ; MessageBoxA(0, "www.reversecore.com", "ReverseCore", 0)
00401149    33C0             XOR EAX,EAX                        
0040114B    8BE5             MOV ESP,EBP
0040114D    5D               POP EBP                            
0040114E    C3               RETN
*/

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                    &hToken) )
    {
        printf("OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

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

    hMod = GetModuleHandleA("kernel32.dll");

    // set THREAD_PARAM
    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
    param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");

    // 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
    if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess,                  // hProcess
                                         NULL,                      // lpAddress
                                         sizeof(THREAD_PARAM),      // 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
                            sizeof(THREAD_PARAM),                   // nSize
                            NULL) )                                 // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for ThreadProc()
    if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess,                  // hProcess
                                         NULL,                      // lpAddress
                                         sizeof(g_InjectionCode),   // 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)&g_InjectionCode,               // lpBuffer
                            sizeof(g_InjectionCode),                // 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],
                                       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;
}

int main(int argc, char *argv[])
{
    DWORD dwPID     = 0;

  if( argc != 2 )
  {
      printf("\n USAGE  : %s <pid>\n", argv[0]);
    return 1;
  }

  // change privilege
  if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // code injection
    dwPID = (DWORD)atol(argv[1]);
    InjectCode(dwPID);

  return 0;
}

结语

主要学习了钩子、DLL注入和代码注入


红客突击队于2019年由队长k龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 红客突击队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2、SetWindowsHookEx()
  • 4、调试练习
    • (1)调试HookMain.exe
      • (2)调试KeyHook.dll
      • 2、AppInit_DLLs
      • 三、DLL卸载
        • 2、myhack3.dll源码
          • 3、修改TextView.exe
            • (1)查看IDT是否有足够空间
            • (2)移动IDT
        • 五、代码注入
          • 1、示例:CodeInjection
          • 六、使用汇编语言编写注入代码
            • 1、OD编写汇编代码
              • 2、编写代码注入程序
              • 结语
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档