Windows Shellcode学习笔记——Shellcode的提取与测试

本文讲的是Windows Shellcode学习笔记——Shellcode的提取与测试


0x00 前言

之前在《Windows Shellcode学习笔记——通过VisualStudio生成shellcode》介绍了使用C++编写(不使用内联汇编),实现动态获取API地址并调用,对其反汇编提取shellcode的方法,并开源了测试代码。

接下来在对shellcode进行提取的过程中,发现了当时开源代码的一些bug,所以本文着重解决测试代码的bug,并介绍使用C++开发shellcode需要考虑的一些问题。

存在bug的测试代码下载地址:

https://github.com/3gstudent/Shellcode-Generater/blob/master/shellcode.cpp

0x01 简介

简单的shellcode提取流程:

使用c++开发代码
更改VisualStudio编译配置
生成exe
在IDA下打开生成的exe,获得机器码

由于是动态获取API地址并调用,所以为了保证shellcode的兼容性,代码中不能出现固定地址,并且要尽量避免使用全局变量,如果代码中包含子函数,根据调用方式,还有注意各个函数之间的排列顺序(起始函数放于最前)

0x02 Bug修复

配置三个编译选项:release、禁用优化、禁用/GS

将代码编译,然后使用IDA提取机器码作为shellcode

在实际调试过程中,发现代码存在bug:

1、代码中应合理处理全局变量

在代码中使用全局变量

FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);
HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);

在编译后会成为一个固定地址,导致shellcode无法兼容不同环境

最简单直接的方式是在shellcode中尽量避免全局变量

2、函数声明方式需要修改

修改全局变量后,以下代码需要修改:

MESSAGEBOXA_INITIALIZE MeassageboxA_MyOwn = reinterpret_cast<MESSAGEBOXA_INITIALIZE>(GetProcAddressAPI(LoadLibraryWAPI(struser32), MeassageboxA_api));
MeassageboxA_MyOwn(NULL, NULL, NULL, 0);

需要全部换成typedef的函数声明方式

3、函数调用顺序

如果使用以下方式加载shellcode:

(*(int(*)()) sc)();

起始函数的定义应该位于这段shellcode的最前面(和函数声明的顺序无关)

注:

shellcode如果包含子函数,应该保证各个函数放在一段连续的地址中,并且起始函数置于最前面,这样在提取机器码后,可以直接加载起始函数执行shellcode

综上,给出新的完整代码:

