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

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 本文讲的是教你如何使用分组密码对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
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。
目录
相关文章
|
3月前
|
存储 NoSQL 数据库
认证服务---整合短信验证码,用户注册和登录 ,密码采用MD5加密存储 【二】
这篇文章讲述了在分布式微服务系统中添加用户注册和登录功能的过程,重点介绍了用户注册时通过远程服务调用第三方服务获取短信验证码、使用Redis进行验证码校验、对密码进行MD5加密后存储到数据库,以及用户登录时的远程服务调用和密码匹配校验的实现细节。
认证服务---整合短信验证码,用户注册和登录 ,密码采用MD5加密存储 【二】
|
3月前
|
存储 算法 数据库
使用python hashlib模块给明文字符串加密,以及如何撞库破解密码
`hashlib` 是 Python 中用于实现哈希功能的模块,它可以将任意长度的输入通过哈希算法转换为固定长度的输出,即散列值。该模块主要用于字符串加密,例如将用户名和密码转换为不可逆的散列值存储,从而提高安全性。`hashlib` 提供了多种哈希算法,如 `md5`、`sha1`、`sha256` 等。
54 1
|
27天前
|
存储 Java 数据库
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
这篇文章介绍了如何在Java中通过加盐和加密算法(如MD5和SHA)安全地存储密码,并提供了一个密码工具类PasswordUtils和密码编码类PasswordEncoder的实现示例。
29 10
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
|
4天前
|
数据库 数据安全/隐私保护 Windows
Windows远程桌面出现CredSSP加密数据修正问题解决方案
【10月更文挑战第30天】本文介绍了两种解决Windows系统凭据分配问题的方法。方案一是通过组策略编辑器(gpedit.msc)启用“加密数据库修正”并将其保护级别设为“易受攻击”。方案二是通过注册表编辑器(regedit)在指定路径下创建或修改名为“AllowEncryptionOracle”的DWORD值,并将其数值设为2。
14 3
|
4月前
|
关系型数据库 MySQL 数据安全/隐私保护
windows mysql8 安装后 提示密码不对,修改下密码认证方式就可以了
windows mysql8 安装后 提示密码不对,修改下密码认证方式就可以了
1027 3
|
20天前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。
|
27天前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
22 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
1月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
103 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
2月前
|
存储 安全 算法
RSA在手,安全我有!Python加密解密技术,让你的数据密码坚不可摧
【9月更文挑战第11天】在数字化时代,信息安全至关重要。传统的加密方法已难以应对日益复杂的网络攻击。RSA加密算法凭借其强大的安全性和广泛的应用场景,成为保护敏感数据的首选。本文介绍RSA的基本原理及在Python中的实现方法,并探讨其优势与挑战。通过使用PyCryptodome库,我们展示了RSA加密解密的完整流程,帮助读者理解如何利用RSA为数据提供安全保障。
108 5
|
2月前
|
安全 数据安全/隐私保护 Python
情书也能加密?Python AES&RSA,让每一份数据都充满爱的密码
【9月更文挑战第8天】在这个数字化时代,情书不再局限于纸笔,也可能以电子形式在网络中传递。为了确保其安全,Python提供了AES和RSA等加密工具,为情书编织爱的密码。首先,通过安装pycryptodome库,我们可以利用AES对称加密算法高效保护数据;接着,使用RSA非对称加密算法加密AES密钥和IV,进一步增强安全性。即使情书被截获,没有正确密钥也无法解读内容。让我们用Python为爱情编织一张安全的网,守护每份珍贵情感。
46 2