2013-01-19 66 views
3

我讀文章Tips for Evading Anti-Virus During Pen Testing,並給出Python程序一驚:ctypes:將字符串轉換爲函數?

from ctypes import * 
shellcode = '\xfc\xe8\x89\x00\x00....' 

memorywithshell = create_string_buffer(shellcode, len(shellcode)) 
shell = cast(memorywithshell, CFUNCTYPE(c_void_p)) 
shell() 

shellcode的縮短。有人可以解釋發生了什麼嗎?我熟悉Python和C,我試着讀ctypes模塊上,但還留有兩個主要問題:

  • 什麼是存儲在shellcode
    我知道這有東西與C(文章中它是Metasploit的shellcode,並選擇了不同的ASCII表示法),但我不能確定是否它是C源(可能不是)或源於某些某種彙編(哪個?)。

  • 根據第一個問題,演員陣容中發生了什麼魔術?

+1

你曾經開有十六進制編輯器的可執行文件? :) – pmg

回答

4

看一看這個shellcode的,我土氣從here(它會彈出一個MessageBoxA):

#include <stdio.h> 

typedef void (* function_t)(void); 

unsigned char shellcode[] = 
    "\xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B" 
    "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9" 
    "\xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C" 
    "\x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0" 
    "\x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B" 
    "\x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72" 
    "\x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03" 
    "\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47" 
    "\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F" 
    "\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72" 
    "\x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66" 
    "\x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14" 
    "\x8E\x03\xD3\x52\x33\xFF\x57\x68\x61\x72" 
    "\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F" 
    "\x61\x64\x54\x53\xFF\xD2\x68\x33\x32\x01" 
    "\x01\x66\x89\x7C\x24\x02\x68\x75\x73\x65" 
    "\x72\x54\xFF\xD0\x68\x6F\x78\x41\x01\x8B" 
    "\xDF\x88\x5C\x24\x03\x68\x61\x67\x65\x42" 
    "\x68\x4D\x65\x73\x73\x54\x50\xFF\x54\x24" 
    "\x2C\x57\x68\x4F\x5F\x6F\x21\x8B\xDC\x57" 
    "\x53\x53\x57\xFF\xD0\x68\x65\x73\x73\x01" 
    "\x8B\xDF\x88\x5C\x24\x03\x68\x50\x72\x6F" 
    "\x63\x68\x45\x78\x69\x74\x54\xFF\x74\x24" 
    "\x40\xFF\x54\x24\x40\x57\xFF\xD0"; 

void real_function(void) { 
    puts("I'm here"); 
} 

int main(int argc, char **argv) 
{ 
    function_t function = (function_t) &shellcode[0]; 

    real_function(); 
    function(); 
    return 0; 
} 

編譯它的鉤任何調試器下,我將使用GDB:

> gcc shellcode.c -o shellcode 
> gdb -q shellcode.exe 
Reading symbols from shellcode.exe...done. 
(gdb) 
> 

拆開主要功能看調用real_functionfunction之間的不同:

(gdb) disassemble main 
Dump of assembler code for function main: 
    0x004013a0 <+0>:  push %ebp 
    0x004013a1 <+1>:  mov %esp,%ebp 
    0x004013a3 <+3>:  and $0xfffffff0,%esp 
    0x004013a6 <+6>:  sub $0x10,%esp 
    0x004013a9 <+9>:  call 0x4018e4 <__main> 
    0x004013ae <+14>: movl $0x402000,0xc(%esp) 
    0x004013b6 <+22>: call 0x40138c <real_function> ; <- here we call our `real_function` 
    0x004013bb <+27>: mov 0xc(%esp),%eax 
    0x004013bf <+31>: call *%eax     ; <- here we call the address that is loaded in eax (the address of the beginning of our shellcode) 
    0x004013c1 <+33>: mov $0x0,%eax 
    0x004013c6 <+38>: leave 
    0x004013c7 <+39>: ret 
End of assembler dump. 
(gdb) 

有兩個call,讓我們做一個破發點,在<main+31>,看看什麼是EAX加載:

(gdb) break *(main+31) 
Breakpoint 1 at 0x4013bf 
(gdb) run 
Starting program: shellcode.exe 
[New Thread 2856.0xb24] 
I'm here 

Breakpoint 1, 0x004013bf in main() 
(gdb) disassemble 
Dump of assembler code for function main: 
    0x004013a0 <+0>:  push %ebp 
    0x004013a1 <+1>:  mov %esp,%ebp 
    0x004013a3 <+3>:  and $0xfffffff0,%esp 
    0x004013a6 <+6>:  sub $0x10,%esp 
    0x004013a9 <+9>:  call 0x4018e4 <__main> 
    0x004013ae <+14>: movl $0x402000,0xc(%esp) 
    0x004013b6 <+22>: call 0x40138c <real_function> 
    0x004013bb <+27>: mov 0xc(%esp),%eax 
