11 Temmuz 2017 Salı

Elf x86 Binary Encryption

Giriş
~~~~~~~~~~~~~~~~~~~~~~

Bu yazıda ELF dosyalarının nasıl encryption yapılacağı üzerine konuşacağız. Yazmış olduğum bir encryption programını sizlere adım adım anlatacağım. Öncelikle ELF hakkında temel  bilgiler sunulacak ardından uygulamanın anlatımına geçilecektir. Burada XOR yöntemi kullanılarak encryption gerçekleştirilmiş olsa da mühim olan bu yöntemin sağlıklı bir şekilde ELF dosyasına uygulanabilmesidir. Bu yüzden olayın mantığını anladıktan sonra encryption metodolojisini değiştirmek  sizin keyfinize kalmıştır.




ELF Header
~~~~~~~~~~~~~~~~~~~~~~


Elf dosyasının ilk 52 baytlık alanı ELF başlığından oluşmaktadır. Elf çalıştırılabilir olan dosyalar için Elf başlığı olması  gereken zorunlu bir başlıktır.
Elf başlığının kendisine ilişkin veri yapısı elf.h başlığı içerisinde tanımlanmıştır. Elf başlığına ilişkin veri yapısı şu şekildedir.



e_entry, Elf dosyasının entypoint/başlangıc adresini belirtir. İşletim sistemi dosyayı belleğe yükledikten  sonra e_entry içerisinde tanımlanmış sanal adresden dosyayı çalıştırmaya başlatır.

e_phoff, Program başlık tablosunun dosya içerisindeki offset değerini belirtir.

e_shoff, Section başlık tablosunun dosya içerisindeki offset değerini belirtir.

e_ehsize, Elf başlık dosyasının boyutunu bayt cinsinden belirtir.

e_phentsize, program başlık tablosu segment kayıtlarından oluşmaktadır. Elf structının bu üyesi program başlık tablosunun bir kayıtının bayt cinsinden boyutunu belirtir. Her kayıt aynı boyuttadır.

e_phnum,  program başlık tablosunun kaç adet kayıta sahip olduğunu belirtir.

e_shentsize, section başlık tablosu section kayıtlarından oluşmaktadır. Elf başlığının bu üyesi section başlık tablosunun bir kayıtının bayt cinsinden boyutunu belirtir. Her kayıt aynı boyuttadır.

e_shnum, section başlık tablosunun kaç adet kayıta sahip olduğunu belirtir.

Section Header
~~~~~~~~~~~~~~~~~~~~~~
ELF dosyalarının tamamı direk olarak çalıştırılabilir dosyalar değildir. Örneğin, Elf dosyaların bazıları relocatable türünde olabilir ve  birden fazla relocatable dosya birleşerek yeni bir executable dosya oluşturabilir. Linker birden fazla relocatable dosyasını sağlıklı bir şekilde  birleştirmek için section başlık tablosuna göz atar ve istenilen elf türündeki dosyayı oluşturur.

Section başlık tablosu, ELF dosyasındaki sectionlar için tutulan bir tablodur. Bu tablonun dosya üzerinde nerede olduğunu bulabilmek için  ELF headerındaki e_shoff değerine bakılır.
Section başlık tablosundaki bazı  önemli sectionlar şunlardır.
Text section, Elf dosyasının  kodlarının tutulduğu kısımdır.
Data section, ilklendirilmiş global değişkenlerin tutulduğu kısımdır.
Bss section, ilk değer ataması gerçekleştirilmemiş değişkenlerin tutulduğu kısımdır.
Symtab section, fonksiyon ve değişken isimleri gibi bilgiler birer semboldür ve bu sembolere ilişkin bilgi bu bölümde tutulur.

Elf.h başlığı içerisinde bulunan section tablosundaki kayıtları ilişkin structın yapısı şu şekildedir.


