GetModuleHandle的API = 获取Get/模块Module/句柄Handle 其实“句柄/Handle”就是地址。 GetModuleHandle返回的是模块地址 见过“api-x-x-xxx.dll”的文件名称吧?就叫它是“api集”吧
当GetModuleHandle(api集名称)参数里面的字符串是api集, GetModuleHandle还能找到正确的模块地址。看看微软官方介绍API集
https://docs.microsoft.com/zh-cn/windows/win32/apiindex/windows-apisets?redirectedfrom=MSDN
没有说明其中转换字符的原理。emmm不想打字了,我上成果吧,国内是没有关于API集的知识的。 很多人自写“GetModuleHandle”功能 却不会解析 API集 导致不够兼容。。API集有4个版本,分别是6.1、6.2、6.3、10.0版本
我下面直接放代码吧实在不想打字了。累了!
既然都看到这里了!!! 你肯定是理解不透的!!! 来看看我下面翻译的文章
API 集架构 Windows 7 对 Win32 子系统的较低级别进行了重大重组。长期熟悉的 ADVAPI32 函数移至 KERNEL32。这两个函数中的许多函数都移到了一个名为 KERNELBASE 的新 DLL 中。其他 ADVAPI32 函数被移动到名为 SECHOST 的新 DLL。Windows 7 中的许多可执行文件从名称异常长的新 DLL 中导入函数,例如 API-MS-Win-Core-LocalRegistry-L1-1-0.dll。这种导入由 ADVAPI32 和 KERNEL32、用于一般支持的 DLL(如 MFC42、MSVCRT 和 OLE32)、许多服务以及各种其他可执行文件完成,所有其他可执行文件都通过 Windows 的较低级别,直至 SHELL32 和 SHLWAPI。不管发生了什么,它在 Windows 8 中得到了进一步的发展。不仅有很多具有长名称的 DLL,而且它们也适用于内核模式。
这方面的官方文档并不多。适用于 Windows 7 的原始软件开发工具包 (SDK) 在关于新低级二进制文件的简短页面中只提到了 KERNELBASE外部链接, 并没有提及具有不寻常名称的新 DLL。如果没有更多关于它的记录,那么从某种意义上说,应该不会有太多惊喜。毕竟,Windows 分发的更高级别的可执行文件继续像以前一样从 KERNEL32 之类的 DLL 中导入,并且由于 SDK 没有用于新 DLL 的导入库,因此其意图肯定是在 Microsoft 之外编写的程序,而且可能大多数是在 Microsoft 内部编写,对新的 DLL 一无所知,应该不受影响。无论如何,具有长名称的新 DLL 只是存根,其中实现的所有导出函数不会超过硬编码失败所需。此外,这些失败的实现并非都得到了很好的关注:例如,参见CreateFileW在 API-MS-Win-Core-File-L1-1-0.dll 中返回硬编码的NULL (0) 而不是 INVALID_HANDLE_VALUE (-1)。
在另一种意义上,文档的缺乏可能令人惊讶,这取决于人们期望被告知的有关 Windows 体系结构的内容,以评估其安全性和稳健性。这些新的 DLL 是 NTDLL 如何在加载用户模式模块时解析导入的小而重要的修饰的一部分。事实证明,所有从任何模块名称以 API- 开头(不区分大小写)的 DLL 中的所有导入都会检查新形式的重定向。Windows 8 添加了 EXT- 作为适用的前缀,并在加载内核模式模块时对内核的导入解析以及在加载内核(和 HAL 等)时对加载器的导入应用相同的修饰。由于很多 Windows 可执行文件从具有这些前缀的模块导入,
也许微软和计算机安全行业都只是缓慢地正式确定或评估 Windows 中的位置的这种巨大变化。尽管 Windows 7 的引入似乎在 Microsoft 之外几乎完全没有引起注意,但 Windows 8 和 Windows 8.1 的 SDK 带来了导入库,非 Microsoft 程序员可以使用这些库从新的 DLL 中导入,甚至还有一些文档,通常是Windows API 集外部链接,以及 Windows 8 API 集外部链接和 Windows 8.1 API 集外部链接。
奇怪的是——与否,也许取决于你是将它看作是一个实践的 Windows 程序员还是一个竞争操作系统的设计者——API 集外部链接的大部分文档都在一项专利中: 可组合 API 集的动态管理(提交2013 年 6 月 7 日,即 Windows 8.1 发布前一点)。推断微软更关心阻止其发明被复制而不是帮助其用户了解他们如何受到影响可能过于愤世嫉俗了,但即便如此,Windows 体系结构正在做一些重要的事情,而且大多数情况下都没有公开由微软或外部评论。
机制 在 Windows 7 中,从 DLL 导入的新重定向由 NTDLL 管理,作为通过激活上下文进行隔离的首选替代方法。是否重定向来自任何特定 API 或 EXT 模块的导入完全取决于 System32 目录中名为 ApiSetSchema.dll 的新文件的内容。虽然 ApiSetSchema 是一个 DLL,但它只需要用于数据。在系统初始化的第 1 阶段,NT 内核将整个文件映射到内核模式地址空间。从那里,想要的数据被映射到每个新初始化进程的用户模式地址空间,并且指向这个数据的指针被放置在一个名为ApiSetMap的新成员中 (在 x86 和 x64 构建中分别位于偏移量 0x38 和 0x68),流程的半文件化PEB 结构体。内核仅将数据识别为名为“.apiset”并对齐到 64KB 的部分的全部内容(即,IMAGE_SECTION_HEADER 中的VirtualAddress成员的低 16 位清零)。内核与解释这些内容无关:它只是将它们提供给 NTDLL 来解释。相反,NTDLL 不知道内容来自哪里。对于 NTDLL,每当要解析从另一个模块到一个模块的导入时,ApiSetMap给出的地址处的任何内容都被接受为一个映射,从中可以了解是否要从其他地方解析导入。
通过让内核加入重定向游戏,Windows 8 使这种巧妙的划分变得复杂。实际上,内核自己的导入会受到重定向的影响。为了在任何内核代码执行之前完成这种重定向,加载 ApiSetSchema 的工作被提交给加载内核的 Windows 加载器 (WINLOAD)。同样,地图是“.apiset”部分的全部内容(尽管现在没有对齐要求)。同样,这些内容只是假设具有正确的格式。WINLOAD 加载映射并使用它(以解析内核本身、HAL 和内核首次执行之前必须准备好的其他模块的导入)。内核不加载映射,但它知道映射来自 ApiSetSchema 文件。 LOADER_PARAMETER_BLOCK 结构。然后内核找到加载的映像,提取它自己的“.apiset”部分副本,然后卸载映像。除了这些准备中的更改以及内核也解释用于重定向内核模式模块(例如驱动程序)中的导入的映射之外,该机制(甚至到文件格式)与 Windows 7 相同。
API 集架构扩展 在 Windows 8.1 及更高版本中,WINLOAD 全权负责构建地图。ApiSetSchema 在内核初始化时已经卸载。内核对地图的来源一无所知。它只是在LOADER_PARAMETER_BLOCK的扩展中获取地址和大小,并接受那里的任何内容。
在这些版本中,API 集到主机的映射不需要来自一个 DLL。System32 目录中名为 ApiSetSchema.dll 的文件是必需的,但仅作为基本架构。除非该文件中的标志将此映射标记为密封WINLOAD 会在注册表中查找更多文件以扩展映射。可以有任意多个 模式扩展:
的子项是除了每个不同无关 子项允许不同的扩展名。WINLOAD 识别 子项中的Name和FileName值,但只解释后者。数据的 文件名value 命名 System32 目录中的文件。该文件具有与 ApiSetSchema 相同的格式,但具有不同的解释。基本架构是一个 API 集列表,并为每个 API 集提供了将 API 集重定向到主机的规则(可能取决于谁正在导入),架构扩展是一个主机列表,并为每个主机提供了一个 API 集列表本主机实现。WINLOAD 负责将扩展合并到基础中。原则似乎是,如果 API 集由架构扩展中列出的主机实现,则 API 集重定向到该主机,而不是基本架构中指定的任何内容。的组成架构,与基本模式格式相同,是 WINLOAD 本身用于解析导入的内容,也是内核和 NTDLL 接收的所有内容,作为它们用于解析导入的映射。
原始数据格式 该映射以标题开头,紧接着是一组条目,每个条目描述一个 API 集。但是请注意,Windows 8.1 中的架构扩展适用不同的解释。在下面的整个描述中,首先呈现基本模式和组合模式的结构,模式扩展的差异留给每个结构后的注释。
命名空间数组 固定大小的头部最初是 8 字节,但 6.3 版本将其扩展为 0x10。适用于 Windows 8 和 Windows 8.1 的 SDK 中名为 APISET.H 的文件记录了 Microsoft 的地图标题(包括数组)的名称是API_SET_NAMESPACE_ARRAY。(本文中给出的所有符号名称均来自该文件。)
Version 成员的唯一已知解释是 WINLOAD 并且只有在寻找模式扩展时才会这样做。Size成员的唯一已知解释也是由 WINLOAD 进行的,并且只有在实际发生扩展时才如此(例如,新主机的名称至少会附加到基本架构中)。
该旗成员是有意义的,只是到WINLOAD。0x01 位(API_SET_SCHEMA_FLAGS_SEALED)仅在 System32 目录中的 ApiSetSchema.dll 中重要。如果已设置,则此文件中的基本架构是整个地图。WINLOAD 不会在注册表中查找架构扩展。0x02 位 ( API_SET_SCHEMA_FLAGS_HOST_EXTENSION ) 仅在命名为架构扩展的文件中重要。必须设置它,否则 WINLOAD 会忽略该文件。
在架构扩展中,Array中的Count条目 列出主机,而不是 API 集。
命名空间条目 数组中的每个条目都是一个API_SET_NAMESPACE_ENTRY。每个最初都是 0x0C 字节,在 6.3 版本中扩展为 0x18。每个都命名一个 API 集,但没有 API- 前缀(或 6.2 及更高版本中的 EXT- 前缀)并且没有文件扩展名。名称采用 Unicode 格式且不以空字符结尾。假定数组已按不区分大小写的字母顺序排序。
该旗成员是有意义的,只是到WINLOAD。0x01 位 ( API_SET_SCHEMA_ENTRY_FLAGS_SEALED ) 仅在 System32 目录中的 ApiSetSchema.dll 中重要。如果已设置,则此条目描述的 API 集不能被架构扩展覆盖。
尽管NameLength正式是Microsoft 随 SDK 发布的标头中的 ULONG以供更高版本使用,但 NTDLL 的 Windows 7 实现仅使用低 16 位。
列出 API 集主机的结构是 API_SET_VALUE_ARRAY,如下所述。
在模式扩展中,API_SET_NAMESPACE_ENTRY 命名主机,即从一个或多个 API 集导入的 DLL 可以重定向到。名称再次采用 Unicode 格式,而不是以空字符结尾。假设没有对数组进行排序。该DataOffset被再次从地图到起始位置的偏移API_SET_VALUE_ARRAY,但列出API设置主机工具(并应重定向到这台主机,没有任何主机的基础架构被指定为)。
值数组 如果要从中导入的模块是在数组中找到的 API 集,则导入可能会重定向到某个主机模块。在 6.3 版之前,NTDLL 假定至少指定了一个主机(否则为什么要列出 API 集)。更高版本允许 API 集可以定义但不活动,从不命名主机的意义上说(大概是预期将在模式扩展中指定主机)。API 集的主机由标头和数组描述。Microsoft 的标题(包括数组)的名称是API_SET_VALUE_ARRAY。最初,标头仅包含数组中的条目计数。6.3 版将此标头扩展为 8 个字节。
在架构扩展中,数组列表 API集中的Count条目 ,而不是主机。
值输入 值数组中的每个条目都是一个API_SET_VALUE_ENTRY。每个是 0x10 或 0x14 字节,具体取决于版本。数组中的第一个条目描述了一个默认主机。根据导入模块的名称选择后续条目(如果有)。假定这些特殊主机的条目已经按导入模块的不区分大小写的字母顺序排序。但是请注意,还没有看到为任何一个 API 集定义两个以上主机的模式。
两个名称均采用 Unicode 并且不以空字符结尾。对于默认主机,没有指定导入模块,NameOffset和 NameLength成员是不相关的,观察到为 0。
尽管ValueLength正式是Microsoft 随 Windows 8 和 Windows 8.1 的 SDK 发布的标头中的 ULONG,但静态分析工具的注释记录了长度必须适合 16 位,而 NTDLL 仅使用低 16 位。当 WINLOAD 处理模式扩展中的值条目时,它使用整个双字。
在架构扩展中,ValueOffset和 ValueLength命名一个 API 集。WINLOAD 要求此 API 集已在基本架构中定义。
Windows 10 中的数据格式 版本 10.0 对数据格式进行了足够多的更改,因此看起来更新鲜。最显着的变化是引入了哈希表,这样命名空间条目的二进制搜索可以更快地比较 32 位哈希,而不是不区分大小写的字符串。另一个是删除API_SET_VALUE_ARRAY的简化。也许对某些人来说更值得注意的不是数据格式本身的变化,而是微软正式披露的变化:在适用于 Windows 10 的 SDK 中,APISET.H 删除了结构定义。
命名空间头 标头是 0x1C 字节,其中第一个 0x10 与 6.3 版的标头兼容。地图现在有两个数组。即使命名空间条目数组仍然跟在标题之后,如果仅在一个尚未观察到的示例中,两个数组都通过给出它们的偏移量来定位。
哈希条目 散列字符序列的算法是从零开始,然后对于每个字符,将前一个散列乘以来自标题的乘数,并添加字符的小写转换。每个哈希条目是 0x08 字节:
假设散列条目已经按散列的递增顺序排序。为了找到一个假定的 API 集的API_SET_NAMESPACE_ENTRY,NTDLL 首先将假定的名称散列到但不包括最后一个连字符,然后在散列条目数组中搜索具有相同散列的条目。只有当找到匹配的散列时,才会比较名称本身。请注意,在版本 10 中,API 集名称的最后一部分,即从最后一个连字符开始,是无关紧要的。
命名空间条目 描述单个 API 集的API_SET_NAMESPACE_ENTRY 略有变化。API 集的名称再次使用 Unicode,没有空终止符,但现在包含早期版本省略的前缀。API 集的主机由API_SET_VALUE_ENTRY 结构数组直接描述,而不是通过API_SET_VALUE_ARRAY间接描述。后者的主机计数移动到命名空间条目。
该API_SET_VALUE_ENTRY是从6.3版本不变,但非零标志为一个API集观察(为0x18的API-MS-WIN-安全提供商l1-1-0,然后才在x86版本)。
/
/
宽是
1
窄是
0
DWORD 查找指定字节(ULONG64 地址, INT8 字节, INT8 宽与窄)
{
INT
i
=
0
;
INT
字节
1
=
0
;
if
(宽与窄)
/
/
宽是
1
{
if
(!字节)
{
while
(
*
((INT8
*
)地址
+
i
*
2
) !
=
0
)
{
i
+
+
;
}
return
i
*
2
;
}
do
{
if
(
*
((INT8
*
)地址
+
i
*
2
) >
=
'a'
)
字节
1
=
*
((INT8
*
)地址
+
i
*
2
)
-
0x20
;
else
字节
1
=
*
((INT8
*
)地址
+
i
*
2
);
if
(字节
1
=
=
字节)
{
return
i
*
2
;
}
i
+
+
;
}
while
(
*
((INT8
*
)地址
+
i
*
2
) !
=
0
);
}
else
{
/
/
窄是
0
if
(!字节)
{
while
(
*
((INT8
*
)地址
+
i) !
=
0
)
{
i
+
+
;
}
return
i;
}
do
{
if
(
*
((INT8
*
)地址
+
i) >
=
'a'
)
字节
1
=
*
((INT8
*
)地址
+
i)
-
0x20
;
else
字节
1
=
*
((INT8
*
)地址
+
i);
if
(字节
1
=
=
字节)
{
return
i;
}
i
+
+
;
}
while
(
*
((INT8
*
)地址
+
i) !
=
0
);
}
return
0
;
}
/
/
返回的是长度 如果是
0
就是失败
BOOLEAN 宽到窄字符(ULONG64 宽地址, ULONG64 窄地址, WORD 长度)
{
WORD i
=
0
;
BYTE
*
窄节字
=
NULL;
while
(
*
(BYTE
*
)(宽地址
+
i
*
2
))
{
窄节字
=
(BYTE
*
)窄地址
+
i;
*
窄节字
=
*
(BYTE
*
)(宽地址
+
i
*
2
);
i
+
+
;
if
(i >
=
长度)
{
return
1
;
}
}
return
0
;
}
/
/
宽字符比较窄字符 按窄字符长度标准
BOOLEAN 宽比较窄字符(ULONG64 s1, ULONG64 s2,WORD 长度)
{
WORD i
=
0
;
BYTE 字节, 字节
1
;
if
(
*
((BYTE
*
)s1
+
i
*
2
) >
=
'a'
)
字节
=
*
((BYTE
*
)s1
+
i
*
2
)
-
0x20
;
else
字节
=
*
((BYTE
*
)s1
+
i
*
2
);
if
(
*
((BYTE
*
)s2
+
i) >
=
'a'
)
字节
1
=
*
((BYTE
*
)s2
+
i)
-
0x20
;
else
字节
1
=
*
((BYTE
*
)s2
+
i);
while
((字节 ^ 字节
1
)
=
=
0
)
{
if
(
*
((BYTE
*
)s1
+
i
*
2
) >
=
'a'
)
字节
=
*
((BYTE
*
)s1
+
i
*
2
)
-
0x20
;
else
字节
=
*
((BYTE
*
)s1
+
i
*
2
);
if
(
*
((BYTE
*
)s2
+
i) >
=
'a'
)
字节
1
=
*
((BYTE
*
)s2
+
i)
-
0x20
;
else
字节
1
=
*
((BYTE
*
)s2
+
i);
i
+
+
;
if
(长度
=
=
i)
{
return
1
;
}
}
return
0
;
}
void 解析模块名(char
*
模块名,char
*
正确模块名)
{
/
/
x64
ULONG64 ApiSetSchema地址
=
*
(ULONG64
*
)(__readgsqword(
0x60
)
+
0x68
);
WORD 版本
=
*
(DWORD
*
)ApiSetSchema地址;
WORD API集数量
=
0
;
WORD API集_命名空间条目_偏移量
=
0
;
WORD API集名称_偏移量
=
0
;
ULONG64 API集名称_地址
=
0
;
WORD API集名称_长度
=
0
;
WORD API集_值数组_偏移量
=
0
;
ULONG64 API集_值数组_地址
=
0
;
WORD API集_原名称_偏移量
=
0
;
WORD API集_原名称_长度
=
0
;
ULONG64 API集_原名称_地址
=
0
;
ULONG64 API集遍历数
=
0
;
WORD 模块名长度
=
查找指定字节((ULONG64)模块名,
0
,
0
);
WORD API集版本_偏移
=
0
;
WORD API集数量_偏移
=
0
;
WORD API集命名空间条目_偏移量_偏移
=
0
;
WORD API集命名空间条目_大小
=
0
;
WORD API集名称_长度_偏移
=
0
;
WORD API集名称_偏移量_偏移
=
0
;
WORD API集_值数组_地址_偏移
=
0
;
WORD API集_原名称_偏移量_偏移
=
0
;
WORD API集_原名称_长度_偏移
=
0
;
WORD API集名称_地址_偏移
=
0
;
WORD 模块名长度_偏移
=
0
;
if
(!模块名长度)
{
return
;
}
else
{
/
/
-
4
是因为.dll 字节大小是
4
在API集名称里面 没有.dll 是为了节省字节内存
模块名长度
-
=
4
;
}
if
(版本
=
=
6
||版本
=
=
5
)
{
API集版本_偏移
=
0x10
;
API集数量_偏移
=
0xC
;
API集命名空间条目_偏移量_偏移
=
0x10
;
API集命名空间条目_大小
=
0x18
;
API集名称_长度_偏移
=
0x8
;
API集名称_偏移量_偏移
=
0x4
;
API集_值数组_地址_偏移
=
0x8
;
API集_原名称_偏移量_偏移
=
0x4
;
API集_原名称_长度_偏移
=
0x8
;
API集名称_地址_偏移
=
0
;
模块名长度_偏移
=
0
;
API集_命名空间条目_偏移量
=
*
(WORD
*
)(ApiSetSchema地址
+
API集命名空间条目_偏移量_偏移);
goto 解析;
}
else
if
(版本
=
=
3
||版本
=
=
4
)
{
API集版本_偏移
=
0x14
;
API集数量_偏移
=
0xC
;
API集命名空间条目_偏移量_偏移
=
0x10
;
API集命名空间条目_大小
=
0x18
;
API集名称_长度_偏移
=
0x8
;
API集名称_偏移量_偏移
=
0x4
;
API集_值数组_地址_偏移
=
0x8
;
API集_原名称_偏移量_偏移
=
0x4
;
API集_原名称_长度_偏移
=
0x8
;
API集名称_地址_偏移
=
0
;
模块名长度_偏移
=
0
;
API集_命名空间条目_偏移量
=
ApiSetSchema地址
+
API集命名空间条目_偏移量_偏移;
goto 解析;
}
else
if
(版本
=
=
2
)
{
API集版本_偏移
=
0x8
;
API集数量_偏移
=
0x4
;
API集命名空间条目_偏移量_偏移
=
0x8
;
API集命名空间条目_大小
=
0xC
;
API集名称_长度_偏移
=
0x4
;
API集名称_偏移量_偏移
=
0
;
API集_值数组_地址_偏移
=
0x4
;
API集_原名称_偏移量_偏移
=
0
;
API集_原名称_长度_偏移
=
0x4
;
API集名称_地址_偏移
=
0x4
;
模块名长度_偏移
=
0x4
;
API集_命名空间条目_偏移量
=
API集命名空间条目_偏移量_偏移;
goto 解析;
}
else
{
goto 无API集;
}
无API集:
memmove(正确模块名, 模块名, 模块名长度
+
4
);
return
;
解析:
API集数量
=
*
(WORD
*
)(ApiSetSchema地址
+
API集数量_偏移);
WORD i
=
0
;
继续遍历:
/
/
循环检测有没有一致的 API集名称长度
do
{
API集遍历数
=
i
*
API集命名空间条目_大小;
API集名称_长度
=
*
(BYTE
*
)(ApiSetSchema地址
+
API集_命名空间条目_偏移量
+
API集遍历数
+
API集名称_长度_偏移)
/
2
;
if
(API集名称_长度
=
=
(模块名长度
-
模块名长度_偏移))
{
break
;
}
i
+
+
;
}
while
(i <
=
API集数量);
if
(i > API集数量)
/
/
自己循环完毕就是没有 API集
{
goto 无API集;
}
API集名称_偏移量
=
*
(WORD
*
)(ApiSetSchema地址
+
API集_命名空间条目_偏移量
+
API集遍历数
+
API集名称_偏移量_偏移);
API集名称_地址
=
ApiSetSchema地址
+
API集名称_偏移量;
/
/
检测 字符串是不是一样! 不是一样的话就去 循环
10
结束函数
if
(!宽比较窄字符(API集名称_地址, (ULONG64)模块名
+
API集名称_地址_偏移, API集名称_长度))
{
i
+
+
;
goto 继续遍历;
}
/
/
6.3
版本是 API集版本_偏移
=
0x14
,
10
版本是API集版本_偏移
=
0x10
API集_值数组_偏移量
=
*
(WORD
*
)(ApiSetSchema地址
+
API集_命名空间条目_偏移量
+
API集遍历数
+
API集版本_偏移);
API集_值数组_地址
=
ApiSetSchema地址
+
API集_值数组_偏移量
+
API集_值数组_地址_偏移;
API集_原名称_偏移量
=
*
(WORD
*
)(API集_值数组_地址
+
API集_原名称_偏移量_偏移);
API集_原名称_长度
=
*
(BYTE
*
)(API集_值数组_地址
+
API集_原名称_长度_偏移);
API集_原名称_地址
=
ApiSetSchema地址
+
API集_原名称_偏移量;
if
(!API集_原名称_长度)
{
API集_原名称_长度
=
*
(BYTE
*
)(API集_值数组_地址
+
API集_原名称_长度_偏移
+
0x8
);
if
(!API集_原名称_长度)
{
goto 无API集;
}
API集_原名称_偏移量
=
*
(WORD
*
)(API集_值数组_地址
+
API集_原名称_偏移量_偏移
+
0x8
);
API集_原名称_地址
=
ApiSetSchema地址
+
API集_原名称_偏移量;
}
goto 结束;
结束:
if
(宽到窄字符(API集_原名称_地址, (ULONG64)正确模块名, API集_原名称_长度
/
2
))
{
return
;
}
else
{
memmove(正确模块名, 模块名, 模块名长度
+
4
);
return
;
}
}
/
/
宽是
1
窄是
0
DWORD 查找指定字节(ULONG64 地址, INT8 字节, INT8 宽与窄)
{
INT
i
=
0
;
INT
字节
1
=
0
;
if
(宽与窄)
/
/
宽是
1
{
if
(!字节)
{
while
(
*
((INT8
*
)地址
+
i
*
2
) !
=
0
)
{
i
+
+
;
}
return
i
*
2
;
}
do
{
if
(
*
((INT8
*
)地址
+
i
*
2
) >
=
'a'
)
字节
1
=
*
((INT8
*
)地址
+
i
*
2
)
-
0x20
;
else
字节
1
=
*
((INT8
*
)地址
+
i
*
2
);
if
(字节
1
=
=
字节)
{
return
i
*
2
;
}
i
+
+
;
}
while
(
*
((INT8
*
)地址
+
i
*
2
) !
=
0
);
}
else
{
/
/
窄是
0
if
(!字节)
{
while
(
*
((INT8
*
)地址
+
i) !
=
0
)
{
i
+
+
;
}
return
i;
}
do
{
if
(
*
((INT8
*
)地址
+
i) >
=
'a'
)
字节
1
=
*
((INT8
*
)地址
+
i)
-
0x20
;
else
字节
1
=
*
((INT8
*
)地址
+
i);
if
(字节
1
=
=
字节)
{
return
i;
}
i
+
+
;
}
while
(
*
((INT8
*
)地址
+
i) !
=
0
);
}
return
0
;
}
/
/
返回的是长度 如果是
0
就是失败
BOOLEAN 宽到窄字符(ULONG64 宽地址, ULONG64 窄地址, WORD 长度)
{
WORD i
=
0
;
BYTE
*
窄节字
=
NULL;
while
(
*
(BYTE
*
)(宽地址
+
i
*
2
))
{
窄节字
=
(BYTE
*
)窄地址
+
i;
*
窄节字
=
*
(BYTE
*
)(宽地址
+
i
*
2
);
i
+
+
;
if
(i >
=
长度)
{
return
1
;
}
}
return
0
;
}
/
/
宽字符比较窄字符 按窄字符长度标准
BOOLEAN 宽比较窄字符(ULONG64 s1, ULONG64 s2,WORD 长度)
{
WORD i
=
0
;
BYTE 字节, 字节
1
;
if
(
*
((BYTE
*
)s1
+
i
*
2
) >
=
'a'
)
字节
=
*
((BYTE
*
)s1
+
i
*
2
)
-
0x20
;
else
字节
=
*
((BYTE
*
)s1
+
i
*
2
);
if
(
*
((BYTE
*
)s2
+
i) >
=
'a'
)
字节
1
=
*
((BYTE
*
)s2
+
i)
-
0x20
;
else
字节
1
=
*
((BYTE
*
)s2
+
i);
while
((字节 ^ 字节
1
)
=
=
0
)
{
if
(
*
((BYTE
*
)s1
+
i
*
2
) >
=
'a'
)
字节
=
*
((BYTE
*
)s1
+
i
*
2
)
-
0x20
;
else
字节
=
*
((BYTE
*
)s1
+
i
*
2
);
if
(
*
((BYTE
*
)s2
+
i) >
=
'a'
)
字节
1
=
*
((BYTE
*
)s2
+
i)
-
0x20
;
else
字节
1
=
*
((BYTE
*
)s2
+
i);
i
+
+
;
if
(长度
=
=
i)
{
return
1
;
}
}
return
0
;
}
void 解析模块名(char
*
模块名,char
*
正确模块名)
{
/
/
x64
ULONG64 ApiSetSchema地址
=
*
(ULONG64
*
)(__readgsqword(
0x60
)
+
0x68
);
WORD 版本
=
*
(DWORD
*
)ApiSetSchema地址;
WORD API集数量
=
0
;
WORD API集_命名空间条目_偏移量
=
0
;
WORD API集名称_偏移量
=
0
;
ULONG64 API集名称_地址
=
0
;
WORD API集名称_长度
=
0
;
WORD API集_值数组_偏移量
=
0
;
ULONG64 API集_值数组_地址
=
0
;
WORD API集_原名称_偏移量
=
0
;
WORD API集_原名称_长度
=
0
;
ULONG64 API集_原名称_地址
=
0
;
ULONG64 API集遍历数
=
0
;
WORD 模块名长度
=
查找指定字节((ULONG64)模块名,
0
,
0
);
WORD API集版本_偏移
=
0
;
WORD API集数量_偏移
=
0
;
WORD API集命名空间条目_偏移量_偏移
=
0
;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)