GlobalPrevalence

GlobalPrevalence 是 Microsoft Defender for Endpoint(MDE)中的一个关键指标,用于表示 某个文件在全球范围内被观察到的设备数量。它通过微软全球威胁情报网络统计得出,反映了文件的分布普遍性。以下是对其功能、应用场景及注意事项的详细解析。


核心作用

  • 恶意性评估
    • 低 GlobalPrevalence(如 <50)表明文件在极少数设备中存在,可能是定制化攻击工具、新型恶意软件或内部开发文件。
    • 高 GlobalPrevalence(如 >10,000)表明文件广泛分布,通常是操作系统组件、流行软件或广泛传播的合法文件。
  • 信任度分级
    安全团队可结合该指标判断文件的可信度:

    GlobalPrevalence值域      | 典型用途
    --------------------------|---------------------------------
    <50                       | 可疑(如零日攻击载荷)
    50–1,000                  | 中等风险(如灰色软件)
    >1,000                    | 高可信度(如微软签名的系统文件)
    

在查询中的应用

在您提供的 Kusto 查询中:

| invoke FileProfile(SHA1, 1000)
| where GlobalPrevalence < 50 and SignatureState != "SignedValid"
  • 筛选逻辑
    • GlobalPrevalence <50:排除常见文件,聚焦罕见对象。
    • SignatureState != "SignedValid":排除有效签名的合法文件(攻击者常使用未签名或伪造签名的文件)。
  • 实战意义
    此条件组合高效定位 低传播+未签名 的异常文件,这类文件更可能为恶意攻击载荷。

典型场景

  1. 新型勒索软件检测
    攻击者编译的勒索软件因未大规模传播,其 GlobalPrevalence 接近于零,结合无效签名可告警。
  2. 定向攻击工具
    针对某企业的定制化后门通常 GlobalPrevalence 极低(仅在目标设备存在),但文件行为异常(如与 explorer.exe 交互)。
  3. 供应链投毒
    被篡改的开源库 DLL 在攻击初期 GlobalPrevalence 可能小于 50,但会通过软件更新逐步扩散。

注意事项

  1. 误报处理
    • 内部工具:企业自研软件的 GlobalPrevalence 天然较低,需添加白名单规则(如特定文件夹、哈希)。
    • 行业软件:某些垂直行业工具(如医疗设备驱动)全球使用量低,但本地普遍性高,需结合本地日志过滤。
  2. 阈值调优
    • 严格模式:威胁狩猎时可设为 <10 提高精准度。
    • 宽松模式:应急响应时可设为 <500 扩大覆盖面。
  3. 结合其他信号
    • 文件路径(如 %Temp%)、进程树(如由 Office 启动的脚本)、网络连接(如与 C2 通信)等指标应联合分析。

关联字段

字段 作用 与 GlobalPrevalence 的对比
GlobalOrgPrevalence 文件在 您所在组织内 的设备分布数量 定位内部传播恶意文件(如横向移动工具)
FileOriginUrl 文件首次下载来源(如 URL) 识别钓鱼或恶意网站传播的文件
Signer 文件签名者信息 验证是否为可信发布者(如微软、Adobe)

优化示例

// 添加白名单条件
| where GlobalPrevalence < 50 
    and FolderPath !has @"C:\InternalTools\"  // 忽略内部工具目录
    and FileName !in ("custom_lib.dll")       // 忽略已知合法文件

总结

GlobalPrevalence 是威胁狩猎中高效筛选异常文件的利器,但需结合环境上下文与其他指标联动。合理配置阈值和白名单,可显著提升检测精度,降低运营噪音。

发表在 威胁检测 | 留下评论

HijackLibs 项目介绍

HijackLibs 是一个专注于研究和收集 DLL 劫持(DLL Hijacking) 漏洞的开源安全项目,旨在帮助安全研究人员、渗透测试人员及防御者识别、利用和缓解 Windows 应用程序中的动态链接库(DLL)加载漏洞。


核心功能与价值

  1. 漏洞数据库
    • 包含大量已知易受 DLL 劫持攻击的 应用程序列表(如浏览器、办公软件、系统工具等)。
    • 记录目标程序加载的 DLL 文件名、搜索路径顺序及潜在劫持点(例如缺少签名验证的 DLL)。
  2. 技术分析工具
    • 提供自动化脚本检测应用程序的 DLL 加载逻辑(例如通过 Process Monitor 分析文件系统访问行为)。
    • 生成 恶意 DLL 模板,演示如何通过劫持合法 DLL 实现代码注入、权限提升或持久化后门。
  3. 防御指南
    • 推荐应用程序开发中的安全编码实践(如使用绝对路径加载 DLL、启用 SafeDLLSearchMode)。
    • 为系统管理员提供检测和修复策略(如启用系统审计日志、部署文件完整性监控)。