#include <windows.h>
#include <Winternl.h>
#pragma optimize( "", off ) 
void shell_code();
HANDLE GetKernel32Handle();
BOOL __ISUPPER__(__in CHAR c);
CHAR __TOLOWER__(__in CHAR c);
UINT __STRLEN__(__in LPSTR lpStr1);
UINT __STRLENW__(__in LPWSTR lpStr1);
LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2);
INT __STRCMPI__(__in LPSTR lpStr1, __in LPSTR lpStr2);
INT __STRNCMPIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2, __in DWORD dwLen);
LPVOID __MEMCPY__(__in LPVOID lpDst, __in LPVOID lpSrc, __in DWORD dwCount);
typedef FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);
typedef HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);
typedef ULONG (WINAPI *MESSAGEBOXAPI)(HWND, LPWSTR, LPWSTR, ULONG);
void shell_code() {
    LoadLibraryWAPI loadlibrarywapi = 0;
    GetProcAddressAPI getprocaddressapi=0;
    MESSAGEBOXAPI messageboxapi=0;
    wchar_t struser32[] = { L'u', L's', L'e', L'r', L'3',L'2', L'.', L'd', L'l', L'l', 0 };
    char MeassageboxA_api[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', 0 };
    HANDLE hKernel32 = GetKernel32Handle();
    if (hKernel32 == INVALID_HANDLE_VALUE) {
        return;
    }
    LPBYTE lpBaseAddr = (LPBYTE)hKernel32;
    PIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER)lpBaseAddr;
    PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(lpBaseAddr + lpDosHdr->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(lpBaseAddr + pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    LPDWORD pNameArray = (LPDWORD)(lpBaseAddr + pExportDir->AddressOfNames);
    LPDWORD pAddrArray = (LPDWORD)(lpBaseAddr + pExportDir->AddressOfFunctions);
    LPWORD pOrdArray = (LPWORD)(lpBaseAddr + pExportDir->AddressOfNameOrdinals);
    CHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'W', 0x0 };
    CHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };
    for (UINT i = 0; i < pExportDir->NumberOfNames; i++) {
        LPSTR pFuncName = (LPSTR)(lpBaseAddr + pNameArray[i]);
        if (!__STRCMPI__(pFuncName, strGetProcAddress)) {
            getprocaddressapi=(GetProcAddressAPI)(lpBaseAddr + pAddrArray[pOrdArray[i]]);
        }
        else if (!__STRCMPI__(pFuncName, strLoadLibraryA)) {
            loadlibrarywapi=(LoadLibraryWAPI) (lpBaseAddr + pAddrArray[pOrdArray[i]]);
        }
        if (getprocaddressapi != nullptr && loadlibrarywapi != nullptr) {               
            messageboxapi=(MESSAGEBOXAPI)getprocaddressapi(loadlibrarywapi(struser32), MeassageboxA_api);
            messageboxapi(NULL, NULL, NULL, 0);
            return;
        }
    }
}
inline BOOL __ISUPPER__(__in CHAR c) {
    return ('A' <= c) && (c <= 'Z');
};
inline CHAR __TOLOWER__(__in CHAR c) {
    return __ISUPPER__(c) ? c - 'A' + 'a' : c;
};
UINT __STRLEN__(__in LPSTR lpStr1)
{
    UINT i = 0;
    while (lpStr1[i] != 0x0)
        i++;
    return i;
}
UINT __STRLENW__(__in LPWSTR lpStr1)
{
    UINT i = 0;
    while (lpStr1[i] != L'')
        i++;
    return i;
}
LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2)
{
    CHAR c = __TOLOWER__(((PCHAR)(lpStr2++))[0]);
    if (!c)
        return lpStr1;
    UINT dwLen = __STRLENW__(lpStr2);
    do
    {
        CHAR sc;
        do
        {
            sc = __TOLOWER__(((PCHAR)(lpStr1)++)[0]);
            if (!sc)
                return NULL;
        } while (sc != c);
    } while (__STRNCMPIW__(lpStr1, lpStr2, dwLen) != 0);
    return (lpStr1 - 1); // FIXME -2 ?
}
INT __STRCMPI__(
    __in LPSTR lpStr1,
    __in LPSTR lpStr2)
{
    int  v;
    CHAR c1, c2;
    do
    {
        c1 = *lpStr1++;
        c2 = *lpStr2++;
        // The casts are necessary when pStr1 is shorter & char is signed 
        v = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);
    } while ((v == 0) && (c1 != '') && (c2 != ''));
    return v;
}
INT __STRNCMPIW__(
    __in LPWSTR lpStr1,
    __in LPWSTR lpStr2,
    __in DWORD dwLen)
{
    int  v;
    CHAR c1, c2;
    do {
        dwLen--;
        c1 = ((PCHAR)lpStr1++)[0];
        c2 = ((PCHAR)lpStr2++)[0];
        /* The casts are necessary when pStr1 is shorter & char is signed */
        v = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);
    } while ((v == 0) && (c1 != 0x0) && (c2 != 0x0) && dwLen > 0);
    return v;
}
LPSTR __STRCAT__(
    __in LPSTR  strDest,
    __in LPSTR strSource)
{
    LPSTR d = strDest;
    LPSTR s = strSource;
    while (*d) d++;
    do { *d++ = *s++; } while (*s);
    *d = 0x0;
    return strDest;
}
LPVOID __MEMCPY__(
    __in LPVOID lpDst,
    __in LPVOID lpSrc,
    __in DWORD dwCount)
{
    LPBYTE s = (LPBYTE)lpSrc;
    LPBYTE d = (LPBYTE)lpDst;
    while (dwCount--)
        *d++ = *s++;
    return lpDst;
}
HANDLE GetKernel32Handle() {
    HANDLE hKernel32 = INVALID_HANDLE_VALUE;
#ifdef _WIN64
    PPEB lpPeb = (PPEB)__readgsqword(0x60);
#else
    PPEB lpPeb = (PPEB)__readfsdword(0x30);
#endif
    PLIST_ENTRY pListHead = &lpPeb->Ldr->InMemoryOrderModuleList;
    PLIST_ENTRY pListEntry = pListHead->Flink;
    WCHAR strDllName[MAX_PATH];
    WCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'' };
    while (pListEntry != pListHead) {
        PLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        if (pModEntry->FullDllName.Length) {
            DWORD dwLen = pModEntry->FullDllName.Length;
            __MEMCPY__(strDllName, pModEntry->FullDllName.Buffer, dwLen);
            strDllName[dwLen / sizeof(WCHAR)] = L'';
            if (__STRSTRIW__(strDllName, strKernel32)) {
                hKernel32 = pModEntry->DllBase;
                break;
            }
        }
        pListEntry = pListEntry->Flink;
    }
    return hKernel32;
}
int main()
{
    printf("1");
    shell_code();
    printf("2");
    return 0;
}