sh_name, section isminin string tablosundaki index değeridir.
sh_type, section türünü belirtir. Progbits, symtab, dynsym, dynamic section türlerinden bazılarıdır.
Section türü progbits olan bir section içerisinde kod,data ve debugger bilgileri içerir.
Section türü symtab ve dynsym olan bir section sembol tablosunu içinde barındırır.
Section türü dynamic olan bir section dinamik linker için bir takım bilgiler içerir.
sh_addr, elf dosyası belleğe yüklenirken  bu sectionın yükleneceği başlangıc sanal adresini belirtir.
sh_offset, sectionın disk üzerindeki offset bilgisini belirtir.
sh_size, sectionın boyutunu bayt cinsinden ifade eder.
sh_addralign, sectionın belleğe haritalanırken hizalanma şeklini belirtir. bu section sh_addr sanal adresinden itibaren sh_addraling katı şeklinde belleğe hizalanır.
sh_entsize, sectionın kayıt uzunluğunu belirtir. Tüm sectionlar section başlık tablosu içerisinde aynı kayıt uzunluğundadırlar.

ELF Program Header
~~~~~~~~~~~~~~~~~~~~~~
Program başlık tablosu çalışma zamanında kesinlikle bulunması gereken tablodur. Section Tablosu, linker tarafından birden fazla elf dosyasını birbirine bağlarken  göz atılır. Fakat linux işletim sistemi bir dosyayı çalıştırmak için program başlık tablosuna ihtiyaç duyar ve bu tablonun olmaması durumunda linux işletim sistemi elf dosyasını çalıştıramaz.



Segmentler bir veya birden fazla sectionın birleşmesinden oluşur.
p_type, segmentin türünü belirtir. linux işletim sistemi dosyayı belleğe yüklerken segment türlerine bakarak bu işlemi yapar.
PT_LOAD, bu segmentin yüklenebilir bir segment olduğunu belirtir. Bu segment için tanımlı taban adres ve segmentin boyutu hesaplanarak bu segment belleğe yüklenir.
PT_DYNAMIC, bu segment dinamik linkleme aşamasında ihtiyaç duyulan bilgileri içerir.
PT_INTERP, bu segment dinamik linkerın tam yolunu string olarak belirtir.
PT_PHDR, bu segment program header tablosunun kendisini içerir.
p_offset, bu segmentin disk üzerindeki dosyanın neresinde bulunduğu belirten offset değerini içerir.
p_vaddr, bu segmentin  virtual memoryde nereden itibaren  yükleneceğini belirten sanal adresdir.
p_filesz, bu segmentin bayt cinsinden boyutunu ifade eder. Bu değer disk üzerindeki segmentin boyutunu ifade eder.
p_memsz, bu segmentin bellek boyutunu ifade eder. Eğer bu değer p_filesz değerinden büyükse bu durumda işletim sistemi bu segment için gerekli alanları 0 değerine setler.
p_flags, segmentin izinleri bu değer ile belirtirlir.
P_flags ile bir segmentin çalıştırılabilir, okunabilir , yazılabilir izinleri belirtilir.
p_align , segment belleğe yüklenirken  bu değere göre hizalanır. Eğer p_align 4 değerini içeriyorsa bu durumda bu segment taban adresinden başlanarak 4 bayt şeklinde taban adresinden itibaren belleğe yüklenmeye başlanılacaktır.

Statik Linklenmiş ELF Dosyalarının Belleğe Yüklenmesi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Statik olarak linklenmiş elf executable dosyalarının ihtiyaç duyduğu tüm kütüphaneler  statik linker tarafından kendisine linkleme aşamasında bağlanmıştır. Bu yüzden statik olarak linklenmiş dosyalar çalışma esnasında çağrılacak fonksiyonların herhangi biri için adres çözümlemelerine ihtiyaç duymadığından INTERP ve DYNAMIC segmentlerine ihtiyaç duymazlar.
işletim sistemi, statik olarak linklenmiş elf dosyasını belleğe yüklerken program başlık tablosundaki kayıtlardan p_type türü  PT_LOAD olan segmentleri belleğe yükler. PT_LOAD türü dışındaki diğer segmentlerin belleğe yüklenmez.
Segmentlerin belleğe yüklenmesi gerçekleştirildikten sonra ELF başlığında belirtilen giriş adresinden program çalıştırılmaya başlanır.