项目结构(示例)

HijackLibs/
├── /docs                # 技术文档
│   ├── Detection.md     # 检测方法
│   └── Mitigation.md    # 防御方案
├── /targets             # 易受攻击的应用程序清单
│   ├── App1/
│   │   ├── app_info.json  # 应用名称、版本、厂商
│   │   └── vuln_dlls      # 可劫持的 DLL 列表及利用方式
│   └── App2/
├── /tools               # 自动化工具
│   ├── DLLMonitor.ps1   # DLL 加载行为分析脚本
│   └── MaliciousDLL      # 生成恶意 DLL 的示例代码
└── README.md            # 项目概述与使用说明

典型应用场景

攻击视角(红队)

  1. 权限维持
    • 劫持常用软件(如记事本、浏览器)的 DLL,在用户启动应用时执行恶意代码。
    • 利用 C:\ProgramData 等可写目录投仿冒 DLL,绕过系统权限限制。
  2. 绕过防护机制
    • 通过劫持未签名或弱权限的 DLL,实现 UAC 绕过反病毒软件规避

防御视角(蓝队)

  1. 威胁狩猎
    • 使用项目提供的规则(如 Sysmon/Sigma 规则)监控异常 DLL 加载事件。
    • 分析应用程序的 %PATH% 目录权限,防止低权限用户写入高危路径。
  2. 开发加固
    • 开发者通过参考漏洞清单,避免在代码中调用 LoadLibrary 时的路径缺陷。

技术原理(以 DLL 劫持为例)

  1. 漏洞成因
    Windows 应用程序默认按以下顺序搜索 DLL:
    内存加载 → 应用程序目录 → 系统目录 → PATH 环境变量目录
    攻击者可利用目录优先级,在可写路径中放置同名恶意 DLL。
  2. 攻击步骤
    • 识别目标 DLL:通过监控工具发现目标程序加载的非系统 DLL(如 version.dll)。
    • 生成恶意 DLL:编译包含恶意代码的同名 DLL(需导出与原 DLL 相同的函数)。
    • 投递恶意文件:将 DLL 放置在程序优先加载的路径中(如当前工作目录)。
    • 触发执行:用户启动目标程序时,恶意代码随 DLL 加载自动运行。

项目链接与资源

  • GitHub 仓库
    https://github.com/yourusername/HijackLibs (需替换为实际项目地址)
  • 相关工具推荐
    • Process Monitor:分析 DLL 加载行为。
    • DLLSpy:检查 DLL 导出函数。
    • SigCheck:验证文件签名。

总结

HijackLibs 通过系统化的漏洞积累与工具集成,为安全社区提供了 DLL 劫持 研究的一站式资源。它不仅服务于攻击测试,还强化了防御侧的主动检测能力,是理解 Windows 生态安全风险的重要切入点。使用时需遵循 合法授权道德准则,所有技术仅应用于安全研究与防御演练。

发表在 威胁检测 | 留下评论

SeDebug Privilege Enabled for Access Token

“SeDebug Privilege”是Windows中的一个权限,允许进程调试其他进程,访问其内存。这可能被恶意软件利用。“Access Token”是安全凭证,包含用户的权限信息

发表在 威胁检测 | 留下评论

Win32 API读取文件全过程

在 Win32 API 中如何读取一个文件,包括会用到的函数、每一步的作用,以及完整的代码例子和流程图思路。


🧭 一、Win32 API 读取文件的整体流程

读取文件(Read File)的标准流程是:

CreateFileReadFileCloseHandle

可以理解为:

“打开文件 → 读取内容 → 关闭文件”


🧩 二、涉及的主要 API 函数

步骤 API 函数 作用说明
1️⃣ CreateFile 打开或创建文件,返回一个句柄(HANDLE)
2️⃣ ReadFile 从文件中读取数据到内存(lpBuffer)
3️⃣ CloseHandle 关闭文件句柄,释放资源

📘 三、通俗讲解 + 举例

我们来一步步看:


✅ 第一步:CreateFile 打开文件

c
HANDLE hFile = CreateFile(
    "C:\\test\\data.txt",      // 文件路径
    GENERIC_READ,              // 读取权限
    FILE_SHARE_READ,           // 允许别的进程也读
    NULL,                      // 默认安全属性
    OPEN_EXISTING,             // 打开已存在文件
    FILE_ATTRIBUTE_NORMAL,     // 普通文件属性
    NULL                       // 无模板文件
);

👉 如果文件存在,这一步就相当于“把文件拿在手上”,
系统会返回一个“文件句柄” hFile,后面读文件都靠它。


✅ 第二步:ReadFile 读取文件内容

