windows核心编程---DLL注入和API拦截

-派生子类窗口,要派生窗口属于另一个进程
SetWindowLongPtr会检查一个进程试图修改的WndProc是否是另一个进程的窗口的,如果是,忽略调用。

可以在进程A获得进程B窗口的句柄。
FindWindow(xx);
// 派生子类窗口,现在应用:
SetWindowSubclass
GetWindowSubclass
RemoveWindowSubclass
DefSubclassProc

-通过使用注册表来注入DLL
1.注册表编辑器
Registry Editor
2.
HKEY_LOCAL_MACHINE\SoftWare\Microsoft\ Windows NT\CurrentVersion\Windows\

AppInit_Dlls键值可能包含一个DLL文件名或一组DLL文件名(用空格或逗号分隔)。
第一个DLL的文件名可包含路径,其它DLL包含的路径被忽略。
为了让系统使用这个注册表项,须创建一个项:
DWORD LoadAppInit_Dlls = 1

3.
处理过程:
User32.dll被映射到一个新进程时,会收到DLL_PROCESS_ATTACH通知。
User32.dll对它处理时,会取得上述注册表键值。调用LoadLibrary载入这个字符串中指定的每个DLL。
被注入的DLL,在进程生命期早期载入的。User32.dll不会检查每个DLL的载入和初始化是否成功。

-使用windows挂钩来注入DLL

// 安装挂钩
HHOOK WINAPI SetWindowsHookEx(
 // 要挂钩的类型
  _In_ int       idHook,
 // 挂钩函数地址
  _In_ HOOKPROC  lpfn,
 // DLL标识,进程地址空间DLL被映射到的虚拟内存地址
  _In_ HINSTANCE hMod,
 // 要安装挂钩的线程
 // 0 给系统中所有线程安装
  _In_ DWORD     dwThreadId
);

举例:
a.进程A安装了WH_GETMESSAGE挂钩。
HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hInstDll, 0);

b.
进程B的一个线程准备向一个窗口 派送消息。
系统检查该线程是否安装了 WH_GETMESSAGE挂钩。
系统检查GetMsgProc所在的DLL是否被映射到了进程B的地址空间。
如未映射,系统会强制将该DLL映射到进程B的地址空间,并将进程B中该DLL的锁计数器递增。
检查该DLL在进程B的基地址和进程A的基地址是否相同。
相同时,可直接在进程A的地址空间中调用GetMsgProc。
不同时,系统须确定GetMsgProc在进程B地址空间的虚地址:
GetMsgProc B = hInstDll B + (GetMsgProc A - hInstDll A)
系统在进程B中递增该DLL的锁计数器
系统在进程B的地址空间调用GetMsgProc函数。
GetMsgProc返回时,系统递减该DLL在进程B的锁计数器。

系统把挂钩过滤函数所在的DLL注入或映射到地址空间时,会映射整个DLL。

c.不需要DLL时,从进程地址空间撤销对它的映射

BOOL WINAPI UnhookWindowsHookEx(
  _In_ HHOOK hhk
);

线程调用UnhookWindowsHookEx时,系统会遍历自己内部的一个已经注入过该DLL的进程列表,并将该DLL的锁计数器递减。
锁计数器减到0时,系统自动从进程的地址空间撤销对该DLL的映射。

-其它
一些公共空间的窗口消息,携带指针信息,指针一般使窗口所在进程地址空间内的虚拟地址,这些地址不应被另一进程使用。

1.

// 由窗口句柄,取得窗口所在线程,进程ID
DWORD WINAPI GetWindowThreadProcessId(
  _In_      HWND    hWnd,
  _Out_opt_ LPDWORD lpdwProcessId
);

2.

// 安装一个应用定义的挂钩处理函数。
// 用于监视指定线程的,或和调用线程在相同桌面的所有线程的事件。
HHOOK WINAPI SetWindowsHookEx(
// 事件类型:消息投递到线程队列
// WH_GETMESSAGE 
  _In_ int       idHook,
  // 处理函数
  _In_ HOOKPROC  lpfn,
  // 处理函数所在的模块。
  // 指定为当前进程内线程时,模块为NULL
  // 指定为其它进程的线程时,模块为包含处理函数的DLL
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);

