教你如何使用分组密码对shellcode中的windows api字符串进行加密-阿里云开发者社区

开发者社区> 玄学酱> 正文

教你如何使用分组密码对shellcode中的windows api字符串进行加密

简介: 本文讲的是教你如何使用分组密码对shellcode中的windows api字符串进行加密,字符串/模式匹配算法是当前最流行和最简单的检测shellcode方法。原理很简单:所有代码都有其独特的特征,可以根据这种特征去在内存中验证。
+关注继续查看
本文讲的是教你如何使用分组密码对shellcode中的windows api字符串进行加密

介绍

字符串/模式匹配算法是当前最流行和最简单的检测shellcode方法。原理很简单:所有代码都有其独特的特征,可以根据这种特征去在内存中验证。即使没有事先分析shellcode,我们也可以得到一些有用的变量,或者至少看起来它是一串可疑代码,需要进一步分析。

我提到的有用变量是指API字符串以及hash值,它们已经在检测恶意代码历史上使用了20多年。如果想要绕过这种检测方法,编写shelcode过程中我们要考虑使用像C或C++这样的HLL语言,进行非常规的编程,保证shellcode中的内容是可以变换的,不固定的。

这一过程中不止可执行代码可以变换,任何内容都可以变换:代码,数据,常量,字符串等等。今天我要展示的是使用名为Maru的hash函数生成可排列的API散列。Maru使用分组密码生成字符串的hash值,当然你大可不必使用分组密码做此事,但是这样可以防止使用SMT进行hash碰撞攻击。

想要获取更多有关信息,你可以阅读:使用SAT和SMT消除简单的散列算法

签名检测

在MS-DOS病毒在30年前刚出现时,基于签名的检测是很容易的。为了隐藏恶意代码,攻击者使用八比特的变量对代码进行异或运算,举个例子,在1991年出现的Skism Rythem Stack Virus-808病毒,使用了感染时间时的毫秒数字进行异或运算,所以对于100个版本来说,每一个都是不同的。

这是该代码的入口,以及其解密过程:

jmp    virus_start

encrypt_val db 00h

virus_start:

    call   encrypt         ; encrypt/decrypt file
    jmp    virus           ; go to start of code

encrypt:

    push   cx
    mov    bx, offset virus_code ; start encryption at data

xor_loop:
    mov    ch, [bx]        ; read current byte
    xor    ch, encrypt_val ; get encryption key
    mov    [bx], ch        ; switch bytes
    inc    bx              ; move bx up a byte
    cmp    bx,  offset virus_code + virus_size
    ; are we done with the encryption

    jle    xor_loop        ; no? keep going
    pop    cx
    ret

执行完这一串代码之后,病毒会使用当前时间值的毫秒数对'encrypt_val'进行更新:

;
    mov    ah, 2ch         ; get time
    int    21h

    mov    encrypt_val, dl ; save m_seconds to encrypt val so
                           ; theres 100 mutations possible

攻击者为做到隐蔽自己做了一些努力,但是解密器是可以被察觉到的,并且病毒很容易被杀掉。此外,杀毒软件可以模拟解密程序,通过验证签名,在病毒执行前阻止他。

在1997年Win32病毒出现时,已经不存在MS-DOS中断,相反,可以通过动态链接库dll对API函数进行访问,这使得检测恶意软件变得更加容易,当存在大量调用API函数的代码块时,系统就会发出警报。

攻击者最终使用了32位校验码,其中最受欢迎的就是CRC32。不久之后,LSD-PL首先使用了基于ADD-Rotate—Xor方法,在他们的WASM包中,代码如下:

i3: mov   esi,[4*eax+ebx]    ; ptr to symbol name
    add   esi,edx            ; rva2va

    push  ebx      ; hash: h=((h<<5)|(h>>27))+c
    push  eax
    xor   ebx,ebx
i4: xor   eax,eax  ; hash loop
    lodsb
    rol   ebx,5
    add   ebx,eax
    cmp   eax,0
    jnz   i4 
    ror   ebx,5 
    cmp   ebx,ecx  ; hash compare
    pop   eax
    pop   ebx
    je    i5       ; same: symbol found
    inc   eax
    jmp   i3       ; different: go to the next symbol

大部分合法代码是不需要调用api的,也不会手动导入地址表或者导出地址表的。如果碰到这种行为代码,那么通常它会对你的计算机造成危害。

API散列是很有趣的,它不会被字符串签名验证给杀掉,所以如果杀毒软件只是对是否包含调用API的代码进行检测,那么这种检测方法是无效的。尽管现在很多攻击软件(BeEF,meterpreter,Veil)都会使用无熵或者随机化的散列算法。但是他们是不会被改变的,所以检测起来还是很容易的。

当然,这些工具都提供了像RC4、AES这样的加密算法,不过这样即使通过了杀毒软件和IDS进行的简单扫描,但是通过仿真器去解密主要代码,观察它的行为,对它防御是完全没有问题的。

字符串问题

在理想情况下,攻击者希望在编译过程中对所有代码进行混淆或者加密,并且在解密过程中不会使用冗余的代码和工具。

