Linux脚本编译成机器码,编写shellcode的全过程

1、很简单的一段代码,功能就是打开Windows自带的计算器程序。而我要实现的shellcode的功能就是这个。

#include 

int main()

{

LoadLibraryA("kernel32.dll");

WinExec("calc.exe", SW_SHOW);

return 0;

}

2、将WinExec("calc.exe", SW_SHOW);改成汇编后的样子。

#include 

int main()

{

char str[]="calc.exe";

char* p = str;

LoadLibraryA("kernel32.dll");

__asm

{

push 0;

mov eax,p;

push eax;

mov eax,0x7c86250d;  //WinExec的地址

call eax;

}

return 0;

}

3、在堆栈中构造字符串,经典!这招是我在书上学来的,并非本人原创 -_-。

/*

*  Author: Leng_que

*  Date: 2009年10月12日11:02:40

*  E-mail: leng_que@yahoo.com.cn

*  Description: 一段通过WinExec运行calc.exe的shellcode雏形I

*  Comment: 在WindowsXP SP3 + VC6.0环境下调试通过

*/

#include 

int main()

{

//因为WinExec这个API函数在kernel32.dll这个动态链接库里,所以要先加载它

LoadLibraryA("kernel32.dll");

__asm

{

push ebp;

mov ebp,esp;

//在堆栈中构造字符串:calc.exe

xor eax,eax;

push eax;

sub esp,08h;

mov byte ptr [ebp-0ch],63h;

mov byte ptr [ebp-0bh],61h;

mov byte ptr [ebp-0ah],6Ch;

mov byte ptr [ebp-09h],63h;

mov byte ptr [ebp-08h],2Eh;

mov byte ptr [ebp-07h],65h;

mov byte ptr [ebp-06h],78h;

mov byte ptr [ebp-05h],65h;

//执行WinExec,启动计算器程序

push 0;

lea eax,[ebp-0ch];

push eax;

mov eax,0x7c86250d;  //WinExec的地址

call eax;

//平衡堆栈

mov esp,ebp;

pop ebp;

}

return 0;

}

4、具有shellcode特点的汇编代码完成了!

值得一提:在这一步我这么也得不到LoadLibrary(在原来的代码中我是写这个的)的地址,于是只好先去吃饭,在来回饭堂的校道上,我突然想到了,原来自己一时忘记,于是犯了一个低级错误,其中在Kernel32.dll里是没有LoadLibrary这个函数的,只有LoadLibraryA和LoadLibraryW,它们的区别在于传入的参数是ANSI编码的还是Unicode编码的,但是为什么我们在平时的编程中写LoadLibrary又可以正常运行呢?那是因为其实这个LoadLibrary只是一个宏而已啦,在VC6.0下选中这个LoadLibrary函数(当然,应该叫宏更准确些),然后右键->"Go To Definition"一下就知道了。

/*

*  Author: Leng_que

*  Date: 2009年10月12日15:15:32

*  E-mail: leng_que@yahoo.com.cn

*  Description: 一段通过WinExec运行calc.exe的完整shellcode雏形

*  Comment: 在WindowsXP SP3 + VC6.0环境下调试通过

*/

#include 

int main()

{

//因为WinExec这个API函数在kernel32.dll这个动态链接库里,所以要先加载它

__asm

{

push ebp;

mov ebp,esp;

//在堆栈中构造字符串:kernel32.dll

xor eax,eax;

push eax;

sub esp,0Ch;

mov byte ptr [ebp-10h],6Bh;

mov byte ptr [ebp-0Fh],65h;

mov byte ptr [ebp-0Eh],72h;

mov byte ptr [ebp-0Dh],6Eh;

mov byte ptr [ebp-0Ch],65h;

mov byte ptr [ebp-0Bh],6Ch;

mov byte ptr [ebp-0Ah],33h;

mov byte ptr [ebp-09h],32h;

mov byte ptr [ebp-08h],2Eh;

mov byte ptr [ebp-07h],64h;

mov byte ptr [ebp-06h],6Ch;

mov byte ptr [ebp-05h],6Ch;

lea eax,[ebp-10h];

push eax;

mov eax,0x7c801d7b;  //LoadLibraryA的地址

call eax;  //相当于执行LoadLibraryA("kernel32.dll");

//平衡堆栈

mov esp,ebp;

//在堆栈中构造字符串:calc.exe

xor eax,eax;

push eax;

sub esp,08h;

mov byte ptr [ebp-0Ch],63h;

mov byte ptr [ebp-0Bh],61h;

mov byte ptr [ebp-0Ah],6Ch;

mov byte ptr [ebp-09h],63h;

mov byte ptr [ebp-08h],2Eh;

mov byte ptr [ebp-07h],65h;

mov byte ptr [ebp-06h],78h;

mov byte ptr [ebp-05h],65h;

push 05h;

lea eax,[ebp-0Ch];

push eax;

mov eax,0x7c86250d;  //WinExec的地址

call eax;  //相当于执行WinExec("calc.exe", SW_SHOW);

//平衡堆栈

mov esp,ebp;

pop ebp;

}

return 0;

}

5、然后就可以在VC6.0下用Debug功能提取出机器码:

55 8B EC 33 C0 50 83 EC 0C C6

45 F0 6B C6 45 F1 65 C6 45 F2