Dinamik Linklenmiş ELF Dosyalarının Belleğe Yüklenmesi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Dinamik olarak linklenmiş elf executable dosyalarında   ihtiyaç duyulan kütüphanelerin tamamı statik linklenmiş dosyalardaki gibi  linkleme aşamasında bağlanmaz. Fakat bunların kullanıldığı ve ihtiyaç duyulduğu bilgileri statik linker tarafından dinamik linklenmiş elf  executable dosyasının içerisinde belirtilmiştir.
Dinamik olarak linklenmiş elf executable dosyalar için program başlık tablosunda INTERP ve DYNAMIC adında iki segment bulunmaktadır.
işletim sistemi, program başlık tablosunu dolaşırken öncelikle PT_INTERP türündeki INTERP segmentini aramaktadır. Elf executable dosyasının dinamik olarak linklenmiş olması durumunda PT_INTERP türündeki INTERP segmenti bulunur.
Bu segment çalışma esnasında  ihtiyaç duyulan dinamik linkerın tam yolunu belirtmektedir ve dinamik linker işletim sistemi tarafından prosesin adres alanına yerleştirilir.
PT_LOAD türündeki LOAD segmentlerin ve dinamik linkerın prosesin adres alanına  yerleştirilmesinden sonra işletim sistemi kontrolü statik linklenmiş  programlardaki gibi direk programın kendisine bırakmak yerine dinamik linkera bırakır.
İşletim sistemi dinamik linkera kontrolü bırakmadan önce bir takım yardımcı bilgileri prosesin stack alanına iter. Bu yardımcı bilgiye auxiliary vektörü denilmektedir.
Dinamik linker program hakkında gerekli bilgileri  kernel tarafından stack üzerine itilmiş auxiliary vektöründen elde eder.





Auxiliary Vektörüne ilişkin bilgiler ;

AT_EXECFD, programın file descriptor numarasını belirtir.
AT_PHDR, belleğe yüklenen program için program başlık tablosunun adresini belirtir.
AT_PHTENT, program başlık tablosunun kayıtlarının uzunluğunu belirtir.
AT_PHNUM, program başlık tablosundaki kayıtların adetini belirtir.
AT_PAGESZ, işletim sisteminin kullandığı sayfa boyutunu belirtir.
AT_BASE, dinamik linkerın kendisinin yüklendiği taban adresi belirtir.
AT_ENTRY, programın giriş adresini belirtir.
AT_UID, programın gerçek user id numarasını belirtir.
AT_EUID, programın geçici user id numarasını belirtir.
AT_GID, programın gerçek group id numarasını belirtir.
AT_EGID, programın geçici group id numarasını belirtir.


Dinamik Linker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Dinamik linker, auxiliary vektörü aracılığı ile çalıştırılacak dinamik elf executable dosya hakkında bilgi sahibi olur. Statik linker dinamik linklenmiş elf executable için DYNAMIC segmenti içerisinde dosyanın ihtiyaç duyduğu kütüphaneleri belirtir.
DYNAMIC segmenti içerisindeki kayıtlardan NEEDED türüne ait olanlar kütüphaneleri belirtmektedir ve dinamik linker bu kütüphaneleri LD_LIBRARY_PATH çevre değişkenin belirttiği path listesi  içerisinde arar . Bu path listesi içerisinde  olmaması durumunda sırasıyla /lib, /usr/lib klasörleri içerisinde aramaktadır.
Dinamik linker, kütüphaneleri aradığı yerde bulması durumunda  programın  adres alanına yükler.

Position Independent Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Position independent code yöntemi ile kodlanmış bir uygulama belleğin istenilen  adres alanına yüklenir.

Program ön tanımlı bir adres  baz alınarak yazılmaz. Emirlerin kendi aralarındaki uzaklığının, kısacası göreceli uzaklık durumu göz önünde bulundurularak yazılır.
Bu kodlama biçiminde göreceli call emri gibi atlama emirleri kullanılarak programın yüklendiği adres elde edilir.



