郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,如果您不同意请关闭该页面!任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!
前言
终于写完了,应该挺详细的,奥利给

环境配置讲解
当前项目使用的vs2019,其他vs编译器都可以
清除多余的函数
首先创建一个项目,然后什么配置都不修改使用release
生成如下代码
然后我们把生成的exe文件放到ida里面去可以看到下图,明明我们什么函数都没有加为什么会多出这么多函数呢?

其实这些函数都是vs编译器自动帮我加进去的,我们的代码段加上这些函数就组成了一个PE文件
位置:项目->配置属性->高级->入口点中添加MyMian
字段,这个字段可以随意修改
接着我们把原先的代码替换为如下代码,然后重新生成

可以看到多余的函数都不见了
禁用安全检查
上面是不是还有一个多余函数,该函数是用于安全检查用的,我们一样可以关闭它。
位置:项目->配置属性->C/C++
->代码生成->安全检查,点击禁用安全检查
然后重新生成后就能看到只剩下一个字段了

设置兼容XP系统
虽然win7都淘汰了但是还是有不少哔哔哔哔
(自带消音)还用着windwos XP,但是v142已经不支持xp编程了,所以我们只能下载v141_xp来使用
位置:项目->配置属性->常规->平台工作集,选用Visual Studio 2017 - Windows XP (v141_xp)
(需要自己下载该项目包

接着修改运行库
位置:项目->配置属性->C/C++
->代码生成->运行库,改成MT格式

关闭资源段
当我们把以上调试好的exe文件放入到PEiD中的时候可以看到还多出了资源段和只读数据段

位置:项目->配置属性->链接器->清单文件->生成清单,改成否
重新生成后的文件放入PEiD中可以看到资源段已经消失了

关闭只读数据段
位置:项目->配置属性->链接器->调试->生成调试信息,改为否
注意:
- vs2019关闭了也没办法删除只读数据段
- vs2008可以删除
- 不删除该字段影响不大
关闭优化
vs编译器会自动把没用用到的参数优化了,所以我们要关闭优化,会更直观。
位置:项目->配置属性->C/C++
->优化,修改为已禁用
总结
- 项目->配置属性->链接器->高级->入口点中添加
MyMian
字段
- 项目->配置属性->
C/C++
->代码生成->安全检查,点击禁用安全检查
- 项目->配置属性->常规->平台工作集,选用
Visual Studio 2017 - Windows XP (v141_xp)
- 项目->配置属性->链接器->清单文件->生成清单,改成否
- 项目->配置属性->链接器->调试->生成调试信息,改为否
- 项目->配置属性->
C/C++
->优化,修改为已禁用
- 项目->配置属性->
C/C++
->代码生成->运行库,改成MT格式
- 项目->配置属性->
C/C++
->语言->符合模式,改成否格式
代码中的注意事项
杜绝双引号
vs开放平台中,双引号字符串会被编译到只读数据段,以引用绝对地址的方式使用,shellcode
是为了便于所有机器上使用的,所有要避免一切绝对地址的使用,下面的函数调用会报错,之所以这样写是为了直观理解,实际需要用动态调用。
-
禁止使用的格式
char szBuff[]="XXXXXXXXXX"; char *pzBuff="XXXXXXXXXX";
char szDate[10]; char *pzBuff="XXXXX"; sprintf(szDate,"%s",pzBuff); memcpy(szDate,"xxxx");
|
-
应该写成的格式
char szBuff[]={'x','x','x','\0'};
_asm { _EMIT 'X' _EMIT 'X' _EMIT 'X' _EMIT 'X' _EMIT 0 }
char szDate[10]; char szBuff[]={'1','2','3','4','5'}; char szOut[]={'%','s','\0'}; char *pzBuff="XXXXX"; memcpy(szDate,szBuff); sprintf(szDate,szOut,pzBuff);
|
函数动态调用
如果不是用该方法,在我们修改了main函数的入口后,调用include中的函数就好花式报错,但是你自己写的函数就不会
先贴一个调用Windows API中CreateFileA
函数的例子,后面再详细讲解
#include<windows.h> #pragma comment(linker,"/entry:MyMain") int MyMain() { typedef HANDLE(WINAPI* FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); FN_CreateFileA fn_CreateFileA; fn_CreateFileA = (FN_CreateFileA)GetProcAddress(LoadLibraryA("kernel32.dll"), "CreateFileA"); fn_CreateFileA("ascotbe.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
return 0; }
|
首先如果使用自定义的函数入口点需要添加#pragma comment(linker,"/entry:MyMain")
,MyMain的值为你的入口函数名
接着我们需要获取DLL中函数的地址,使用GetProcAddress(LoadLibraryA("kernel32.dll"), "CreateFileA");
这段代码的意思就是获取kernel32.dll动态链接库中的CreateFileA函数的相对地址。

