2021-06-02 14:23:07    1061    0    0

博客地址已换,请于此地址查看:

snowming04's new blog

2021-06-02 14:16:30    2566    0    0

出于图片显示的稳定性考虑,请跳转至此链接阅读:

https://shimo.im/docs/WgQPTXJGWtRcJPTg/

2021-04-06 19:11:10    1223    0    1

① 项目代码

根据 https://www.freebuf.com/articles/web/231663.html 一文中展示的 shellcode 生成框架、实现了基于 Win32 API 的远程文件下载功能。

项目代码位于:
https://github.com/Snowming04/shellcode_generate-test_framework

因为此文中原理论述足够清楚,故本文中不再谈论。

② 编译选项

本次工程创建是win32空项目,使用的是VS2019编译relase/x86,编译之前进行如下设置:

  1. 创建工程时关闭SDL检查
  2. 属性->C/C++->代码生成->运行库->多线程 (/MT)如果是debug则设置成MTD
  3. 属性->常规->平台工具集->设置为Visual Studio 2017- Windows XP (v141_xp),如果没有则可以去安装上对应的兼容xp的组件
  4. 属性->C/C++->代码生成->禁用安全检查GS
  5. 关闭生成清单 属性->链接器->清单文件->生成清单 选择否
  6. C/C++ -> 语言 -> 禁用语言扩展 选择否
  7. C/C++ -> 语言 -> 符合模式 选择否

③ 项目结构

头文件:

  • api.h:在此 头文件中定义实现 shellcode 功能用到的函数对应的签名,以及结构体
  • header.h:shellcode 生成框架中的函数原型

源代码文件:

  • 0.entry.cpp
  • a.start.cpp
  • b.work.cpp:shellcode 功能代码
  • z.end.cpp

其他文件:

  • SCer.exe:快速测试 shellcode 功能的工具

④ 效果展示

title

title

title

2021-04-01 11:46:22    1437    0    0

本文希望通过使用 Windbg 调试 kernel32.dll,查看其导出表、 探索 IMAGE_EXPORT_DIRECTORY 数据结构,理解以下三个表之间的相互关系:

  • AddressOfFunctions
  • AddressOfNames
  • AddressOfNameOrdinals

便于更好的理解后续通过函数名称在内存中获取函数指针的 shellcode 编程。


① kernel32.dll 的 Export Directory

将 WinDbg attach 到 C:\Windows\System32\notepad.exe(x64) 程序上。

  1. 0:000> !peb
  2. PEB at 00000093ec76a000
  3. InheritedAddressSpace: No
  4. ReadImageFileExecOptions: No
  5. BeingDebugged: Yes
  6. ImageBaseAddress: 00007ff626d20000
  7. NtGlobalFlag: 70
  8. NtGlobalFlag2: 0
  9. Ldr 00007fff777053c0
  10. Ldr.Initialized: Yes
  11. Ldr.InInitializationOrderModuleList: 000001986c942ef0 . 000001986c943770
  12. Ldr.InLoadOrderModuleList: 000001986c9430a0 . 000001986c94a760
  13. Ldr.InMemoryOrderModuleList: 000001986c9430b0 . 000001986c94a770
  14. Base TimeStamp Module
  15. 7ff626d20000 5e82e461 Mar 31
2021-03-29 19:52:12    1551    0    0

本文希望通过 shellcode 编程获取 kernel32.dll 的内存地址。获取 kernel32 的内存地址是通过 LoadLibraryA -> GetProcAddress 调用链进行 shellcode win32 编程的基本步骤。


① kernel32.dll 的内存地址

首先通过 windows c 代码获取 kernel32.dll 的内存地址,用于后续的结果验证。

title

注:编译环境为 win32/x86。


② peb 中寻找 kernel32.dll 的内存地址

已知 peb 中加载的第三个模块是 kernel32.dll。所以 shellcode 作为一段 PIC (位置无关)代码,我们是希望从 peb 中获取 kernel32.dll 的内存地址。

将 WinDbg attach 到 C:\Windows\SysWOW64\notepad.exe(x86) 程序上。

PEB 数据结构覆盖在所指向的内存上,以查看该结构成员,在此重点关注 Ldr

