ShellCode原理以及编写

0x0 ShellCode编写注意事原理

1)不能使用字符串的直接偏移

即使你在C/C++代码中定义一个全局变量,一个取值为“Hello world”的字符串,
或直接把该字符串作为参数传递给某个函数。
但是,编译器会把字符串放置在一个特定的Section中(如.rdata或.data)。

2)不能确定函数的地址(如printf)

在shellcode中,我们却不能以逸待劳了。
因为我们无法确定包含所需函数的DLL文件是否已经加载到内存。
受ASLR(地址空间布局随机化)机制的影响,系统不会每次都把DLL文件加载到相同地址上。
而且DLL文件可能随着Windows每次新发布的更新而发生变化,所以我们不能依赖DLL文件中某个特定的偏移。
我们需要把DLL文件加载到内存,然后直接通过shellcode查找所需要的函数。
幸运的是,Windows API为我们提供了两个函数:LoadLibrary和GetProcAddress。我们可以使用这两个函数来查找函数的地址。

3)必须避免一些特定字符(如NULL字节)

空字节(NULL)的取值为:0×00。在C/C++代码中,空字节被认为是字符串的结束符。
正因如此,shellcode存在空字节可能会扰乱目标应用程序的功能,而我们的shellcode也可能无法正确地复制到内存中。
虽然不是强制的,但类似利用strcpy()函数触发缓冲区溢出的漏洞是非常常见的情况。该函数会逐字节拷贝字符串,直至遇到空字节。
因此,如果shellcode包含空字节,strcpy函数便会在空字节处终止拷贝操作,引发栈上的shellcode不完整。
正如你所料,shellcode当然也不会正常的运行。
例如MOV EAX,0; XOR EAX,EAX; 两条指令从功能上来说是等价的,但你可以清楚地看到第一条指令包含空字节,而第二条指令却包含空字节。
虽然空字节在编译后的代码中非常常见,但是我们可以很容易地避免。还有,在一些特殊情况下,shellcode必须避免出现类似\r或\n的字符,甚至只能使用字母数

0x1 ShellCode编写原理

1.Windows ShellCode原理

1)获取kernel32.dll 基地址;
2)定位 GetProcAddress函数的地址;
3)使用GetProcAddress确定 LoadLibrary函数的地址;
4)然后使用 LoadLibrary加载DLL文件(例如user32.dll);
5)使用 GetProcAddress查找某个函数的地址(例如MessageBox);6)指定函数参数;
7)调用函数。

0x2 ShellCode编写过程

1.PEB与ASLR机制

PEB是一个位于所有进程内存中固定位置的结构体。此结构体包含关于进程的有用信息,如可执行文件加载到内存的位置,模块列表(DLL),指示进程是否被调试的标志,还有许多其他的信息。
它可能随着新的Windows发行版发生改变。
ASLR:
地址空间布局随机 机制的影响,系统不会每次都把DLL文件加载到相同地址上。而且,DLL文件可能随着Windows每次新发布的更新而发生变化,所以我们不能依赖DLL文件中某个特定的偏移。
PEB:
typedef struct _PEB {

    BYTE Reserved1[2];

    BYTE BeingDebugged;

    BYTE Reserved2[1];

    PVOID Reserved3[2];

    PPEB_LDR_DATA Ldr;

    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;

    PVOID Reserved4[3];

    PVOID AtlThunkSListPtr;

    PVOID Reserved5;

    ULONG Reserved6;

    PVOID Reserved7;

    ULONG Reserved8;

    ULONG AtlThunkSListPtr32;

    PVOID Reserved9[45];

    BYTE Reserved10[96];

    PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;

    BYTE Reserved11[128];

    PVOID Reserved12[1];

    ULONG SessionId;

} PEB, *PPEB;


PEB_LDR_DATA:

typedef struct _PEB_LDR_DATA {

    BYTE Reserved1[8];

    PVOID Reserved2[3];

    LIST_ENTRY InMemoryOrderModuleList;

} PEB_LDR_DATA, *PPEB_LDR_DATA;



LIST_ENTRY结构是一个简单的双向链表,包含指向下一个元素(Flink)的指针和指向上一个元素的指针(Blink),其中每个指针占用4个字节:

