首页
社区
课程
招聘
[原创]简单Win32的Shellcode实现框架和解析
发表于: 2022-11-25 15:25 15664

[原创]简单Win32的Shellcode实现框架和解析

2022-11-25 15:25
15664

本文主要讲述如何快速将函数转为shellcode然后加密存储用于混淆反汇编器。该技巧广泛应用于恶意软件中,作为一次一密或者反分析的方法,在信息不足时可以有效掩盖可执行段。

由于shellcode被编写后将以硬编码的形式被安置在程序内部进行加密、隐藏和压缩等操作,因此其中的诸多引用、布局和跳转需要特别注意。关于shellcode的编写读者可以自行寻找各参考资料进行学习。在此将总结大部分shellcode的编写原则。

为了保证shellcode在引用变量时能够准确定位,需要保证shellcode使用的变量被声明、释放在shellcode内部。如果实在需要引用外部变量,请参见任意调用文章。

错误方式

上述这种声明方式需要保证被声明的变量完全位于shellcode内部,否则在shellcode生成并读出之后,由于代码段的变更,诸多变量的引用地址会发生变动,这时编码在原来shellcode中的引用地址就将失效。

正确方式

众所周知,诸windows.h文件中的函数都依赖于在windows根目录下的大量dll文件,因此要调用系统函数,需手动载入系统函数。手动载入系统动态链接库需要使用LoadLibrary函数。

LoadLibrary函数是kernal32.dll的导出函数。要获得此函数,首先需要载入kernal32.dll。好在绝大多数Windows程序在载入时都需要同时载入这一关键系统动态链接库。根据PEB表可以获得此动态链接库的地址。

找到kernal32.dll位置(存入ebx)

获取kernal32.dll的导出表

获取GetProcAddress的位置(存入edx)

获取LoadLibrary的位置(最终存入eax)

要实现任意调用,需要事先给出标志性的可控机器码。一种可行的方法是使用__asm关键字配合_emit指令嵌入一小段关键字节,用于寻址。(对于msvc而言)

一种示例方法如下

参考 c++ - Calling a non-exported function in a DLL - Stack Overflow

使用下述方式将任意unsigned int数组转换为函数并执行调用。

其中关键的部分在于需要绕过DEP,因为Windows的保护措施,存储shellcode的位置总是存在于全局变量中,而全局变量的段是不可执行的。因此在执行shellcode之前需要先更改shellcode所在内存区的可执行权限。

另外,在书写内嵌shellcode时需要不断结合反汇编结果,时刻注意编译器的优化是否影响了生成的硬编码。在实战中可能遇到优化器将**__stdcall**更改为**__fastcall**的情况,这个时候就需要结合反汇编结果来适当的插入push和mov指令来有效的补足shellcode的错误。

int __stdcall func(int a)
{
    if (a > 50)
    {
        return a - 50;
    }
    else
    {
        return a + 50;
    }
}
int __stdcall func(int a)
{
    if (a > 50)
    {
        return a - 50;
    }
    else
    {
        return a + 50;
    }
}
 
LPSTR const_string = {"This is a very loooooooooooog constant"};
DWORD const_aes_s_box = {0x63, 0x7C, 0x77, 0x7B...}
void func()
{
    //use(const_string);
    //use(const_aes_s_box);   
}
LPSTR const_string = {"This is a very loooooooooooog constant"};
DWORD const_aes_s_box = {0x63, 0x7C, 0x77, 0x7B...}
void func()
{
    //use(const_string);
    //use(const_aes_s_box);   
}
 