然后找到CreateFileA函数的声明,然后复制过来

接着就是使用typedef定义复制过来的函数,以及声明调用等
如果使用printf函数也是一样的,代码如下
#include<stdio.h> #pragma comment(linker,"/entry:MyMain") int MyMain() { typedef int(__CRTDECL* FN_printf)(_In_z_ _Printf_format_string_ char const* const _Format, ...); FN_printf Fn_printf; Fn_printf = (FN_printf)GetProcAddress(LoadLibraryA("msvcrt.dll"), "printf"); Fn_printf("asoctbe is a vegetable~"); return 0; }
|
函数动态调用优化
由上文可以知道我们需要动态调用函数或者API,但是有两个地方违背了我们核心的原理。
- 动态调用的前提我们需要获取kernel32.dll的基址,这个地方还是需要用到API中的LoadLibraryA函数!
- 即使获取到kernel32.dll的基址我们还是需要用GetProcAddress函数来提取里面的函数!
首先我们解决第一个痛点:
获取kernel32.dll基址
先贴代码
#include<windows.h> #pragma comment(linker,"/entry:MyMain")
__declspec(naked)DWORD getKernel32() { __asm { XCHG EAX, EBX MOV EBX, FS:[30H] MOV EBX, [EBX + 0CH] MOV EBX, [EBX + 14H] MOV EBX, [EBX] MOV EBX, [EBX] MOV EBX, [EBX + 10H] XCHG EAX, EBX RETN } }
int MyMain() { HMODULE hLoadLibraryA = (HMODULE)getKernel32(); typedef int(__CRTDECL* FN_printf)( _In_z_ _Printf_format_string_ char const* const _Format, ...); FN_printf fn_printf; fn_printf = (FN_printf)GetProcAddress(LoadLibraryA("msvcrt.dll"), "printf"); fn_printf("0x%08X\n", hLoadLibraryA); fn_printf("0x%08X\n", LoadLibraryA("kernel32.dll")); return 0; }
|
可以看到返回的结果是一模一样的

备注:
获取kernel32.dll中的函数
有了kernel32.dll基址后,我们可以通过基址来获取到里面需要用到API函数
依旧直接贴代码
#include<windows.h> #pragma comment(linker,"/entry:MyMain")
__declspec(naked)DWORD getKernel32() { __asm { XCHG EAX, EBX MOV EBX, FS:[30H] MOV EBX, [EBX + 0CH] MOV EBX, [EBX + 14H] MOV EBX, [EBX] MOV EBX, [EBX] MOV EBX, [EBX + 10H] XCHG EAX, EBX RETN } }
FARPROC _GetProcAddress(HMODULE hModuleBase) { PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase; PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew); if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) { return NULL; } if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { return NULL; } PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PDWORD lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames); PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions);
DWORD dwLoop = 0; FARPROC pRet = NULL; for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase);
if (pFunName[0] == 'G' && pFunName[1] == 'e' && pFunName[2] == 't' && pFunName[3] == 'P' && pFunName[4] == 'r' && pFunName[5] == 'o' && pFunName[6] == 'c' && pFunName[7] == 'A' && pFunName[8] == 'd' && pFunName[9] == 'd' && pFunName[10] == 'r' && pFunName[11] == 'e' && pFunName[12] == 's' && pFunName[13] == 's') { pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; } int MyMain() { HMODULE hLoadLibraryA = (HMODULE)getKernel32();
typedef int(__CRTDECL* FN_printf)( _In_z_ _Printf_format_string_ char const* const _Format, ...); FN_printf fn_printf; fn_printf = (FN_printf)GetProcAddress(LoadLibraryA("msvcrt.dll"), "printf");
typedef FARPROC(WINAPI* FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName );
FN_GetProcAddress fn_GetProcAddress; fn_GetProcAddress = (FN_GetProcAddress)_GetProcAddress(hLoadLibraryA);
fn_printf("0x%xn\n", fn_GetProcAddress); fn_printf("0x%xn\n", GetProcAddress); return 0; }
|
看下图的结果,说明代码没错