// WH_GETMESSAGE 
类型回调函数
// 调用时机:线程消息队列出现有效消息
// 挂钩处理完,指定线程的GetMessage和PeekMessage调用才能返回
LRESULT CALLBACK GetMsgProc(
// HC_ACTION
// < 0,return CallNextHookEx继续处理
  _In_ int    code,
  _In_ WPARAM wParam,
  // MSG*,消息细节
  _In_ LPARAM lParam
);

窗口句柄可以跨进程使用。
进程A可以取得进程B的所创建的一个窗口的句柄,进而给进程B的窗口发消息。
一些后来的通用控件,不能接收和处理另一个进程给它发的消息。
早期的控件,为了和16为windows兼容(16位windows所有进程共享同一地址空间),都做了,跨进程接收处理消息。

-注册表操作
注册表是系统定义的数据库,应用和系统组件在其中取得和存储配置数据。
可用注册表API来取得,修改,删除注册数据。

1.注册表的结构
键,有名字。
键名大小写不敏感。不包含’\’,值名和值可包含。
键–子键
子键–子键
值:值名,类型,值内容

1.1.注册值类型
REG_BINARY
二进制
REG_DWORD
32位数
REG_EXPAND_SZ
形如:”%PATH%”,ExpandEnvironmentStrings用于展开。

REG_LINK

REG_MULTI_SZ
形如:
“String1\0 String2\0 String3\0 LastString\0\0

“\0”

REG_QWORD
64位数
REG_SZ
字符串

存储字符串时,指定的长度要包含终止符’\0’。
字符串以ANSI还是UNICODE被存储,取决与存储时API的类型。
1.2.注册表元素尺寸限制
键名:255字节,包含键的绝对路径。
值名:16383字符。
值:
树:512深度

2.注册表容量空间
3.预定义键
用于在添加数据到注册表前,须先打开键。
为了打开键,应用须提供一个注册表中其它已打开键的句柄,系统预定义键通常被打开。
预定义键帮助应用在注册表导航。
HKEY_CLASSES_ROOT:
Shell和COM应用使用存储在这个键下的信息。
文件浏览和用户界面扩展存储它们的OLE类标识在此键下。
进程服务。
HKEY_CURRENT_CONFIG

包含本机系统当前硬件配置信息。
描述当前硬件配置和标准配置信息的不同。
标准硬件配置信息,存储在HKEY_LOCAL_MACHINE的software和System下。
HKEY_CURRENT_USER

当前用户,环境变量,程序组数据,颜色,打印,网络链接,应用设置。
HKEY_CURRENT_USER_LOCAL_SETTINGS

HKEY_LOCAL_MACHINE

电脑物理状态
HKEY_PERFORMANCE_DATA

存储性能数据
HKEY_USERS

默认用户配置
当前用户配置

4.Registry Hives:当前用户配置信息
存放配置信息文件目录:%SystemRoot%\System32\Config
文件后缀及含义:
none:hive数据的完整拷贝
.alt:HKEY_LOCAL_MACHINE\System
.log:日志
.sav
:备份

HKEY_CURRENT_CONFIG :
System, System.alt, System.log, System.sav
HKEY_CURRENT_USER:
Ntuser.dat, Ntuser.dat.log
HKEY_LOCAL_MACHINE\SAM:
Sam, Sam.log, Sam.sav
HKEY_LOCAL_MACHINE\Security:
Security, Security.log, Security.sav
HKEY_LOCAL_MACHINE\Software:
Software, Software.log, Software.sav
HKEY_LOCAL_MACHINE\System:
System, System.alt, System.log, System.sav
HKEY_USERS.DEFAULT:
Default, Default.log, Default.sav

5.数据目录
电脑指定数据:
放置于 HKEY_LOCAL_MACHINE\Software下
用户指定数据:
放置于HKEY_CURRENT_USER\Software下
6.打开,创建,关闭键

