标签:
工作需要,需要注入其他程序监控一些东西,检测到的数据通过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 < 2) { printf("Use %s <a/w> <string> \n",argv[0]); }else { printf("DWORD str[] = {\n\t"); if (argv[1][0] == ‘a‘) { int strLen = strlen(argv[2]); strLen = (strLen /4 +1)*4; int n = strLen /4 ; DWORD* pArry = (DWORD*)malloc(strLen); ZeroMemory(pArry,strLen); memcpy(pArry,argv[2],strlen(argv[2])); for (int i=0;i<n;i++ ) { printf("0x%08x",*(pArry+i)); if (i != n-1) { printf(", "); } if (i && i%9 ==8) { printf("\n\t"); } } }else { int strLen = strlen(argv[2]); int nAlloc = strLen*2+4; DWORD* pWchar = (DWORD*)malloc(nAlloc); ZeroMemory(pWchar,nAlloc); MultiByteToWideChar(GetACP(),NULL,argv[2],strLen,(WCHAR*)pWchar,strLen); int n = (strLen/2+1); for (int i=0;i< n;i++) { printf("0x%08x",*(pWchar+i)); if (i != n-1) { printf(", "); } if (i && i%9 ==8) { printf("\n\t"); } } } printf("\n};"); //getchar(); } return 0; }
##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} .(我用的这种办法,不用每次进行复制黏贴,编译,执行)
##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 .}
- 函数重定位
我们的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 -= 8; //计算得到 GetEip()函数在内存中的地址 pFuns->lpThreadProc = (DWORD)dwGetEip+( (DWORD)ThreadProc- (DWORD)GetEip ); // 以GetEip()函数做参考动态计算出其他 函数的地址 }
// 调用的时候这样写
hThread = modules.kernel32.CreateThread(NULL,0,adjustFuns.lpThreadProc,¶m,0,NULL);
ps:shellcode真省地方,写了这么多代码还没2K呢。
标签:
原文地址:http://www.cnblogs.com/M4ster/p/shellcode_write.html