前言

shellcode是不依赖环境,放到任何地方都可以执行的机器码。shellcode的应用场景很多,本文不研究shellcode的具体应用,而只是研究编写一个shellcode需要掌握哪些知识。

ShellCode编写原则

1、不能有全局变量

因为我们编写shellcode时,使用的全局变量是自己的进程里面的全局变量,注入到别的进程里,这个地址就没用了。

2、不能使用常量字符串

和第一点原因一样,字符串常量值也是全局变量,注入到别的进程里,根本没有这个字符串。

要使用字符串,需要使用字符数组。

char s[] = {'1','2',0};

3、不能直接调用系统函数

调用系统函数的方式是间接调用(FF15),需要从IAT表里获取API地址,每个进程的IAT表位置不同,且对方的进程可能没有导入你需要调用的函数的DLL,那么你是不能调用这个系统函数的。

所以我们需要用到 LoadLibrary 和 GetProcAddress 这两个函数,来动态获取系统API的函数指针。

但是 LoadLibrary,GetProcAddress 本身就是系统函数,它们本身就依赖IAT表,咋办呢?

解决方案是这样的:通过FS:[0x30] 找到PEB,然后通过PEB里的LDR链表 [PEB+0x0C]找到 kernel32.dll 的地址,然后我们遍历它的 IAT表,找到 LoadLibrary 和 GetProcAddress 函数。

4、不能嵌套调用其他函数

和前两点道理是一样的,本进程里的函数地址,拿到别的进程的虚拟地址空间是无效的。

TEB/PEB

每个线程都有一个TEB结构来存储线程的一些属性结构,TEB的地址用fs:[0]来获取

在0x30这个地址有一个指针指向PEB结构,PEB就是进程用来记录自己信息的一个结构

完整结构如下

在PEB的0x00c偏移有一个 Ldr _PEB_LDR_DATA结构跟进去

可以得到3个结构如下所示

InLoadOrderModuleList:模块加载的顺序

InMemoryOrderModuleList:模块在内存的顺序

InInitializationOrderModuleList:模块初始化的顺序

思路

我们一般使用api会直接使用LoadLibraryGetProcessAddress,但是这里肯定会依赖IAT表,所以这里我们就需要自己实现api所完成的功能

TEB -> PEB -> PEB + 0x0C -> Ldr _PEB_LDR_DATA -> InLoadOrderModuleList -> kernel32.dll -> 导出表定位GetProcessAddress -> 通过找到的GetProcessAddress实现LoadLibrary

实现过程

首先我们自己定义几个结构体,因为我们不依赖系统自己实现

typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;typedef struct _PEB_LDR_DATA
{DWORD Length;bool Initialized;PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList;LIST_ENTRY InMemoryOrderModuleList;LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA,*PPEB_LDR_DATA;typedef struct _LDR_DATA_TABLE_ENTRY
{LIST_ENTRY InLoadOrderLinks;LIST_ENTRY InMemoryOrderLinks;LIST_ENTRY InInitializationOrderLinks;PVOID DllBase;PVOID EntryPoint;UINT32 SizeOfImage;UNICODE_STRING FullDllName;UNICODE_STRING BaseDllName;UINT32 Flags;USHORT LoadCount;USHORT TlsIndex;LIST_ENTRY HashLinks;PVOID SectionPointer;UINT32 CheckSum;UINT32 TimeDateStamp;PVOID LoadedImports;PVOID EntryPointActivationContext;PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;typedef HMODULE (WINAPI * PLOADLIBRARY)(LPCSTR);
typedef DWORD (WINAPI * PGETPROCADDRESS)(HMODULE, LPCSTR);
typedef DWORD (WINAPI * PMESSAGEBOX)(HWND, LPCSTR,LPCSTR,UINT);

然后定义shellcode,这里因为kernel32.dll是unicode字符串所以用两字节存储

 char szKernel32[] = {'k',0,'e',0,'r',0,'n',0,'e',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,0,0}; // Unicodechar szUser32[] = {'u','s','e','r','3','2','.','d','l','l',0};char szGetProcAddress[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};char szLoadLibrary[] = {'L','o','a','d','L','i','b','r','a','r','y','A',0};char szMessageBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};char szHelloShellCode[] = {'H','e','l','l','o','S','h','e','l','l','C','o','d','e',0};

找到InLoadOrderModuleList存入寄存器

 __asm{mov eax,fs:[0x30] // PEBmov eax,[eax+0x0C] // PEB->LDRadd eax,0x0C // LDR->InLoadOrderModuleListmov pBeg,eaxmov eax,[eax]mov pPLD,eax}

找到kernel32.dll,通过遍历的方式来寻找,通过LDR指向DllBase获取基址

 // Find Kerner32.dllwhile (pPLD != pBeg){pLast = (WORD*)pPLD->BaseDllName.Buffer;pFirst = (WORD*)szKernel32;while (*pFirst && *pLast == *pFirst)pFirst++,pLast++;if (*pFirst == *pLast){dwKernelBase = (DWORD)pPLD->DllBase;break;}pPLD = (LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderLinks.Flink;}

然后通过指针定位到导出表

  // 通过指针定位到导出表PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernelBase;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(/images/shellcode/image_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)dwKernelBase + pOptionHeader->DataDirectory[0].VirtualAddress);// 导出函数地址表RVADWORD *pAddOfFun_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfFunctions);// 导出函数名称表RVAWORD *pAddOfOrd_Raw = (WORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNameOrdinals);// 导出函数序号表RVADWORD *pAddOfNames_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNames);

还是通过遍历找到GetProcessAddress,用指针指向这个地址

  DWORD dwCnt = 0;char* pFinded = NULL, *pSrc = szGetProcAddress;for (; dwCnt < pExportDirectory->NumberOfNames;dwCnt++){pFinded = (char*)((DWORD)dwKernelBase + pAddOfNames_Raw[dwCnt]);while (*pFinded && *pFinded == *pSrc)pFinded++, pSrc++;if (*pFinded == *pSrc){pGetProcAddress = (PGETPROCADDRESS)(pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]] + (DWORD)dwKernelBase);break;}pSrc = szGetProcAddress;}

然后就可以使用pGetProcessAddress实现LoadLibraryMessageBox

 // 通过pGetProcAddress进行调用pLoadLibrary = (PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase, szLoadLibrary);pMessageBox = (PMESSAGEBOX)pGetProcAddress(pLoadLibrary(szUser32),szMessageBox);pMessageBox(NULL,szHelloShellCode,0,MB_OK);

完整代码如下

// shellcode.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;typedef struct _PEB_LDR_DATA
{DWORD Length;bool Initialized;PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList;LIST_ENTRY InMemoryOrderModuleList;LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA,*PPEB_LDR_DATA;typedef struct _LDR_DATA_TABLE_ENTRY
{LIST_ENTRY InLoadOrderLinks;LIST_ENTRY InMemoryOrderLinks;LIST_ENTRY InInitializationOrderLinks;PVOID DllBase;PVOID EntryPoint;UINT32 SizeOfImage;UNICODE_STRING FullDllName;UNICODE_STRING BaseDllName;UINT32 Flags;USHORT LoadCount;USHORT TlsIndex;LIST_ENTRY HashLinks;PVOID SectionPointer;UINT32 CheckSum;UINT32 TimeDateStamp;PVOID LoadedImports;PVOID EntryPointActivationContext;PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;typedef HMODULE (WINAPI * PLOADLIBRARY)(LPCSTR);
typedef DWORD (WINAPI * PGETPROCADDRESS)(HMODULE, LPCSTR);
typedef DWORD (WINAPI * PMESSAGEBOX)(HWND, LPCSTR,LPCSTR,UINT);DWORD WINAPI ShellCode();int main(int argc, char* argv[])
{ShellCode();getchar();return 0;
}DWORD WINAPI ShellCode()
{PGETPROCADDRESS pGetProcAddress = NULL;PLOADLIBRARY pLoadLibrary = NULL;PMESSAGEBOX  pMessageBox = NULL;PLDR_DATA_TABLE_ENTRY pPLD;PLDR_DATA_TABLE_ENTRY pBeg;WORD *pFirst = NULL;WORD *pLast = NULL;DWORD ret = 0, i = 0;DWORD dwKernelBase = 0;char szKernel32[] = {'k',0,'e',0,'r',0,'n',0,'e',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,0,0}; // Unicodechar szUser32[] = {'u','s','e','r','3','2','.','d','l','l',0};char szGetProcAddress[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};char szLoadLibrary[] = {'L','o','a','d','L','i','b','r','a','r','y','A',0};char szMessageBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};char szHelloShellCode[] = {'H','e','l','l','o','S','h','e','l','l','C','o','d','e',0};__asm{mov eax,fs:[0x30] // PEBmov eax,[eax+0x0C] // PEB->LDRadd eax,0x0C // LDR->InLoadOrderModuleListmov pBeg,eaxmov eax,[eax]mov pPLD,eax}// Find Kerner32.dllwhile (pPLD != pBeg){pLast = (WORD*)pPLD->BaseDllName.Buffer;pFirst = (WORD*)szKernel32;while (*pFirst && *pLast == *pFirst)pFirst++,pLast++;if (*pFirst == *pLast){dwKernelBase = (DWORD)pPLD->DllBase;break;}pPLD = (LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderLinks.Flink;}// Kernel32.dll -> GetProcAddressif (dwKernelBase != 0){// 通过指针定位到导出表PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernelBase;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(/images/shellcode/image_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)dwKernelBase + pOptionHeader->DataDirectory[0].VirtualAddress);// 导出函数地址表RVADWORD *pAddOfFun_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfFunctions);// 导出函数名称表RVAWORD *pAddOfOrd_Raw = (WORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNameOrdinals);// 导出函数序号表RVADWORD *pAddOfNames_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNames);DWORD dwCnt = 0;char* pFinded = NULL, *pSrc = szGetProcAddress;for (; dwCnt < pExportDirectory->NumberOfNames;dwCnt++){pFinded = (char*)((DWORD)dwKernelBase + pAddOfNames_Raw[dwCnt]);while (*pFinded && *pFinded == *pSrc)pFinded++, pSrc++;if (*pFinded == *pSrc){pGetProcAddress = (PGETPROCADDRESS)(pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]] + (DWORD)dwKernelBase);break;}pSrc = szGetProcAddress;}}// 通过pGetProcAddress进行调用pLoadLibrary = (PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase, szLoadLibrary);pMessageBox = (PMESSAGEBOX)pGetProcAddress(pLoadLibrary(szUser32),szMessageBox);pMessageBox(NULL,szHelloShellCode,0,MB_OK);return 0;
}

成功弹窗

这里我们进反汇编看一下,是有检测堆栈平衡的代码的

在物理机里面查看也是有的

这里关闭一下堆栈平衡的检测,默认情况如下

修改为禁用安全检查

即可生成没有检查堆栈平衡的代码

shellcode编写探究相关推荐

  1. WINDOWS的SHELLCODE编写高级技巧

    WINDOWS的SHELLCODE编写高级技巧 unix等系统因为有用户概念,所以往往溢出是使用先得到普通帐号,然后登陆后用溢出 再加载一个SHELL的办法得到ROOT权限,其系统调用又方便,所以SH ...

  2. 溢出的原理及 shellcode 编写

    本讲的预备知识: 首先你应该了解intel汇编语言,熟悉寄存器的组成和功能.你必须有堆栈和存储分配方面 的基础知识,有关这方面的计算机书籍很多,我将只是简单阐述原理,着重在应用.其次, 你应该了解li ...

  3. 【移动安全高级篇】————3、Android系统ShellCode编写

    随着Android手机的普及,Android系统安全日益受人关注.漏洞攻防是安全的一大课题,其中自然少不了shellcode的编写.本文将以提出问题.解决问题的方式教你如何编写Android系统she ...

  4. 缓冲区溢出漏洞攻击——Shellcode编写

    一.实验内容 利用一个程序漏洞,编写shellcode,达成效果:蹦出对话框,显示"You have been hacked!(by JWM)" 二.实验原理 因为输入了过长的字符 ...

  5. arm shellcode 编写详析1

    在编写arm shell code 之前,先介绍下arm中r0-r15寄存器的主要用途: Register Alt. Name Usage r0 a1 First function argument ...

  6. windows 平台shellcode编写

    0x00.介绍 比方说你手头上有一个IE或FlashPlayer现成的漏洞利用代码,但它只能够打开计算器calc.exe.但是这实际上并没有什么卵用,不是吗?你真正想要的是可以执行一些远程命令或实现其 ...

  7. 浅析缓冲区溢出漏洞的利用与Shellcode编写

    文章目录 前言 汇编语言 寄存器 内存堆栈 CPU指令 函数调用 缓冲区溢出 栈溢出原理 栈溢出攻击 ShellCode 总结 前言 缓冲区溢出(Buffer Overflow)是计算机安全领域内既经 ...

  8. Windows 10_X64环境shellcode编写

    环境 windows 10_x64 windbg_x64 x64dbg nasm 适用于 VS 2017 的 x64 本机工具命令提示(安装visual studio会自带) redasm shell ...

  9. windows下shellcode编写入门

    0x00.介绍 比方说你手头上有一个IE或FlashPlayer现成的漏洞利用代码,但它只能够打开计算器calc.exe.但是这实际上并没有什么卵用,不是吗?你真正想要的是可以执行一些远程命令或实现其 ...

最新文章

  1. php 工资 2018,2018年我国公务员级别工资标准
  2. android点击事件的优先级,android中进程的优先级
  3. 电脑怎么换自己的壁纸_电脑硬件到底应该怎么选?自己应该如何组装电脑?
  4. KubeVela 高可扩展的云原生应用平台与核心引擎
  5. 利用钩子机制取得Windows的消息监控权
  6. 中国 AI 的“黄埔军校”?MSRA 被曝停招“国防七子”及北邮学生
  7. 小白如何快速学会C++?
  8. 博文视点Open Party第5期:操作系统与嵌入式开发 圆满结束
  9. Xshell连接不上行Linux系统
  10. 书柜的尺寸(bzoj 1933)
  11. 集成ahci驱动的xp系统_IDE转AHCI模式 for win7+SSD
  12. cad画多段线时不显示轨迹_为什么CAD直线、多段线等图形只显示一个夹点? - CAD自学网...
  13. Arduino蓝牙无线自动下载程序
  14. 计算机如何分割硬盘,电脑硬盘怎么分区 电脑硬盘分区方法大全
  15. xdb 服务_如何删除默认的XPT和XDB这两个服务
  16. 中南大学计算机学院复试2021,中南大学2021年硕士研究生拟录取名单汇总
  17. LoRa及LoRaWAN简介
  18. Lync 2010升级到Lync 2013之设定Lync Mobile!
  19. 抖音seo源码·源代码搭建·支持二开(开源)系统
  20. 外语学习的真实方法与误区19

热门文章

  1. 在Windows下编译多种VS版本的Skia
  2. 2019 字节跳动双指针字符串题目 万万没想到之聪明的编辑
  3. Redis集群脑裂导致数据丢失问题处理
  4. [shell] zcat不解压查看文件内容
  5. 编程c语言还是e语言,程序员选择编程语言的3大建议,选择C语言还是Python?
  6. MYSQL数据库初窥门径, SQL语句地熟练使用, 图形化界面提高效率
  7. Severlet跳转JSP,切换div
  8. 最新支付方式悄然到来,手机支付或将被淘汰!还是马云说的对
  9. 【Go】自定义json序列化
  10. MVP的使用 + 闪屏页广告