0x03 Shellcode提取

将以上代码编译成exe后使用IDA打开,查看Function Window,找到各子函数起始地址

如图

可以看到各个函数保存在一段连续的地址,并且shellcode起始函数位于最开始

双击第一个函数shell_code(void),进入IDA文本视图,可查看shell_code(void)函数具体在exe文件中的位置为00000400

如图

查看main函数在exe文件中的位置为00000A00

如图

结合c代码的结构,推断出在exe文件中的偏移范围00000400-00000A00即为我们需要的机器码

使用十六进制编辑器将其中的机器码提取并保存到文件中,文件中的内容即我们需要的shellcode

当然,以上手动提取机器码并保存到文件的功能可通过程序自动实现,完整代码如下:

#include <stdafx.h>
#include <windows.h>
#include <Winternl.h>
#pragma optimize( "", off ) 
void shell_code();
HANDLE GetKernel32Handle();
BOOL __ISUPPER__(__in CHAR c);
CHAR __TOLOWER__(__in CHAR c);
UINT __STRLEN__(__in LPSTR lpStr1);
UINT __STRLENW__(__in LPWSTR lpStr1);
LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2);
INT __STRCMPI__(__in LPSTR lpStr1, __in LPSTR lpStr2);
INT __STRNCMPIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2, __in DWORD dwLen);
LPVOID __MEMCPY__(__in LPVOID lpDst, __in LPVOID lpSrc, __in DWORD dwCount);
typedef FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);
typedef HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);
typedef ULONG (WINAPI *MESSAGEBOXAPI)(HWND, LPWSTR, LPWSTR, ULONG);
void shell_code() {
    LoadLibraryWAPI loadlibrarywapi = 0;
    GetProcAddressAPI getprocaddressapi=0;
    MESSAGEBOXAPI messageboxapi=0;
    wchar_t struser32[] = { L'u', L's', L'e', L'r', L'3',L'2', L'.', L'd', L'l', L'l', 0 };
    char MeassageboxA_api[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', 0 };
    HANDLE hKernel32 = GetKernel32Handle();
    if (hKernel32 == INVALID_HANDLE_VALUE) {
        return;
    }
    LPBYTE lpBaseAddr = (LPBYTE)hKernel32;
    PIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER)lpBaseAddr;
    PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(lpBaseAddr + lpDosHdr->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(lpBaseAddr + pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    LPDWORD pNameArray = (LPDWORD)(lpBaseAddr + pExportDir->AddressOfNames);
    LPDWORD pAddrArray = (LPDWORD)(lpBaseAddr + pExportDir->AddressOfFunctions);
    LPWORD pOrdArray = (LPWORD)(lpBaseAddr + pExportDir->AddressOfNameOrdinals);
    CHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'W', 0x0 };
    CHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };
    for (UINT i = 0; i < pExportDir->NumberOfNames; i++) {
        LPSTR pFuncName = (LPSTR)(lpBaseAddr + pNameArray[i]);
        if (!__STRCMPI__(pFuncName, strGetProcAddress)) {
            getprocaddressapi=(GetProcAddressAPI)(lpBaseAddr + pAddrArray[pOrdArray[i]]);
        }
        else if (!__STRCMPI__(pFuncName, strLoadLibraryA)) {
            loadlibrarywapi=(LoadLibraryWAPI) (lpBaseAddr + pAddrArray[pOrdArray[i]]);
        }
        if (getprocaddressapi != nullptr && loadlibrarywapi != nullptr) {               
            messageboxapi=(MESSAGEBOXAPI)getprocaddressapi(loadlibrarywapi(struser32), MeassageboxA_api);
            messageboxapi(NULL, NULL, NULL, 0);
            return;
        }
    }
}
inline BOOL __ISUPPER__(__in CHAR c) {
    return ('A' <= c) && (c <= 'Z');
};
inline CHAR __TOLOWER__(__in CHAR c) {
    return __ISUPPER__(c) ? c - 'A' + 'a' : c;
};
UINT __STRLEN__(__in LPSTR lpStr1)
{
    UINT i = 0;
    while (lpStr1[i] != 0x0)
        i++;
    return i;
}
UINT __STRLENW__(__in LPWSTR lpStr1)
{
    UINT i = 0;
    while (lpStr1[i] != L'')
        i++;
    return i;
}
LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2)
{
    CHAR c = __TOLOWER__(((PCHAR)(lpStr2++))[0]);
    if (!c)
        return lpStr1;
    UINT dwLen = __STRLENW__(lpStr2);
    do
    {
        CHAR sc;
        do
        {
            sc = __TOLOWER__(((PCHAR)(lpStr1)++)[0]);
            if (!sc)
                return NULL;
        } while (sc != c);
    } while (__STRNCMPIW__(lpStr1, lpStr2, dwLen) != 0);
    return (lpStr1 - 1); // FIXME -2 ?
}
INT __STRCMPI__(
    __in LPSTR lpStr1,
    __in LPSTR lpStr2)
{
    int  v;
    CHAR c1, c2;
    do
    {
        c1 = *lpStr1++;
        c2 = *lpStr2++;
        // The casts are necessary when pStr1 is shorter & char is signed 
        v = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);
    } while ((v == 0) && (c1 != '') && (c2 != ''));
    return v;
}
INT __STRNCMPIW__(
    __in LPWSTR lpStr1,
    __in LPWSTR lpStr2,
    __in DWORD dwLen)
{
    int  v;
    CHAR c1, c2;
    do {
        dwLen--;
        c1 = ((PCHAR)lpStr1++)[0];
        c2 = ((PCHAR)lpStr2++)[0];
        /* The casts are necessary when pStr1 is shorter & char is signed */
        v = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);
    } while ((v == 0) && (c1 != 0x0) && (c2 != 0x0) && dwLen > 0);
    return v;
}
LPSTR __STRCAT__(
    __in LPSTR  strDest,
    __in LPSTR strSource)
{
    LPSTR d = strDest;
    LPSTR s = strSource;
    while (*d) d++;
    do { *d++ = *s++; } while (*s);
    *d = 0x0;
    return strDest;
}
LPVOID __MEMCPY__(
    __in LPVOID lpDst,
    __in LPVOID lpSrc,
    __in DWORD dwCount)
{
    LPBYTE s = (LPBYTE)lpSrc;
    LPBYTE d = (LPBYTE)lpDst;
    while (dwCount--)
        *d++ = *s++;
    return lpDst;
}
HANDLE GetKernel32Handle() {
    HANDLE hKernel32 = INVALID_HANDLE_VALUE;
#ifdef _WIN64
    PPEB lpPeb = (PPEB)__readgsqword(0x60);
#else
    PPEB lpPeb = (PPEB)__readfsdword(0x30);
#endif
    PLIST_ENTRY pListHead = &lpPeb->Ldr->InMemoryOrderModuleList;
    PLIST_ENTRY pListEntry = pListHead->Flink;
    WCHAR strDllName[MAX_PATH];
    WCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'' };
    while (pListEntry != pListHead) {
        PLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        if (pModEntry->FullDllName.Length) {
            DWORD dwLen = pModEntry->FullDllName.Length;
            __MEMCPY__(strDllName, pModEntry->FullDllName.Buffer, dwLen);
            strDllName[dwLen / sizeof(WCHAR)] = L'';
            if (__STRSTRIW__(strDllName, strKernel32)) {
                hKernel32 = pModEntry->DllBase;
                break;
            }
        }
        pListEntry = pListEntry->Flink;
    }
    return hKernel32;
}
void __declspec(naked) END_SHELLCODE(void) {}
int main()
{
    shell_code();
    FILE *output_file;
    fopen_s(&output_file,"shellcode.bin", "wb");
    fwrite(shell_code, (int)END_SHELLCODE - (int)shell_code, 1, output_file);
    fclose(output_file);
    return 0;
}