constexpr是使用c++代码的理想选择,不过对于C来说,则没有这样的功能,除非编译器在编译过程中对所有字符串进行修改和加密。

一些汇编程序比如MASM,NASM是支持基于宏的编译,但是工作过程中是基于很复杂的函数的(并不是你不能)。Z0MBiE已经在2002年在”Meta病毒中的编码“以及”在HLL中解决字符串问题“中对这一问题进行了讨论,但是如果进一步查看这些解决方法,这里只是使用很少的熵值而已。

使用熵对API字符串进行加密的目的很显然是增加检测有效载荷的难度。不过这不是一个长久之计,迟早会被发现,但是这种方法肯定比现有的没有熵的hash算法要好很多。

散列函数构造

我们可以从分组密码中构造新的hash算法,于是对于提供的不同key值,就会生成不同的API散列。但是,很少的分组密码是适用于shellcode生成的。

这里有三个可以使用的分组密码机制(其实有很多的, 只是目前发现了三个)。

1.Davies–Meyer

教你如何使用分组密码对shellcode中的windows api字符串进行加密

将每个块中的消息(mi)作为块密文的key,然后将先前的hash值(Hi-1)作为要被加密的明文。然后将输出的密文和之前的hash值(Hi-1)进行异或,生成下一个hash值(Hi)。在加密的第一轮中,之前并没有hash值产生,会使用我们预先设定的一个常量(H0)进行加密操作。

2.Matyas–Meyer–Oseas

教你如何使用分组密码对shellcode中的windows api字符串进行加密

将每块中的消息(mi)作为要被加密的明文。输出的密文同样和相同的明文块进行异或,产生下一组hash值(Hi)。前一组hash值会作为这一组加密的密钥。在第一轮中,并没有之前产生的hash值,所以使用之前设定的初始化变量H0.

3. Miyaguchi–Preneel

教你如何使用分组密码对shellcode中的windows api字符串进行加密

每一块消息都会作为要加密的明文,输出之后的密文和同样的明文进行异或运算,然后再和前一组产生的hash进行异或运算,产生这一组hash值(Hi)。前一组产生的hash值作为了加密的密钥。

在第一轮中,并没有之前产生的hash值,所以使用之前设定的初始化变量H0。这些是我认为使用的3种简单设计。对于Maru哈希,我使用Davies-Meyer与一些非标准填充。

选择分组密码

我们知道在加密过程中需要一些静态值,与加密常数相比,有什么更好的静态值吗?

你可以对这些静态值进行改变,不过可能会影响加密函数的安全性,所以最好使用一个不包含任何静态变量的密码。在查看可能使用的分组密码时,我选择的前三个是基于ADD-Rotate-Xor(ARX)设计实现的。

1. Speck