c
char buffer[256] = {0};       // 准备一个缓冲区
DWORD bytesRead = 0;          // 实际读取字节数

BOOL result = ReadFile(
    hFile,                    // 文件句柄
    buffer,                   // 缓冲区(lpBuffer)
    sizeof(buffer)-1,         // 想读的最大字节数
    &bytesRead,               // 实际读了多少
    NULL                      // 同步读取
);

👉 这一步就相当于“把文件内容读进内存”。

  • buffer 是装数据的“碗”;
  • 文件内容会被复制到 buffer
  • bytesRead 记录实际读入多少字节。

✅ 第三步:显示内容(或处理)

c
if (result) {
    printf("读取成功,内容如下:\n%s\n", buffer);
} else {
    printf("读取失败,错误码:%lu\n", GetLastError());
}

✅ 第四步:关闭文件句柄

c
CloseHandle(hFile);

👉 相当于“用完把文件放回去”,不关掉会导致系统资源泄露。


📄 四、完整可运行示例

c
#include <windows.h>
#include <stdio.h>

int main() {
    HANDLE hFile = CreateFile(
        "C:\\test\\data.txt",     // 文件路径
        GENERIC_READ,             // 读取权限
        FILE_SHARE_READ,          // 允许共享读
        NULL,                     // 默认安全属性
        OPEN_EXISTING,            // 文件必须已存在
        FILE_ATTRIBUTE_NORMAL,    // 普通文件
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        printf("打开文件失败!错误码:%lu\n", GetLastError());
        return 1;
    }

    char buffer[256] = {0};
    DWORD bytesRead = 0;

    BOOL success = ReadFile(
        hFile, buffer, sizeof(buffer) - 1,
        &bytesRead, NULL
    );

    if (success) {
        printf("成功读取 %lu 字节\n", bytesRead);
        printf("文件内容:\n%s\n", buffer);
    } else {
        printf("读取失败!错误码:%lu\n", GetLastError());
    }

    CloseHandle(hFile);
    return 0;
}

📘 五、运行效果举例

假设文件 C:\test\data.txt 内容是:

Hello Win32!

程序输出:

成功读取 12 字节
文件内容:
Hello Win32!

🧠 六、过程图(文字版)

┌──────────────┐
│ data.txt 文件 │
└──────┬───────┘
       │  CreateFile(…) → 返回句柄 hFile
       ▼
┌────────────────────┐
│ 内存缓冲区 (lpBuffer) │ ← ReadFile(hFile,…)
└──────┬─────────────┘
       │
       ▼
┌─────────────┐
│ 屏幕/控制台 │ ← printf(buffer)
└─────────────┘
       │
       ▼
CloseHandle(hFile)

✅ 七、总结表

步骤 函数 关键参数 功能
1️⃣ CreateFile 文件路径、访问方式 打开文件,获取句柄
2️⃣ ReadFile 文件句柄、缓冲区、字节数 读取文件内容
3️⃣ CloseHandle 文件句柄 关闭文件,释放资源
发表在 安全分析 | 留下评论

ReadFile

CreateFile,现在说的 ReadFile 就是它的“搭档”——用来读取文件或设备的数据的 Win32 API。
它非常常见,也非常实用。下面我用通俗、生活化的方式讲清楚它。


🧭 一、函数原型(简化版)

c
BOOL ReadFile(
  HANDLE hFile,                // 文件句柄
  LPVOID lpBuffer,             // 数据读到哪里去
  DWORD  nNumberOfBytesToRead, // 想读多少字节
  LPDWORD lpNumberOfBytesRead, // 实际读了多少字节
  LPOVERLAPPED lpOverlapped    // 异步用的,一般填 NULL
);

返回值:

  • 成功:返回 TRUE
  • 失败:返回 FALSE(可用 GetLastError() 查看原因)

🧩 二、参数通俗解释

参数 含义 通俗比喻
hFile 文件句柄 你手上拿到的“书”或“文件”对象,之前 CreateFile() 打开的那个
lpBuffer 数据缓冲区 你准备好的“碗”,用来装读出来的内容
nNumberOfBytesToRead 计划读取的字节数 想读多少?(比如读 100 个字节)
lpNumberOfBytesRead 实际读取的字节数 实际读到了多少?(可能比预期少)
lpOverlapped 异步参数 一般不用,普通读文件时填 NULL

🧠 三、简单例子(读文件)

✅ 例1:读取一个文本文件

假设磁盘上有个文件:C:\test\data.txt,内容是:

Hello World!

我们写个程序去读它👇

c
#include <windows.h>
#include <stdio.h>

