Shellcode编程小技巧
工作需要,需要注入其他程序监控一些东西,检测到的数据通过WM_COPY 消息发送给显示窗体。(大体是这样的还没定稿)
##1 选择一个框架 ##
tombkeeper/Shellcode_Template_in_C
框架选择上,我选择了第一个,妇科圣手tomkeeper提供的框架,对比发现这个比较简单。
##2 搭建框架 ##
我使用的是vs10,(vs15我试了下各种错误 ,可能是自己笨吧,用管了vs10了)
- 新建个解决方案(名字看自己喜好)
- 解决方案上新添个空的 win32console 工程
- 再在工程上新建个 xxxx.c的文件,把tomkeeper工程中的shellcode.c的内容复制过来
- 编译试试吧
- 其他的2个工具文件代码,分别是生成字符串用的工具(str2intarr.c) 和 生成函数hash宏定义的 gethash.c (方法同上)
- 我自己定义了个字符串生成的增强版(如下,我这个是cpp文件,和上面的有所不同),根据 第2个参数 <a/w> 将字符串转为多字节字符串和宽字节字符串。
#include <Windows.h> int _tmain(int argc, char* argv[])
{
if (argc < )
{
printf("Use %s <a/w> <string> \n",argv[]);
}else
{
printf("DWORD str[] = {\n\t"); if (argv[][] == 'a')
{
int strLen = strlen(argv[]);
strLen = (strLen / +)*;
int n = strLen / ;
DWORD* pArry = (DWORD*)malloc(strLen);
ZeroMemory(pArry,strLen);
memcpy(pArry,argv[],strlen(argv[])); for (int i=;i<n;i++ )
{ printf("0x%08x",*(pArry+i));
if (i != n-)
{
printf(", ");
}
if (i && i% ==)
{
printf("\n\t");
}
} }else
{
int strLen = strlen(argv[]);
int nAlloc = strLen*+;
DWORD* pWchar = (DWORD*)malloc(nAlloc);
ZeroMemory(pWchar,nAlloc);
MultiByteToWideChar(GetACP(),NULL,argv[],strLen,(WCHAR*)pWchar,strLen);
int n = (strLen/+);
for (int i=;i< n;i++)
{
printf("0x%08x",*(pWchar+i));
if (i != n-)
{
printf(", ");
}
if (i && i% ==)
{
printf("\n\t");
}
} }
printf("\n};");
//getchar();
}
return ;
}
##3 编译选项的设置 ##
编译选项的设置直接关系到shellcode大小的准确计算,代码的排列顺序,以及代码的精简程度,还有 GS。。。 是否被添加。
- 设置为release模式,我就不多说了,大家都会
- 去掉Gs 选项,如果不去掉shellcode()中会有gs检测函数的插入,这是我们不想要的。
- opt, 调试时我选择 disable, 发布时我使用 minsize (体积小些)。disable代码顺序是一样的就是多了不少int3 (cc)。
- linker 下的debuging :generate Debug info 我设置为YES。便于调试,对shellcode无影响
##4 测试shellcode的正确性 ##
tomkeeper的工程能将shellcode以c数组的形式输出出来,我增加了个函数可以将 shellcode dump到2进制文件中。
- 利用c数组, char shellcode[] = {0xb8,....}; __asm{ call shellcode} ,这样的话要注意将 工程的NX关闭(数据执行保护DEP) ,不会的自行补脑
- 第2种将 dump出的文件读入到申请的可读可写可执行的内存中,然后 __asm { call lpMem} .(我用的这种办法,不用每次进行复制黏贴,编译,执行)
FILE* file = ;
int iRead = ; HANDLE hFile =CreateFile("c:\\temp\\c.sc",GENERIC_WRITE|GENERIC_READ,,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); fprintf(stdout,"test shellcode ........... \n");
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwFileSizeHigh = ;
DWORD dwFileSizeLow = GetFileSize(hFile,&dwFileSizeHigh);
fprintf(stdout,"file size is 0x%x \n",dwFileSizeLow);
BYTE* lpMem = (BYTE*)VirtualAlloc(NULL,dwFileSizeLow+,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
ZeroMemory(lpMem,dwFileSizeLow+);
ReadFile(hFile,lpMem,dwFileSizeLow,&dwFileSizeHigh,NULL);
printf("should read %d ,readed %d byte \n",dwFileSizeLow,dwFileSizeHigh);
CloseHandle(hFile);
//getchar();
_asm{
call lpMem
} }else
{
fprintf(stdout,"open file error [0x%x] \n",GetLastError());
}
我先把shellcode dump到 c:\\temp\\c.sc 中,在用这个代码去测试它。
##5 小技巧 (排错、编程 经验) ##
- 不要用 *1.02 这种浮点算法,会死在这种命令上,那个2B2040绝对会出错,所以尽量别用了。
000300B3 DF6D D8 FILD QWORD PTR SS:[EBP-28]
000300B6 DC0D 40202B00 FMUL QWORD PTR DS:[2B2040]
000300BC D97D D6 FSTCW WORD PTR SS:[EBP-2A]
- 如何在shellcode后面追加上 配置信息便于测试呢
shellcode会带着些配置信息很长见的。 一般这些都是后期用工具合并上去的。
对于我们调试每次都去追加修改太麻烦了。所以要用到 #define NAKED __declspec(naked) 和 __asm{ __emit 0xbb }
__emit 可以使数据以代码的形式写入到代码段,也就是shellcode中。记得和 DWORD str[] = {0xabcdef,0xbaddef..}这种技术分开。这样就实现了任意数据的存入
NAKED 进制优化:当我们使用minsize选项的时候,这些代码可能就被优化掉,我试过了,所以才用到了NAKED,注意函数必须有 参数,声明不用实现用。
最后:在永远不会调用到的地方调用下,要不编译器一定会给你省略掉
void StoreConfigData(DWORD dwVoid); NAKED void StoreConfigData(DWORD dwVoid)
{ __asm{
__emit 0xff
__emit 0xfe
__emit 0xff .}
我这里专门写了个小工具将bin文件转为上面的形式
// bin2emit.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include <Windows.h> int _tmain(int argc, _TCHAR* argv[])
{
if (argc < )
{
printf("Use %s <path of bin> \n",argv[]);
}else
{
printf("path of bin: %s \n",argv[]);
HANDLE hFile = CreateFile(argv[],GENERIC_WRITE|GENERIC_READ,,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("open '%s' error \n",argv[]);
return ;
}
DWORD dwFileSizeLow,dwFileSizeHigh;
dwFileSizeLow = GetFileSize(hFile,&dwFileSizeHigh); LPVOID lpBuffer = HeapAlloc(GetProcessHeap(),0x08,dwFileSizeLow); if (!ReadFile(hFile,lpBuffer,dwFileSizeLow,&dwFileSizeHigh,))
{
CloseHandle(hFile);
printf("read file '%s' error \n",argv[]);
HeapFree(GetProcessHeap(),HEAP_NO_SERIALIZE,lpBuffer);
return ;
}
CloseHandle(hFile);
printf("\n\n\n __asm{\n\t");
for ( int i=;i < dwFileSizeLow;i++)
{
printf("__emit 0x%02x \n\t", *(((LPBYTE)lpBuffer)+i) );
}
printf("\n}"); }
return ;
}
- 函数重定位
我们的shellcode都是经过 call联系出来的,在调用的时候编译器已经算好了偏移,所以不存在重定位的问题,但是,向createthread这种将传入函数地址作为参数的函数就会出先这种问题;
解决的办法就是要动态计算地址。
//记得-8
DWORD GetEip()
{
__asm{
call get_eip
get_eip:
pop eax
}
}
void WINAPI AdjustFunction(struct ADJUSTFUNCTIONS* pFuns)
{
DWORD dwGetEip; dwGetEip = GetEip();
dwGetEip -= ; //计算得到 GetEip()函数在内存中的地址 pFuns->lpThreadProc = (DWORD)dwGetEip+( (DWORD)ThreadProc- (DWORD)GetEip ); // 以GetEip()函数做参考动态计算出其他 函数的地址
}
// 调用的时候这样写
hThread = modules.kernel32.CreateThread(NULL,0,adjustFuns.lpThreadProc,¶m,0,NULL);
- 函数动态获取
开发中发现通过函数hash获取时会出现和GetProcAddress返回地址不同的问题。出现以上问题有3种可能:
1.hash碰撞了,没办法那只能找替换函数了,例如CreateFile 函数,可以看看ntdll重的zw 、nt、rtl、有没有名称相似的函数,我们知道很多函数都是层层封装的,不怕找不到的。再说tk教主的hash计算时带着i也就是长度进行了计算,大大减小碰撞几率。
2.函数名搞错了,注意 xxxA xxxB xxxx 有些函数有A和W版本,你可要看清楚自己要的是哪个,但有些函数没有A和W区分。还有很多函数是通过lib链接的,根本导不出函数如 —beginthreadex,所以还是用 createthread代替吧 。
3. 还有些函数导出地址不是最终真正的函数地址,而是像ntdll.xxxxx的这种形式的内存地址,这样就还要再次去计算才能得到,而GetProcAddress这个函数应该进行了2次转换。例如:HeapAlloc是kernel32导出的,通过hash找到的是ntdll!RtlAllocateHeap字符串的地址(大约是这个样子,反正是个ntdll开通的),所以还是直接去ntdll下找 RtlAllocateHeap 它吧,所以 找函数尽量去ntdll中招吧。
http://www.onlinedown.net/soft/59825.htm
http://www.pc6.com/softview/SoftView_16910.html dllviewer 可以看看dll导出函数表
- 初始化局部变量
struct MODULES modules = {0x00};
这种初始化还是不要有的好,我跟踪发现这样会让执行流程变乱。
还是用自己写的memset吧。
ps:shellcode真省地方,写了这么多代码还没2K呢。
# 自我实现一些函数 #
有些函数没必要动态获取,太麻烦了。很多函数很简单,用代码就能实现
- GetProcessHeap (测试环境 win7 x64 \win8 x64 \win10 x64)
HANDLE WINAPI GetProcessHeap()
{
__asm{ mov eax,fs:[0x30]
mov eax,[eax+0x18]
}
}
- memcpy
void WINAPI zMemcpy(LPVOID lpDest,LPVOID lpSrc,DWORD dwSize)
{
if (!lpDest || !lpSrc ||!dwSize)
{
_asm int
}
__asm{
mov ecx,dwSize
mov edi,lpDest
mov esi,lpSrc
rep movsb
}
}
为了兼容x64shellcode开发,这里将内嵌的汇编都去掉了。
最近发现在大数据除法和取余时,vs会嵌入alldiv、allrem等函数,这将直接造成,shellcode跑飞
Shellcode编程小技巧的更多相关文章
- Java编程小技巧(1)——方法传回两个对象
原文地址:Java编程小技巧(1)--方法传回两个对象 | Stars-One的杂货小窝 题目是个伪命题,由Java语法我们都知道,方法要么返回一个对象,要么就不返回 当有这样的情况,我们需要返回两个 ...
- 学会这些 pycharm 编程小技巧,编程效率提升 10 倍
PyCharm 是一款非常强大的编写 python 代码的工具.掌握一些小技巧能成倍的提升写代码的效率,本篇介绍几个经常使用的小技巧. 一.分屏展示 当你想同时看到多个文件的时候: 1.右击标签页: ...
- hadoop编程小技巧(5)---自定义输入文件格式类InputFormat
Hadoop代码测试环境:Hadoop2.4 应用:在对数据需要进行一定条件的过滤和简单处理的时候可以使用自定义输入文件格式类. Hadoop内置的输入文件格式类有: 1)FileInputForma ...
- WTL编程小技巧汇编
1.设置窗体生成大小并中央显示窗口 2.设置窗体最大/小尺寸 3.动态设置窗体标题 4.设置对话框的字体和背景颜色 5.设置窗体控件默认字体 以下技巧可应用于SDI和MDI程序: 1.设置窗体生成大小 ...
- hadoop编程小技巧(5)---自己定义输入文件格式类InputFormat
Hadoop代码測试环境:Hadoop2.4 应用:在对数据须要进行一定条件的过滤和简单处理的时候能够使用自己定义输入文件格式类. Hadoop内置的输入文件格式类有: 1)FileInputForm ...
- 编程小技巧之 Linux 文本处理命令
合格的程序员都善于使用工具,正所谓君子性非异也,善假于物也.合理的利用 Linux 的命令行工具,可以提高我们的工作效率. 本文简单的介绍三个能使用 Linux 文本处理命令的场景,给大家开阔一下思路 ...
- VC编程小技巧之框架窗口及其他
1.修改主窗口风格 AppWizard生成的应用程序框架的主窗口具有缺省的窗口风格,比如在窗口标题条中自动添加文档名.窗口是叠加型的.可改变窗口大小等.要修改窗口的缺省风格,需要重载CWnd::Pre ...
- Javascript 编程小技巧总结(部分内容借鉴他人)
1 – 使用===,而不是== ==(或!=)操作符在需要的时候会自动执行类型转换.===(或!==)操作不会执行任何转换.它将比较值和类型,而且在速度上也被认为优于==. 2 – 使用闭包实现私有变 ...
- 积累的VC编程小技巧之框架窗口及其他
1.修改主窗口风格 AppWizard生成的应用程序框架的主窗口具有缺省的窗口风格,比如在窗口标题条中自动添加文档名.窗口是叠加型的.可改变窗口大小等.要修改窗口的缺省风格,需要重载CWnd::Pre ...
随机推荐
- Windows 7无法卸载及安装IE11的解决方法
1. 清空 C:\Windows\TEMP\ 中的所有内容 2. 以管理员身份运行命令行,在命令中行输入下面的代码: FORFILES /P %WINDIR%\servicing\Packages ...
- cuplayer酷播播放器 swf 带参数直接播放
客户需要使用cuplayer,直接调用swf 播放器. /Player/player.swf?FlvID=745,此处写入视频ID; 官方给的例子,运行是有问题的. http://www.cuplay ...
- PHP之简单实现MVC框架
PHP之简单实现MVC框架 1.概述 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种 ...
- PHP配置参数
PHP使用到两个比较重要的配置文件: php.ini:决定php语言运行的环境,支持扩展的模块 , 开发环境的配置.(路径一般位于/etc/php.ini 或 /usr/local/php/etc/p ...
- Spring 000 框架简介 (转载)
转载自:https://my.oschina.net/myriads/blog/37922 1.使用框架的意义与Spring的主要内容 随着软件结构的日益庞大,软件模块化趋势出现,软件开发也需要多人合 ...
- js window.onload 的一个验证
window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. 以下验证是否是图片加载完成后才执行 <img class="icon" id="ic ...
- ios程序发布测试打包
1. 获取测试机UDID 手机连接电脑,打开iTunes-摘要-点击序列号,设备概要界面显示设备udid-反键-拷贝 2. 添加设备 取得UDID后,进入开发者中心-iOS PRovisioning ...
- css控制文字大小及颜色、字体
字体:font-style:italic:斜体 font-weight:bold:加粗 font-size:30px:大小 line-height:30 ...
- Nexus安装及部署(含如何在Tomcat中部署)
1. Nexus价值 1)方便-节约带宽-快 2)便于统一管理 3)持续集成需要 2.Nexus下载 http://www.sonatype.org/nexus/go 3.Nexus启动 解压后进入\ ...
- IE中cookie问题,带下划线的前置域名会不给设cookie,谷歌和火狐浏览器则不受影响
!! WARNING !!: Server hostname contains an underscore and this response sets a cookie. Internet Expl ...