由NSA设计,于2013年进行发布(https://eprint.iacr.org/2013/404)。对于不同的架构,产生不同的密钥以及分组大小。成为大多数架构可用的最小的基于软件的块密码。

32位机器的参数为64位的分组大小以及128位的密钥长度。64位模式下的参数为128位的分组大小,以及256为的密钥长度。这两种加密代码长度分别为64字节以及86字节。

如下是x86代码:

教你如何使用分组密码对shellcode中的windows api字符串进行加密

2. Chaskey-LTS

在2015年发布(https://eprint.iacr.org/2015/1182),基于Even-Mansour加密方式,使用SipHash中的置换函数进行产生。参数为128位的密钥,和128位的分组大小。在x86机器上实现大小为90字节:

教你如何使用分组密码对shellcode中的windows api字符串进行加密

3.XTEA

拓展微加密算法。一种Feistel密码,在1997年发现微加密算法弱点之后发表(http://www.cix.co.uk/~klockstone/xtea.pdf).参数为128比特的密钥,分组大小为64比特。他有一个已知的常量,0x9E3779B9,这使他容易被基于签名检测出来,大小约80个字节:

教你如何使用分组密码对shellcode中的windows api字符串进行加密

Speck是轻量级应用程序更好的设计,不能通过签名轻松识别。 (至少不是从任何常数)所以我选择它生成hash。

Maru Hash

那么在关于需要消除shellcode中常量的讨论之后,我在这里介绍hash函数的一部分。在转换为整数之前,64位常数是137素数的乘法逆元。它是加密过程中的初始化参数H0。

进一步来说:

1. 0.00729927007299270072992700729927是137的乘法逆元。
2. 舍弃小数部分中的前两个0,将剩下的转换为64位整数。
3. 结果为:0x654C37754C5E9939

H是通过用Maru常数和64位密钥进行异或初始化的。然后将API字符串作为密钥重复使用Speck进行加密,直到达到终止符为止。

下方代码是C原型:

uint64_t maru(const void *str, const void *key);

str参数指向我们的API字符串,长度不应超过128字节(即使函数接受任意长度的字符串)。key参数应该指向我们唯一的64位值,这应该是理想的随机选择。

uint64_t maru(const void *str, const void *key) 
{
    uint8_t *s=(uint8_t*)str;
    w64_t   *k=(w64_t*)key;
    w64_t    h;
    w128_t   m;
    uint32_t idx, end=0, i, t, k0, k1, k2, k3, x0, x1;

    // set the initial hash which is 64-bit key xor 1/137
    h.q = k->q ^ MARU_INIT_H; 

    // now update the hash using string as key
    for (idx=0; !end; ) {
      // if bytes remaining
      if (*s != 0) {
        // add byte to buffer
        m.b[idx++] = *s++;
      } else {
        // this is last block
        // pad with end bits
        while (idx < MARU_BLK_LEN) {
          m.b[idx] = (uint8_t)idx;
          idx++;
        }
        end++;
      } 
      // if m filled  
      if (idx == MARU_BLK_LEN) {
        // copy 128-bit key to local registers
        k0 = m.w[0]; k1 = m.w[1];
        k2 = m.w[2]; k3 = m.w[3];

        // copy H to local space
        x0 = h.w[0]; x1 = h.w[1];

        for (i=0; i<27; i++)
        {
          // encrypt block
          x0 = (ROTR32(x0, 8) + x1) ^ k0;
          x1 =  ROTL32(x1, 3) ^ x0;

          // create next subkey
          k1 = (ROTR32(k1, 8) + k0) ^ i;
          k0 =  ROTL32(k0, 3) ^ k1;

          XCHG(k3, k2);
          XCHG(k3, k1);    
        }
        // update previous hash
        h.w[0] ^= x0; h.w[1] ^= x1;
        // reset index
        idx = 0;
      }
    }
    // return 64-bit hash
    return h.q;
}

hash举例

以下是对API以及潜在密钥的测试案例,他们不必是字符串。

教你如何使用分组密码对shellcode中的windows api字符串进行加密

从示例散列可以看出,一旦密钥被更改,生成的API哈希是完全不同的。通过选择不同的密钥,这使得我们的哈希值也会不同。

总结

设计自己的hash函数或者加密算法通常会很好的绕过杀毒软件。但是像Maru这样的hash算法并不适用于对防碰撞要求很高的场合。Maru hash真的是一个关于如何对shellcode中的字符串进行随机变换,进而绕过杀毒软件的字符串检测的方法。




原文发布时间为:2017年8月16日
本文作者:xnianq
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
C#&nbsp;字符串加密解密函数
原文:C# 字符串加密解密函数 using System;using System.Text;using System.Security.Cryptography;using System.
617 0
如何判断自己使用的是appid请求还是pid(partner)请求
appid请求(如下图):   网关是openapi([url]https://openapi.alipaydev.com/gateway.do[/url]) ,appid (年份开头)  pid请求:(如下图):   网关是:mapi ([url]https://mapi.
449 0
Spring Boot参数校验以及分组校验的使用
做web开发基本上每个接口都要对参数进行校验,如果参数比较少,还比较容易处理,一但参数比较多了的话代码中就会出现大量的if-else语句。虽然这种方式简单直接,但会大大降低开发效率和代码可读性。所以我们可以使用validator组件来代替我们进行不必要的coding操作。本文将基于validator的介绍资料,同时结合作者自己在项目中的实际使用经验进行了总结。
4054 0
关于分组后字段拼接的问题 (Oracle)
-----关于分组后字段拼接的问题来自:www.itpub.net  最近在论坛上,经常会看到关于分组后字段拼接的问题,大概是类似下列的情形:SQL> select no,q from test2 /NO Q---------- ------------------------------001 ...
830 0
如何使用Visual Studio Code调试PHP CLI应用和Web应用
在按照Jerry的公众号文章 什么?在SAP中国研究院里还需要会PHP开发? 进行XDebug在本地的配置之后,如果想使用Visual Studio Code而不是Eclipse来调试PHP应用,步骤也比较简单。
1078 0
如何仅使用内部链接策略来提高搜索排名
如何仅使用内部链接策略来提高搜索排名 网站架构修改的三个例子,在数字营销中提供了大量的SEO收益。 链接,即使在网站内,也显示内容之间的关系。它们在页面之间传递价值和重要性。更重要的是,内部链接定义了您网站的结构。
1454 0
新买阿里云linux服务器如何设置账号密码xshell远程登陆
官方文档: 阿里云安全组应用案例 虽然阿里云上有文档,但是作为小白,可能多多少少还是看不懂,如果已经看了但是又看不懂的话,可以继续看这篇文章。
1161 0
如何使用Xshell连接阿里云服务器
许多新手第一次买了Linux云服务器都不知道该怎样使用,尤其是没有接触过linux的朋友对命令行界面几乎不了解,今天就记录下使用xshell连接linux服务器的过程。
11845 0
如何使用 GeoTrellis 和 React 构建地理处理应用程序
这篇博文是使用 GeoTrellis、Akka HTTP和 React 创建GIS处理的网页应用程序的指南。翻译自凯利·英尼斯 (Kelly Innes)的博客。
160 0
+关注
玄学酱
这个时候,玄酱是不是应该说点什么...
20710
文章
438
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载