上面代码的流程,首先获取kernel32.dll的基址,然后利用typedef来声明动态调用,接着调用写好的_GetProcAddress
函数来把地址负责给声明好的fn_GetProcAddress
中,接着我们直接使用fn_GetProcAddress
函数就和调用windows api一样的。
备注:
_GetProcAddress
函数的写法涉及到PE结构编写,后面有时间会单独写一篇相关的文章
禁止使用全局变量
因为全局变量在使用vs平台进行编译的时候会加载到PE结构中的特定区段中,类似于使用了绝对地址,这样是我们写shellcode的时候需要避免的。
备注:static
关键字也是一样的,需要避免
确保加载所需要的动态链接库
在上面的printf
函数中我们也是使用动态调用的方式来实现的,所以在使用非自己写的函数的时候都必须确保加载了所需要的动态链接库!!!!!
编写我们第一个shellcode
我们普通的代码写法
#include <windows.h> #include <stdio.h> #include <windows.h> int main() { CreateFileA("1.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); MessageBoxA(NULL, "hello world", tip, MB_OK); return 0; }
|
shellcode写法
#include<windows.h> #pragma comment(linker,"/entry:MyMain")
FARPROC _GetProcAddress(HMODULE hModuleBase); DWORD getKernel32();
int MyMain() { typedef FARPROC(WINAPI* FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName );
FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)_GetProcAddress((HMODULE)getKernel32());
typedef HANDLE(WINAPI* FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); char xyCreateFile[] = { 'C','r','e','a','t','e','F','i','l','e','A',0 }; FN_CreateFileA fn_CreateFileA = (FN_CreateFileA)fn_GetProcAddress((HMODULE)getKernel32(), xyCreateFile); char xyNewFile[] = { 'a','s','c','o','t','b','e','.','t','x','t','\0' }; fn_CreateFileA(xyNewFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
typedef HMODULE(WINAPI* FN_LoadLibraryA)( __in LPCSTR lpLibFileName ); char xyLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 }; FN_LoadLibraryA fn_LoadLibraryA = (FN_LoadLibraryA)fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryA);
typedef int (WINAPI* FN_MessageBoxA)( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType);
char xy_user32[] = { 'u','s','e','r','3','2','.','d','l','l',0 }; char xy_MessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 }; FN_MessageBoxA fn_MessageBoxA = (FN_MessageBoxA)fn_GetProcAddress(fn_LoadLibraryA(xy_user32), xy_MessageBoxA); char xy_Hello[] = { 'H','e','l','l','o',' ','w','o','r','l','d',0 }; char xy_tip[] = { 't','i','p' }; fn_MessageBoxA(NULL, xy_Hello, xy_tip, MB_OK); return 0; }
__declspec(naked)DWORD getKernel32() { __asm { XCHG EAX, EBX MOV EBX, FS: [30H] MOV EBX, [EBX + 0CH] MOV EBX, [EBX + 14H] MOV EBX, [EBX] MOV EBX, [EBX] MOV EBX, [EBX + 10H] XCHG EAX, EBX RETN } }
FARPROC _GetProcAddress(HMODULE hModuleBase) { PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase; PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew); if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) { return NULL; } if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { return NULL; } PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PDWORD lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames); PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions);
DWORD dwLoop = 0; FARPROC pRet = NULL; for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase);
if (pFunName[0] == 'G' && pFunName[1] == 'e' && pFunName[2] == 't' && pFunName[3] == 'P' && pFunName[4] == 'r' && pFunName[5] == 'o' && pFunName[6] == 'c' && pFunName[7] == 'A' && pFunName[8] == 'd' && pFunName[9] == 'd' && pFunName[10] == 'r' && pFunName[11] == 'e' && pFunName[12] == 's' && pFunName[13] == 's') { pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; }
|
效果如下图