title

Ldr 是一个 _PEB_LDR_DATA 类型的成员,包含有关当前进程加载模块的信息。

title

看到偏移 +0x014 处的成员 InMemoryOrderModuleList。这是一个 _LIST_ENTRY 类型的成员。

title

可以看到这其实是一个双向链表的指针,其有两个成员变量:

  • Flink 指向前一个结点,其大小为一个 x86 指针长度4。
  • Blink 指向下一个结点,其大小为一个 x86 指针长度4。

我们都知道,链表的一个结点,既包含了指针信息,也包含了数据。

title

将此地址覆盖到 LDR_DATA_TABLE_ENTRY 结构查看其数据:

title

可以看到其 Flink(前一个结点)的 FullDllName 为 notepad.exe。但是要注意这是头节点,要获得当前节点的 FullDllName 需要 -8(一个结点长度8字节,因为双向链表结点两个指针 4 bytes *2):

title

可以看到为当前 exe,即为第1个模块。而我们的目的是查看第3个模块并获取其内存地址。

重新回到 InMemoryOrderModuleLis

2021-02-05 19:02:33    1456    0    0
  1. MSVCRT.lib(exe_main.obj) : error LNK2001: 无法解析的外部符号 main

遇到这个问题的排错思路是:

  • main 函数是否拼写正确?
  • main 对应 属性配置属性C/C++预处理器预处理器定义中的 _CONSOLE。以及属性配置属性链接器系统子系统中的 控制台(/SUBSYSTEM:CONSOLE),而不要错选成 WINDOWS

但在这两个前提下,依然报这个错误。那么就要考虑到是否真的编译出 main 函数了。

原来是因为最开始在添加包含 main 函数的源代码文件时候,错选成了 .h 头文件,后面虽然改成了 .cpp 文件,但实质上在解决方案中此文件并未参与编译。于是把此文件从项目中排除然后重新添加现有项就可以了。

注意要点
最后稍微列一下C++包含头文件的顺序,同样来源于上一个引用链接。

要注意的是一些头文件也有依赖关 系,这些文件的包含顺序也小心,否则就会出错。ps,头文件的包含顺序应该是从最特殊到一般,比如:我们应该以这样的方式来#include头文件:
从最特殊到最一般,也就是:

  1. #include "本类头文件"
  2. #include "本目录头文件"
  3. #include "自己写的工具头文件"
  4. #include "第三方头文件"
  5. #include "平台相关头文件"
  6. #include "C++库头文件"
  7. #include "C库头文件"
2020-11-25 18:39:44    1058    0    1

0x01 什么是 CNG 以及为什么要使用它

本来我在做使用 WIN32 API 加密的时候(比如 AES、RC4) 是通过 CSP 实现的。CSP 是一个独立的软件模块,实际上执行用于身份验证,编码和加密的密码算法。CSP 提供了一组密码学 API,使应用程序开发人员可以向基于 Windows 的应用程序添加身份验证,编码和加密。

但是现在有了新一代密码学 API,也被称为 CNGCryprography API: Next Generation),MSDN 号称:

之前的 Cryptography API 不再推荐使用。

如:

  • CryptAcquireContextA
  • CryptCreateHash
  • CryptDeriveKey
  • ...

新的和现有的软件应开始使用Cryptography Next Generation API。Microsoft可能会在将来的版本中删除之前的 Cryptography API

本文就是使用 CNG(新一代密码学 API)来示范 AES 的使用,因为避免关联,不会给出完整代码,使用的尽量是 MSDN 上的代码或来源于其他合法渠道的公开代码示例。

0x02 实现 AES CBC 模式

CBC 模式是需要使用 IV 的。IV 即初始化向量,相比于 ECB 模式,CBC 模式对每个明文分组要与前一个密文分组进行异或,这样的作用是即时两个明文分组的值是相等的,经过多字节异或,对应的两个密文分组的值也不一定是相等的。而IV 的使用就是为了解决第一个明文分组跟谁异或的问题。

当加密第一个明文分组时,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来代替“前一个密文分组”,这个比特序列称为初始化向量(Initialization Vector),通常缩写为IV,一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。