注:

打开文件需要以”wb”模式打开二进制文件 

如果以”w”模式,写入文件的过程中,0A字符会被替换为0D0A,导致shellcode出现问题

0x04 Shellcode测试

使用以下代码可读取文件中保存的shellcode,加载并测试其功能:

#include <windows.h>
size_t GetSize(char * szFilePath)
{
    size_t size;
    FILE* f = fopen(szFilePath, "rb");
    fseek(f, 0, SEEK_END);
    size = ftell(f);
    rewind(f);
    fclose(f);
    return size;
}
unsigned char* ReadBinaryFile(char *szFilePath, size_t *size)
{
    unsigned char *p = NULL;
    FILE* f = NULL;
    size_t res = 0;
    *size = GetSize(szFilePath);
    if (*size == 0) return NULL;        
    f = fopen(szFilePath, "rb");
    if (f == NULL)
    {
        printf("Binary file does not exists!n");
        return 0;
    }
    p = new unsigned char[*size];
    rewind(f);
    res = fread(p, sizeof(unsigned char), *size, f);
    fclose(f);
    if (res == 0)
    {
        delete[] p;
        return NULL;
    }
    return p;
}
int main(int argc, char* argv[])
{
    char *szFilePath="c:testshellcode.bin";
    unsigned char *BinData = NULL;
    size_t size = 0;    
    BinData = ReadBinaryFile(szFilePath, &size);
    void *sc = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (sc == NULL) 
        return 0;   
    memcpy(sc, BinData, size);
    (*(int(*)()) sc)(); 
    return 0;
}

