shellcode加密绕过检测
为shellocde瘦身: 有时候同样的功能,也许会有不同的的长度的指令,我们应该尽量使用较短的指令:
xchg eax,reg ;交换eax和其他寄存器中的值
lodsd ;把esi指向的一个dword装入eax,并且增加esi
lodsb ;把esi指向的一个byte装入al,并且增加esi
stosd ;
stosb
pushad/popad ;从栈中存储、恢复所有寄存器的值
cdq ;用edx把eax扩展成四字。这条指令在eax<0x80000000时可用作mov edx.
妙用内存——另类的API调用方式
有些API中许多参数都是NULL,通常的做法是多次向栈中压入NULL。如果我们换一个思路,把栈中的一大片区域一次性全部置为NULL,在调用API的时候可以只压入那些非NULL的参数,从而节省出许多压栈指令。
我们经常遇到API中需要一个很大的结构体做参数的情况,大部分时候,健壮的API都可以允许两个结构体相互重叠,尤其是当一个参数是输入结构体,另一个参数是输出结构体时。这样基金使用一个字节的段指令push esp就可以代替一大段初始化结构体的代码。
色即是空,空即是色:
很多windows的API都会要求输入参数是一中特定的数据类型,或者要求特定的取值区间,虽然如此,通过实验我们发现,大多数API出于函数健壮性的考虑,在实现时已经对非法的参数做了正确的处理。
变废为宝:——调整栈顶回收数据
普通程序员不会直接与系统栈打交道,通常与栈沟通的总时编译器,在编译器看来,栈仅仅是用来保护函数调用断点,暂存函数输入参数和返回值等的场所。但是作为一个shellcode的开发人员,必须富有想象力。栈顶之上的数据在逻辑上视为废弃数据,但其物理内容实际上并没有遭到破坏,如果栈顶上有需要的数据,不妨调整esp的值将栈顶抬高,把他们保护起来以便后面使用,这样能节省出很多有用的数据初始化指令。
打破常规,巧用寄存器
按照默认的函数调用约定,在调用API时有些寄存器总时被保护在栈中。把函数调用信息存在寄存器中而不是存在栈中会给shellcode带来很多好处。比如大多数函数的运行过程中都不会使用EBP寄存器,故我们可以使用它来保存数据。
取其精华,去其糟粕——永痕的压缩法宝,hash
实用的shellcode通常需要超过200甚至300字节的机器码,所以对原始的二进制shellcode进行编码或者压缩是值得的,上节实验在搜索API函数名时,并没有在shellcode中存储原始的函数名,而是使用了函数名的摘要。在需要的API比较多的情况下,这样能够节省不少shellcode的篇幅;
选择恰当的hash算法
我们想要在shellcode中实现的功能如下
(1)绑定一个shell到6666端口
(2)允许外部网络连接使用这个shell
(3)程序能够正常退出。
这个shellcode应当具有较强的通用性,能够咋爱Windows NT4、windows2000、windowsxp和windows 2003上运行。开发过程中实际要解决的问题有这样两个。
(1)在不同的操作系统版本中,用通用 的方法定位所需API函数的 地址。
(2)调用这些API,完成shellcode的功能
实现shellcode需要的函数包括
1.kernel32.dll中的导出函数
LoadLibraryA 用来装载ws2_32.dll
CreateProcess 用来为客户创建一个shell命令窗口
ExitPocess 用于程序的正常退出
2.ws2_32.dll中的导出函数
WSAStartup 需要初始化winsock
WSASocketA 创建套接字
bind 绑定套接字到本地端口
listen 监听外部连接
accept 处理一个外部连接
下面是选择这中算法时需要考虑的因素:
(1)所需的每个库文件(dll)内所有导出函数的函数名经过hash后的摘要不能有碰撞。
(2)函数名经过hash后得到的摘要应该最短
可认为单字节的摘要是最佳的,kernel32.dll的导出表中有超过900个函数,8bit有256中可能,应该可行。
(3)hash算法实现所需的代码篇幅最短
(4)经过hash后的摘要可等价于指令的机器码,即把数据也当作代码使用。
查看代码
hash_loop:
lodsb ;把函数名中的一个字符装入al,并且esi+1,指向函数名中下一个字符
xor al,0x71 ;用0x71异或当前的字符
sub dl,al ;更新dl中的hash值
cmp al,0x71 ;继续循环,直到遇见字符串的结尾null
jne hash_loop

浙公网安备 33010602011771号