// 打开注册表
// 成功时,返回ERROR_SUCCESS.
LONG WINAPI RegOpenKeyEx(
// 打开的注册表键的句柄,可由RegOpenKeyEx或RegCreateKeyEx返回。
// 可为以下预定义键:
// HKEY_CLASSES_ROOT 
// HKEY_CURRENT_CONFIG 
// HKEY_CURRENT_USER 
// HKEY_LOCAL_MACHINE 
// HKEY_USERS
  _In_     HKEY    hKey,
// 要被打开的注册表子键名
  _In_opt_ LPCTSTR lpSubKey,
  // 打开选项
  // REG_OPTION_OPEN_LINK
  // 0
  _In_     DWORD   ulOptions,
  // 想要的权限
  _In_     REGSAM  samDesired,
  // 接收打开键句柄
  _Out_    PHKEY   phkResult
); 


// 创建
// 调用进程须有对指定键的KEY_CREATE_SUB_KEY权限。
// 成功时,ERROR_SUCCESS
// 不能创建HKEY_USER HKEY_LOCAL_MACHINE的一级子键
LONG WINAPI RegCreateKeyEx(
// 注册表键的句柄,可由RegOpenKeyEx或RegCreateKeyEx返回。
// 可为以下预定义键:
// HKEY_CLASSES_ROOT 
// HKEY_CURRENT_CONFIG 
// HKEY_CURRENT_USER 
// HKEY_LOCAL_MACHINE 
// HKEY_USERS
  _In_       HKEY                  hKey,
  // 要打开或创建的子键名
  _In_       LPCTSTR               lpSubKey,
  // 0
  _Reserved_ DWORD                 Reserved,
  // NULL
  _In_opt_   LPTSTR                lpClass,
  // REG_OPTION_BACKUP_RESTORE 
  // REG_OPTION_NON_VOLATILE 键被写入文件,值被保存
  // REG_OPTION_VOLATILE 键信息放于内存
  _In_       DWORD                 dwOptions,
  // 想要的权限
  _In_       REGSAM                samDesired,
  // 安全描述
  _In_opt_   LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  // 接收创建或打开键句柄
  _Out_      PHKEY                 phkResult,
  // REG_CREATED_NEW_KEY 创建的
  // REG_OPENED_EXISTING_KEY 打开的
  _Out_opt_  LPDWORD               lpdwDisposition
);

// 关闭指定键值句柄
// 关闭时,数据并不立即写入磁盘
LONG WINAPI RegCloseKey(
  _In_ HKEY hKey
);

// 立即写数据到磁盘
LONG WINAPI RegCloseKey(
  _In_ HKEY hKey
);

7.写/删除注册表数据

// 设置键
// 成功时,ERROR_SUCCESS
LONG WINAPI RegSetValueEx(
// 一个打开键的句柄,键应以KEY_SET_VALUE权限被打开
// 或 
// HKEY_CLASSES_ROOT 
// HKEY_CURRENT_CONFIG 
// HKEY_CURRENT_USER 
// HKEY_LOCAL_MACHINE 
// HKEY_USERS
  _In_             HKEY    hKey,
  // 值名。值名在键下不存在时,创建。
  _In_opt_         LPCTSTR lpValueName,
  // 0
  _Reserved_       DWORD   Reserved,
  // 数据类型
  _In_             DWORD   dwType,
  // 值
  // 字符串中'\'应以'\\'形式被存储
  _In_       const BYTE    *lpData,
  // 尺寸。类型为字符串时,尺寸大小要包括终止符。
  _In_             DWORD   cbData
);

// 删除值
LONG WINAPI RegDeleteValue(
  _In_     HKEY    hKey,
  _In_opt_ LPCTSTR lpValueName
);

// 删除键
// 键下的值也会被删除
// 删除的键,在指向它的最后一个句柄被关闭时,才移除。
LONG WINAPI RegDeleteKey(
  _In_ HKEY    hKey,
  // 键本身不能有子键
  _In_ LPCTSTR lpSubKey
);

8.从注册表取得数据
从注册表取得数据,应用枚举键的子键,直到发现目标,然后取得数据。
// 枚举键的子键
RegEnumKeyEx

// 取得详细数据
RegQueryInfoKey

// 取得安全描述
RegGetKeySecurity

// 枚举键的值 值名。
RegEnumValue