int main() {
    HANDLE hFile = CreateFile(
        "C:\\test\\data.txt",      // 文件路径
        GENERIC_READ,              // 只读
        FILE_SHARE_READ,           // 可共享读
        NULL,                      // 默认安全
        OPEN_EXISTING,             // 文件必须存在
        FILE_ATTRIBUTE_NORMAL,     // 普通文件
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        printf("打开文件失败: %d\n", GetLastError());
        return 1;
    }

    char buffer[100] = {0};        // 准备一个缓冲区
    DWORD bytesRead = 0;

    BOOL success = ReadFile(
        hFile,                     // 文件句柄
        buffer,                    // 存放读取的数据
        sizeof(buffer) - 1,        // 想读多少字节
        &bytesRead,                // 实际读了多少
        NULL                       // 同步读取
    );

    if (success) {
        printf("读取成功:%d 字节\n", bytesRead);
        printf("内容是:%s\n", buffer);
    } else {
        printf("读取失败: %d\n", GetLastError());
    }

    CloseHandle(hFile);
    return 0;
}

📘 输出结果:

读取成功:12 字节
内容是:Hello World!

🧩 四、类比理解(生活版)

可以把 CreateFileReadFile 想象成这样的生活过程:

动作 类比
CreateFile 打开一本书
ReadFile 从书上读内容到你的笔记本里
WriteFile 把笔记写回书上

你先“打开文件”(拿到句柄),再“读内容”(复制到缓冲区),最后“关闭文件”(放下书)。


🧱 五、重要细节

  1. 读取位置

    • ReadFile 每次从当前文件指针位置开始读。
    • 读完后,文件指针会自动往后移动。
    • 可以用 SetFilePointer() 改变读的位置。
  2. 实际读取字节数可能少于期望值

    • 比如文件只剩 20 个字节,你要读 100 个,结果只读到 20 个。
  3. 异步读取(高级用法)

    • 如果用 FILE_FLAG_OVERLAPPED 打开文件,可以异步读取(不阻塞主线程)。
    • 但普通文件操作中,我们通常用同步方式(lpOverlapped = NULL)。

📘 六、再举几个实际用途例子

✅ 读取二进制文件(比如图片)

c
BYTE buffer[1024];
DWORD bytesRead;
ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
// buffer 里现在就是文件的原始字节内容

✅ 读取设备或管道

ReadFile 不仅能读文件,还能从:

  • 串口 (COM1)
  • 命名管道 (\\.\pipe\mypipe)
  • 磁盘设备 (\\.\PhysicalDrive0) 这些“文件型设备”里读取数据。

🧾 七、总结(口语记忆法)

参数 一句话记忆
hFile 我要从哪个文件读?
lpBuffer 数据放哪?
nNumberOfBytesToRead 想读多少?
lpNumberOfBytesRead 实际读了多少?
lpOverlapped 要不要异步?(普通情况填 NULL)

是否希望我帮你画一张图,展示“CreateFile → ReadFile → CloseHandle”整个读取流程?(文件 → 内存缓冲区 → 输出)
图会让你更容易记住数据是怎么流动的。

lpBuffer 举例说明
Robot Avatar

非常好 👍,你问的这个 lpBufferReadFile 函数中最关键的参数之一。
它决定了读取到的数据要放到哪里
下面我来通俗讲解,并配上例子让你立刻明白。


🧭 一、lpBuffer 是什么?

lpBuffer 全称是 “Long Pointer to Buffer”,意思是:

👉 指向一个**内存区域(缓冲区)**的指针,ReadFile() 读到的数据就会放到这里。

简单说:

  • 文件 → 数据读出来 → 放到你准备的 lpBuffer 这块内存里。

就像你要打水,lpBuffer 就是你拿的“桶”,ReadFile 是“打水的人”,文件是“水源”。


🧩 二、lpBuffer 的常见定义方式

一般是一个 数组内存指针。常见写法如下👇

c
char buffer[100];      // 定义一个 100 字节的缓冲区
ReadFile(hFile, buffer, 100, &bytesRead, NULL);

或者你也可以用动态内存分配:

c
char* buffer = (char*)malloc(100);
ReadFile(hFile, buffer, 100, &bytesRead, NULL);

🧠 三、举几个通俗例子

✅ 例1:读取文本文件

c
char buffer[128] = {0};      // 准备一个128字节的缓冲区
DWORD bytesRead;

ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, NULL);
printf("文件内容是:%s\n", buffer);

📘 输出:

文件内容是:Hello World!

👉 buffer 就是 lpBuffer
ReadFile 把文件中的 “Hello World!” 拷贝进了 buffer 里。


✅ 例2:读取二进制文件(如图片)

c
BYTE buffer[1024];
DWORD bytesRead;

ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
printf("前4个字节是:%02X %02X %02X %02X\n", buffer[0], buffer[1], buffer[2], buffer[3]);

📘 输出:

4个字节是:89 50 4E 47

