1

Тема: [FASM] x86 Position-independent code, proof of concept

У якості розминки, наваяв приклад частково позиційно-незалежного коду. Завдяки частковості, ми отримуємо дуже компактне PI-тіло, яке, наприклад, можемо кудись заінжектити.

http://i63.сайт-злодій/big/2015/0126/0f/207014b4b480eca9a364dae9e4f5d80f.png

За приклад візьмемо наступний код:

WSADATA wsaData;
char *m, name[128];

WSAStartup(0x202, &wsaData);
gethostname(name, 128);
m = inet_ntoa(*(struct in_addr*)gethostbyname(name)->h_addr_list[0]);
MessageBoxA(0, m, "Заголовок", MB_OK);
WSACleanup();
ExitProcess(0);

shellcode.asm

    format pe gui 5.01

    entry start

    include 'win32ax.inc'

    struct APIS
        MessageBoxA     rd 1
        ExitProcess     rd 1
        WSAStartup      rd 1
        gethostname     rd 1
        inet_ntoa       rd 1
        gethostbyname   rd 1
        WSACleanup      rd 1
    ends

    struct CONFIG
        apis        APIS
        wsaData     WSADATA
        name        rb 128
        tittle      rb 20
    ends

;   ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
section '.data' data  readable writeable                                 // секція даних

api_table dd    nMessageBoxA,\
                nExitProcess,\
                nWSAStartup,\
                ngethostname,\
                ninet_ntoa,\
                ngethostbyname,\
                nWSACleanup,\
                -1

    nMessageBoxA    db 'MessageBoxA',       0
    nExitProcess    db 'ExitProcess',       0
    nWSAStartup     db 'WSAStartup',        0
    ngethostname    db 'gethostname',       0
    ninet_ntoa      db 'inet_ntoa',         0
    ngethostbyname  db 'gethostbyname',     0
    nWSACleanup     db 'WSACleanup',        0

    user32          db 'user32',            0
    kernel32        db 'kernel32',          0
    ws2_32          db 'ws2_32',            0

    ttl             db 'Заголовок',         0

    shc             rd                      1
    cfg             rd                      1
    hHeap           rd                      1


shellcode:
    virtual at ebp
        config CONFIG
    end virtuaL

    lea eax, [config.wsaData]
    push eax
    push 0x202
    call [config.apis.WSAStartup]

    push 128
    lea eax, [config.name]
    push eax
    call [config.apis.gethostname]

    lea eax, [config.name]
    push eax
    call [config.apis.gethostbyname]

    mov eax, [eax + hostent.h_addr_list]
    mov eax, [eax]

    push dword[eax]
    call [config.apis.inet_ntoa]

    push 0
    lea edx, [config.tittle]
    push edx
    push eax
    push 0
    call [config.apis.MessageBoxA]

    call [config.apis.WSACleanup]

    push 0
    call [config.apis.ExitProcess]
size_shellcode = $ - shellcode
;   ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~


;   ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
section '.text' code  readable executable                                // секція коду

start:
    // аналог GetProcessHeap()
    mov eax, [fs:0x30]
    mov eax, [eax + 0x18]
    mov [hHeap], eax

    // виділяємо пам'ять під структуру
    invoke HeapAlloc, [hHeap], HEAP_ZERO_MEMORY, sizeof.CONFIG
    mov [cfg], eax

    // ..під шеллкод
    invoke HeapAlloc, [hHeap], HEAP_ZERO_MEMORY, size_shellcode
    mov [shc], eax

    // знаходимо адреси winapi
    mov ebx, [cfg]
    mov esi, api_table
.a: cmp dword[esi], nMessageBoxA
    ja @f
    push user32
    jmp .b
@@: cmp dword[esi], nExitProcess
    ja @f
    push kernel32
    jmp .b
@@: push ws2_32
.b: invoke LoadLibraryA
    invoke GetProcAddress, eax, dword[esi]
    mov dword[ebx], eax
    add esi, 4
    add ebx, 4
    cmp dword[esi], -1
    jne .a

    mov ebx, [cfg]
    invoke lstrcpyA, addr ebx + CONFIG.tittle, ttl

    // копіюємо шеллкод у пам'ять
    mov esi, shellcode
    mov edi, [shc]
    mov ecx, size_shellcode
    rep movsb

    // передаєм вказівник на структуру та стартуємо шеллкод
    mov ebp, [cfg]
    push ebp
    jmp [shc]
