第六课 代码注入(汇编语言)

这节课的目标是把上节课的ThreadProc函数通过纯汇编语言注入到notepad.exe进程

等会要用到内联汇编,将汇编指令插入到C语言代码中,使用的工具可以是MASM,这里为了方便起见,我使用OllyDbg的汇编命令编写汇编代码

首先随便拿一个程序当做我们写汇编代码的载体,这里使用asmtest.exe当载体,载入od后,goto到401000写代码

 

把401000设为eip(下一条执行的指令)

取消“使用nop填充的功能”,如果选中的话,输入代码的长度短于已有代码时,剩余长度会填充为nop指令(No Operation),以整体对齐代码

下面使用汇编语言写ThreadProc()函数,与上节课使用C语言编写的ThreadProc()函数相比,其不同之处在于,需要的字符串包含在Code中(参数都在代码段),这里要注意,代码段一般情况下只有可读权限,如果强制写数据,需要通过PE头修改代码段的权限。

以上就是完整的代码,现在保存到可执行文件(asmtest_patch.exe)

下面将刚写的汇编代码通过C语言的混合注入到notepad.exe中

然后保存到文件

把每个16进制前面加上0x 后面加上 逗号

Word打开txt文件,然后用Ctrl+H打开替换对话框,勾选使用通配符,在查找中输入[0-9A-Z]{2},替换为中输入0x^&, 单击全部替换即可。

 

转换完成了

下面看一下注入程序的源码

#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, 0x58, 0x75, 0x65, 0x6E, 0x69,

  0x78, 0x69, 0x61, 0x6E, 0x67, 0x00, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,

  0x77, 0x2E, 0x78, 0x75, 0x65, 0x6E, 0x69, 0x78, 0x69, 0x61, 0x6E, 0x67, 0x2E, 0x63,

  0x6F, 0x6D, 0x20, 0x00, 0x6A, 0x00, 0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3

};



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;

}

 

 

输入注入指令 成功的给notepad.exe添加了一个弹窗

下面对注入过程进行逆向分析:

首先用OD载入notepad.exe 并且运行起来

然后OD选项-调试设置-中断于新线程

现在注入代码,输入注入指令后,OD会中断

如果出现下面这种情况,不用担心,只是OD把数据当成指令翻译了~~~~

 

 

这时候只要删除分析就可以了

现在开始逐行分析最关键的线程注入的过程

1.生成栈帧(保证参数读取正确,函数结束时也方便清理堆栈空间)

2.THREAD_PARAM结构体指针

查看结体构指针在notepad.exe进程内存空间中分配的内存缓冲区地址

3.拼接出user32.dll字符串

  1. 压栈user32.dll字符串首地址(调用字符串)

结构体定义如下

 

  1. 调用LoadLibraryA(“user32.dll”)

取esi中4个字节的数据,然后压栈下一条指令的地址,jmp到[esi],最后再retn esp

查看notepad.exe的user32.dll加载地址,发现和LoadLibraryA获取到的完全一致

6.拼接MessageBoxA字符串

  1. 调用GetProcAddress(hMod,“MessageBoxA”)

Push esp 将MessageBoxA字符串压栈

Push eax 将刚才获取到的user32.dll的加载地址压栈

[esi+4]就是GetProcAddress的地址

 

  1. 压入MessageBoxA的第四个参数

  1. 压入MessageBoxA函数的第三个参数(xuenixiang)

下面介绍 使用call指令将包含在代码间的字符串数据地址压入栈 的技术(非常巧妙)

大家都知道call xx指令=push下一条指令地址+jmp xx

首先,执行A2002E处的指令使得MessageBoxA的第四个参数0入栈,然后call A2003F,这时候call的下一行指令的地址A20033就会被压入栈顶,但A20033地址下的内容并不是指令,而是我们预先存储的字符串首地址(字符串指针),这个字符串如图所示为Xuenixiang(此时第三个参数也进栈了),然后jmp到A2003F。

 

9.压入MessageBoxA函数的第二个参数(www.xuenixiang.com)

 

和上面类似,利用call指令把A20044-A20056处的字符串指针A20044(第二个参数)压栈,然后jmp到A20058

  1. 压入MessageBoxA函数的第一个参数

将第一个参数压栈,最后执行call eax (eax是前面获取到的MessageBoxA函数地址)

  1. 设置ThreadProc函数的返回值

  1. 删除栈帧及函数返回

写到这里我又一次感受到栈帧的强大之处(始终保持堆栈的变量读取平衡)

DLL注入的全部教程到此结束,接下来会开始讲解API HOOK

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuenixiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值