免杀学习-认识shellcode

免杀学习-认识shellcode

接着暑假的学习继续更,这学期比较忙,学校里要过逆向考试,加上学生会加上新生班助,还要忙着研究小车做比赛作品,杂七杂八的事一大堆就没更。

这个学期正好学了点逆向的基础知识,那就借此机会学习一下免杀,也是内网渗透比较重要的一个东西,争取这个寒假搓个免杀马,玩一玩兄弟的电脑

什么是shellcode

 这一大堆反斜杠加数字是一串msfvenom生成的shellcode,例如\x75代表两个十六进制数,它会被翻译成汇编语言,

例如:

 这里55被翻译成了push ebp

55这2个16进制转换成8位二进制,然后告诉CPU执行,CPU识别发现是代码就会执行push ebp

无文件(内存马)和有文件

上面利用msfvenom生成的shellcode,被翻译成汇编语言后,可以直接在内存中运行

 可以直接在内存中运行,不依赖硬盘文件运行,我们称之为无文件运行

比如上面生成的shellcode在成功运行后,我们在后续利用过程中可以做进程迁移,磁盘文件只是起到了一个触发作用,我们可以把这一段shellcode迁移到其他进程上,此时触发文件可以直接删除,不会对木马进程造成影响,除非关机,关机会清理内存(无文件!=进程迁移)

例如通过利用java漏洞或者apache,tomcat的漏洞写入的内存马

如果木马程序与磁盘中的文件关联我们需要依托文件运行,我们称之为有文件(程序运行时我们会无法删除文件,只有关闭进程才能删除文件)

当然这段shellcode只有在data段的时候,才会被CPU识别成是代码,才有机会运行

(图为未做免杀的木马编写)

shellcode在msf和cs中扮演了一个什么样的角色?

staged和stageless

shellcode--小马--只负责连接

meterprete--大马--真正包含功能模块,实现各项功能

小马第一次把大马拉上的时候,只加载了Core Commands中的功能

 实现其他功能就需要meterpreter再拉其他文件,反射DLL注入(什么是反射DLL注入?)

 上面这种阶段化的过程我们称之为staged

如果我们的shellcode直接就是meterpreter,不需要小马拉大马

那么在实际使用中如何区分大马和小马?

我们以meterpreter中的windows相关的payload举例

 

 通过对比可以发现,第一张中名字和第二张的区别在于一个是'/'一个是'_'

通过这一区别我们可以区分shell_reverse_tcp和shell/reverse_tcp分别是stageless和staged类型

在Cobalt Strike中,如果选择生成的是payload generator,那么默认是staged的,分阶段

 如果直接选择生成可执行文件,windows executable如果不带s,就是分阶段的,带就是不分阶段的

再聊聊msf中payload名称的分类

 windows代表使用的操作系统平台,x64代表系统位数,vncinject代表监听端要做的具体操作(拿到会话后的操作工具),reverse代表反向连接,对方主动来连我们(bind代表正向,我们主动去连对方,不常用)tcp代表了连接方式,采用的什么通信协议,rc4代表加密方式,其他在rc4这个位置的通常代表一些加密方法或者其他特殊手段

shellcode实现的细节

可以在下面的链接中去研究下msf源码

https://github.com/rapid7/metasploit-framework/tree/master/external/source/shellcode/windows/x86/src/block

1.hash寻找api函数

windows api函数都会写在dll文件中

为什么要hash寻找api函数?

can can汇编

上面的代码做了什么?

这里就和前面谈到无文件和有文件有关了,

Windows平台下的可执行文件的格式,我们称之为PE(Portable Executable)文件结构(学PE看这个)

PE表中有一个导入表,操作系统可以知道用文件调用了哪些windows api函数,函数地址就在其中

而无文件shellcode是没有PE结构的,也就无法去调用各种api

所以汇编代码中如果为了实现一些功能需要调用windows api,那么只能自行去寻找,这也是第一段shellcode实现的方法

任何一个exe文件都会调用kernal32.dll,如果我们要调用的函数不在这个dll中,就需要利用kernal32.dll中的LoadLibrary函数去加载其他DLL文件

例如LoadLibrary(user32.dll)

这里首先把需要的函数名字转换成一个hash值,

 然后通过一种方式去找函数,接下来是一个复杂的过程

需要通过遍历进程模块的方式去查找user32.dll的地址

具体就是做了下图所示的过程,teb和peb是两个结构体,teb中包含进程中运行线程的各种信息,每个线程都对应一个TEB结构体。

TEB