👉 这四个字节其实是 PNG 图片文件头的标志(89 50 4E 47 = “.PNG”)。


✅ 例3:读取串口数据(设备)

c
char recvData[50];
DWORD bytesRead;
ReadFile(hSerial, recvData, sizeof(recvData), &bytesRead, NULL);
printf("收到的数据:%.*s\n", bytesRead, recvData);

👉 这时 lpBuffer(即 recvData)装的不是文件内容,而是串口传来的原始数据。


📘 四、注意事项

注意点 说明
必须分配足够内存 否则可能导致越界或程序崩溃
lpBuffer 必须可写 因为 ReadFile 要往里写数据
不会自动添加字符串结束符 读文本时记得自己加上 '\0',否则打印会乱码
二进制数据不要用 %s 打印 因为里面可能没有 \0,会乱输出

🧩 五、图示理解

[文件内容] → "Hello World!"

ReadFile(
    hFile,
    buffer,        ← lpBuffer (存放数据的“碗”)
    100,
    &bytesRead,
    NULL
);

执行后:

buffer = "Hello World!"
bytesRead = 12

🧾 六、总结一句话记忆

lpBuffer = 我准备好的“内存碗”,
ReadFile = “把文件内容舀进碗里”的动作。


是否希望我帮你画一个示意图,展示 ReadFile 从文件到 lpBuffer 的数据流?
(那张图会像是“文件 → 内核 → 内存 → 变量”的过程,非常直观。)

在x64dbg中 lpBuffer 记录什么。举例说明
Robot Avatar

在 x64dbg 中调试 ReadFile 时,lpBuffer一个指针,它指向的内存区域会存放从文件或设备读取出来的数据
简单又形象的说法:

lpBuffer 是“碗”,真正的数据装在它指向的那块“内存碗”里。
x64dbg 显示的是这个指针和它指向的内容。


✅ x64dbg 中如何查看 lpBuffer 内容?

假设代码中有:

c
char buffer[100] = {0};
ReadFile(hFile, buffer, 100, &bytesRead, NULL);

当程序运行到 ReadFile 调用之后

  1. 查看寄存器里传参
    在 32 位下调用约定为:

    • 第 2 个参数 lpBuffer 通常在 ESP+8
    • 或者在 x64 中第 2 个参数在寄存器 RDX
  2. 跳转到这个指针指向的内存
    在反汇编界面右键 → Follow in Dump → Memory Address
    就能看到内存中的真实数据内容


🧩 举例:读取文件 “Hello World!”

文件内容:

Hello World!

ReadFile 执行完后,在 x64dbg Dump 窗口会看到类似:

地址 Hex 内容 ASCII 显示
0x00xxxxxx 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 00 … Hello World!

说明:

  • 48 = ‘H’
  • 65 = ‘e’
  • 6C 6C = ‘ll’
  • 看到右边 ASCII 中直观显示文件内容

👉 这块内存就是 lpBuffer 指向的地方
👉 x64dbg 通过 Dump 界面展示它的内容


🧠 再举例:读二进制文件(如 PNG)

读某张图片前 8 字节
x64dbg 中 Dump:

Hex ASCII
89 50 4E 47 0D 0A 1A 0A .PNG….

此时不用 %s 打印,因为没有字符串结束符
x64dbg 可以正确显示二进制原始内容


✨ 重点总结

说明
lpBuffer 是什么 一个内存地址,用于存放读出的数据
在 x64dbg 查看什么 它指向的那段内存的 Hex 和 ASCII 内容
什么时候有数据 ReadFile 调用完成之后

一句话背下来:

lpBuffer 在 x64dbg 里显示的不是变量本身,而是它背后的“内存碗”里的真实数据内容。


如果你愿意,我可以带你操作一个完整示例:
断在 ReadFile 前后 → 查看 lpBuffer 内容变化 → 识别 ASCII/二进制信息。
只要你说一声“来个实操”。 😄

lpBuffer 存放的指针 是开机的地方 还是结束的地方
Robot Avatar

非常好的问题 👍,这个细节很多人一开始都会弄混。


🧭 简答一句话:

lpBuffer 存放的是内存起始地址(开头的地方),不是结束的地方。


📘 详细解释:

lpBuffer 是一个指针,它指向一块内存区域(缓冲区)的开始位置
ReadFile 把文件数据读进来时,是从这个地址开始写入

也就是说:

  • lpBuffer → 缓冲区的第一个字节的地址(起点)
  • lpBuffer + n → 后续第 n 个字节的位置(偏移量)
  • lpBuffer + bytesRead - 1 → 最后一个有效字节的位置(结束点)

🧩 举个形象例子:

假设:

c
char buffer[10];
ReadFile(hFile, buffer, 10, &bytesRead, NULL);

