相关文章推荐

在计算机安全领域,ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度,研究人员经常探索新的传递 ShellCode 的方式。本文介绍了一种使用共享内存的方法,通过该方法,两个本地进程可以相互传递ShellCode,从而实现一种巧妙的本地传输手段。如果你问我为何在本地了还得这样传,那我只能说在某些时候我们可能会将 ShellCode 打散,而作为客户端也不需要时时刻刻在本地存放 ShellCode 代码,这能保证客户端的安全性。

服务端部分

CreateFileMapping

用于创建一个文件映射对象,将文件或者其他内核对象映射到进程的地址空间。这个函数通常用于共享内存的创建。

下面是 CreateFileMapping 函数的基本语法:

HANDLE CreateFileMapping(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName
);

参数说明:

  • hFile : 文件句柄,可以是一个磁盘文件或者其他内核对象的句柄。如果是 INVALID_HANDLE_VALUE ,则表示创建一个只在内存中的映射,而不与文件关联。
  • lpFileMappingAttributes : 安全属性,一般为 NULL ,表示使用默认的安全设置。
  • flProtect : 内存保护选项,指定内存页的保护属性,例如读、写、执行等。常见的值有 PAGE_READONLY PAGE_READWRITE PAGE_EXECUTE_READ 等。
  • dwMaximumSizeHigh dwMaximumSizeLow : 指定文件映射对象的最大大小。如果映射的是一个文件,可以通过这两个参数指定文件映射的大小。
  • lpName : 文件映射对象的名字,如果是通过共享内存进行跨进程通信,可以通过这个名字在不同的进程中打开同一个文件映射对象。
  • 成功调用 CreateFileMapping 会返回一个文件映射对象的句柄,失败则返回 NULL 。通常创建成功后,可以通过 MapViewOfFile 函数将文件映射对象映射到当前进程的地址空间中,进行读写操作。

    MapViewOfFile

    用于将一个文件映射对象映射到调用进程的地址空间中,使得进程可以直接操作映射区域的内容。

    以下是 MapViewOfFile 函数的基本语法:

    LPVOID MapViewOfFile(
    HANDLE hFileMappingObject,
    DWORD dwDesiredAccess,
    DWORD dwFileOffsetHigh,
    DWORD dwFileOffsetLow,
    SIZE_T dwNumberOfBytesToMap
    );

    参数说明:

  • hFileMappingObject : 文件映射对象的句柄,这个句柄通常是通过 CreateFileMapping 函数创建得到的。
  • dwDesiredAccess : 映射区域的访问权限,常见的值有 FILE_MAP_READ FILE_MAP_WRITE FILE_MAP_EXECUTE
  • dwFileOffsetHigh dwFileOffsetLow : 文件映射的起始位置。在这里,通常指定为0,表示从文件的开头开始映射。
  • dwNumberOfBytesToMap : 指定映射的字节数,通常可以设置为 0 表示映射整个文件。
  • 成功调用 MapViewOfFile 会返回映射视图的起始地址,失败则返回 NULL 。映射成功后,可以直接通过返回的地址进行读写操作。当不再需要映射时,应该通过 UnmapViewOfFile 函数解除映射。

    CreateMutex

    用于创建一个互斥体对象。互斥体(Mutex)是一种同步对象,用于确保在多线程或多进程环境中对资源的互斥访问,防止多个线程或进程同时访问共享资源,以避免数据竞争和冲突。

    以下是 CreateMutex 函数的基本语法:

    HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,
    BOOL bInitialOwner,
    LPCTSTR lpName
    );

    参数说明:

  • lpMutexAttributes : 一个指向 SECURITY_ATTRIBUTES 结构的指针,决定了互斥体的安全性。通常可以设为 NULL ,表示使用默认的安全描述符。
  • bInitialOwner : 一个布尔值,指定互斥体的初始状态。如果设置为 TRUE ,表示创建互斥体时已经拥有它,这通常用于创建一个已经锁定的互斥体。如果设置为 FALSE ,则表示创建互斥体时未拥有它。
  • lpName : 一个指向包含互斥体名称的空终止字符串的指针。如果为 NULL ,则创建一个匿名的互斥体;否则,创建一个具有指定名称的互斥体。通过指定相同的名称,可以在多个进程中共享互斥体。
  • 成功调用 CreateMutex 会返回互斥体对象的句柄,失败则返回 NULL 。在使用完互斥体后,应该通过 CloseHandle 函数关闭句柄以释放资源。

    CreateEvent

    用于创建一个事件对象。事件对象是一种同步对象,用于实现多线程或多进程之间的通信和同步。通过事件对象,可以使一个或多个线程等待某个事件的发生,从而协调它们的执行。

    以下是 CreateEvent 函数的基本语法:

    HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    BOOL bManualReset,
    BOOL bInitialState,
    LPCTSTR lpName
    );

    参数说明:

  • lpEventAttributes : 一个指向 SECURITY_ATTRIBUTES 结构的指针,决定了事件对象的安全性。通常可以设为 NULL ,表示使用默认的安全描述符。
  • bManualReset : 一个布尔值,指定事件对象的复位类型。如果设置为 TRUE ,则为手动复位;如果设置为 FALSE ,则为自动复位。手动复位的事件需要通过 ResetEvent 函数手动将其重置为非触发状态,而自动复位的事件会在一个等待线程被释放后自动复位为非触发状态。
  • bInitialState : 一个布尔值,指定事件对象的初始状态。如果设置为 TRUE ,表示创建事件对象时已经处于触发状态;如果设置为 FALSE ,则表示创建事件对象时处于非触发状态。
  • lpName : 一个指向包含事件对象名称的空终止字符串的指针。如果为 NULL ,则创建一个匿名的事件对象;否则,创建一个具有指定名称的事件对象。通过指定相同的名称,可以在多个进程中共享事件对象。
  • 成功调用 CreateEvent 会返回事件对象的句柄,失败则返回 NULL 。在使用完事件对象后,应该通过 CloseHandle 函数关闭句柄以释放资源。

    WaitForSingleObject

    用于等待一个或多个内核对象的状态变为 signaled。内核对象可以是事件、互斥体、信号量等等。

    以下是 WaitForSingleObject 函数的基本语法:

    DWORD WaitForSingleObject(
    HANDLE hHandle,
    DWORD dwMilliseconds
    );

    参数说明:

  • hHandle : 要等待的内核对象的句柄。可以是事件、互斥体、信号量等。
  • dwMilliseconds : 等待的时间,以毫秒为单位。如果设为 INFINITE ,表示无限等待,直到内核对象变为 signaled。
  • WaitForSingleObject 返回一个 DWORD 类型的值,表示等待的结果。可能的返回值包括:

  • WAIT_OBJECT_0 :内核对象已经变为 signaled 状态。
  • WAIT_TIMEOUT :等待时间已过,但内核对象仍然没有变为 signaled 状态。
  • WAIT_FAILED :等待出错,可以通过调用 GetLastError 获取详细错误信息。
  • 这个函数是同步函数,调用它的线程会阻塞,直到等待的对象变为 signaled 状态或者等待时间超时。

    ReleaseMutex

    用于释放之前由 WaitForSingleObject WaitForMultipleObjects 等函数获取的互斥体对象的所有权。

    以下是 ReleaseMutex 函数的基本语法:

    BOOL ReleaseMutex(
    HANDLE hMutex
    );

    参数说明:

  • hMutex : 要释放的互斥体对象的句柄。
  • ReleaseMutex 返回一个 BOOL 类型的值,表示释放互斥体对象是否成功。如果函数成功,返回值为非零;如果函数失败,返回值为零。可以通过调用 GetLastError 获取详细错误信息。

    互斥体(Mutex)是一种同步对象,用于控制对共享资源的访问。在多线程或者多进程环境中,互斥体可以确保在同一时刻只有一个线程或者进程能够访问被保护的共享资源。当一个线程或者进程成功获取互斥体的所有权后,其他试图获取该互斥体所有权的线程或者进程将会被阻塞,直到拥有互斥体的线程或者进程调用 ReleaseMutex 释放互斥体所有权。

    SetEvent

    用于将指定的事件对象的状态设置为 signaled(有信号)。该函数通常与等待函数(如 WaitForSingleObject WaitForMultipleObjects )一起使用,以实现线程之间或进程之间的同步。

    以下是 SetEvent 函数的基本语法:

    BOOL SetEvent(
    HANDLE hEvent
    );

    参数说明:

  • hEvent : 事件对象的句柄。
  • SetEvent 函数返回一个 BOOL 类型的值,表示设置事件对象状态是否成功。如果函数成功,返回值为非零;如果函数失败,返回值为零。可以通过调用 GetLastError 获取详细错误信息。

    事件对象是一种同步对象,用于在线程或者进程之间发信号。通过 SetEvent 可以将事件对象的状态设置为 signaled,表示某个条件已经满足,其他等待该事件对象的线程或者进程可以继续执行。

    有了上述API函数的支持,那么实现这个服务端将变得很容易,如下所示则是服务端完整代码,通过创建一个共享内存池,并等待用户按下简单,当键盘被按下时则会自动填充缓冲区为特定内容。

    #include <iostream>
    #include <Windows.h>
    #define BUF_SIZE 1024

    HANDLE H_Mutex = NULL;
    HANDLE H_Event = NULL;

    char ShellCode[] = "此处是ShellCode";

    using namespace std;

    int main(int argc,char *argv[])
    {
    // 创建共享文件句柄
    HANDLE shareFileHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, "SharedMem");
    if (shareFileHandle == NULL)
    {
    return 1;
    }

    //映射缓冲区视图,得到指向共享内存的指针
    LPVOID lpBuf = MapViewOfFile(shareFileHandle, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
    if (lpBuf == NULL)
    {
    CloseHandle(shareFileHandle);
    return 1;
    }

    // 创建互斥器
    H_Mutex = CreateMutex(NULL, FALSE, "sm_mutex");
    H_Event = CreateEvent(NULL, FALSE, FALSE, "sm_event");

    // 操作共享内存
    while (true)
    {
    getchar();

    // 使用互斥体加锁,获得互斥器的拥有权
    WaitForSingleObject(H_Mutex, INFINITE);
    memcpy(lpBuf, ShellCode, strlen(ShellCode) + 1);
    ReleaseMutex(H_Mutex); // 放锁
    SetEvent(H_Event); // 激活等待的进程
    }

    CloseHandle(H_Mutex);
    CloseHandle(H_Event);
    UnmapViewOfFile(lpBuf);
    CloseHandle(shareFileHandle);
    return 0;
    }

    客户端部分

    OpenFileMapping

    用于打开一个已存在的文件映射对象,以便将它映射到当前进程的地址空间。文件映射对象是一种用于在多个进程间共享内存数据的机制。

    以下是 OpenFileMapping 函数的基本语法:

    HANDLE OpenFileMapping(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName
    );

    参数说明:

  • dwDesiredAccess : 指定对文件映射对象的访问权限。可以使用标准的访问权限标志,如 FILE_MAP_READ FILE_MAP_WRITE 等。
  • bInheritHandle : 指定句柄是否可以被子进程继承。如果为 TRUE ,子进程将继承句柄;如果为 FALSE ,子进程不继承句柄。
  • lpName : 指定文件映射对象的名称。此名称在系统内必须是唯一的。如果是 NULL ,函数将打开一个不带名称的文件映射对象。
  • OpenFileMapping 函数返回一个文件映射对象的句柄。如果函数调用失败,返回值为 NULL 。可以通过调用 GetLastError 获取详细错误信息。

    OpenEvent

    用于打开一个已存在的命名事件对象。事件对象是一种同步对象,用于在多个进程间进行通信和同步。

    以下是 OpenEvent 函数的基本语法:

    HANDLE OpenEvent(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName
    );

    参数说明:

  • dwDesiredAccess : 指定对事件对象的访问权限。可以使用标准的访问权限标志,如 EVENT_MODIFY_STATE EVENT_QUERY_STATE 等。
  • bInheritHandle : 指定句柄是否可以被子进程继承。如果为 TRUE ,子进程将继承句柄;如果为 FALSE ,子进程不继承句柄。
  • lpName : 指定事件对象的名称。此名称在系统内必须是唯一的。如果是 NULL ,函数将打开一个不带名称的事件对象。
  • OpenEvent 函数返回一个事件对象的句柄。如果函数调用失败,返回值为 NULL 。可以通过调用 GetLastError 获取详细错误信息。

    VirtualAlloc

    用于在进程的虚拟地址空间中分配一段内存区域。这个函数通常用于动态分配内存,而且可以选择性地将其初始化为零。

    以下是 VirtualAlloc 函数的基本语法:

    LPVOID VirtualAlloc(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
    );

    参数说明:

  • lpAddress : 指定欲分配内存的首地址。如果为 NULL ,系统将决定分配的地址。
  • dwSize : 指定欲分配内存的大小,以字节为单位。
  • flAllocationType : 指定分配类型。可以是以下常量之一:
  •  
    推荐文章