一个简单的ShellCode执行器&代码(just for fun)

本文探讨如何在程序运行时读入并执行代码,基于冯·诺依曼结构的原理,通过动态链接库和脚本语言实现。文章提供了一个简单的C/C++ Demo,展示如何在运行时读入汇编代码并执行,强调了编写Shellcode的关键点,包括全局变量布局、字符字面常量处理和外部函数定位,并给出了几个示例Shellcode。
摘要由CSDN通过智能技术生成

好久没有写BLOG啦.

最近看到有个问题:如何做到在程序运行时读入并执行一段代码
这是很容易做到的,能够运行是因为冯·诺依曼结构中程序和数据并没有本质区别.

其实完全没有必要读入代码运行,动态链接库完全可以胜任这方面的需求

在运行时读入代码并执行其实是完全可行的,脚本语言更容易做到,当然了C/C++也可以,写个解释器吧

考虑最简单的情况,何为代码?机械码算不算代码呢?当然算啦

我们先观察一下代码是怎么样在机器(CPU)上运行的,观察内存和反汇编:

int add(int a, int b){
	return a + b;
}

反汇编如下:

add:
55                   push        ebp  
8B EC                mov         ebp,esp  
	return a + b;
8B 45 08             mov         eax,dword ptr [a]  
03 45 0C             add         eax,dword ptr [b]  
}
5D                   pop         ebp  
C3                   ret  

调用函数add:

8B 55 F8             mov         edx,dword ptr [b]  
52                   push        edx  
8B 45 F4             mov         eax,dword ptr [a]  
50                   push        eax  
E8 BE FF FF FF       call        add (0A11020h)  

那么实际上在CPU上运行的就是左边的机器码,这代码被称为Shellcode:

0x55 0x8B 0xEC 0x8B 0x45 0x08 0x03 0x45 0x0C 0x5D 0xC3

那么很明显,我们可以在运行时读入这串代码然后用一个函数指针指向该地址便可执行


写了个小Demo,在运行时提供了与系统交互的API

读入代码原型:

void __stdcall prime_number(void *funcAddrList);
ShellCode执行器:

#include <stdio.h>
#include <memory>
#include <windows.h>

#define BUFSIZE 1<<12

struct BUFFER{
	byte *buf;
	int len;
	BUFFER(){
		len = BUFSIZE;
		buf = (byte*)malloc(len);
	}
	~BUFFER(){
		free(buf);
	}
};

typedef int(__stdcall *FUNC)(void*);
typedef std::shared_ptr<BUFFER> Buffer;

void eval(Buffer Addr,void* Prmt){
	DWORD OldProtect;
	VirtualProtectEx(GetCurrentProcess(),\
		Addr->buf, Addr->len,\
		PAGE_EXECUTE_READWRITE,\
		&OldProtect);
	FUNC func = (FUNC)Addr->buf;
	func(Prmt);
	VirtualProtectEx(GetCurrentProcess(),\
		Addr->buf,Addr->len,\
		OldProtect,\
		NULL);
}

int main(){
	Buffer code(new BUFFER);
	int i = 0;
	printf("Code Platform V1.0.2\n");
	while (i < BUFSIZE){
		scanf_s("%x", &code->buf[i]);
		if ((i>1)&&(code->buf[i] == 0x90 && 
			code->buf[i - 1] == 0xCC && 
			code->buf[i - 2] == 0x90)) 
			break;
		i++;
	}  //end with 0x90 0xCC 0x90
	printf("Running...\n");

	void* Prmt[] = { scanf_s, printf_s };
	eval(code, Prmt);
	return 0;
}

运行时输入代码并直接运行,存放代码的内存必须为可读和可执行的,使用VirtualProtectEx修改内存标志为可读可运行

编写Shellcode一般使用汇编和C编写,不能直接反汇编,有几个关键点:全局变量的内存布局,字符字面常量的处理,外部函数的定位.其他的和编写正常的汇编和C代码差不多,仍需要人工编写汇编代码.具体编写方式可以Google,看雪论坛上也有不少这方面的资料,特别是外部函数的定位,如果是一段溢出攻击的Shellcode那么要想有更高的权限必须定位关键的API,而这时并不像Demo里直接提供地址,这方面需要更高级的技术.

好,我编写了几段运行在该Demo上的代码,要想修改成其他的Shellcode也非常容易.

最简单的Hello World!:

0x55 0x8b 0xec 0x6a 0x0a 0x68 0x72 0x6c 0x64 0x21 0x68 0x6f 0x20 0x57 0x6f 0x68 
0x48 0x65 0x6c 0x6c 0x54 0x8b 0x5d 0x08 0x8b 0x43 0x04 0xff 0xd0 0x83 0xc4 0x14 
0x8b 0xe5 0x5d 0xc2 0x04 0x00 0x90 0xcc 0x90 
a+b:

输入两个数a,b输出a+b

0x55 0x8b 0xec 0x33 0xc0 0x83 0xec 0x08 0x6a 0x00 0x68 0x25 0x64 0x25 0x64 0x8d 
0x44 0x24 0x0c 0x50 0x83 0xe8 0x04 0x50 0x83 0xe8 0x08 0x50 0x8b 0x5d 0x08 0x8b 
0x03 0xff 0xd0 0x83 0xc4 0x1c 0x8b 0x44 0x24 0xf8 0x03 0x44 0x24 0xfc 0x68 0x25 
0x64 0x0a 0x00 0x50 0x8d 0x44 0x24 0x04 0x50 0x8b 0x5d 0x08 0x8b 0x43 0x04 0xff 
0xd0 0x83 0xc4 0x0c 0x8b 0xe5 0x5d 0xc2 0x04 0x00 0x90 0xcc 0x90 

输入整数n

输出:比n小的所有质数

0x8b 0x44 0x24 0x04 0x8b 0x58 0x04 0x53 0x8b 0x18 0x53 0x8b 0xcc 0xe8 0x06 0x00 
0x00 0x00 0x83 0xc4 0x08 0xc2 0x04 0x00 0x55 0x8b 0xec 0x83 0xec 0x24 0x53 0x56 
0x57 0x8b 0x79 0x04 0xc7 0x45 0xf8 0x00 0x00 0x00 0x00 0x6a 0x0a 0x89 0x65 0xdc 
0x68 0x25 0x64 0x20 0x00 0x89 0x65 0xe0 0x68 0x25 0x64 0x00 0x00 0x89 0x65 0xf0 
0x8d 0x45 0xfc 0x50 0xff 0x75 0xf0 0x8b 0x01 0xff 0xd0 0x8b 0x4d
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值