iki kodlama biçiminden non-position indepedent code olan, eğer işletim sistemi tarafından 0x0 adresinden itibaren yüklenmezse  bu durumda non-position independent code ile yazılmış program jmp emrini doğru bir şekilde gerçekleştiremeyecektir.
Bu durumda kodun akışı bozulacaktır veya atlanılan adres geçerli makine kodu içermediğinden illegal emir hatası alınacaktır.

Position independent code ile yazılmış program ise belleğin hangi alanına yüklenirse yüklensin düzgün bir şekilde çalışacaktır.

çünkü buradaki kodlamada  göreceli call emirinin çalışma biçiminden yararlanılmıştır. Göreceli call emirleri kendinden bir sonraki adrese göre göreceli atlama  gerçekleştirir.
göreceli call emrinin çalışma biçimi şu şekildedir :
  • bir sonraki adresi stack alanına it.
  • bir sonraki adres ile operand değerini topla.
  • toplama işlemi sonucu elde edilen adresi instruction pointerın içerisine  yerleştir.

Self-Modifying Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Self Modifying code, programın  kendi kendisini runtime/çalışma esnasında  değiştirebilen kodlama biçimidir. Bu kodlama biçimiyle  uygulama belleğe yüklendikten sonra dinamik olarak  kodlama yapılabilir.  Bu  kodlama biçimi performans açısından kötü sonuçlar doğuracağından kullanılmaz.
Bu kodlama biçiminde üzerinde değişiklik yapılacak segmentlerin Read,Write,Execute haklarına sahip olmalıdır.
Self-Modifying Code için örnek kodlama aşağıdaki gibidir. Aşağıdaki kod parçasının belleğin 0.adresinden itibaren yüklendiği varsayılmıştır.
MOV [0xB],EDX emiri ile kendisinden sonraki 4 baytlık alan NOP emri ile değiştirilmektedir. Bu durumda pop ebp, pop ecx, pop edx, ret emirleri MOV [0xB],EDX emrinin çalışmasından sonra NOP (0x90) emirlerine dönüşecektir.

Uygulama 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dosya üzerinde işlem yapmaya başlanılmadan önce bir takım bilgilerin saklanması ve bu bilgiler üzerinden hesaplama yapılması gerekmektedir.
Bu bilgilerin bazıları dosya üzerinde işlem yapılırken ihtiyaç duyulmaktadır  ve bazı bilgilere ise dosya belleğe yüklendikten sonra ihtiyaç duyulmaktadır.
Bu bilgileri tutmak için Info adında bir veri yapısı tanımlanmıştır.
struct info{
Elf32_Addr  old_entry;
Elf32_Off   offset;
Elf32_Word  endofsegment;
Elf32_Addr  rva;
Elf32_Addr  text_rva;
int         text_distance;
int         ep_distance;
int         size;
char        xor_key; 
};

old_entry, dosyanın orjinal giriş adresi burada tutulur.
offset, son LOAD segmentin başlangıç offset bilgisi burada tutulur.
endofsegment, son LOAD segmentin  son offset bilgisi burada tutulur.
rva, son LOAD segmentin taban virtual adresi burada tutulur.
text_rva , text sectionın taban virtual adresi burada tutulur.
text_distance, inject kodunun ilk baytının  adresi ile text sectionın taban virtual adresi arasındaki fark burada tutulur.
ep_distance, inject kodunun(ileride açıklanmıştır) ilk baytının adresi ile dosyanın orjinal giriş adresinin arasındaki fark burada tutulur.
size, text sectionın uzunluk bilgisi burada tutulur.
xor_key,şifrelemede kullanılan anahtar burada tutulur.



Uygulamada Öncelikle Elf dosyasının header bilgisi okunur.
void read_elf(Elf32_Ehdr *ELF,int fd){
read(fd,ELF,sizeof(Elf32_Ehdr));
}

EHT=(Elf32_Ehdr *)malloc(52);
read_elf(EHT,fd);

Daha sonra Program Header ve Section Header tabloları doldurulur.