原文发布时间为:2017年3月15日

本文作者:3gstudent

本文来自合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。

原文链接

时间: 2025-01-06 15:59:20

Windows Shellcode学习笔记——Shellcode的提取与测试的相关文章

Windows Shellcode学习笔记——shellcode在栈溢出中的利用与优化

本文讲的是Windows Shellcode学习笔记--shellcode在栈溢出中的利用与优化, 0x00 前言 在<Windows Shellcode学习笔记--shellcode的提取与测试>中介绍了如何对shellcode作初步优化,动态获取Windows API地址并调用,并通过程序实现自动提取机器码作为shellcode并保存到文件中. 0x01 简介 先从最入门的缓冲区溢出开始 本文将要结合<0day安全:软件漏洞分析技术>中的"栈溢出原理与实践"

Windows Shellcode学习笔记——通过VisualStudio生成shellcode

本文讲的是Windows Shellcode学习笔记--通过VisualStudio生成shellcode, 0x00 前言 shellcode是一段机器码,常用作漏洞利用中的载荷(也就是payload). 在渗透测试中,最简单高效的方式是通过metasploit生成shellcode,然而在某些环境下,需要定制开发自己的shellcode,所以需要对shellcode的开发作进一步研究. 0x01 简介 编写Shellcode的基本方式有3种: 直接编写十六进制操作码 采用C或者Delphi这

