欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

shellcode分段执行

程序员文章站 2022-07-15 14:21:30
...

最后,写完了,开始吃饭,哈哈。自知能力不住,哈哈,所以请您担待着看吧,如果有哪里表达的不是很清楚,欢迎留言一起探讨哦!

0、综述

我是将一个弹出messagebox的代码为例子,制成shellcode。
然后再分成三段分别根据不同的xor进行加密,然后在边解密边执行。

如下图,整个encode总的分为两部分,第一部分是decode解密子,第二部分是shellcode,但是由于执行的时候需要解密,因此需要在每一段加密后的shellcode代码前面加上 jmp 进行地址跳转,在末尾加上 0x90 是作为加密时的结束符号。

首先,将encode的首地址赋值给eax,这样每次跳转语句固定为: jmp eax;
利用ecx来跟踪加密代码的执行轨迹,edi 作为每次解密时的偏移量。可见【ecx+edi】控制解密时的内存单元位置。

PS:一定要注意decode中的堆栈平衡,否则容易影响到decode的执行。
shellcode分段执行

1、代码示例

弹出messagebox,这个代码直接执行将会报错,这个是考虑到了执行完shellcode需要返回主程序,因为我在末尾加了 ret 指令,如果直接运行的话,ret跳转到不知名的地方,因而报错。PS:在这里,我用edx保存函数的eip,ret返回时将会跳转到 edx 指定的值,

// test0304_xor.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "string.h"
#include "stdlib.h"
#include "windows.h"

char key1 = 0x51; 
char key2 = 0x63;
char key3 = 0x78;

int main(int argc, char* argv[])
{
	printf("Hello World!\n");
	LoadLibraryA("user32.dll"); 
	HMODULE hmd=GetModuleHandleW(L"user32.dll"); 
	ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA"); 

	char* a = "test shellcode";
	char* b = "test";

	_asm
	{
	add edx, 2
	push edx  //edx入栈的值是ret返回程序后将要进行的地址,即return 0的地址。
	push 0
	push a
	push b
	push 0
	mov eax, addr
	call eax
	ret

	}
	return 0;
}

shellcdoe执行版:
根据上面的程序,我们知道,在执行时我们应该给定一个edx(即将return 0的地址赋值给edx),便于messagebox程序中的ret跳转。
我们用如下语句,获取当前的eip,然后在messagebox中利用add edx, 2,使得edx的值为 return 0的地址。

		call next
next:	pop edx 

shellcode执行:

int main(int argc, char* argv[])
{
	printf("Hello World!\n");
	LoadLibraryA("user32.dll"); 
	HMODULE hmd=GetModuleHandleW(L"user32.dll"); 
	ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA"); 

	char* a = "test shellcode";
	char* b = "test";

	char shellcode[] = "\x83\xC2\x02\x52\x6A\x00\xFF\x75\xF4\xFF\x75\xF0\x6A\x00\x8B\x45\xF8\xFF\xD0\xC3";

	_asm
	{
		lea eax, shellcode
		push eax
		call next
next:	pop edx  //获取当前eip
		ret
	}
	return 0;
}

2、shellcode加密

如分别改变key的值,进行加密。

	int length = sizeof(shellcode)/sizeof(shellcode[0]);
	for(int i=0;i<length-1;i++)
	{
		encode[i] = shellcode[i]^key3;
	}

将加密后的shellcode分成三段。,如图,在每一个加密后的代码中分别取一段
shellcode分段执行

3、构造代码段

shellcode分段执行
如上图,代码段组成为:

跳转语句 + 加密的shellcode + 加密的\x90(解密判断符)

(1)、跳转语句
在每个encode前面加上跳转语句:

解密子跳转:
31:           mov bh, 0x51//0x51是key1的值,不同的代码段,是不一样的。
0040D809 B7 51                mov         bh,51h
32:           jmp eax
0040D80B FF E0                jmp         eax

(2)、\x90末尾截止
在每个encode后面加上 \x90(解密后的) 作为终止符号。

如下,为构造好的代码段。

	char encode_1="\xB7\x51\xFF\xE0\xD2\x93\x53\x03\x3B\x51\xC1";
	char encode_2="\xB7\x63\xFF\xE0\x9C\x16\x97\x9C\x16\x93\x09\x63\xF3";
	char encode_3="\xB7\x78\xFF\xE0\xF3\x3D\x80\x87\xA8\xBB\xE8";

4、生成解密子

shellcode分段执行

如上图,我们是由jmp跳转带解码子部分(即绿色区域),,因此解密时需要跳过jmp部分(我的jmp跳转语句没有加密,因此解密时需要跳过,如果加密了的话,就不需要跳过了)。
jmp语句长度为4,一昵称我们将 ecx+4 后在进行解密。此时ecx为encode1的首地址.

		add ecx, 4  //跳过jmp语句
		push ecx	//ecx入栈,让ret跳转到encode1处开始执行