typedef struct _NT_TEB
{
    NT_TIB Tib;                         // 00h
    PVOID EnvironmentPointer;           // 1Ch
    CLIENT_ID Cid;                      // 20h
    PVOID ActiveRpcInfo;                // 28h
    PVOID ThreadLocalStoragePointer;    // 2Ch
    PPEB Peb;                           // 30h          <--注意这里
    ULONG LastErrorValue;               // 34h
    ULONG CountOfOwnedCriticalSections; // 38h
    PVOID CsrClientThread;              // 3Ch
    PVOID Win32ThreadInfo;              // 40h
    ULONG Win32ClientInfo[0x1F];        // 44h
    PVOID WOW32Reserved;                // C0h
    ULONG CurrentLocale;                // C4h
    ULONG FpSoftwareStatusRegister;     // C8h
    PVOID SystemReserved1[0x36];        // CCh
    PVOID Spare1;                       // 1A4h
    LONG ExceptionCode;                 // 1A8h
    ULONG SpareBytes1[0x28];            // 1ACh
    PVOID SystemReserved2[0xA];         // 1D4h
    GDI_TEB_BATCH GdiTebBatch;          // 1FCh
    ULONG gdiRgn;                       // 6DCh
    ULONG gdiPen;                       // 6E0h
    ULONG gdiBrush;                     // 6E4h
    CLIENT_ID RealClientId;             // 6E8h
    PVOID GdiCachedProcessHandle;       // 6F0h
    ULONG GdiClientPID;                 // 6F4h
    ULONG GdiClientTID;                 // 6F8h
    PVOID GdiThreadLocaleInfo;          // 6FCh
    PVOID UserReserved[5];              // 700h
    PVOID glDispatchTable[0x118];       // 714h
    ULONG glReserved1[0x1A];            // B74h
    PVOID glReserved2;                  // BDCh
    PVOID glSectionInfo;                // BE0h
    PVOID glSection;                    // BE4h
    PVOID glTable;                      // BE8h
    PVOID glCurrentRC;                  // BECh
    PVOID glContext;                    // BF0h
    NTSTATUS LastStatusValue;           // BF4h
    UNICODE_STRING StaticUnicodeString; // BF8h
    WCHAR StaticUnicodeBuffer[0x105];   // C00h
    PVOID DeallocationStack;            // E0Ch
    PVOID TlsSlots[0x40];               // E10h
    LIST_ENTRY TlsLinks;                // F10h
    PVOID Vdm;                          // F18h
    PVOID ReservedForNtRpc;             // F1Ch
    PVOID DbgSsReserved[0x2];           // F20h
    ULONG HardErrorDisabled;            // F28h
    PVOID Instrumentation[0x10];        // F2Ch
    PVOID WinSockData;                  // F6Ch
    ULONG GdiBatchCount;                // F70h
    ULONG Spare2;                       // F74h
    ULONG Spare3;                       // F78h
    ULONG Spare4;                       // F7Ch
    PVOID ReservedForOle;               // F80h
    ULONG WaitingOnLoaderLock;          // F84h
    PVOID StackCommit;                  // F88h
    PVOID StackCommitMax;               // F8Ch
    PVOID StackReserve;                 // F90h
    PVOID MessageQueue;                 // ???
}

PEB