// ERROR_NO_MORE_ITEMS 表示枚举结束
// 存储在注册表的字符串可能存储时没有用终止符结尾。
// 从注册表取得字符串,并使用时,应确保使用的字符串以终止符结尾。
LONG WINAPI RegEnumValue(
  _In_        HKEY    hKey,
 // 每次枚举前,递增该索引
  _In_        DWORD   dwIndex,
  // 接收值名。缓冲要足够大可容纳终止符。
  _Out_       LPTSTR  lpValueName,
  // 缓存字符大小。
  // 返回时,指示缓冲中字符数。不含终止符。
  _Inout_     LPDWORD lpcchValueName,
  // NULL
  _Reserved_  LPDWORD lpReserved,
  // 接收数据类型
  _Out_opt_   LPDWORD lpType,
  // 接收数据
  _Out_opt_   LPBYTE  lpData,
  // 指定缓冲尺寸。尺寸要包含终止符。
  // 返回时,接收缓冲中存储字节数。
  _Inout_opt_ LPDWORD lpcbData
);

// 取得特定值。值名–>值
RegQueryValueEx

// 取得多个值
RegQueryMultipleValues

// 监视键变化
RegNotifyChangeKeyValue,键改变时,通知调用线程。键改变时,事件对象被触发,监视停止。
9.注册表文件
应用可保存部分注册表在文件,之后,把文件内容加载到注册表。
为保存键及其子键,值到注册表文件,应用可调用RegSaveKey或RegSaveKeyEx。

// 创建文件保存注册表部分数据
// 对本地键,文件被创建在进程当前目录
// 对远程键,在%systemroot%\system32
RegSaveKey/RegSaveKeyEx。

// 把注册表文件写回注册表
RegLoadKey/RegReplaceKey/RegRestoreKey

RegLoadKey:
加载注册表数据从指定文件,到HKEY_USERS或HKEY_LOCAL_MACHINE下的指定子键,在调用应用电脑或远程电脑。RegUnLoadKey恢复注册表到之前状态。

RegReplaceKey:
用文件包含数据替代键及其子键,值。下次启动生效。

RegRestoreKey:
加载注册表数据,从指定文件到指定键。

10.注册表键安全性和获取权限
// 获得安全描述
RegGetKeySecurity

//
GetNamedSecurityInfo

//
GetSecurityInfo

权限列表:
KEY_CREATE_SUB_KEY
KEY_ENUMERATE_SUB_KEYS
KEY_EXECUTE
KEY_NOTIFY
KEY_QUERY_VALUE
KEY_READ
KEY_SET_VALUE
KEY_WRITE

11.注册表中的32位和64位应用数据
在64位windows,部分注册表条目为32位应用,64位应用分开存储,且映射到独立的逻辑注册表视图。
12.注册表虚拟化

-使用远程线程来注入DLL
提供了最高的灵活性。

在目标进程中创建一个线程。

HANDLE CreateRemoteThread(
HANDLE hProcess,
PSECURITY_ATTRIBUTES psa,
DWORD dwStackSize,
// 线程函数的内存地址,内存地址应在远程进程地址空间
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD fdwCreate,
PDWORD pdwThreadId
);

// 远程线程调用LoadLibrary
HMODULE LoadLibrary(PCTSTR pszLibFile);

// 线程函数
DWORD WINAPI ThreadFunc(PVOID pvParam);

编译和链接一个程序时,生成的二进制文件中会包含一个导入段。
这个段由一系列转换函数构成。
链接器会生成一个调用,调用模块导入段的转换函数,转换函数会跳转到实际的函数。
略过转换函数,直接调用指定函数,须用GetProcAddress得到函数地址。