当我们cmp判断解密后的元素,等于0x90时,利用add将ecx与edi相加,此时ecx便是下一段encode2的首地址了。
ret跳转到之前压入的ecx指向的地址,即上一段encode1的首地址,即接下来开始执行encode1。

execute:add ecx,edi  //下一段encode2的首地址
		ret //ret跳转到之前压入的ecx指向的地址,即跳转到encode1处开始执行

解密子代码:

		add ecx, 4  //跳过jmp语句
		push ecx	//ecx入栈,让ret跳转到encode1处开始执行
		xor edi, edi //edi作为偏移量
decode:	mov bl, [ecx+edi]
		xor bl, bh	//xor解密,bh是**
		mov [ecx+edi], bl
		inc edi
		cmp bl,0x90  //判断代码段是否全部解密完成
		je execute
		jmp decode

execute:add ecx,edi  //下一段encode2的首地址
		ret //ret跳转到之前压入的ecx指向的地址,即跳转到encode1处开始执行

转变成16机制为:(末尾加上了\x90便于调试,长度为26)
char encode[] = “\x83\xC1\x04\x51\x33\xFF\x8A\x1C\x39\x32\xDF\x88\x1C\x39\x47\x80\xFB\x90\x74\x02\xEB\xF0\x03\xCF\xC3\x90”;

5、encode拼接

相对来说这个就很简单,直接将上面的解码子decode和构造好的encode(123)进行拼接即可。
解码子 + encode1 + encode2 + encode3

	char encode[] = "\x83\xC1\x04\x51\x33\xFF\x8A\x1C\x39\x32\xDF\x88\x1C\x39\x47\x80\xFB\x90\x74\x02\xEB\xF0\x03\xCF\xC3\x90" //解码子
	"\xB7\x51\xFF\xE0\xD2\x93\x53\x03\x3B\x51\xC1" //encode1
	"\xB7\x63\xFF\xE0\x9C\x16\x97\x9C\x16\x93\x09\x63\xF3" //encode2
	"\xB7\x78\xFF\xE0\xF3\x3D\x80\x87\xA8\xBB\xE8"; //encode3
	

6、encode的调用

	_asm
	{
		lea eax, encode  //eax保存encode的首地址,即解码子的地址
		mov ecx, eax  //ecx控制代码执行的位置
		add ecx, 0x1A  //首先跳转到第一段encode处,执行jmp语句,然后解密
		push ecx
		call next
next:	pop edx
		ret
	}

7、附上代码啦:

// test0304_xor.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "string.h"
#include "stdlib.h"
#include "windows.h"

char key1 = 0x51; 
char key2 = 0x63;
char key3 = 0x78;

int main(int argc, char* argv[])
{
	printf("Hello World!\n");
	LoadLibraryA("user32.dll"); 
	HMODULE hmd=GetModuleHandleW(L"user32.dll"); 
	ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA"); 

	char* a = "test shellcode";
	char* b = "test";
	
	
	char encode[] = "\x83\xC1\x04\x51\x33\xFF\x8A\x1C\x39\x32\xDF\x88\x1C\x39\x47\x80\xFB\x90\x74\x02\xEB\xF0\x03\xCF\xC3\x90\xB7\x51\xFF\xE0\xD2\x93\x53\x03\x3B\x51\xC1\xB7\x63\xFF\xE0\x9C\x16\x97\x9C\x16\x93\x09\x63\xF3\xB7\x78\xFF\xE0\xF3\x3D\x80\x87\xA8\xBB\xE8";
	/*生成的shellcode*/
	//char shellcode[] = "\x83\xC2\x02\x52\x6A\x00\xFF\x75\xF4\xFF\x75\xF0\x6A\x00\x8B\x45\xF8\xFF\xD0\xC3";
	
	/*构造好的加密代码段*/
	//char encode_1="\xB7\x51\xFF\xE0\xD2\x93\x53\x03\x3B\x51\xC1";
	//char encode_2="\xB7\x63\xFF\xE0\x9C\x16\x97\x9C\x16\x93\x09\x63\xF3";
	//char encode_3="\xB7\x78\xFF\xE0\xF3\x3D\x80\x87\xA8\xBB\xE8";
	
	/*解码子:*/
	//char encode[] = "\x83\xC1\x04\x51\x33\xFF\x8A\x1C\x39\x32\xDF\x88\x1C\x39\x47\x80\xFB\x90\x74\x02\xEB\xF0\x03\xCF\xC3\x90";
	
	/*开始执行:*/
	_asm
	{
		lea eax, encode
		mov ecx, eax
		add ecx, 0x1A
		push ecx
		call next
next:	pop edx
		ret
	}
	return 0;
}




相关标签: # 汇编