0x09 Windows原理

1. 权限管理


  • 相关名词
    • SID:安全描述符,用于标识用户身份
    • LUID: 局部唯一标识符,系统每次启动都可能变化
    • GUID: 全局唯一标识符,一直都保持不变
    • UAC(User Account Control): 用户账户控制,vista开始启用
  • 相关函数
    • 获取进程令牌: OpenProcessToken
    • 获取线程令牌: OpenThreadToken
    • 获取令牌信息: GetTokenInformation
    • 查询特权名称的LUID: LookupPrivilegeValue
    • 修改令牌权限: AdjustTokenPrivileges
    • 创建管理员进程: ShellExecute传入runas

2. 内存管理


  • Window的内存布局
    • 每个进程都有单独的4GB空间,布局通常如下
      1. 0x00000000~0x0000FFFF : 空指针赋值区
      2. 0x00010000~0x7FFF0000 : 用户领空
      3. 0x7FFF0000~0x7FFFFFFF : 64Kb禁入分区
      4. 0x80000000~0xFFFFFFFF : 系统领空
  • 虚拟内存的三种状态
    • 闲置:没有和物理内存以及虚拟内存建立关联
    • 预定(保留):没有和物理内存建立关联,但是占用了虚拟空间
    • 提交:和物理内存建立了关联,可以直接使用
  • 虚拟内存的映射方式
    • 私有: 其它进程不可直接访问的空间
    • 映射(mapped):内存映射文件
    • 映像(image):通常是可执行文件和模块
  • 内存的分页属性
    • 只读,可读可写,可读可执行, 可读可写可执行, 写时拷贝
    • Windows的模块通常都拥有写时拷贝属性
  • 内存管理方式
    • 堆内存:通常用于进行小块内存的分配
      • 获取默认堆空间:GetProcessHeap()
      • 创建新的堆空间:HeapCreate()
      • 在堆中申请内存:HeapAlloc()
        • 申请的空间以 8 字节对齐,使用HEAP_ZERO_MEMORY可以初始化
      • 重新申请空间: HeapReAlloc()
      • 释放堆空间:HeapFree()
      • 删除堆空间:HeapDestroy()
    • 虚拟内存:通常用于大片内存的管理
      • 申请内存:VirtualAlloc(首地址,大小,申请方式,分页属性)
        • 指定的首地址必须是分配粒度的倍数(0x10000)
        • 分配的内存大小会被对齐到分页大小(0x1000)的整数倍
        • 如果只保留了内存空间,那么不能进行访问
      • 修改分页属性 VirtualProtect() HOOK 调试器
      • 释放虚拟内存 VirtualFree()
      • 远程写进程内存: WriteProcessMemory(进程的句柄,读写哪里,保存到哪里,大小,实际写入)
      • 远程读进程内存: ReadProcessMemory
      • 查看虚拟内存属性: VirtualQuery
      • 注:当使用[EX]版本的函数时,可以对其它进程进行操作
    • 文件映射:可用于进程通信和大文件的操作
      • 创建文件映射对象: CreateFileMappingA
        • 可以映射一个文件,也可以映射一块内存
      • 关联文件到内存:MapViewOfFile
      • 刷新内存数据到文件:FlushViewOfFile
      • 取消关联:UnmapViewOfFile

3. 模块注入


  • 注入,让其它进程执行指定的代码
  • 常见的注入方式:
    • 消息钩子注入 HHOOK SetWindowsHookEx(类型,回调函数,模块句柄,线程)
    • 远程线程注入 CreateRemoteThread
    • 注册表注入 所有的GUI程序在执行前,都会加载注册表的某一个位置提供的所有DLL
    • 劫持模块注入 当前的 环境变量 系统路径,系统模块不适用,应用程序适用
    • COMRes注入 类似注册表注入
    • APC注入 给其它的线程提供APC函数,当目标线程可警醒时,会调用
    • 输入法注入 最麻烦的,自己实现一套输入法的接口

4. HOOK技术


  • 远程线程注入的流程
    1. 确认需要注入的程序并获取目标进程的句柄:GetWindowThreadProcesssId() OpenProcess();
    2. 在目标进程内申请空间: VirtualAllocEx(Process);
    3. 向空间内写入dll名称:WriteProcessMemory(IatHookDllName);, DLL名称需要是绝对路径
    4. 创建远程线程, 关键函数: CreateRemoteThread(LoadLibrary(A/W);
    5. 等待线程执行结束:WaitForSingleObject(Thread, INFINITE);
    6. 释放空间:VirtualFreeEx(Process, Buffer, 0, MEM_RELEASE);
    7. 关闭句柄:CloseHandle();
  • Message Hook
    • windows操作系统维护了一个钩子链,可以通过函数向其中添加或删除函数
    • 添加钩子: SetWindowsHookEx, 钩子的类型
    • 取消钩子: UnhookWindowsHookEx
    • 调用下一个钩子函数:CallNextHookEx 通常是必须调用的
  • IAT(eat) Hook

    • IAT HOOK的流程 FF15 cal FF25 jmp
    • 原理是修改导入地址表中对应的函数地址

      1. 找到想要HOOK的函数,获取函数原型并实现自己的对应函数

        1. HANDLE WINAPI MyHookFunc()
        2. {
        3. if (满足HOOK条件)
        4. {
        5. // 执行自己想执行的代码
        6. }
        7. else
        8. {
        9. // 调用原函数
        10. OldFunc();
        11. }
        12. }
      2. 遍历目标进程的导入表,找到指定模块,并找到模块中对应函数所在IAT中的地址

      3. 修改内存属性,将新的函数地址写入到IAT表中,修改完后恢复。
      4. 卸载: 将保存的原函数地址重新写入到IAT中
    • 注:可以使用GetProcAddress函数过掉 IATHook
      • 通过 EAT HOOK 可以 BYPASS GetProcAddress
  • Inline Hook
    • inline Hook 可用于内核层和用户层,可以hook代码的任意位置
    • inline HOOK 的流程
      1. 找到想要HOOK的函数,获取函数原型并实现自己的对应函数
      2. image.png
      3. 获取目标函数的地址并保存,用于卸载 Hook(GetProcAddress)
      4. 通过目标地址 - 指令所在地址 - 5(E9XXXXXXXX)求得跳转偏移
      5. 使用偏移构建跳转指令并写入到目标函数的执行流程
        1. 写入的时候是非线程安全的, 使用原子操作,暂停其它所有线程
      6. 注:写入代码时需要修改并还原保护属性
    • 注:可以使用指令比对,hash计算的方式检查是否被内联HOOK