boy-hack / boy-hack

boy-hack
62 stars 2 forks source link

Shellcode的艺术 #23

Open boy-hack opened 3 years ago

boy-hack commented 3 years ago

补充 #10 中关于shellcode编码器的部分

boy-hack commented 3 years ago

导入表搜索API image

int main() 
{ 
    _asm{ 
    CLD ;clear flag DF
    ;store hash
    push 0x1e380a6a ;hash of MessageBoxA
    push 0x4fd18963 ;hash of ExitProcess
    push 0x0c917432 ;hash of LoadLibraryA
    mov esi,esp ;esi = addr of first function hash
    lea edi,[esi-0xc] ;edi = addr to start writing function
    ;make some stack space
    xor ebx,ebx
    mov bh, 0x04 
    sub esp, ebx 
    ;push a pointer to "user32" onto stack 
    mov bx, 0x3233 ;rest of ebx is null 
    push ebx 
    push 0x72657375 
    push esp 
    xor edx,edx
    ;find base addr of kernel32.dll 
    mov ebx, fs:[edx + 0x30] ;ebx = address of PEB 
    mov ecx, [ebx + 0x0c] ;ecx = pointer to loader data 
    mov ecx, [ecx + 0x1c] ;ecx = first entry in initialization 
     ;order list 
    mov ecx, [ecx] ;ecx = second entry in list 
     ;(kernel32.dll) 
    mov ebp, [ecx + 0x08] ;ebp = base address of kernel32.dll 
    find_lib_functions: 
    lodsd ;load next hash into al and increment esi 
    cmp eax, 0x1e380a6a ;hash of MessageBoxA - trigger 
     ;LoadLibrary("user32") 
    jne find_functions 
    xchg eax, ebp ;save current hash 
    call [edi - 0x8] ;LoadLibraryA 
    xchg eax, ebp ;restore current hash, and update ebp 
     ;with base address of user32.dll 
    find_functions: 
    pushad ;preserve registers 
    mov eax, [ebp + 0x3c] ;eax = start of PE header 
    mov ecx, [ebp + eax + 0x78] ;ecx = relative offset of export table
    add ecx, ebp ;ecx = absolute addr of export table 
    mov ebx, [ecx + 0x20] ;ebx = relative offset of names table 
    add ebx, ebp ;ebx = absolute addr of names table 
    xor edi, edi ;edi will count through the functions 
    next_function_loop: 
    inc edi ;increment function counter 
    mov esi, [ebx + edi * 4] ;esi = relative offset of current
     ;function name 
    add esi, ebp ;esi = absolute addr of current
     ;function name 
    cdq ;dl will hold hash (we know eax is
     ;small) 
    hash_loop: 
    movsx eax, byte ptr[esi]
    cmp al,ah
    jz compare_hash
    ror edx,7
    add edx,eax
    inc esi
    jmp hash_loop
    compare_hash: 
    cmp edx, [esp + 0x1c] ;compare to the requested hash (saved on 
     ;stack from pushad) 
    jnz next_function_loop 
    mov ebx, [ecx + 0x24] ;ebx = relative offset of ordinals
    ;table 
    add ebx, ebp ;ebx = absolute addr of ordinals
     ;table 
    mov di, [ebx + 2 * edi] ;di = ordinal number of matched
     ;function 
    mov ebx, [ecx + 0x1c] ;ebx = relative offset of address
     ;table 
    add ebx, ebp ;ebx = absolute addr of address table 
    add ebp, [ebx + 4 * edi] ;add to ebp (base addr of module) the 
     ;relative offset of matched function 
    xchg eax, ebp ;move func addr into eax 
    pop edi ;edi is last onto stack in pushad 
    stosd ;write function addr to [edi] and
     ;increment edi 
    push edi
    popad ;restore registers 
     ;loop until we reach end of last hash 
    cmp eax,0x1e380a6a
    jne find_lib_functions 
    function_call: 
    xor ebx,ebx
    push ebx ;cut string
    push 0x74736577
    push 0x6C696166 ;push failwest
    mov eax,esp ;load address of failwest
    push ebx
    push eax
    push eax
    push ebx
    call [edi - 0x04] ;call MessageboxA
    push ebx
    call [edi - 0x08] ;call ExitProcess
    nop
    nop
    nop
    nop
    } 
}