接下来我们来提取进程中的shellcode段,首先用PEID来看看文件的偏移点

我们用16进制进制来看看shellcode代码段,如图所示从200-5D7都是我们shellcode

我们把这段代码复制下来另存为一个文件
然后我们用shellcode加载器加载他,看下是否成功

函数生成规律
单文件函数生成规律
先说结论:
- 在单个文件中,函数的生成顺序和函数书写的顺序相关,和函数声明的位置不想关
首先验证我们结论,我们看下面两张图

第二张图我们把b函数放到MyMain函数上面,其他的不变

可以看到我们的结论是完全正确的!!
多文件函数生成规律
首先直接来结论:
-
文件生成顺序和头文件引用顺序无关,只和工程项目vcxproj
结尾的文件相关
-
如果有多个文件,并且多个文件里面有多个函数,那么函数生成的顺序第一优先级为vcxproj
书写的顺序相关,第二优先级只和cpp
文件中书写函数的顺序相关,举个例子vcxproj
文件中顺序为a.cpp
dome.cpp
b.cpp
,并且a.cpp
文件中有两个函数a
和 c
,b.cpp
文件只有b
函数,dome.cpp
文件只有MyMain
函数,那么函数排列的顺序就是这样的
首先我们来验证第一条结论

用IDA来查看

接着我们来修改vcxproj
文件中文件中的位置,可以发现论证成立

接着我们来验证第二个结论,可见结论完全正确

编写Shellcode
首先vs文件创建排序是按数字其次才是字母排序,编译顺序也是如此(0-9-a-z)
首先我们创建一侧项目,然后按上面的要求来配置我们的文件,最后我们可以得到一个入下图所示的解决方案