typedef struct _LIST_ENTRY { 
    struct _LIST_ENTRY *Flink;
    struct _LIST_ENTRY *Blink; 
} LIST_ENTRY, *PLIST_ENTRY;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DeX3nRoE-1625194912483)(en-resource://database/554:1)]

2.Kernle32.dll地址获取

2.1 读取PEB结构
mov eax,fs:[0x30]
2.2 读取_LDR_DATA结构
mov eax,[eax+0xc]
2.3读取_PEB_LDR_DATA下InMemoryOrderModuleList
2.4 LISt_ENTRT是一个链表Flink
第三个是Kernel.dll
mov esi,[eax+0x14]
2.5 LIST_ENTRT指针是可以是_LDR_DATA_TABLE_ENTRY结构(该结构未被公开)该结构DllBase 0x18位置是dll加载位置,现在需要将LIST_ENTRY链表Fink移动到Kernel.dll
1 Fink exe本身内存位置
2 Fink ntdll.dll内存位置
3 Fink Kernel.dll内存位置
lodsd;  eax <- esi , (esi +0x4  ntdll.dll) esi=exe内存位置
xchg eax,esi; esi eax互换    ntdll.dll下一个
lodsd; Kernle32.dll
mov ebx,[eax+0x10]; _LDR_DATA_TABLE_ENTRY 结构下DllBase属性即模块RVA地址

3.Kernel32.dll导出表

Kernel PE通过DOS头到NT映像头
mov edx,[ebx+0x3c]
找到Kernel NT映像头
add edx,ebx
在NT映像头中找到_IMAGE_DATA_DIRECTORY0x78,可选文件头偏移是0x60
mov edx,[edx+0x78]
add edx,ebx edx=export table
_IMAGE_DATA_DIRECTORY偏移0x20导出函数表RVA
mov esi,[edx+0x20]
add esi,ebx ebx Kernle内存地址+esi 偏移
xor ecx ecx

4.GetProcAddress函数名称与序号获取

Get_Function: 标志
inc ecx  ecx自增1用于记录当前函数地址的序号
lodsd eax <- esi  <- (esi=esi+0x4)
add eax,ebx  eax=第一个导出函数名称
cmp dword ptr [eax],0x50746547 ; GetP 判断开头是不是要找的
jnz Get_Function
cmp dword ptr[eax+0x4],0x41636f72; rocA
jnz Get_Function
cmp dword ptr [eax+0x8],0x65726464; ddre
jnz Get_Function

5. GetProcAddress函数地址获取

AddressOfNameOrdlnals
mov esi,[edx+0x24] 导出函数序号RVA 
add esi,ebx kernel 导出函数RVA
mov cx,[esi+ecx*2] 导出函数序号表RVA是一个 word大小的数组因此ecx*2 找到导出函数序号表RVA GetProcAddress函数地址
dec ecx 因为是数组所以减一
AddressOfFunction
mov esi,[edx+0x1c]  esi=导出函数地址表
add esi,ebx 内存kernel 中导出函数地址表RVA
mov edx,[esi+ecx*4]通过序号ecx找到GetProcAddress
add edx,ebx 找到kernel中GetProcAddress 地址

6.获得LoadLibrary函数地址

xor ecx,ecx
push ebx Kernel32 base Address
push edx GetProcAddress
push ecx 0
push 0x41797261 arryA
push 0x7262694c Libr
push 0x64616f4c Load
push esp
push ebx
call edx

7.加载user32.dll动态库

add esp,0xc 落栈
pop ecx
push eax LoadLibraryA Address
push ecx
mov cx,0x6c6c6 ; ll
push ecx
push 0x642e3233; 32.d
push 0x72657375
push esp
call eax

8.获得MessageBoxA函数地址

add esp,0x10 落栈
mov edx,[esp+0x4] GetProcAddress
xor ecx ecx
push ecx
mov ecx,0x6141786F oxAa
push ecx 结束符
sub dword ptr[esp+0x3],0x61 将a填充删除
push 0x42656761 ageB
push 0x7373654D Mess
push esp MessageBoxA
push eax user32.dll address
call edx GetPorcAddress(user32.dll ,MessageBoxA)
add esp,0x10 落栈 _cdecl调用

9.使用MessageBoxA弹出LiPengYu

xor ecx,ecx
push 61616161
sub dword ptr[esp],0x61
sub dword ptr[esp+0x1],0x61
sub dword ptr[esp+0x2],0x61
sub dword ptr[esp+0x3],0x61    等价于 mov [esp+0x4],0x0但是shellcode不能有0出现
push 0x7559676E ngYu
push 0x6550694C LiPe
push edi
lea edi,[esp+0x4]
push ecx
push ecx
push edi
push ecx
call eax MessageBoxA
pop edi

10.结束ShellCode

add esp,0x10 _cdecl
pop edx
pop ebx
mov ecx,0x61737365 essa
push ecx
sub dowrd ptr [esp+0x3],0x61
push 0x636f7250
push 0x74697845
push esp
push ebx;kernle32.dll Address
call edx;GetProcAddress(ExitProcess)
xor ecx,ecx
push ecx
call eax 结束ShellCode

11.完整ASM代码

void ShellCodeMessageBoxW() {

     __asm {

         nop

         nop

         nop

         nop

         nop

         nop

         nop

         nop

         xor ecx, ecx

         mov eax, fs: [ecx + 0x30] ; EAX = PEB

         mov eax, [eax + 0xc]; EAX = PEB->Ldr

         mov esi, [eax + 0x14]; ESI = PEB->Ldr.InMemOrder

         lodsd; EAX = Second module

         xchg eax, esi; EAX = ESI, ESI = EAX

         lodsd; EAX = Third(kernel32)

         mov ebx, [eax + 0x10]; EBX = Base address

         mov edx, [ebx + 0x3c]; EDX = DOS->e_lfanew

         add edx, ebx; EDX = PE Header

         mov edx, [edx + 0x78]; EDX = Offset export table

         add edx, ebx; EDX = Export table

         mov esi, [edx + 0x20]; ESI = Offset namestable

         add esi, ebx; ESI = Names table

         xor ecx, ecx; EXC = 0



         Get_Function:



         inc ecx; Increment the ordinal

             lodsd; Get name offset

             add eax, ebx; Get function name

             cmp dword ptr[eax], 0x50746547; GetP

             jnz Get_Function

             cmp dword ptr[eax + 0x4], 0x41636f72; rocA

             jnz Get_Function

             cmp dword ptr[eax + 0x8], 0x65726464; ddre

             jnz Get_Function

             mov esi, [edx + 0x24]; ESI = Offset ordinals

             add esi, ebx; ESI = Ordinals table

             mov cx, [esi + ecx * 2]; Number of function

             dec ecx

             mov esi, [edx + 0x1c]; Offset address table

             add esi, ebx; ESI = Address table

             mov edx, [esi + ecx * 4]; EDX = Pointer(offset)

             add edx, ebx; EDX = GetProcAddress



             xor ecx, ecx; ECX = 0

             push ebx; Kernel32 base address

             push edx; GetProcAddress

             push ecx; 0

             push 0x41797261; aryA

             push 0x7262694c; Libr

             push 0x64616f4c; Load

             push esp; "LoadLibrary"

             push ebx; Kernel32 base address

             call edx; GetProcAddress(LL)



             add esp, 0xc; pop "LoadLibrary"

             pop ecx; ECX = 0

             push eax; EAX = LoadLibrary

             push ecx

             mov cx, 0x6c6c; ll

             push ecx

             push 0x642e3233; 32.d

             push 0x72657375; user

             push esp; "user32.dll"

             call eax; LoadLibrary("user32.dll")



             add esp, 0x10; Clean stack

             mov edx, [esp + 0x4]; EDX = GetProcAddress

             xor ecx, ecx; ECX = 0

             push ecx

             mov ecx, 0x6141786F; oxWa  MessageBoxA

             push ecx

             sub dword ptr[esp + 0x3], 0x61

             push 0x42656761; eBut   ageB

             push 0x7373654D; Mous   Mess



             push esp; "MessageBoxW"

             push eax; user32.dll address

             call edx; GetProc(MessageBoxA)



             add esp, 0x10; Cleanup stack

             xor ecx, ecx; ECX = 0

             push 0x61616161

             sub dword ptr[esp], 0x61

             sub dword ptr[esp + 0x1], 0x61

             sub dword ptr[esp + 0x2], 0x61

             sub dword ptr[esp + 0x3], 0x61

             push 0x7559676E;     ngYu

             push 0x6550694C;     LiPe

             push edi

             lea edi,[esp+0x4]

             push ecx;  

             push ecx;

             push edi;

             push ecx;

             call eax; Mess!

             pop edi;



             add esp, 0x10; Clean stack

             pop edx; GetProcAddress

             pop ebx; kernel32.dll base address

             mov ecx, 0x61737365; essa

             push ecx

             sub dword ptr[esp + 0x3], 0x61; Remove "a"

             push 0x636f7250; Proc

             push 0x74697845; Exit

             push esp

             push ebx; kernel32.dll base address

             call edx; GetProc(Exec)

             xor ecx, ecx; ECX = 0

             push ecx; Return code = 0

             call eax; ExitProcess

             nop

             nop

             nop

             nop

             nop

             nop

             nop

             nop

     }

 }


12.ShellCode机器码(WinHex提取)

char shellcode[] = { 

        0x33, 0xC9,

        0x64, 0x8B, 0x41, 0x30, 0x8B, 0x40, 0x0C, 0x8B,

        0x70, 0x14, 0xAD, 0x96, 0xAD, 0x8B, 0x58, 0x10,

        0x8B, 0x53, 0x3C, 0x03, 0xD3, 0x8B, 0x52, 0x78,

        0x03, 0xD3, 0x8B, 0x72, 0x20, 0x03, 0xF3, 0x33,

        0xC9, 0x41, 0xAD, 0x03, 0xC3, 0x81, 0x38, 0x47,

        0x65, 0x74, 0x50, 0x75, 0xF4, 0x81, 0x78, 0x04,

        0x72, 0x6F, 0x63, 0x41, 0x75, 0xEB, 0x81, 0x78,

        0x08, 0x64, 0x64, 0x72, 0x65, 0x75, 0xE2, 0x8B,

        0x72, 0x24, 0x03, 0xF3, 0x66, 0x8B, 0x0C, 0x4E,

        0x49, 0x8B, 0x72, 0x1C, 0x03, 0xF3, 0x8B, 0x14,

        0x8E, 0x03, 0xD3, 0x33, 0xC9, 0x53, 0x52, 0x51,

        0x68, 0x61, 0x72, 0x79, 0x41, 0x68, 0x4C, 0x69,

        0x62, 0x72, 0x68, 0x4C, 0x6F, 0x61, 0x64, 0x54,

        0x53, 0xFF, 0xD2, 0x83, 0xC4, 0x0C, 0x59, 0x50,

        0x51, 0x66, 0xB9, 0x6C, 0x6C, 0x51, 0x68, 0x33,

        0x32, 0x2E, 0x64, 0x68, 0x75, 0x73, 0x65, 0x72,

        0x54, 0xFF, 0xD0, 0x83, 0xC4, 0x10, 0x8B, 0x54,

        0x24, 0x04, 0x33, 0xC9, 0x51, 0xB9, 0x6F, 0x78,

        0x41, 0x61, 0x51, 0x83, 0x6C, 0x24, 0x03, 0x61,

        0x68, 0x61, 0x67, 0x65, 0x42, 0x68, 0x4D, 0x65,

        0x73, 0x73, 0x54, 0x50, 0xFF, 0xD2, 0x83, 0xC4,

        0x10, 0x33, 0xC9, 0x68, 0x61, 0x61, 0x61, 0x61,

        0x83, 0x2C, 0x24, 0x61, 0x83, 0x6C, 0x24, 0x01,

        0x61, 0x83, 0x6C, 0x24, 0x02, 0x61, 0x83, 0x6C,

        0x24, 0x03, 0x61, 0x68, 0x6E, 0x67, 0x59, 0x75,

        0x68, 0x4C, 0x69, 0x50, 0x65, 0x57, 0x8D, 0x7C,

        0x24, 0x04, 0x51, 0x51, 0x57, 0x51, 0xFF, 0xD0,

        0x5F, 0x83, 0xC4, 0x10, 0x5A, 0x5B, 0xB9, 0x65,

        0x73, 0x73, 0x61, 0x51, 0x83, 0x6C, 0x24, 0x03,

        0x61, 0x68, 0x50, 0x72, 0x6F, 0x63, 0x68, 0x45,

        0x78, 0x69, 0x74, 0x54, 0x53, 0xFF, 0xD2, 0x33,

        0xC9, 0x51, 0xFF, 0xD0

    };



    void* exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(exec, shellcode, sizeof shellcode);

    ((void(*)())exec)();


你可能感兴趣的:(免杀,汇编,c++,安全,信息安全,反病毒)