Windows Shellcode学习笔记——通过VirtualProtect绕过DEP

本文讲的是Windows Shellcode学习笔记--通过VirtualProtect绕过DEP, 0x00 前言 在掌握了栈溢出的基本原理和利用方法后,接下来就要研究如何绕过Windows系统对栈溢出利用的重重防护,所以测试环境也从xp转到了Win7(相比xp,Win7的防护更全面).本文将要介绍经典的DEP绕过方法--通过VirtualProtect绕过DEP. 0x01 简介 本文将要介绍以下内容: VS2012的编译配置 利用Immunity Debugger的mona插件自动获取RO

Windows Shellcode学习笔记——利用VirtualAlloc绕过DEP

本文讲的是Windows Shellcode学习笔记--利用VirtualAlloc绕过DEP, 0x00 前言 接着介绍DEP绕过的另一种方法--利用VirtualAlloc绕过DEP.通过VirtualAlloc函数可以申请一段具有可执行属性的内存,相比于VirtualProtect,传入VirtualAlloc的四个参数不需要先读取再赋值,可在shellcode中直接指定,结构更简单.当然,利用Immunity Debugger的mona插件可自动构造利用VirtualAlloc绕过DEP

Windows Api学习笔记-动态连接库(DLL)的使用

#include <windows.h> #include <iostream> #include "12dll.h" using namespace std; #pragma comment(lib,"12Dll")//要链接到什么库文件 void main() { //CMy12Dll a; cout<<fnMy12Dll()<<endl; char b; cin>>b; } VS2008 新建WIN3

windows api学习笔记-使用定时器

#include <windows.h> #include "resource.h" #include <string> LRESULT CALLBACK MainWndProc(HWND,UINT,WPARAM,LPARAM);//窗口函数的函数原型 int APIENTRY WinMain( //APIENTRY是__stdcall的宏定义 HINSTANCE hInstance, //本模块的实例句柄 HINSTANCE hPrevInstance, //

windows api学习笔记-简单的记事本

#include <windows.h> #include "resource.h" #include <string> LRESULT CALLBACK MainWndProc(HWND,UINT,WPARAM,LPARAM);//窗口函数的函数原型 int APIENTRY WinMain( //APIENTRY是__stdcall的宏定义 HINSTANCE hInstance, //本模块的实例句柄 HINSTANCE hPrevInstance, //

windows api学习笔记-遍历系统进程,获取进程名称和ID(进程快照)

#include <windows.h>//系统会自动连接到指定的库文件lib #include <tlhelp32.h>//声明快照函数的头文件 #include <stdio.h>//std io 标准输入输出接口 #include <iostream> using namespace std; int main() { PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); HANDLE hProcessSna

windows api学习笔记-键盘钩子

DLL项目的头文件 #ifdef KEYHOOKLIB_EXPORT //此宏将在CPP文件中定义 #define KEYHOOKLIB_API __declspec(dllexport) #else #define KEYHOOKLIB_API __declspec(dllimport) #endif #define HM_KEY WM_USER+101 BOOL KEYHOOKLIB_API WINAPI SetKeyHook(BOOL bInstall,DWORD dwThreadId =