//
PTHREAD_START_ROUTINE pfnThreadRtn = 
(PTHREAD_START_ROUTINE)GetProcAddress(
GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

HANDLE hThread = CreateRemoteThread(
hProcessRemote,
NULL,
0,
pfnThreadRtn,
L("C:\\MyLib.dll"),
0,
NULL
);

// ANSI
PTHREAD_START_ROUTINE pfnThreadRtn = 
(PTHREAD_START_ROUTINE)GetProcAddress(
GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");

HANDLE hThread = CreateRemoteThread(
hProcessRemote,
NULL,
0,
pfnThreadRtn,
"C:\\MyLib.dll",
0,
NULL
);

需要把DLL的路径字符串存放到远程进程的地址空间,然后,调用CreateRemoteThread时,需传入远程进程中存放字符串的地址。

// 允许在另一进程地址空间预订+调拨。预订的地址是远程进程地址空间的
LPVOID WINAPI VirtualAllocEx(
  _In_     HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);

// 释放另一进程地址空间的内存。撤销预订+撤销调拨
BOOL WINAPI VirtualFreeEx(
  _In_ HANDLE hProcess,
  _In_ LPVOID lpAddress,
  _In_ SIZE_T dwSize,
  _In_ DWORD  dwFreeType
);

// windows提供了一些函数,可以让一个进程对另一个进程的地址空间进行读,写
BOOL ReadProcessMemory(
// 远程进程
HANDLE hProcess,
// 远程进程地址空间地址
LPCVOID pvAddressRemote,
// 调用进程地址空间地址
PVOID pvBufferLocal,
// 要传输字节
SIZE_T dwSize,
// 实际传输字节
SIZE_T* pdwNumBytesRead
);

BOOL WriteProcessMemory(
// 远程进程
HANDLE hProcess,
// 远程进程地址空间地址
PVOID pvAddressRemote,
// 调用进程地址空间地址
LPCVOID pvBufferLocal,
// 要传输字节
SIZE_T dwSize,
// 实际传输字节
SIZE_T* pdwNumBytesWritten
);

-Image Walk DLL
依据模块地址,取得模块路径

// 不去的以LOAD_LIBRARY_AS_DATAFILE 标志加载模块的路径
// 不包含终止符的拷贝字符数
DWORD WINAPI GetModuleFileName(
  _In_opt_ HMODULE hModule,
  _Out_    LPTSTR  lpFilename,
  // 字符数。包含终止符。
  _In_     DWORD   nSize
);

-使用木马来注入DLL
把知道的进程必然会载入的一个DLL替换掉。
例:
知道进程A会载入Xyz.dll。
创建自己的DLL,给它起同样的文件名。将原来的Xyz.dll改名。
自己的DLL内部,导出原来Xyz.dll导出的所有符号。

可给DLL起一个独一无二名字,修改exe模块的导入段。
在导入段找到要被替换的DLL名称,改为我们自己的DLL。

-把DLL作为调试器注入
载入被调试程序时,会在被调试程序地址空间准备完毕后,被调试程序主线程尚未开始执行前,通知调试器。
调试器这时,可将一些代码注入到被调试程序的地址空间,然后让被调试程序主线程执行注入代码。

-使用CreateProcess注入代码
父进程,在子进程主线程地址处,写入机器指令。达到主线程处,执行LoadLibrary功能,载入完毕。恢复主线程地址处内容,让进程从原来起始地址执行。

-API拦截
例:
DLL在卸载时,执行清理代码。
清理代码可能使用其它DLL中的函数,来执行清理。
但清理时,其它DLL可能已被卸载,导致该DLL函数调用失败,清理失败。

拦截ExitProcess:
ExitProcess被调用时,目标DLL立刻被通知,执行清理。之后,执行ExitProcess默认行为:通知所有DLL。

目标DLL被载入时,遍历所有已被载入的可执行模块和DLL模块,找到对ExitProcess的所有调用。
对各模块修改,使他们调用该公司DLL中的一个函数。
替代函数,先执行清理代码,再执行ExitProcess。

1.通过覆盖代码来拦截API
在内存对要拦截的函数进行定位,得到它的内存地址。
把函数的起始几个字节保存到我们自己的内存。
用CPU的一条JUMP指令来覆盖这个函数起始的几个字节,这条JUMP用来跳转到我们替代函数的内存地址。替代函数的函数签名须与要拦截函数的函数签名相同。
线程调用被拦截函数时,跳转指令会跳转到替代函数。
把保存的字节放回被拦截函数的起始几个字节来撤销拦截。

缺陷:
跳转的机器指令对CPU有依赖性,不同CPU下跳转指令不同。
不具备多线程安全性,线程覆盖起始位置代码需要时间,此时另一线程可能调用同一函数。

-修改模块的导入段来拦截API
须理解动态链接的工作方式。
理解模块的导入段包含了什么信息。

模块的导入段,包含一组DLL,还包含一个符号表。
当该模块调用一个导入函数时,线程实际先从模块的导入表,得到导入函数地址,再跳转到那个地址。
为拦截一个特定函数,须修改它在模块导入段中地址。

// 在一个模块的导入段,查找对一个符号的引用。如存在,修改该符号的地址。
void CAPIHook::ReplaceIATEntryInOneMod(
PCSTR pszCalleeModName,
PROC pfnCurrent,
PROC pfnNew,
HMODULE hmodCaller
)
{
ULONG ulSize;

PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
__try
{
pImportDesc = 
(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
hmodCaller, 
TRUE, 
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ulSize);
}

__except(InvalidReadExceptionFilter(GetExceptionInformation()))
{
//
}

if(pImportDesc == NULL)
{
return;
}

for(; pImportDesc->Name; pImportDesc++)
{
PSTR pszModName = (PSTR)((PBYTE)hmodCaller + pImportDesc->Name);
if(lstrcmpiA(pszModName, pszCalleeModName) == 0)
{
PIMAGE_THUNK_DATA pThunk = 
(PIMAEG_THUNK_DATA)
((PBYTE)hmodCaller + pImportDesc->FirstThunk);

for(; pThunk->ul.Function; pThunk++)
{
PROC* ppfn = (PROC*)&pThunk->ul.Function;

BOOL bFound = (*ppfn == pfnCurrent);
if(bFound)
{
    if(!WriteProcessMemory(
    GetCurrentProcess(), 
    ppfn, 
    &pfnNew,
    sizeof(pfnNew),
    NULL)
    &&
    (ERROR_NOACCESS == GetLastError()))
    {
        DWORD dwOldProtect;
        if(VirtualProtect(ppfn,
        sizeof(pfnNew),
        PAGE_WRITECOPY,
        &dwOldProtect))
        {
            WriteProcessMemory(
            GetCurrentProcess(),
            ppfn,
            &pfnNew,
            sizeof(pfnNew),
            NULL);

            VirtualProtect(
            ppfn,
            sizeof(pfnNew),
            dwOldProtect,
            &dwOldProtect
            );
        }
    }
    return;
}
}
}
}
}


PROC pfnOrig = GetProcAddress(
GetModuleHandle("Kernel32"),
"ExitProcess"
);

HMODULE hmodCaller = GetModuleHandle(
"Database.exe"
);

// 之后,任何线程执行Database.exe模块调用的ExitProcess时,会调用我们的替代函数。
// 须对应用的所有模块做相应处理
// 动态调用的LoadLibrary产生的载入模块(包含载入模块会由于DLL关联而隐式载入的DLL)也需考虑
// 通过GetProcAdddress直接获取目标函数地址方式的调用也须考虑
ReplaceIATEntryInOneMod(
"Kernel32.dll",
pfnOrig,
MyExitProcess,
hmodCaller
);

模块的导入段中的所有字符串都以ANSI格式保存。

-其它
GetProcAddress
参数:
模块基地址
函数名/函数序号
返回:
函数地址

// 定位一个目录项,在映像文件头,
// 返回对目录项数据地址
// 不具备多线程安全性
// 成功时,返回指向查询项相关信息的结构
PVOID WINAPI ImageDirectoryEntryToDataEx(
// 映像或数据文件的基地址
  _In_      PVOID                 Base,
 // TRUE 文件按映像(DLL或EXE)被映射
 // FALSE 文件按数据被映射
  _In_      BOOLEAN               MappedAsImage,
  // 要定位的目录项
  // IMAGE_DIRECTORY_ENTRY_BASERELOC  基地址重定位表
  // IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 绑定的导入目录
  // IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 延迟导入表
  // IMAGE_DIRECTORY_ENTRY_EXPORT 导出目录
  // IMAGE_DIRECTORY_ENTRY_GLOBALPTR 全局指针相关虚拟地址
  // IMAGE_DIRECTORY_ENTRY_IAT 导入地址表
  // IMAGE_DIRECTORY_ENTRY_IMPORT 导入目录
  // IMAGE_DIRECTORY_ENTRY_RESOURCE 资源目录
  // IMAGE_DIRECTORY_ENTRY_TLS 线程本地存储目录
  _In_      USHORT                DirectoryEntry,
  // 定位目录项数据尺寸
  _Out_     PULONG                Size,
  // 接收数据
  _Out_opt_ PIMAGE_SECTION_HEADER *FoundHeader
);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值