72 C6 45 F3 6E C6 45 F4 65 C6

45 F5 6C C6 45 F6 33 C6 45 F7

32 C6 45 F8 2E C6 45 F9 64 C6

45 FA 6C C6 45 FB 6C 8D 45 F0

50 B8 7B 1D 80 7C FF D0 8B E5

33 C0 50 83 EC 08 C6 45 F4 63

C6 45 F5 61 C6 45 F6 6C C6 45

F7 63 C6 45 F8 2E C6 45 F9 65

C6 45 FA 78 C6 45 FB 65 6A 05

8D 45 F4 50 B8 0D 25 86 7C FF

D0 8B E5 5D

6、接着实际执行一下这段shellcode,看看效果,你会发现真的打开了Windows计算器程序。

//作者:冷却

//在WindowsXP SP3 + VC6.0环境下运行成功

unsigned char shellcode[] =

"/x55/x8B/xEC/x33/xC0/x50/x83/xEC/x0C/xC6/x45/xF0/x6B/xC6/x45/xF1/x65/xC6/x45"

"/xF2/x72/xC6/x45/xF3/x6E/xC6/x45/xF4/x65/xC6/x45/xF5/x6C/xC6/x45/xF6/x33/xC6"

"/x45/xF7/x32/xC6/x45/xF8/x2E/xC6/x45/xF9/x64/xC6/x45/xFA/x6C/xC6/x45/xFB/x6C"

"/x8D/x45/xF0/x50/xB8/x7B/x1D/x80/x7C/xFF/xD0/x8B/xE5/x33/xC0/x50/x83/xEC/x08"

"/xC6/x45/xF4/x63/xC6/x45/xF5/x61/xC6/x45/xF6/x6C/xC6/x45/xF7/x63/xC6/x45/xF8"

"/x2E/xC6/x45/xF9/x65/xC6/x45/xFA/x78/xC6/x45/xFB/x65/x6A/x05/x8D/x45/xF4/x50"

"/xB8/x0D/x25/x86/x7C/xFF/xD0/x8B/xE5/x5D";

void main()

{

__asm

{

lea eax,shellcode;

jmp eax;

}

}

7、最后,我写了个小程序,把那些十六进制的shellcode转换为纯二进制的机器码。大家看!这就是CPU老兄眼中的代码了。感觉很神奇吧,0和1在计算机中真的很有魔力。

01010101100010111110110000110011110000000101000010000011111011000000110011000110010001011111000001101011110001100100010111110001011001011100011001000101111100100111001011000110010001011111001101101110110001100100010111110100011001011100011001000101111101010110110011000110010001011111011000110011110001100100010111110111001100101100011001000101111110000010111011000110010001011111100101100100110001100100010111111010011011001100011001000101111110110110110010001101010001011111000001010000101110000111101100011101100000000111110011111111110100001000101111100101001100111100000001010000100000111110110000001000110001100100010111110100011000111100011001000101111101010110000111000110010001011111011001101100110001100100010111110111011000111100011001000101111110000010111011000110010001011111100101100101110001100100010111111010011110001100011001000101111110110110010101101010000001011000110101000101111101000101000010111000000011010010010110000110011111001111111111010000100010111110010101011101

后记:

大家所看到的在计算机里的种种五彩缤纷的一切,在CPU看来不过是0和1……

更新补充:

针对评论中有位朋友指出运行时会弹出错误提示框的问题,我做了修正,解决了这个问题。

下面简单说说道理吧,是这样的:直接通过jmp指令跳到指定的函数去执行,这种做法是“有去无回”的,函数运行完后无法正确的返回原来的位置继续执行,所以修正的方式就是通过call指令来调用函数,然后在函数中通过ret指令(这条指令的机器码:C3)返回,这样就OK了。(详细的还是去看看汇编方面的东东吧)

//作者:冷却

//在WindowsXP SP3 + VC6.0环境下运行成功

//修正:运行后弹出错误提示框 @ 2011-5-15 11:24:16

unsigned char shellcode[] =

"/x55/x8B/xEC/x33/xC0/x50/x83/xEC/x0C/xC6/x45/xF0/x6B/xC6/x45/xF1/x65/xC6/x45"

"/xF2/x72/xC6/x45/xF3/x6E/xC6/x45/xF4/x65/xC6/x45/xF5/x6C/xC6/x45/xF6/x33/xC6"

"/x45/xF7/x32/xC6/x45/xF8/x2E/xC6/x45/xF9/x64/xC6/x45/xFA/x6C/xC6/x45/xFB/x6C"

"/x8D/x45/xF0/x50/xB8/x7B/x1D/x80/x7C/xFF/xD0/x8B/xE5/x33/xC0/x50/x83/xEC/x08"

"/xC6/x45/xF4/x63/xC6/x45/xF5/x61/xC6/x45/xF6/x6C/xC6/x45/xF7/x63/xC6/x45/xF8"

"/x2E/xC6/x45/xF9/x65/xC6/x45/xFA/x78/xC6/x45/xFB/x65/x6A/x05/x8D/x45/xF4/x50"

"/xB8/x0D/x25/x86/x7C/xFF/xD0/x8B/xE5/x5D/xC3";

void main()

{

__asm

{

lea eax,shellcode;

call eax;

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值