Elf32_Phdr *PHT[EHT->e_phnum];
Elf32_Shdr *SHT[EHT->e_shnum];

while(EHT->e_phnum > i++){
PHT[i]=(Elf32_Phdr *)malloc(EHT->e_phentsize);
}

i=0;

while(EHT->e_shnum > i++){
SHT[i]=(Elf32_Shdr *)malloc(EHT->e_shentsize);
}

read_pht_eht(EHT,PHT,SHT,fd);

Bu 3 header bilgisi kullanılarak ile disk üzerinde text section xorlanır. 

int xor_text(int fd,Elf32_Shdr *SHT[],Elf32_Ehdr *EHT){
.
.
while(EHT->e_shnum > i++){
  if(EHT->e_entry >= SHT[i]->sh_addr && EHT->e_entry < SHT[i]->sh_addr + SHT[i]->sh_size ){
  index=i; break;
}
inf->old_entry=EHT->e_entry;
inf->text_rva=SHT[index]->sh_addr;
inf->size=SHT[index]->sh_size;

yukarıdaki kod ile text sectionın Section Header Tablosu içerisinde nerede olduğu bulunur. Info structının ilgili üyeleri ilgili değerlerle setlenir.


lseek(fd,SHT[index]->sh_offset,SEEK_SET);
char *text_section=(char *)malloc(SHT[index]->sh_size);

read(fd,text_section,SHT[index]->sh_size);
i=0;
while(i++<SHT[index]->sh_size){
*(text_section+i)=*(text_section+i) ^ 0x12;
}

inf->xor_key=0x12;
lseek(fd,SHT[index]->sh_offset,SEEK_SET);
write(fd,text_section,SHT[index]->sh_size);

yukarıdaki kod parçası text sectionını 0x12 hexadecimal değeri ile xorlar.İstenilirse dinamik olarak xor key için bir değişken tanımlanarak dışarıdan key girdisi alınabilir.


void reconstruct_segments(int fd,int index,Elf32_Ehdr *EHT,Elf32_Phdr *PHT[],Elf32_Shdr *SHT[])
{
.
.

while(EHT->e_phnum > i){
if(PHT[i]->p_type==PT_LOAD && (PHT[i]->p_vaddr < SHT[index]-> sh_addr && PHT[i]->p_vaddr+PHT[i]->p_filesz > SHT[index]->sh_addr)){
PHT[i]->p_flags=PF_X + PF_R + PF_W;
i++;
continue;
}
if(PHT[i]->p_type==PT_LOAD)
index2last=i;

i++;

}

Yukarıdaki kod parçası text sectionın hangi segment içerisinde olduğunu bulduktan sonra o segmentin izinlerini yazılabilir, okunabilir,çalıştırılabilir olarak setlemektedir. Bunun nedeni Çalışma zamanında xorlanmış text sectionının tekrardan düzeltilmesi gerekecektir.

Ardından son LOAD segmentinin index numarası index2last değişkenine atılmaktadır.

inf->offset = PHT[index2last]->p_offset;
inf->endofsegment = PHT[index2last]->p_offset+PHT[index2last]->p_filesz;
inf->rva=PHT[index2last]->p_vaddr;
son Load segmentine ilişkin bilgiler info structı içerisine yerleştirilmektedir.

PHT[index2last]->p_flags=PF_X + PF_R + PF_W;
PHT[index2last]->p_filesz+=500;
PHT[index2last]->p_memsz+=500;


Son LOAD segmentinin disk üzerindeki dosya boyutunu ve memorydeki dosya boyutunu arttırıyoruz ve ayrıca segmentin izinlerini  çalıştırılabilir, okunabilir , yazılabilir olarak setliyoruz.

Son LOAD segmentinin sonuna inject kod adını verdiğimiz bir kodu yerleştireceğiz. Bu kod text sectionı tekrar xorlayarak düzeltecektir.


Elf32_Addr inject_runtimecode(int fd,char *code){
int current_offset = inf->endofsegment + sizeof(struct info)+10;
int current_rvaddr = inf->rva +(current_offset-inf->offset);  
current_offset ile inject kodunun ilk baytının disk üzerindeki offset bilgisini tutuyoruz.
current_rvaddr ile inject kodunun ilk baytının relative virtual addres değerini tutuyoruz. 
inf->ep_distance=current_rvaddr-inf->old_entry+0x32; 
inf->text_distance=current_rvaddr-inf->text_rva+0x32;


text sectionı düzelttikten sonra  eski entrypoint adresine jump emri ile sıçrama yapacağımız için ep_distance ,text_distance ile inject kod ile text section adresi arasındaki uzaklık hesaplanmaktadır.


lseek(fd,current_offset,SEEK_SET);
write(fd,inf,sizeof(struct info));
lseek(fd,current_offset+sizeof(struct info)+5,SEEK_SET);
write(fd,code,70);
return current_rvaddr+sizeof(struct info)+5;


yukarıdaki kod parçası ile info structı ve inject kod son LOAD segmentine yerleştirilmiştir.
inject_runtimecode dönüş olarak bize yeni entrypoint adresini döndermektedir.

Elf32_Addr new_ep=inject_runtimecode(fd,inject_code);
EHT->e_entry=new_ep;

lseek(fd,0,SEEK_SET);
write(fd,EHT,sizeof(Elf32_Ehdr));


Son olarak elf header içerisinde entrypoint adresini değiştirerek , dosya belleğe yüklendikten sonra inject kodundan itibaren başlamasını sağlıyoruz.



Inject Code Design
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Son olarak Inject edilen koddan bahsederek yazıyı bitirebiliriz.

char *inject_code="\x60\x90\x90\x90\xe8\x2c\x00\x00\x00\x8b\x4d\xea\x89\xeb"
"\x2b\x5d\xe2\x8a\x13\x32\x55\xee\x88\x13\x49\x43\x83\xf9\x00\x0f"
"\x85\xee\xff\xff\xff\x8b\x4d\xe6\x83\xc1\x2c\xf7\xd9\x89\x4d\x28"
"\x61\x90\xe9\xef\xbe\xad\xde\x8b\x2c\x24\xc3";


yukarıdaki kod inject kodumuzun opcode halidir. Aşağıda bu kodun assembly hali ve memory düzeni görülmektedir.

/*

___INJECT__CODE__ :

0x0:old_entry  
0x4 :offset  
0x8 :endofsegment 
0xc :rva          
0x10:text_rva 
0x14:text_distance
0x18:ep_distance
0x1c:size
0x20:xor_key
0x21:JUNK_BYTE
0x22:JUNK_BYTE
0x23:JUNK_BYTE
0x24:JUNK_BYTE
0x25:JUNK_BYTE    
0x26:pusha
0x27:nop
0x28:nop
0x29:nop
0x2a:call 0x2c
0x2f:mov ecx,DWORD PTR[ebp-0x16]
0x32:mov ebx,ebp
0x34:sub ebx,DWORD PTR[ebp-0x1e]
0x37:mov dl,BYTE PTR[ebx]
0x39:xor    dl,BYTE PTR [ebp-0x12]
0x3c:mov    BYTE PTR [ebx],dl 
0x3d:dec ecx
0x3e:inc ebx
0x3f:cmp cx,0x0
0x42:jnz -0x12
0x48:mov    ecx,DWORD PTR [ebp-0x1a]
0x4b:add    ecx,0x2c
0x4f:neg ecx
0x51:mov DWORD PTR [ebp+0x28],ecx
0x54:popa
0x55:nop
0x56:jmp 0xdeadbeef
0x5b:mov ebp,[esp]
0x5e:ret
0x5f:nop

________________________________________________________________________

*/

Inject kodunun 0.bayttan itibaren yüklendiğini varsayılmıştır. Bu durumda kodumuz 0x26.bayttan itibaren çalışmaya başlayacaktır.

Pusha emri ile registerların kendisi stacke itilmektedir. ELF dosyasının Startup aşamasında bazı registerlar kullanıldığı için registerların korunması gerekmektedir. Bu yüzden öncelikle tüm register içeriklerini stacke itmemiz gerekmektedir.

Yukarıda keyfi olarak bazı yerler JUNK_BYTE bazı yerlerde NOP emri ile doldurulmuştur. Bu emirleri değiştirmeniz durumunda inject kod ile text sectionı arasındaki uzaklık hesabını yeniden gözden geçirmeniz gerekecektir.

Call 0x2c emri ile bir sonraki adresin(0x2f adresi) adresi stacke itildikten sonra  0x5b adresine atlanacaktır. Burada mov ebp,[esp] ile 0x2f emrinin bellekteki adresi elde edilecektir. Bunu yapmamızın nedeni daha önce bahsettiğimiz gibi Position Independent Code yöntemini kullanmamız.
0x5e adresindeki ret instructionı ile tekrardan 0x2f adresine dönmekteyiz. Burada text sectionın size
bilgisi 0x2f adresinden -0x16 bayt gerideki info structında tutulduğundan dolayı 0x2f(ebp
içerisindeki adres 0x2f  memorydeki adres değeri) değerinden 0x16 hexadecimal değeri çıkarılmıştır.

0x32:mov ebx,ebp
0x34:sub ebx,DWORD PTR[ebp-0x1e]

Bu emirler ile text sectionın başlangıc adresini hesaplıyoruz


0x37:mov dl,BYTE PTR[ebx]   // text section içerisideki kodu al 

0x39:xor    dl,BYTE PTR [ebp-0x12]   // text section ile ebp-0x12 adresindeki değeri xorla. Ebp-0x12 xor_key değerinin tutulduğu adrestir.

0x3c:mov    BYTE PTR [ebx],dl // xorlanan değeri ilgili adrese tekrar yaz. 
0x3d:dec ecx         // size değerini bir azalt
0x3e:inc ebx         // adres değerini bir arttır
0x3f:cmp cx,0x0   // text sectionın tamamı xorlandımı ?
0x42:jnz -0x12     // hayırsa 0x37 adresine atlama gerçekleştir.

Yukarıdaki kod parçası bir döngü içerisinde text sectionını xorlamaktadır. Aynı xor keyi ile xorlama decryption işlemine karşılık gelmektedir. Neden tekrar xorladığımızı anlamadıysanız  lütfen xor doğruluk tablosuna göz atınız.

0x48:mov    ecx,DWORD PTR [ebp-0x1a]
0x4b:add    ecx,0x2c
0x4f:neg ecx
0x51:mov DWORD PTR [ebp+0x28],ecx
0x54:popa
0x55:nop
0x56:jmp 0xdeadbeef


Yukarıda ecx registerına ep_distance atanmaktadır ardından bu değer 0x2c değeri ile toplanmaktadır. Bunun nedeni uzaklık hesaplaması yaparken entrypoint adresi ile mov ecx,[ebp-0x16] emrinin arasındaki uzaklığı hesaplamamızdır. Eğer jmp 0xdeadbeef emri ile entry point adresi arasında uzaklık hesabı yapmış olsaydık buna gerek kalmazdı.

Ayrıca neg ecx ile hesapladığımız değerin tümleyenini alarak negatif bir değere çeviriyoruz çünkü kullandığımız jmp emri operand değeri ile bir sonraki adresi toplayarak göreceli olarak  bir atlama gerçekleştirecektir.

0x51:mov DWORD PTR [ebp+0x28],ecx

Bu emir çalıştığı zaman  entrypoint adresi ile jump adresi arasındaki uzaklık jmp emrinin operand değerine yazılacaktır.

Popa emiri ile , Pusha emri ile tüm stacke ittiğimiz register değerlerini tekrardan registerlara yüklüyoruz. Burada tabi stacki korumamız gerektiğine dikkat ediniz.

son olarak 0x56 adresindeki jmp emri icra edilerek orijinal/eski entrypoint adresine sıçrama gerçekleşecektir.


Kodun tamamına ulaşmak için şuraya tıklayabilirsiniz.


umarım faydalı olmuştur.

EOF

Hiç yorum yok:

Yorum Gönder