假设 buffer 的地址是 0x1000,文件内容是 "ABCDEF"(6 字节)。

执行完后内存中是这样的👇:

地址 内容 说明
0x1000 41 (‘A’) lpBuffer 起始位置
0x1001 42 (‘B’) 第二个字节
0x1002 43 (‘C’) 第三个字节
0x1003 44 (‘D’) 第四个字节
0x1004 45 (‘E’) 第五个字节
0x1005 46 (‘F’) 第六个字节
0x1006 ?? 未使用

所以:

  • lpBuffer = 0x1000(指向开头)
  • 结束位置 = 0x1000 + bytesRead - 1 = 0x1005

🧠 在 x64dbg 中观察:

  1. 在寄存器窗口中找到 lpBuffer(如 RDX 或栈参数)
    假设值是 0x1000
  2. 右键 → “Follow in Dump → Memory address”
  3. 在 Dump 窗口中你看到的第一行就是从 lpBuffer(开头)开始的内容

所以在 x64dbg 里看到的内存数据第一字节,就是从 lpBuffer 开始的!


✅ 总结:

问题 答案
lpBuffer 是开头还是结尾? ✅ 开头地址
数据存放顺序 从 lpBuffer 开始,往后依次写入
如何找结束地址? lpBuffer + bytesRead - 1
x64dbg Dump 里第一行是什么? 从 lpBuffer 开始的内容

一句话记忆:

lpBuffer 指向“碗的口”,不是“碗的底”。
数据是从这个口往后装进去的。

发表在 安全分析 | 留下评论

CreateFile

CreateFile 是 Win32 API 中最常用也最容易“被名字骗”的函数之一

虽然名字叫 “CreateFile”(创建文件),但它不光能创建文件,还可以打开文件、设备、管道、串口、磁盘等各种对象


🧭 一、函数原型(简化版)

c
HANDLE CreateFile(
  LPCSTR lpFileName,            // 文件名或设备名
  DWORD dwDesiredAccess,        // 访问权限
  DWORD dwShareMode,            // 共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性(一般填 NULL)
  DWORD dwCreationDisposition,  // 创建/打开方式
  DWORD dwFlagsAndAttributes,   // 文件属性
  HANDLE hTemplateFile          // 模板文件句柄(一般填 NULL)
);

返回值是一个 句柄(HANDLE),成功时可用于后续操作(如读写),失败时返回 INVALID_HANDLE_VALUE


🧩 二、各参数通俗讲解 + 举例

