|
|
|
|
|
|
|
Kullanılan
Araçlar:
|
|
|
Bazı arkadaşlar unpack yapmayı yahut unpacker yazmayı gözlerinde çok büyütüyorlar.Size bu yazıda bir packerın nasıl analiz edileceğini ve iki şekilde unpacker yazmayı göstericem
|
|
Unpacker yazmak -asprotect,telock vs gibi komplike packerlar hariç- o kadar da zor birşey değil.Asprotect telock gibi packerlarda bir kaç layer üst üste ve ayrıca yüksek bir polymorphism mevcut.Unpacker yazmayı öğrenmenizin en iyi yolu öncelikle cryptorlardan başlamak.Cryptorlarda sectionların boyutu değişmiyor.O yüzden o kadar zor olmuyor.Fakat packerlar biraz daha zor çünkü her bir section'ın alignmentını ve offset değerleri ile de uğraşmak zorundasınız.Eğer crypterlara unpacker yazmak isterseniz size basit bir örnek olarak LameCrypti önerebilirim.
Yeniden konumuza dönelim.Unpacker yazacağımız packer PeDimisher.Packerda hiç bir Antisoftice IAT yok etme vs gibi şeyler yok.Şimdi bir programı bu packer ile sıkıştırıp sonra da softice ile izleyelim.Ayrıca IDA ile de dissamble ederseniz olayı çok daha iyi anlarsınız.Evet macere başlıyor.
Programın entrpointi şöyle oluyor.
push ebx push ecx push edx push esi push edi push ebp call $+5 ;delta yı al pop ebp mov edx, ebp sub ebp, offset unk_4030A2 sub edx, [ebp+403391h] sub edx, 0Bh mov [ebp+40339Ah], edx ;ImgBase cmp byte ptr [ebp+403399h], 0 ;paketlenen section sayısı 0 mı ? jz short loc_40907F ;o zaman direkt OEP e zıpla call GetApis ;gerekli apilerin adreslerini bul VirtualAlloc,VirtualFree mov edi, ebp loc_409036: ; CODE XREF: start+78j lea ebx, [ebp+40339Ah] mov ebx, [ebx] ; ImageBase lea eax, [edi+40339Eh] mov eax, [eax] ; section'ın RVA sı add ebx, eax lea ecx, [edi+4033A2h] mov ecx, [ecx] ;section'ın boyutu mov ax, [ebp+40338Fh] ; Anahtar loc_409057: ; CODE XREF: start+68j add byte ptr [ebx], 10h ;burada basit bir decrypt yapılıyor. xor [ebx], al xor [ebx], ah add byte ptr [ebx], 0AAh rol ax, 3 xchg ah, al inc ebx loop loc_409057 call UnpackSect ;sectionı unpackle add edi, 8 dec byte ptr [ebp+403399h] ; section sayacı jnz short loc_409036 call FillImport ;Import tablosunu doldur loc_40907F: ; CODE XREF: start+2Dj mov eax, [ebp+403395h] ; OEP mov ebx, [ebp+40339Ah] ; ImgBase add eax, ebx pop ebp pop edi pop esi pop edx pop ecx pop ebx jmp eax ; OEP'e zıpla GetApis proc near ;bu fonksiyon virtualalloc ve virtualfree apilerinin adreslerini bulup saklıyor lea eax, [ebp+403402h] push eax call dword ptr [ebp+403464h] ; GetModuleHandle mov edi, eax lea ebx, [ebp+40340Fh] push ebx ; push offset Virtualalloc push eax call dword ptr [ebp+403460h] ; GetProcAddress mov [ebp+4033FAh], eax ; virtualalloc'un adresi lea ebx, [ebp+40341Ch] push ebx ; push offset Virtualfree push edi call dword ptr [ebp+403460h] ; GetProcAddress mov [ebp+4033FEh], eax ; virtualfree'nin adresi retn GetApis endp ; sp = -14h UnpackSect proc near ; CODE XREF: start+6Ap push edi lea ecx, [edi+4033A2h] mov ecx, [ecx] push PAGE_READWRITE push MEM_COMMIT push ecx push 0 call dword ptr [ebp+4033FAh] ; VirtualAlloc mov [ebp+403267h], eax lea ebx, [ebp+40339Ah] mov ebx, [ebx] lea eax, [edi+40339Eh] mov eax, [eax] add ebx, eax push ebx push dword ptr [ebp+403267h];output push ebx ;input call UnpackSection ;sectionı oluşturulan memory e kopyala add esp, 8 ;stackı düzenle pop edi mov ecx, eax mov esi, [ebp+403267h] ;unpack edilen kısmı programa kopyala repe movsb mov eax, [ebp+403267h] push MEM_RELEASE push 0 push eax call dword ptr [ebp+4033FEh] ; VirtualFree pop edi retn UnpackSect endp ; sp = -1Ch FillImport proc near ; CODE XREF: start+7Ap mov edx, [ebp+40339Ah] ;IAT mov esi, [ebp+4033F6h] ; ImgBase xor edi, edi add esi, edx add edi, edx loc_4090A7: ; CODE XREF: FillImport+93j mov eax, [esi+0Ch] test eax, eax jz short locret_40912D add eax, edx mov ebx, eax push eax call dword ptr [ebp+403464h] ; GetModuleHandleA test eax, eax jnz short loc_4090C4 ; bu dll zaten yüklü push ebx call dword ptr [ebp+403468h] ; LoadLibraryA loc_4090C4: ; CODE XREF: FillImport+26j mov [ebp+4031C5h], eax ; dllnin handle'ı mov dword ptr [ebp+4031C9h], 0 loc_4090D4: ; CODE XREF: FillImport+88j mov edx, [ebp+40339Ah] mov eax, [esi] add eax, edx add eax, [ebp+4031C9h] mov ebx, [eax] mov edi, [esi+10h] add edi, edx add edi, [ebp+4031C9h] test ebx, ebx jz short loc_40911F test ebx, 80000000h jnz short loc_409101 add ebx, edx inc ebx inc ebx loc_409101: ; CODE XREF: FillImport+66j and ebx, 0FFFFFFFh push ebx push dword ptr [ebp+4031C5h] call dword ptr [ebp+403460h] ; GetProcAddress mov [edi], eax add dword ptr [ebp+4031C9h], 4 ;diğer importa geç jmp short loc_4090D4 loc_40911F: ; CODE XREF: FillImport+5Ej add esi, 14h ;diğer dll ye geç mov edx, [ebp+40339Ah] jmp loc_4090A7 locret_40912D: ; CODE XREF: FillImport+17j retn FillImport endp
Şimdi packerı inceliyelim.Ne zaman
push ebp call $+5 ;delta yı al pop ebp
Tipinde birşey görürseniz gözleriniz felfecir okuması lazım.Bu tip kodları genelde viruslerde çok görürüz.Bu kodun yaptığı şey o andaki EIP i bulmak böylelikle bulunduğu yere göre değişken kullanabilmek.Unpacker lar da bu mantıkla çalışırlar.Unpackerın başlangıçlarında buna benzer kodlar vardır.Dikkat ederseniz IDA bunu dissamble etmede pek yardımcı olmuyor.Bunun yerine softice ile trace edip her bir satırın ne olduğunu kendiniz bulmalısınız. Program ilk olarak ImageBase ve paketlenmiş kısım sayısını buluyor.Daha sonrada kullanıcağı apilerin adreslerini GetApis fonksiyonu ile tespit ediyor.Her bir sectiona bir key vasıtası ile bir decrypt işlemi gerçekleştirip daha sonra UnpackSection fonksiyonu ile unpackleyip yeniden exeye kopyalıyor.Bütün kısımlar bittikten sonra, import tablosunu dolduruyor.Importu yaparken basit getprocaddress döngüsünü kullanıyor.Paketlenen section sayısı,IAT'ın yeri,OEP,sectionların RVA ve raw büyüklüklükleri exe de yazıyor.Bu bilgiler bize çok gerekli olduğunda onların exenin neresinde yazılı olduğunu bulmakta fayda var.SoftICE ile debug ederken açıklama yazdığım yerlerin exenin neresinde olduğunu yazmanız faydanıza olacaktır.
start+02F8H ;değişkenlerimiz OEP ten 2f8h sonra başlıyor
key dw 370h ;decrypt ederken kullanılan key (değişiyor)
startofPedimin dd 9000h ;pediminisherın başlangıç adresi
OEP dd 1000h ; OEP
sectioncounter db 3 ; paketlenen section sayısı
ImgBase dd 0 ; ImgBase
codestart dd 1000h ; RVA
codelen dd 600h ; raw size
baseofdata dd 2000h ; RVA
datalen dd 600h ; raw size
rdatstart dd 3000h ; RVA
rdatlen dd 200h ; raw size
db 40h dup(0)
dd 20A8h ; IAT start RVA
Olay gördüğünüz gibi o kadar da zor değil.Şimdi unpacker yazımına geçiyoruz.İlk olarak dosyayı seçiyoruz ve PeDimisher ile korunup korunmadığına bakıyoruz.Bunu OEP i bulup ordaki byteları karşılaştırarak yapıyoruz.Artık pediminisher olduğunda eminiz.Buradan itibaren eside OEP var
push [esi+35Fh];IAT'ı sakla push [esi+2FEh] ;OEP'i sakla mov al, byte ptr[esi+302h] mov numofpacksect,al ;paketlenen section sayısı mov ax,word ptr[esi+2F8h] mov Key,ax invoke GlobalAlloc,GMEM_FIXED,ImgSize mov gMem,eax mov esi,pMapping ;file headerı oluşturulan memory e kopyala mov edi,gMem mov ecx,HeaderSize rep movsb mov unpack_start,edi ;burası diğer sectionlar için referans olucak mov esi,pMapping mov edi,gMem assume edi:ptr IMAGE_DOS_HEADER add edi, [edi].e_lfanew ; + offset PE add edi,sizeof IMAGE_NT_HEADERS ; edi = section header assume edi:ptr IMAGE_SECTION_HEADER @unpackloop: mov ebx,[edi].PointerToRawData ;sectionun yeri add ebx,esi mov ecx,[edi].SizeOfRawData mov ax,Key push ebx ;şu anki offseti sakla @xorloop: add byte ptr [ebx], 10h xor [ebx], al xor [ebx], ah add byte ptr [ebx], 0AAh rol ax, 3 xchg ah, al inc ebx loop @xorloop pop ebx ;offseti geriye al mov eax,unpack_start push eax ;destination push ebx ;source call Unpack ;eax=unpack edilen byte sayısı add esp,8 mov ecx,unpack_start ;şuanki offset sub ecx,gMem ;- gMem = offset değeri mov [edi].PointerToRawData,ecx mov [edi].SizeOfRawData,eax add unpack_start,eax add edi,sizeof IMAGE_SECTION_HEADER ;diğer sectiona geç dec numofpacksect jnz @unpackloop ;program da unpack edilmemiş section kaldı mı? .while !(dword ptr[edi]=="ret.") ;ismi .teraphy değilse mov eax,[edi].PointerToRawData add eax,esi mov ebx,[edi].SizeOfRawData invoke MemCopy,eax,unpack_start,ebx ;sectionı kopyala sub edi,sizeof IMAGE_SECTION_HEADER ;bir önceki section a git mov eax,[edi].PointerToRawData ;offset +size add eax,[edi].SizeOfRawData add edi,sizeof IMAGE_SECTION_HEADER ;come back mov [edi].PointerToRawData,eax ;yeni sectionun yeri add edi,sizeof IMAGE_SECTION_HEADER ;diğer sectiona geç add unpack_start,ebx .endw sub edi,sizeof IMAGE_SECTION_HEADER ;bir önceki sectiona dön
mov eax,[edi].Misc.VirtualSize ;son sectionın virtual addresi
mov ecx,SecAlign ;alignment değeri
invoke AlignSection,eax,ecx ;size of image i align et
add eax,[edi].VirtualAddress
mov ebx,eax ;store size of image
add edi,sizeof IMAGE_SECTION_HEADER ;teraphy sectionı sil mov ecx,sizeof IMAGE_SECTION_HEADER mov eax,0 rep stosb ;IAT+OEP+imgsize fixing mov edi, gMem assume edi:ptr IMAGE_DOS_HEADER add edi, [edi].e_lfanew ; + offset PE assume edi:ptr IMAGE_NT_HEADERS dec [edi].FileHeader.NumberOfSections ;section sayısını bir azalt mov [edi].OptionalHeader.SizeOfImage,ebx ;size of Image'i düzelt pop eax mov [edi].OptionalHeader.AddressOfEntryPoint,eax ;OEP'i düzelt pop eax ;IAT RVA yı düzelt mov [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,eax invoke RVAToOffset,gMem,eax mov esi,eax add esi,gMem ;IAT büyüklüğünü IAT ta yürüyerek bul. assume esi:ptr IMAGE_IMPORT_DESCRIPTOR mov eax,sizeof IMAGE_IMPORT_DESCRIPTOR .while !([esi].OriginalFirstThunk==0 && [esi].TimeDateStamp==0 && \ [esi].ForwarderChain==0 && [esi].Name1==0 && [esi].FirstThunk==0) add eax,sizeof IMAGE_IMPORT_DESCRIPTOR add esi,sizeof IMAGE_IMPORT_DESCRIPTOR .endw mov [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].isize,eax
İşte olay bu kadar.Unpack edilen dosya bazen orjinal dosyadan da küçük olabiliyor.Çünkü biz section headerdan hemen sonra kodu yazıyoruz.Unpacker orjinal pediminisher ı unpack ederken hata verdi.Fazla incelemedim ama sanırım buffer yetersiz geliyor.Eğer düzeltirseniz bana sonucu yollayın J
Peki bu kadar zahmete ne gerek var? Şimdi size bu programa unpacker yazmanın daha basit bir yolunu göstericem.Size soruyorum.Eğer programı kendiniz sadece SoftICE ile dump etmek isteseydiniz ne yapardınız ? Ben olsam call FillImport olan satıra breakpoint koyardım ve orada dump ederdim daha sonra exedeki IAT ve OEP değerleri ile dosyayı düzeltirdim.FillImport fonksiyonunu geçersek IAT bozulacağından bir de IAT ile uğraşmak zorunda kalırız.
Windowsun built-in debugger desteği var.Yani API ler vasıtasıyla bir programı debug edebilirsiniz.Bizim yapacağımız, breakpoint koyarak exeyi istediğimiz yerde durdurmak ve daha sonra dump etmek.Şimdi koda geçiyoruz.
push [esi+35Fh];IAT'ı sakla
push [esi+2FEh] ;OEP'i sakla
mov stinfo.dwFlags,STARTF_USESHOWWINDOW
mov stinfo.wShowWindow,SW_HIDE ;kullanıcıyı rahatsız etme :)
invoke CreateProcess, 0, offset buffer, 0, 0, 0,\
DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, 0, 0,\
OFFSET stinfo, OFFSET prinfo
.while TRUE
invoke WaitForDebugEvent, addr DBEvent, INFINITE
.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
;process sonlandı
.break
.elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
mov ebx,DBEvent.u.CreateProcessInfo.lpStartAddress
add ebx,07Ah ;patch addresi
invoke WriteProcessMemory, prinfo.hProcess, ebx ,addr patch, sizeof patch, NULL ;debug edileni patchle
.IF eax!=TRUE ;writeprocessmemory hatası
invoke ShowErr,hWin,CTEXT("Cant Write Process Memory")
.break ;döngüden çık
.endif
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT ;exception oluştu
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT ;breakpoint mi ?
mov cont.ContextFlags, CONTEXT_INTEGER ;eax,ebx,ecx... i okumak istiyoruz
invoke GetThreadContext, prinfo.hThread, addr cont
cmp ebx, DBEvent.u.Exception.pExceptionRecord.ExceptionAddress
jnz @dontcareothers ;exception bizim breakpoint adresinde değilse ilgilenme
invoke GlobalAlloc,GMEM_FIXED,ImgSize ;memory oluştur
mov gMem,eax
invoke ReadProcessMemory,prinfo.hProcess,ImgBase,gMem,ImgSize,0 ;process i memory e oku
;RVA-> offset çevirmesine uğraşmaya gerek yok kernel bizim için onu yaptı
mov edi, gMem
assume edi:ptr IMAGE_DOS_HEADER
add edi, [edi].e_lfanew ; + offset PE
assume edi:ptr IMAGE_NT_HEADERS
pop eax
mov [edi].OptionalHeader.AddressOfEntryPoint,eax ;OEP'i düzelt
pop eax ;IAT'ı düzelt
mov [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,eax
mov esi,eax
add esi,gMem
;IAT büyüklüğünü IAT ta yürüyerek bul
assume esi:ptr IMAGE_IMPORT_DESCRIPTOR
mov eax,sizeof IMAGE_IMPORT_DESCRIPTOR
.while !([esi].OriginalFirstThunk==0 && [esi].TimeDateStamp==0 && \
[esi].ForwarderChain==0 && [esi].Name1==0 && [esi].FirstThunk==0)
add eax,sizeof IMAGE_IMPORT_DESCRIPTOR
add esi,sizeof IMAGE_IMPORT_DESCRIPTOR
.endw
mov [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].isize,eax
add edi,sizeof IMAGE_NT_HEADERS
assume edi:ptr IMAGE_SECTION_HEADER
mov edx,NumSections
mov ecx,SecAlign
@fixheader:
;VS=RS VA=RA ve filealignmentı yapıyoruz
mov eax,[edi].Misc.VirtualSize
mov [edi].SizeOfRawData,eax
invoke AlignSection,eax,ecx
mov [edi].Misc.VirtualSize,eax
mov eax,[edi].VirtualAddress
mov [edi].PointerToRawData,eax
add edi,sizeof IMAGE_SECTION_HEADER
dec edx
jnz @fixheader
Bu sefer section alignment ı da yapmak zorundayız.Çünkü bir dosya memorye yüklendiğinde fiziksel yapısı değişir.Ayrıca PeDiminsher sectionları packledikten sonra align etmiyor bu yüzden paketlenen dosyalar bazen XP ve Win2K da çalışmıyor.İşte hepsi bu kadar.Umarım size faydalı olur.İkinci yöntem ile bir sürü packer a unpacker yazabilirsiniz.Çünkü sonuçta biz bir debugger gibi çalışıyoruz.Multiple breakpointlerde koyabilirsiniz.Bundan sonrası artık sizin kabiliyet ve hayal gücünüze kalmış.Bu yazıdan faydalanırsanız, bana feedbackte bulunmanız beni sevindirir.Boşa yazmadığımı bilmek istiyorum.Ayrıca size üzerinde çalışmanız için iki unpackerın kaynak kodunu da veriyorum.Hadi kolay gelsin.
|
|
Bu yazıda yazım yanlışları hatta bilgi hatası olabilir.Eğer bir yanlış bulursanız bana email atın düzeltmeye çalışırım.Bir programı eğer gerçekten kullanıyorsanız, programı satın almayı düşünün.Micro$oft programlarını - bırakın satın almayı - kullanmayı bile düşünmeyin.WinXP rulez J