之所以在汇编代码的前后都加上一段 nop(0x90),是为了在反汇编工具或调试 时非常方便地区分出 shellcode 的代码。

"\x90"// NOP 
"\xFC"// CLD 
"\x68\x6A\x0A\x38\x1E"// PUSH 1E380A6A 
"\x68\x63\x89\xD1\x4F"// PUSH 4FD18963 
"\x68\x32\x74\x91\x0C"// PUSH 0C917432 
"\x8B\xF4"// MOV ESI,ESP 
"\x8D\x7E\xF4"// LEA EDI,DWORD PTR DS:[ESI-C] 
"\x33\xDB"// XOR EBX,EBX 
"\xB7\x04"// MOV BH,4 
"\x2B\xE3"// SUB ESP,EBX 
"\x66\xBB\x33\x32"// MOV BX,3233 
"\x53"// PUSH EBX 
"\x68\x75\x73\x65\x72"// PUSH 72657375 
"\x54"// PUSH ESP 
"\x33\xD2"// XOR EDX,EDX 
"\x64\x8B\x5A\x30"// MOV EBX,DWORD PTR FS:[EDX+30] 
"\x8B\x4B\x0C"// MOV ECX,DWORD PTR DS:[EBX+C] 
"\x8B\x49\x1C"// MOV ECX,DWORD PTR DS:[ECX+1C] 
"\x8B\x09"// MOV ECX,DWORD PTR DS:[ECX] 
"\x8B\x69\x08"// MOV EBP,DWORD PTR DS:[ECX+8] 
"\xAD"// LODS DWORD PTR DS:[ESI] 
"\x3D\x6A\x0A\x38\x1E"// CMP EAX,1E380A6A 
"\x75\x05"// JNZ SHORT popup_co.00401070 
"\x95"// XCHG EAX,EBP 
"\xFF\x57\xF8"// CALL DWORD PTR DS:[EDI-8] 
"\x95"// XCHG EAX,EBP 
"\x60"// PUSHAD 
"\x8B\x45\x3C"// MOV EAX,DWORD PTR SS:[EBP+3C] 
"\x8B\x4C\x05\x78"// MOV ECX,DWORD PTR SS:[EBP+EAX+78] 
"\x03\xCD"// ADD ECX,EBP 
"\x8B\x59\x20"// MOV EBX,DWORD PTR DS:[ECX+20] 
"\x03\xDD"// ADD EBX,EBP 
"\x33\xFF"// XOR EDI,EDI 
"\x47"// INC EDI 
"\x8B\x34\xBB"// MOV ESI,DWORD PTR DS:[EBX+EDI*4] 
"\x03\xF5"// ADD ESI,EBP 
"\x99"// CDQ 
"\x0F\xBE\x06"// MOVSX EAX,BYTE PTR DS:[ESI] 
"\x3A\xC4"// CMP AL,AH 
"\x74\x08"// JE SHORT popup_co.00401097 
"\xC1\xCA\x07"// ROR EDX,7 
"\x03\xD0"// ADD EDX,EAX 
"\x46"// INC ESI 
"\xEB\xF1"// JMP SHORT popup_co.00401088 
"\x3B\x54\x24\x1C"// CMP EDX,DWORD PTR SS:[ESP+1C] 
"\x75\xE4"// JNZ SHORT popup_co.00401081 
"\x8B\x59\x24"// MOV EBX,DWORD PTR DS:[ECX+24] 
"\x03\xDD"// ADD EBX,EBP 
"\x66\x8B\x3C\x7B"// MOV DI,WORD PTR DS:[EBX+EDI*2] 
"\x8B\x59\x1C"// MOV EBX,DWORD PTR DS:[ECX+1C] 
"\x03\xDD"// ADD EBX,EBP 
"\x03\x2C\xBB"// ADD EBP,DWORD PTR DS:[EBX+EDI*4] 
"\x95"// XCHG EAX,EBP 
"\x5F"// POP EDI 
"\xAB"// STOS DWORD PTR ES:[EDI] 
"\x57"// PUSH EDI 
"\x61"// POPAD 
"\x3D\x6A\x0A\x38\x1E"// CMP EAX,1E380A6A 
"\x75\xA9"// JNZ SHORT popup_co.00401063
"\x33\xDB"// XOR EBX,EBX 
"\x53"// PUSH EBX 
"\x68\x77\x65\x73\x74"// PUSH 74736577 
"\x68\x66\x61\x69\x6C"// PUSH 6C696166 
"\x8B\xC4"// MOV EAX,ESP 
"\x53"// PUSH EBX 
"\x50"// PUSH EAX 
"\x50"// PUSH EAX 
"\x53"// PUSH EBX 
"\xFF\x57\xFC"// CALL DWORD PTR DS:[EDI-4] 
"\x53"// PUSH EBX 
"\xFF\x57\xF8";// CALL DWORD PTR DS:[EDI-8] 
boy-hack commented 3 years ago