;   ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~


;   ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
section '.idata' import data  readable writeable                         // секція імпорту

    library kernel32, 'kernel32.dll',\
            user32, 'user32.dll'


    import  kernel32,\
            GetProcAddress, 'GetProcAddress',\
            LoadLibraryA, 'LoadLibraryA',\
            HeapAlloc, 'HeapAlloc',\
            lstrcpyA, 'lstrcpyA'
;   ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

http://i58.сайт-злодій/big/2015/0126/d9/9db084d9a94c691a72908347aa9172d9.png

Подякували: 0xDADA11C7, Pernat1y2

2

Re: [FASM] x86 Position-independent code, proof of concept

Дивно все це - шеллкод, розкиданий по секціях РЕ файла, зі статичним імпортом,

 
// виділяємо пам'ять під структуру
invoke HeapAlloc, [hHeap], HEAP_ZERO_MEMORY, sizeof.CONFIG
mov [cfg], eax

Іноді стеку достатньо і без танку з бубном.

3

Re: [FASM] x86 Position-independent code, proof of concept

0xDADA11C7 написав:

Дивно все це - шеллкод, розкиданий по секціях РЕ файла, зі статичним імпортом,

частково позиційно-незалежного коду

Як приклад: розробити руткіт для контролю запису в реєстрі, або ж наявності файла на диску, і у разі видалення яких - потрібно усе відновити. У цьому випадку, навіщо мені тягнути зайве?

4

Re: [FASM] x86 Position-independent code, proof of concept

Так, таке буває. Я сам розширення для свого пакувальника писав у позиційнонезалежному стилі.

5

Re: [FASM] x86 Position-independent code, proof of concept

Під "зайве", я мав на увазі loadlibrary та getprocaddress, відповідно до мого способу. Звісно він не компенсує повної незалежності, але все ж таки, воно буває необхідним. Не економно, заради невеликої кількості call'ів, тягнути свої велосипеди, якщо є можливість елегантно підготувати та використати малу "бойову" ділянку коду.

6

Re: [FASM] x86 Position-independent code, proof of concept

Так, і не буде працювати при інжекті, якщо одна із бібліотек не буде завантажена (user32/kernel32/ws2_32). Лажа вийшла =\ Гра не варта свічок. Краще один раз написати повноцінний пі-код, аніж частковий, та годинами його дебажити. 200-300 байт не така уж і роскіш..
На граблях, як на батуті, c#$&. Закрийте нафіг тему ((

7

Re: [FASM] x86 Position-independent code, proof of concept

Ваша тема це цілком нормальний творчій пошук, що навіть може комусь знадобится.

Гра не варта свічок.

Варта, але не скрізь. Мій варіант позиційно-незалежного коду (плагін відновлення стеку і початкового(системного) обробника виключень SEH):

    include '%FASMINC%\Win32a.inc'
    include 'loadpe.inc'
    szHash 'UnhandledExceptionFilter', hashUnhandledExceptionFilter
     
    org 0x0
    use32
    __3BIP_PLUGIN_HEADER__ start, 'RESTORESEH'
    pUnhandledExceptionFilter dd 0
    start:
    stdcall DWord [edi+_LOADPE_DATA.pAltGetProcAddress], edi, DWord [edi+_LOADPE_DATA.hKernel32], DWord [edi+_LOADPE_DATA.pCompareHash], hashUnhandledExceptionFilter
    xor edx, edx
    dec edx
    mov eax, DWord [fs:0]
    .unchain:
    cmp DWord [eax], edx
    jz .down
    mov eax, DWord [eax]
    jmp short .unchain
    .down:
    mov DWord [fs:0], eax
    add esp, DWord [edi+_LOADPE_DATA.dwDeepOfStack]
    jmp DWord [esi+_3B1P_PLUGIN_HEADER.pReturn]
    __3BIP_PLUGIN_TAIL__ 'RESTORESEH'

Звідсіля.