=> 0x004013bf <+31>: call *%eax     ; now we are here 
    0x004013c1 <+33>: mov $0x0,%eax 
    0x004013c6 <+38>: leave 
    0x004013c7 <+39>: ret 
End of assembler dump. 
(gdb) 

看前3個字節的數據,在eax中的地址繼續:

(gdb) x/3x $eax 
0x402000 <shellcode>: 0xfc 0x33 0xd2 
(gdb)     ^-------^--------^---- the first 3 bytes of the shellcode 

CPU因此call 0x402000,我們的shell代碼在0x402000開始,讓我們在0x402000拆卸過什麼:

(gdb) disassemble 0x402000 
Dump of assembler code for function shellcode: 
    0x00402000 <+0>:  cld 
    0x00402001 <+1>:  xor %edx,%edx 
    0x00402003 <+3>:  mov $0x30,%dl 
    0x00402005 <+5>:  pushl %fs:(%edx) 
    0x00402008 <+8>:  pop %edx 
    0x00402009 <+9>:  mov 0xc(%edx),%edx 
    0x0040200c <+12>: mov 0x14(%edx),%edx 
    0x0040200f <+15>: mov 0x28(%edx),%esi 
    0x00402012 <+18>: xor %ecx,%ecx 
    0x00402014 <+20>: mov $0x18,%cl 
    0x00402016 <+22>: xor %edi,%edi 
    0x00402018 <+24>: xor %eax,%eax 
    0x0040201a <+26>: lods %ds:(%esi),%al 
    0x0040201b <+27>: cmp $0x61,%al 
    0x0040201d <+29>: jl  0x402021 <shellcode+33> 
    .... 

正如你所看到的,shellcode不過是彙編指令,唯一不同的是你在編寫這些指令的方式中,它使用了特殊的技術使它更具可移植性,例如從不使用固定地址。

蟒蛇相當於上面的程序:

#!python 

from ctypes import * 

shellcode_data = "\ 
\xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B\ 
\x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9\ 
\xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C\ 
\x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0\ 
\x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B\ 
\x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72\ 
\x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03\ 
\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\ 
\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\ 
\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72\ 
\x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66\ 
\x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14\ 
\x8E\x03\xD3\x52\x33\xFF\x57\x68\x61\x72\ 
\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\ 
\x61\x64\x54\x53\xFF\xD2\x68\x33\x32\x01\ 
\x01\x66\x89\x7C\x24\x02\x68\x75\x73\x65\ 
\x72\x54\xFF\xD0\x68\x6F\x78\x41\x01\x8B\ 
\xDF\x88\x5C\x24\x03\x68\x61\x67\x65\x42\ 
\x68\x4D\x65\x73\x73\x54\x50\xFF\x54\x24\ 
\x2C\x57\x68\x4F\x5F\x6F\x21\x8B\xDC\x57\ 
\x53\x53\x57\xFF\xD0\x68\x65\x73\x73\x01\ 
\x8B\xDF\x88\x5C\x24\x03\x68\x50\x72\x6F\ 
\x63\x68\x45\x78\x69\x74\x54\xFF\x74\x24\ 
\x40\xFF\x54\x24\x40\x57\xFF\xD0" 

shellcode = c_char_p(shellcode_data) 

function = cast(shellcode, CFUNCTYPE(None)) 
function() 
+1

謝謝!這是我正在尋找的那種洞察力。很可能我不會自己開發shellcode,我想在Python中嵌入彙編有點奇怪,但現在我知道發生了什麼:) – phineas

3
  • shellcode,如果我沒有記錯的話,包含特定架構編譯代碼,大致可以翻譯爲一個函數調用。 (不是建築專家,並且代碼被截斷......)

  • 因此,一旦你已經創建了一個C風格的字符串create_string_buffer,然後你可以愚弄蟒蛇以爲它與cast功能呼叫。然後Python執行最初包含在shellcode中的代碼。

這裏有一個有用的鏈接:http://www.blackhatlibrary.net/Python#Ctypes

2

讓我們不要忘記,爲了有可執行代碼,它必須被轉換成你的機器識別的格式。你在那裏做的是提供一系列可以被你的機器解釋的字節碼,所以你可以告訴你的機器執行它。通過提供最終的字節代碼,您實際上正在跳過編譯器的工作;這種技術在Just-In-Time編譯器中很常見,它必須在程序運行時創建可執行代碼。因此,這實際上與C(或Python或任何其他語言)幾乎沒有任何關係,但與此代碼預計運行的體系結構的細節有很大關係。

第一個字節代碼有CLD(0xFC有),接着是CALL指令(0xe8),這使得該代碼跳轉到基於在此字節碼序列中的下一個4個字節指定的偏移,等等上的地址。