为什么要对 shellcode 编码

xor编码器

void main() 
{ 
__asm 
{ 
add eax, 0x14 //越过 decoder,记录 shellcode 的起始地址
xor ecx,ecx 
decode_loop: 
mov bl,[eax+ecx]
xor bl, 0x44 //这里用 0x44 作为 key,如编码的 key 改变,这里也要相应
//改变
mov [eax+ecx],bl
inc ecx
cmp bl,0x90 //在 shellcode 末尾放上一个字节的 0x90 作为结束符
jne decode_loop
} 
}

整个装载器 image

shellcode精简

1.短指令

xchg eax,reg 交换 eax 和其他寄存器中的值
lodsd 把 esi 指向的一个 dword 装入 eax,并且增加 esi 
lodsb 把 esi 指向的一个 byte 装入 al,并且增加 esi 
stosd 
stosb 
pushad/popad 从栈中存储/恢复所有寄存器的值
cdq 用 edx 把 eax 扩展成四字。这条指令在 eax<0x80000000 时可用作 mov edx , 
NULL

2.复合功能指令

有时候我们可以把两件事情用一条指令完成,例如,用 xchg、lods 或者 stos。

3.妙用内存

有些 API 中许多参数都是 NULL,通常的做法是多次向栈中压入 NULL。如果我们换一个 思路,把栈中的一大片区域一次性全部置为 NULL,在调用 API 的时候就可以只压入那些非 NULL 的参数,从而节省出许多压栈指令。 我们经常会遇到 API 中需要一个很大的结构体做参数的情况。通过实验可以发现,大 多数情况下,健壮的 API 都可以允许两个结构体相互重叠,尤其是当一个参数是输入结构 体[in],另一个用作接收的结构体[out]时,如果让参数指向同一个[in]结构体,函数往往也 能正确执行。这种情况下,仅仅用一个字节的短指令“push esp”就可以代替一大段初始化 [out]结构体的代码

4.代码也可以当数据

5.调整栈顶回收数据

普通程序员不会直接与系统栈打交道,通常与栈沟通的总是编译器。在编译器看来,栈仅 仅是用来保护函数调用断点、暂存函数输入参数和返回值等的场所。但是,作为一个 shellcode 的开发人员,必须富有更多的想象力。栈顶之上的数据在逻辑上视为废弃数据,但其物理内容 往往并未遭到破坏。如果栈顶之上有需要的数据,不妨调整 esp 的值将栈顶抬高,把它们保护 起来以便后面使用,这样能节省出很多用作数据初始化的指令。这与我们前边讲的抬高栈帧保 护 shellcode 有相似之处

6.巧用寄存器

按照默认的函数调用约定,在调用 API 时有些寄存器(如 EBP、ESI、EDI 等)总是被保 存在栈中。把函数调用信息存在寄存器中而不是存在栈中会给 shellcode 带来很多好处。比如大 多数函数的运行过程中都不会使用 EBP 寄存器,故我们可以打破常规,直接使用 EBP 来保存 数据,而不是把数据存在栈中。 一些 x86 的寄存器有着自己特殊的用途。有的指令要求只能使用特定的寄存器;有的指令 使用特定寄存器时的机器码要比使用其他寄存器短。此外,如果寄存器中含有调用函数时需要 的数值,尽管不是立刻要调用这些函数,可能还是要考虑提前把寄存器压入栈内以备后用,以 免到时候还得另用指令重新获取。

boy-hack commented 3 years ago

metasploit shellcode 编码器

boy-hack commented 3 years ago

用C语言写shellcode