void func()
{
    LPSTR const_string = {"This is a very loooooooooooog constant"};
    DWORD const_aes_s_box = {0x63, 0x7C, 0x77, 0x7B...}
    //use(const_string);
    //use(const_aes_s_box);   
}
void func()
{
    LPSTR const_string = {"This is a very loooooooooooog constant"};
    DWORD const_aes_s_box = {0x63, 0x7C, 0x77, 0x7B...}
    //use(const_string);
    //use(const_aes_s_box);   
}
 
 
xor ecx, ecx              ; 置空ecx
mov eax, fs:[ecx + 0x30]  ; 取出peb,存入eax
mov eax, [eax + 0xc]      ; 取出PEB->Ldr
mov esi, [eax + 0x14]     ; 取出PEB->Ldr.InMemoryOrderModuleList
lodsd                     ; 数组操作
xchg eax, esi             ;
lodsd                     ; 取出kernal32.dll
mov ebx, [eax + 0x10]     ; 取出基地址存入ebx
xor ecx, ecx              ; 置空ecx
mov eax, fs:[ecx + 0x30]  ; 取出peb,存入eax
mov eax, [eax + 0xc]      ; 取出PEB->Ldr
mov esi, [eax + 0x14]     ; 取出PEB->Ldr.InMemoryOrderModuleList
lodsd                     ; 数组操作
xchg eax, esi             ;
lodsd                     ; 取出kernal32.dll
mov ebx, [eax + 0x10]     ; 取出基地址存入ebx
mov edx, [ebx + 0x3c] ; 获取任意值的DOS->e_lfanew
add edx, ebx          ; 获取PE头
mov edx, [edx + 0x78] ; 获取导出表偏移
add edx, ebx          ; 获取导出表
mov esi, [edx + 0x20] ;
add esi, ebx          ; 导出表名称
xor ecx, ecx          ;
mov edx, [ebx + 0x3c] ; 获取任意值的DOS->e_lfanew
add edx, ebx          ; 获取PE头
mov edx, [edx + 0x78] ; 获取导出表偏移
add edx, ebx          ; 获取导出表
mov esi, [edx + 0x20] ;
add esi, ebx          ; 导出表名称
xor ecx, ecx          ;
Get_Function:
 
inc ecx                              ; 递增序数
lodsd                                ; 获取导出名称表
add eax, ebx                         ; 获取函数名称,指针存入eax
cmp dword ptr[eax], 0x50746547       ; GetP
jnz Get_Function
cmp dword ptr[eax + 0x4], 0x41636f72 ; rocA
jnz Get_Function
cmp dword ptr[eax + 0x8], 0x65726464 ; ddre
jnz Get_Function
mov esi, [edx + 0x24]                ; 获取序数偏移
add esi, ebx                         ; 将序数表存入esi
mov cx, [esi + ecx * 2]              ; 获得函数数量
dec ecx
mov esi, [edx + 0x1c]                ; 获得函数表偏移
add esi, ebx                         ; 存入esi
mov edx, [esi + ecx * 4]             ;
add edx, ebx                         ; edx中存入GetProcAddress地址
Get_Function:
 
inc ecx                              ; 递增序数
lodsd                                ; 获取导出名称表
add eax, ebx                         ; 获取函数名称,指针存入eax
cmp dword ptr[eax], 0x50746547       ; GetP
jnz Get_Function
cmp dword ptr[eax + 0x4], 0x41636f72 ; rocA
jnz Get_Function
cmp dword ptr[eax + 0x8], 0x65726464 ; ddre
jnz Get_Function
mov esi, [edx + 0x24]                ; 获取序数偏移
add esi, ebx                         ; 将序数表存入esi
mov cx, [esi + ecx * 2]              ; 获得函数数量
dec ecx
mov esi, [edx + 0x1c]                ; 获得函数表偏移
add esi, ebx                         ; 存入esi
mov edx, [esi + ecx * 4]             ;

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-11-25 15:35 被Hedione编辑 ,原因: 更改题目和标题样式
收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 30032
活跃值: (2577)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
建议写一下如何在shellcode里面处理异常,就是针对try except的使用,因为在shellcode里面使用Activex和COM,很难避免。
2022-11-26 21:04
0
游客
登录 | 注册 方可回帖
返回
//