这里要注意,IV 的长度与分组的大小是一致的。而 AES 的标准分组为 128 Bits,即为 16 字节。

AES 有三个输入和一个输出:

  • 输入1:IV。IV 不得大于16字节,如果用户输入大于则截断,小于则填充0。
  • 输入
2020-11-20 17:53:41    847    0    0

CS (测试版本 4.0)生成的默认宏样本,如下定义此结构体:

  1. Private Type PROCESS_INFORMATION
  2. hProcess As Long
  3. hThread As Long
  4. dwProcessId As Long
  5. dwThreadId As Long
  6. End Type

但其实:

  1. hProcess As Long
  2. hThread As Long

在64位机器环境下均定义错误。

因为 64 位环境下, VBA 中 Long 仅为4字节,如果按照这个宏样本去运行,取第三个参数的值,就会发现并不是正确的 PID:

  1. Debug.Print pInfo.dwProcessId

但 CS 的默认宏样本却能正确运行,这是因为 hProcess 不过是一个进程句柄,是可以被4字节容纳下的。但是错误的内存大小定义会影响到占位和第三个参数的值,所以应该根据系统选择性的定义:

  1. #If VBA7 And Win64 Then
  2. Private Type PROCESS_INFORMATION
  3. hProcess As LongPtr
  4. hThread As LongPtr
  5. dwProcessId As Long
  6. dwThreadId As Long
  7. End Type
  8. #Else
  9. Private Type PROCESS_INFORMATION
  10. hProcess As Long
  11. hThread As Long
  12. dwProcessId As Long
  13. dwThreadId As Long
  14. End Type
  15. #End If

注: LongPtr 在64位系统中为8字节。

2020-11-10 21:39:48    838    0    0

因为蚂蚁笔记官方图片服务器极其不稳定,请直接转到此链接查看:

《探索 PEB》
https://shimo.im/docs/jRwpvxXYPwPw9Cdd/

我太懒了,有机会的话会换博客服务器。

2020-11-09 15:26:44    1139    0    0

什么是纤程Fibers)?

用一个类比:线程是进程内部的实体,那么纤程则又是纤程内部的实体。

在《Windows Internals(第六版)》一书中提到:

因为将 CPU 的执行从一个线程切换到另一个线程,将不可避免地涉及内核调度器,所以,这可能是一个开销昂贵的操作,如果两个线程经常频繁地来回切换则尤其如此。 Windows 实现了两种机制来降低这一开销:纤程(fiber)和用户模式调度( UMS , user-mode scheduling )。

纤程使得一个应用程序可以调度它自己的“线程”的执行过程,而不必依赖于 Windows 内置的基于优先级的调度机制。纤程也常被称为“轻量”线程:从调度的角度来看,它们对于内核是不可见的,因为它们是在用户模式下在 Kemel32.dll 中实现的。为了使用纤程,首先要调用 Windows 的 ConvertThreadToFiber 函数。该函数将当前线程转变成一个正在运行的纤程。之后,在转变得到的纤程中,通过调用 CreateFiber 函数,又可以创建额外的纤程(每个纤程可以有它自己的一组纤程)。然而,与线程不同的是,纤程不会自动执行,它必须由 SwitchToFiber 函数手工选中,然后才能执行。新的纤程会一直运行,直到退出,或者调用SwitchToFiber再次选择运行另一个纤程。

总结一下,纤程是执行单元,其必须由应用程序进行手动调度。纤程在对其进行调度的线程的上下文中运行。

本文中,企图通过利用和纤程相关的 Win32 API,来在本地进程中执行 shellcode。绕过一些防御设备对常见进程注入 API 的拦截。

为了使用纤程执行 Shellcode,调用链为:

  1. 将主线程转换为纤程1(这是必需的,因为只有一个纤程才可以创建另一个纤程)。使用 ConvertThreadToFiber 函数。
  2. 将shellcode写入某个内存位置并使其可执行,获取指向此位置的指针。可使用 VirtualAlloc + memcpy 函数。
  3. 创建一个指向 Shellcode 位置的新纤程2——我们将使用在第一步中通过主线程转的纤程1来创建此纤程2。使用 CreateFiber