我们只需要把代码放到z.end.cpp
和a.start.cpp
之间即可
首先我们贴0.main.cpp
的代码,由于该函数不需要加载在shellcode中,所以我们可以按照正常的写法来写,然后获取ShellcodeEnd函数还有ShellcodeStart函数使他们相减即可
#include"statement.h" #pragma comment(linker,"/entry:MyMain") void MyMain() { CreateShellcode();
} void CreateShellcode() {
HMODULE hMsvcrt = LoadLibraryA("msvcrt.dll"); HANDLE hBin = CreateFileA("sh.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (hBin == INVALID_HANDLE_VALUE) { return; } DWORD dwSize = (DWORD)ShellcodeEnd - (DWORD)ShellcodeStart; DWORD dwWrite; WriteFile(hBin, ShellcodeStart, dwSize, &dwWrite, NULL); CloseHandle(hBin);
}
|
接着我们来定义api.h
头文件,这个文件都存放着我们所有的动态调用的api类,定义好动态调用的类后我们把它用typedef来集合起来,看不懂的可以看我原来搬运的一篇文章
#pragma once #include<windows.h> typedef FARPROC(WINAPI* FN_GetProcAddress)( __in HMODULE hModule, __in LPCSTR lpProcName ); typedef HMODULE(WINAPI* FN_LoadLibraryA)( __in LPCSTR lpLibFileName ); typedef int(WINAPI* FN_MessageBoxA)( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType);
typedef HANDLE(WINAPI* FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile );
typedef struct _FUNCTIONS { FN_GetProcAddress fn_GetProcAddress; FN_LoadLibraryA fn_LoadLibraryA; FN_MessageBoxA fn_MessageBoxA; FN_CreateFileA fn_CreateFileA;
}FUNCTIONS, * PFUNCTIONS;
|
接着是statement.h
头文件,这个文件声明了我们所有自定义的函数
#pragma once #include<windows.h> #include"api.h" void ShellcodeStart(); void ShellcodeEntry(); void ShellcodeEnd(); void CreateShellcode(); void InitFunctions(PFUNCTIONS pFn); void CreateConfigFile(PFUNCTIONS pFn);
|
接着是a.start.cpp
文件,首先需要定一个ShellcodeStart函数,这个函数是我们所有代码的入口,一个jmp
的汇编命令,该命令跳转到的我们真正的起始函数中,接下来就是获取基址,以及一些动态调用的赋值
#include"statement.h" #include"api.h"
__declspec(naked) void ShellcodeStart() { __asm { jmp ShellcodeEntry }
}
__declspec(naked) DWORD getKernel32() { __asm { mov eax, fs: [030h] ; test eax, eax; js finished; mov eax, [eax + 0ch]; mov eax, [eax + 14h]; mov eax, [eax]; mov eax, [eax] mov eax, [eax + 10h] finished: ret } }
FARPROC getProcAddress(HMODULE hModuleBase) { FARPROC pRet = NULL; PIMAGE_DOS_HEADER lpDosHeader; PIMAGE_NT_HEADERS32 lpNtHeaders; PIMAGE_EXPORT_DIRECTORY lpExports; PWORD lpwOrd; PDWORD lpdwFunName; PDWORD lpdwFunAddr; DWORD dwLoop;
lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase; lpNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew); if (!lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) { return pRet; } if (!lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { return pRet; } lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (!lpExports->NumberOfNames) { return pRet; } lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames); lpwOrd = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions); for (dwLoop = 0; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pszFunction = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase); if (pszFunction[0] == 'G' && pszFunction[1] == 'e' && pszFunction[2] == 't' && pszFunction[3] == 'P' && pszFunction[4] == 'r' && pszFunction[5] == 'o' && pszFunction[6] == 'c' && pszFunction[7] == 'A' && pszFunction[8] == 'd' && pszFunction[9] == 'd' && pszFunction[10] == 'r' && pszFunction[11] == 'e' && pszFunction[12] == 's' && pszFunction[13] == 's') { pRet = (FARPROC)(lpdwFunAddr[lpwOrd[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; }
void InitFunctions(PFUNCTIONS pFn) { pFn->fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32()); char szLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 }; pFn->fn_LoadLibraryA = (FN_LoadLibraryA)pFn->fn_GetProcAddress((HMODULE)getKernel32(), szLoadLibraryA);
char szUser32[] = { 'U','s','e','r','3','2','.','d','l','l',0 }; char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 }; pFn->fn_MessageBoxA = (FN_MessageBoxA)pFn->fn_GetProcAddress(pFn->fn_LoadLibraryA(szUser32), szMessageBoxA);
char szCreateFileA[] = { 'C','r','e','a','t','e','F','i','l','e','A',0 }; pFn->fn_CreateFileA = (FN_CreateFileA)pFn->fn_GetProcAddress((HMODULE)getKernel32(), szCreateFileA);
}
void ShellcodeEntry() {
FUNCTIONS fn; InitFunctions(&fn); CreateConfigFile(&fn);
}
|
b.code.cpp
这个文件是我们所有的函数使用,如果还想加函数使用之类的都可以写在这边,然后在a.start.cpp
中的 ShellcodeEntry函数调用即可
#include"statement.h" #include"api.h" void CreateConfigFile(PFUNCTIONS pFn) { char szHello[] = { 'H','e','l','l','o',',','a','s','c','o','t','b','e','!',0 }; char szTip[] = { '啊','巴','啊','巴',0 }; pFn->fn_MessageBoxA(NULL, szHello, szTip, MB_OK); char szName[] = { '1','.','t','x','t',0 }; pFn->fn_CreateFileA(szName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
}
|
最后是z.end.cpp
这里面只要写一个结束的函数就行
#include "api.h" void ShellcodeEnd() {
}
|
生成代码,可以发现顺序完全一致

最后我们来看最终成果

参考文章
<Windows平台高效Shellcode编程技术实战> https://bbs.bccn.net/thread-464861-1-1.html
|