参数 含义 通俗解释 示例
lpFileName 文件路径 你要打开或创建的目标文件 "C:\\test\\data.txt"
dwDesiredAccess 访问权限 你想干什么?读?写?读写? GENERIC_READ(只读)<br>GENERIC_WRITE(只写)<br>`GENERIC_READ
dwShareMode 共享模式 别的程序是否能同时访问 FILE_SHARE_READ(允许别人读)<br>0(独占访问)
lpSecurityAttributes 安全属性 一般填 NULL 就行 NULL
dwCreationDisposition 打开/创建方式 如果文件存在/不存在该怎么办? CREATE_NEW(必须新建)<br>CREATE_ALWAYS(总是重建)<br>OPEN_EXISTING(只打开已有文件)<br>OPEN_ALWAYS(打开或创建)<br>TRUNCATE_EXISTING(清空已有文件)
dwFlagsAndAttributes 文件属性 普通文件?隐藏?只读?是否异步? FILE_ATTRIBUTE_NORMAL(普通文件)<br>FILE_FLAG_OVERLAPPED(异步 I/O)
hTemplateFile 模板文件 复制已有文件的属性 一般 NULL

🧠 三、几个常见的使用例子

✅ 1️⃣ 打开一个已存在的文件进行读取

c
HANDLE hFile = CreateFile(
    "C:\\data.txt",             // 文件路径
    GENERIC_READ,               // 只读
    FILE_SHARE_READ,            // 允许别人同时读
    NULL,                       // 默认安全属性
    OPEN_EXISTING,              // 必须文件已存在
    FILE_ATTRIBUTE_NORMAL,      // 普通文件
    NULL                        // 无模板
);

👉 说明:
这就像打开一本书看内容。你只读,不写,也允许别人一起看。


✅ 2️⃣ 创建一个新的文件并写入内容

c
HANDLE hFile = CreateFile(
    "C:\\log.txt",              // 文件路径
    GENERIC_WRITE,              // 写入权限
    0,                          // 不允许别人同时访问
    NULL,                       // 默认安全属性
    CREATE_ALWAYS,              // 总是创建新文件(即使原来有也覆盖)
    FILE_ATTRIBUTE_NORMAL,      // 普通文件
    NULL                        // 无模板
);

👉 说明:
这就像写日记,每次都从头开始写新的内容,原来的内容清空。


✅ 3️⃣ 打开一个串口设备(COM1)

c
HANDLE hCom = CreateFile(
    "COM1",                     // 串口设备名
    GENERIC_READ | GENERIC_WRITE, // 读写权限
    0,                          // 独占
    NULL,                       // 默认安全属性
    OPEN_EXISTING,              // 设备必须存在
    0,                          // 无特殊属性
    NULL                        // 无模板
);

👉 说明:
CreateFile 不光能操作文件,还能打开串口、磁盘、命名管道等“文件型对象”。


✅ 4️⃣ 打开磁盘驱动器(如物理磁盘)

c
HANDLE hDisk = CreateFile(
    "\\\\.\\PhysicalDrive0",    // 特殊路径表示第一个物理磁盘
    GENERIC_READ,               // 读取权限
    FILE_SHARE_READ | FILE_SHARE_WRITE, // 允许共享
    NULL,                       
    OPEN_EXISTING,              
    0,                          
    NULL                        
);

👉 说明:
这让程序能直接读写磁盘的原始数据(低层操作)。


🧩 四、返回值和错误处理

  • 成功:返回一个有效句柄(非 INVALID_HANDLE_VALUE
  • 失败:返回 INVALID_HANDLE_VALUE
    c
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("打开文件失败,错误码: %d\n", GetLastError());
    }
    

常见错误:

错误码 含义
ERROR_FILE_NOT_FOUND 文件不存在
ERROR_ACCESS_DENIED 权限不足
ERROR_SHARING_VIOLATION 文件被占用(共享模式冲突)

🧾 五、总结(口语化记忆法)

参数 一句话记忆
lpFileName 我要操作谁?
dwDesiredAccess 我要干什么?(读/写)
dwShareMode 允许别人同时干吗?
lpSecurityAttributes 安全设置(几乎总填NULL)
dwCreationDisposition 文件存在/不存在怎么办?
dwFlagsAndAttributes 这个文件有啥特殊属性?
hTemplateFile 要不要复制别的文件属性?
发表在 安全分析 | 留下评论

Ghidra反编译工具的安装与使用介绍

Ghidra反编译工具的安装与使用介绍Ghidra是RSA Conference 2019上NSA发布的一个开源的逆向分 – 掘金

发表在 安全分析 | 留下评论

API函数VirtualAlloc VirtualProtect


🧩 一、基本概念对比

函数名 作用 类比
VirtualAlloc 在内存中 申请新的空间(分配内存) “找块新地建房子”
VirtualProtect 修改一块 已有内存的保护属性(可读/可写/可执行) “给房子换个用途或权限,比如从住宅改为工厂”

🧠 二、VirtualAlloc —— 分配虚拟内存

📘 原型

c
LPVOID VirtualAlloc(
  LPVOID lpAddress,   // 申请的起始地址(一般为 NULL)
  SIZE_T dwSize,      // 申请的大小
  DWORD flAllocationType, // 分配类型
  DWORD flProtect     // 初始保护属性(读写/执行)
);

💡 主要功能

  • 在当前进程的虚拟地址空间中,开辟一段新的内存区域
  • 返回一个指向该区域的指针。
  • 这块区域可以指定是否可执行、可写、可读。

🔨 常见用法

申请一段可读可写的内存,用来存放数据、shellcode、或动态代码。

c
LPVOID p = VirtualAlloc(
    NULL,               // 系统自动选择地址
    0x1000,             // 分配 4KB
    MEM_COMMIT | MEM_RESERVE,
    PAGE_READWRITE      // 可读可写
);

📦 这就相当于:

向系统要了一间新的“房子”(内存页),可以在里面读写数据。


🧩 三、VirtualProtect —— 修改内存保护属性

📘 原型

c
BOOL VirtualProtect(
  LPVOID lpAddress,    // 要修改的内存地址
  SIZE_T dwSize,       // 修改的大小
  DWORD flNewProtect,  // 新的保护属性
  PDWORD lpflOldProtect // 保存旧属性
);

💡 主要功能

  • 改变现有内存的访问权限,比如从:
    • 只读 → 可写
    • 可写 → 可执行
    • 不可执行 → 可执行

🧱 四、二者关系形象比喻

操作 类比
VirtualAlloc 向政府申请地皮 + 建房(开辟新内存)
VirtualProtect 改房子的用途或门禁(修改已有内存属性)

举个例子:

你写了个程序,它的代码段是“只读只执行”的。
但你想在运行时动态修改一条指令,就必须:

  1. 先用 VirtualProtect 临时把那块区域改成 可写
  2. 改完后再改回原来的保护属性。

🧪 五、通俗例子:修改一段代码(内存打补丁)

假设你要在程序运行时把某个函数的第一条指令改成 RET(直接返回)。

c
DWORD oldProtect;
BYTE patch[] = { 0xC3 }; // RET 指令

// 将目标函数的前 1 字节改为可写
VirtualProtect(targetFunc, 1, PAGE_EXECUTE_READWRITE, &oldProtect);

// 写入补丁
memcpy(targetFunc, patch, 1);

// 改回原保护
VirtualProtect(targetFunc, 1, oldProtect, &oldProtect);

🔍 过程说明:

  1. 程序原本的代码段是不可写的;
  2. 用 VirtualProtect 临时打开写权限;
  3. 写入字节;
  4. 再把权限恢复;
  5. 程序继续正常执行。

💣 六、恶意代码中的典型用法

比如在 shellcode 或注入代码 中:

c
LPVOID buf = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(buf, shellcode, size);
((void(*)())buf)(); // 直接执行

或:

c
VirtualProtect(shellcode, size, PAGE_EXECUTE_READ, &old);
((void(*)())shellcode)();

这意味着:

程序动态申请或修改了一段可执行内存区域,然后把机器码写进去再运行。

这种行为往往在安全分析中被标记为可疑(代码注入或自修改代码)。


⚙️ 七、保护属性常见值(flProtect

含义
PAGE_NOACCESS 不能读、写、执行
PAGE_READONLY 只读
PAGE_READWRITE 可读可写
PAGE_EXECUTE 只可执行
PAGE_EXECUTE_READ 可执行可读
PAGE_EXECUTE_READWRITE 可执行、可读、可写(常用于 shellcode)

✅ 八、总结对比表

函数 作用 举例 注意事项
VirtualAlloc 分配新内存(可设置保护属性) VirtualAlloc(NULL, 4096, MEM_COMMIT, PAGE_READWRITE) 返回地址可写、可执行等
VirtualProtect 修改已有内存的保护属性 把代码页改成可写后打补丁 必须保存原属性并恢复
配合使用 分配可执行内存 + 修改权限执行 常见于代码注入、JIT 编译、恶意程序 注意权限安全与系统保护

💬 一句话总结:

  • VirtualAlloc:开辟地盘(申请内存)。
  • VirtualProtect:改地盘的用途(改读写执行权限)。

 

发表在 安全分析 | 留下评论

x64dbg的调试命令

在 x64dbg 调试器中,Step Over(步过)、Step In(步入)和 Run(继续执行)是三种常用的执行控制命令,它们在调试过程中有不同的行为和作用。以下是具体区别及示例说明:

1. Step Over(步过,快捷键 F8)‍
行为:单步执行当前指令,但如果遇到函数调用(如 call 指令),不会进入函数内部,而是直接执行完整个函数,并暂停在函数调用后的下一条指令。

适用场景:当函数功能已知或无需深入分析时,快速跳过函数调用。

2. Step In(步入,快捷键 F7)‍
行为:逐条执行指令,遇到函数调用时会进入函数内部,继续单步调试函数中的代码。
适用场景:需要分析函数内部逻辑或排查函数内部的错误。

3. Run(继续执行,快捷键 F9)‍
行为:让程序连续运行,直到遇到下一个断点、异常或程序结束。
适用场景:快速跳过已知正常的代码段,或等待程序触发预设的断点。

在 x64dbg 中,调试代码时常见三种单步执行命令:

命令 英文名称 快捷键 功能
F7 Step Into 单步“步入” 进入函数内部逐条执行
F8 Step Over 单步“步过” 执行当前指令,但不进入被调用函数内部
Shift+F8 Step Out 跳出当前函数 执行到当前函数返回位置
发表在 安全分析 | 留下评论

API函数CreateFileW  ReadFile

 

 

  • Kernel32.dll:CreateFileW
  • Kernel32.dll:ReadFile

 

CreateFileW

参数:

HANDLE CreateFileA(
  [in]           LPCSTR                lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);

 

[in] lpFileName 要创建或打开的文件或设备的名称。

 

 ReadFile

C++

BOOL ReadFile(
  [in]                HANDLE       hFile,
  [out]               LPVOID       lpBuffer,
  [in]                DWORD        nNumberOfBytesToRead,
  [out, optional]     LPDWORD      lpNumberOfBytesRead,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

[in] hFile

设备的句柄(例如文件、文件流、物理磁盘、卷、控制台缓冲区、磁带驱动器、套接字、通信资源、mailslot 或管道)。

[out] lpBuffer

指向接收从文件或设备读取的数据的缓冲区的指针。

此缓冲区在读取操作的持续时间内必须保持有效。 在读取操作完成之前,调用方不得使用此缓冲区。

https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-readfile

 

发表在 安全分析 | 留下评论