zoukankan      html  css  js  c++  java
  • 代码注入——汇编编写注代码

    代码注入——汇编编写注代码

    0x00 思路

      在准备些汇编代码之前,我们先要理清楚整个调用过程的思路,以及过程中的参数传递过程。我的的目标是在指定的目标程序中注入一个messagebox线程,首先要调用messageBox就需要用到LoadLibrary装入user32库函数。我们还需要GetprocessAddress来取得messagebox的函数地址,取得了messagebox的地址,我们需要向messagebox传递四个参数。Messagebox的原型如下:

    MessageBox(hWnd: HWND; Text, Caption: PChar; Type: Word): Integer;

    四个参数中TextPChar是一定要传递进去的。结合上篇中利用c++编写的注入代码我们可以整理出这样一个方案:

    1)使用c++编写获取LoadLibraryGetprocessAddress函数地址的程序。

    2)使用汇编编写获取messagebox地址的函数,以及存储messagebox的各个参数。

    3)使用c++调用createRemotetThread函数开启一个子线程运行汇编代码。

    0x01 汇编部分的代码编写

    1)od载入一个空的exe文件,我们将代码写入开始地址位401000h的地方。如下图:

     

    这里需要注意的是,之所以要先开辟两个堆栈空间,是因为我们需要调用LoadLibrary以及GetProcessAddress。在堆栈开辟两个四字节的空间就是为了存放传过来的两个函数的地址。

    2)接下来编写需要传入的字符串,我们将光标移至401033处。使用快捷键ctrl+e输入字符串ReverseCore,记得以00结尾。如下图:

     

    下一字符串就放在起始地址为401044处。输入www.reversecore.com。如下图。

     

    401058输入返回函数,如下图:

     

    至此,汇编代码输入完毕。我们接下来保存一下文件。

    0x01 汇编指令说明

    被注入的汇编指令从地址401000开始执行,执行到40102e处有一个call 40103f,进入地址40103f,又是一个call指令,call 401058转向地址401058401058后面的指令就是退出指令。

    00401000         55                  push ebp

    00401001         8BEC                mov ebp,esp

    00401003         8B75 08             mov esi,dword ptr ss:[ebp+0x8]

    上面这三条指令就是保存现场,让后为传入的两参数开辟堆栈空间。ebp+0——ebp+3存放着函数LoadLibrary的起始地址,ebp+4——ebp+7存放着函数GetProcAddress的函数地址。

     

    00401006         68 6C6C0000     push 0x6C6C

    0040100B         68 32322E64     push 0x642E3232

    00401010         68 75736572     push 0x72657375

    00401015         54              push esp

    00401016         FF16            call dword ptr ds:[esi]

     

    上面的五条指令就是先将字符串user32.dll压栈,然后使用esp指向字符串,最后一句调用API函数LoadLibrary,其返回值存放在eax中。()

    00401018         68 6F784100         push 0x41786F

    0040101D         68 61676542         push 0x42656761

    00401022         68 4D657373         push 0x7373654D

    00401027         54                  push esp

    00401028         50                  push eax

    00401029         FF56 04             call dword ptr ds:[esi+0x4]

    上面的六条指令先将字符串MessageBox压栈,再让esp指向字符串,eax存放着上个函数LoadLibrary的返回值,这里作为GetProccessAddress的参数。最后一句调用GetProcessAddress函数取得函数MessageBox的地址。

    0040102C         6A 00               push 0x0

    0040102E         E8 0C000000         call asmtest1.0040103F

    00401033          52                 push edx                               

    00401034         65:76 65             jbe short 0040109c

    00401037         72 73                jb short asmtest1.004010AC

    00401039         65:43                inc ebx

    0040103B         6f                   outs dx,dword ptr ds:[esi]

    0040103C         72 65                jb short asmtest1.004010A3

    0040103E         00E8                 add al,ch

     

    0040103F         E8 14000000         call asmtest1.00401058

    00401044        /77 77               ja short asmtest1.004010BD

    00401046        |77 2E               ja short asmtest1.00401076

    00401048        |72 65               jb short asmtest1.004010AF

    0040104A        |76 65               jbe short asmtest1.004010B1

    0040104C        |72 73               jb short asmtest1.004010C1

     

     

    00401058         6A 00                push 0x0

    0040105A         FFD0                call eax

    0040105C         33C0                xor eax,eax

    0040105E         8BE5                mov esp,ebp

    00401060         5D                  pop ebp  

    00401061         C3                  retn

    上面的标蓝代码是真正的代码,其余的的实际上存储的是字符串,仔细观察就以可以发现,我们这是我们之前存放的地址。分别从地址00401033以及00401044开始。

    其实我们关心的是0040102E 0040103F 地址处的call跳转,这两个跳转有啥作用?答:其实这两个跳转是来传递参数的,这两个参数分别是00401033处的字符串ReverseCore以及00401044处的字符串www.reversecore.com。这两个参数都是MessageBox的参数。那为啥用call 跳转来传递参数?答:其实这样传递参数具有一定的安全性,而且也不用调用ebp,省事。那其中的原理是什么?答:我们简单回想一下call指令的整个过程,先将返回地址压栈,执行完在跳转。其实就是push jmp指令的结合。现在我们来看看这两个call后面的首地址,是的。其实就是要传入的字符串地址!这两个call指令把字符串的首地址当作了返回值地址来压栈,这样也就相当于把两个字符串参数入栈!四个参数的入栈过程即:40102c push 0,传递第四个参数,两个call分别传递三二两个参数,最后401058处传递最后一行参数。地址0040105A  call指令调用函数MessageBox。执行到这一步就会弹出messagebox的对话框。

    最后几行代码恢复栈帧并且返回。

    0x02 c++代码编写

    1)先将之前保存汇编文件用OD打开,转到40100处,复制40100——401061机器码。如下图:

     

    整理得到如下机器码数组:

    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

     

    2)编写c++程序。代码如下:

    // CodeInjection2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

    //

     

    #include "pch.h"

    #include <iostream>

    #include<windows.h>

    #include "stdio.h"

    using namespace std;

     

     

    typedef struct _THREAD_PARAM

    {

    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()

    } THREAD_PARAM, *PTHREAD_PARAM;

     

     

    //ThreadProc()

    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

     

    };

     

     

    //提权函数

    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 ", GetLastError());

    return FALSE;

    }

     

    if (!LookupPrivilegeValue(NULL,           // lookup privilege on local system

    lpszPrivilege,  // privilege to lookup

    &luid))        // receives LUID of privilege

    {

    printf("LookupPrivilegeValue error: %u ", 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 ", GetLastError());

    return FALSE;

    }

     

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

    {

    printf("The token does not have the specified privilege. ");

    return FALSE;

    }

     

    return TRUE;

    }

     

    //注入函数

    BOOL Injection(DWORD dwPID)

    {

    HMODULE hMod = NULL;

    THREAD_PARAM param = {0,};

    HANDLE hProcess = NULL;

    HANDLE hThread = NULL;

    LPVOID pRemoteBuf[2] = { 0, };

     

     

    hMod = GetModuleHandleA("kernel32.dll");

     

     

    //设置线程参数

    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");

    param.pFunc[1] = GetProcAddress(hMod, "GetProcessAddress");

     

     

    //打开线程

     // Open Process

    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS,               // dwDesiredAccess

    FALSE,                            // bInheritHandle

    dwPID)))                         // dwProcessId

    {

    printf("OpenProcess() fail : err_code = %d ", GetLastError());

    return FALSE;

    }

    //LoadLibraryA函数分配空间

    if (!(pRemoteBuf[0] = VirtualAllocEx(hProcess,                  // hProcess

    NULL,                      // lpAddress

    sizeof(THREAD_PARAM),      // dwSize

    MEM_COMMIT,                // flAllocationType

    PAGE_READWRITE)))         // flProtect

    {

    printf("VirtualAllocEx() fail : err_code = %d ", GetLastError());

    return FALSE;

    }

    //LoadLibraryA写入内存

    if (!WriteProcessMemory(hProcess,                               // hProcess

    pRemoteBuf[0],                          // lpBaseAddress

    (LPVOID)¶m,                         // lpBuffer

    sizeof(THREAD_PARAM),                   // nSize

    NULL))                                 // [out] lpNumberOfBytesWritten

    {

    printf("WriteProcessMemory() fail : err_code = %d ", GetLastError());

    return FALSE;

    }

    //GetProcessAddress函数分配空间

    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 ", GetLastError());

    return FALSE;

    }

     

    //GetProcessAddress写入内存

    if (!WriteProcessMemory(hProcess,                               // hProcess

    pRemoteBuf[1],                          // lpBaseAddress

    (LPVOID)&g_InjectionCode,               // lpBuffer

    sizeof(g_InjectionCode),                // 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],

    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;

     

     

    }

     

    int main()

    {

        std::cout << "Hello World! ";

    }

     

     

    编译生成名为CodeInjection2.exeReleased文件

    0x03 验证是否成功

    步骤和一篇一样,用管理员权限打开cmd,将CodeInjection2.exemessageBox.dlld盘。打开notepad.exe。打开processexploer查看PIDcmd输入d:转自d盘,输入CodeInjection.exe 10584 。结果如下图:

     

    显然注入成功了。

     

     

     

  • 相关阅读:
    c#读取.config文件内容
    c# 读取配置文件方法
    C# Log4net详细说明
    C# 运算符集
    LeetCode 69_ x 的平方根
    LeetCode 172 _ 阶乘后的零
    LeetCode 171 _ Excel表列序号
    LeetCode 88 _ 合并两个有序数组
    LeetCode 581 _ 最短无序连续子数组
    LeetCode 283 _ 移动零
  • 原文地址:https://www.cnblogs.com/2f28/p/9978604.html
Copyright © 2011-2022 走看看