typedef struct _PEB
{
    UCHAR InheritedAddressSpace;                     // 00h
    UCHAR ReadImageFileExecOptions;                  // 01h
    UCHAR BeingDebugged;                             // 02h    这里QAQ
    UCHAR Spare;                                     // 03h
    PVOID Mutant;                                    // 04h
    PVOID ImageBaseAddress;                          // 08h
    PPEB_LDR_DATA Ldr;                               // 0Ch
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;  // 10h
    PVOID SubSystemData;                             // 14h
    PVOID ProcessHeap;                               // 18h
    PVOID FastPebLock;                               // 1Ch
    PPEBLOCKROUTINE FastPebLockRoutine;              // 20h
    PPEBLOCKROUTINE FastPebUnlockRoutine;            // 24h
    ULONG EnvironmentUpdateCount;                    // 28h
    PVOID* KernelCallbackTable;                      // 2Ch
    PVOID EventLogSection;                           // 30h
    PVOID EventLog;                                  // 34h
    PPEB_FREE_BLOCK FreeList;                        // 38h
    ULONG TlsExpansionCounter;                       // 3Ch
    PVOID TlsBitmap;                                 // 40h
    ULONG TlsBitmapBits[0x2];                        // 44h
    PVOID ReadOnlySharedMemoryBase;                  // 4Ch
    PVOID ReadOnlySharedMemoryHeap;                  // 50h
    PVOID* ReadOnlyStaticServerData;                 // 54h
    PVOID AnsiCodePageData;                          // 58h
    PVOID OemCodePageData;                           // 5Ch
    PVOID UnicodeCaseTableData;                      // 60h
    ULONG NumberOfProcessors;                        // 64h
    ULONG NtGlobalFlag;                              // 68h    还有这里!_(:зゝ∠)_
    UCHAR Spare2[0x4];                               // 6Ch
    LARGE_INTEGER CriticalSectionTimeout;            // 70h
    ULONG HeapSegmentReserve;                        // 78h
    ULONG HeapSegmentCommit;                         // 7Ch
    ULONG HeapDeCommitTotalFreeThreshold;            // 80h
    ULONG HeapDeCommitFreeBlockThreshold;            // 84h
    ULONG NumberOfHeaps;                             // 88h
    ULONG MaximumNumberOfHeaps;                      // 8Ch
    PVOID** ProcessHeaps;                            // 90h
    PVOID GdiSharedHandleTable;                      // 94h
    PVOID ProcessStarterHelper;                      // 98h
    PVOID GdiDCAttributeList;                        // 9Ch
    PVOID LoaderLock;                                // A0h
    ULONG OSMajorVersion;                            // A4h
    ULONG OSMinorVersion;                            // A8h
    ULONG OSBuildNumber;                             // ACh
    ULONG OSPlatformId;                              // B0h
    ULONG ImageSubSystem;                            // B4h
    ULONG ImageSubSystemMajorVersion;                // B8h
    ULONG ImageSubSystemMinorVersion;                // C0h
    ULONG GdiHandleBuffer[0x22];                     // C4h
    PVOID ProcessWindowStation;                      // ???
}

在找到dll文件之后,就要读dll文件的PE结构(EXE文件与DLL文件的区别完全是语义上的。它们使用的是相同的PE格式。惟一的不同在于一个位,这个位用来指示文件应该作为EXE还是DLL。)

前面找导入函数,这里通过dll的导出表找需要导出的函数

 这里和前面一样采用hash的方法将函数名字变数字,然后遍历,匹配相同数字就能找到需要的函数

 上面整串代码相当于定义了一个函数

def hanshu(api函数名字)

2.建立一个tcp连接

上汇编

 这里汇编上来做了一个call ebp

 其实就是将前面第一部分的所做的事调用过来

所以上面的内容可以这么理解

def hanshu(api函数名字)---->def ebp(0x0726774c)

最后加载了一个ws2_32.dll文件

接下来是差不多的操作

找到ws2_32.dll中的WSAStartup函数,然后调用了这个函数WSAStartup()

这个函数会直接通过操作系统的驱动告诉网卡准备网络连接

接下来是设置基本参数开始连接

 这里WSASocketA中AF_INET代表IPV4,push进去的eax值是2(如果eax是23那就是IPV6)

SOCKET_STREAM代表TCP连接

 (具体可见微软官方的函数介绍)

IP地址和端口号

其中IP地址转16进制是小端序读取

(内存中展示是这样的)

这里可以ollydbg调试一下看看返回的IP地址,分析一下

然后就是连接

 

 connect()中s就是前面WSASocketA(),&sockaddr是前面set_address一段中的参数,包括ip地址和端口号信息的整个结构体的指针,16是sockaddr这个结构体的长度

3.将连接接收到的东西放在内存中

 

前面的看完到这里也不是很难理解了

在连接上后,就需要调用meterpreter的功能,所以需要接收

 因为meterpreter的数据不是一个文件,而是一串二进制数据,所以需要申请一块空间地址存放数据然后运行

 

接收以后,就需要不断接收不断读取,然后return的方式直接对程序进行执行

 此时如果在ret出打断点,也可以看到meterpreter的代码

以上是msf的shellcode功能实现剖析,因为msf的裸马很容易被杀,所以可以参照上面的过程用C++自行编写,一样可以达到免杀的目的(代码就不沾了,试了试可以过火绒)

 这一章学习了一些基本的知识,也学到了一种静态免杀的方法,通过修改hash值的方式,感兴趣的师傅可以研究下,这里不做赘述了,我自己尝试了下使用这个方法,然后二分法定位被查杀段再结合花指令是可以绕过杀软检测的

https://www.huntress.com/blog/hackers-no-hashing-randomizing-api-hashes-to-evade-cobalt-strike-shellcode-detection

工具:

https://github.com/embee-research/APIHashReplace

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值