MSVCRT.lib(exe_main.obj) : error LNK2001: 无法解析的外部符号 main
遇到这个问题的排错思路是:
属性
→配置属性
→C/C++
→预处理器
→预处理器定义
中的 _CONSOLE
。以及属性
→配置属性
→链接器
→系统
→子系统
中的 控制台(/SUBSYSTEM:CONSOLE)
,而不要错选成 WINDOWS
。但在这两个前提下,依然报这个错误。那么就要考虑到是否真的编译出 main 函数了。
原来是因为最开始在添加包含 main 函数的源代码文件时候,错选成了 .h 头文件,后面虽然改成了 .cpp 文件,但实质上在解决方案中此文件并未参与编译。于是把此文件从项目中排除
然后重新添加现有项就可以了。
注意要点
最后稍微列一下C++包含头文件的顺序,同样来源于上一个引用链接。
要注意的是一些头文件也有依赖关 系,这些文件的包含顺序也小心,否则就会出错。ps,头文件的包含顺序应该是从最特殊到一般,比如:我们应该以这样的方式来#include头文件:
从最特殊到最一般,也就是:
#include "本类头文件"
#include "本目录头文件"
#include "自己写的工具头文件"
#include "第三方头文件"
#include "平台相关头文件"
#include "C++库头文件"
#include "C库头文件"
本来我在做使用 WIN32 API 加密的时候(比如 AES、RC4) 是通过 CSP 实现的。CSP 是一个独立的软件模块,实际上执行用于身份验证,编码和加密的密码算法。CSP 提供了一组密码学 API,使应用程序开发人员可以向基于 Windows 的应用程序添加身份验证,编码和加密。
但是现在有了新一代密码学 API,也被称为 CNG(Cryprography API: Next Generation
),MSDN 号称:
之前的
Cryptography API
不再推荐使用。
如:
新的和现有的软件应开始使用Cryptography Next Generation API。Microsoft可能会在将来的版本中删除之前的
Cryptography API
。
本文就是使用 CNG(新一代密码学 API)来示范 AES 的使用,因为避免关联,不会给出完整代码,使用的尽量是 MSDN 上的代码或来源于其他合法渠道的公开代码示例。
CBC 模式是需要使用 IV 的。IV 即初始化向量,相比于 ECB 模式,CBC 模式对每个明文分组要与前一个密文分组进行异或,这样的作用是即时两个明文分组的值是相等的,经过多字节异或
,对应的两个密文分组的值也不一定是相等的。而IV 的使用就是为了解决第一个明文分组跟谁异或的问题。
当加密第一个明文分组时,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来代替“前一个密文分组”,这个比特序列称为初始化向量(Initialization Vector),通常缩写为IV,一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
这里要注意,IV 的长度与分组的大小是一致的。而 AES 的标准分组为 128 Bits,即为 16 字节。
AES 有三个输入和一个输出:
CS (测试版本 4.0)生成的默认宏样本,如下定义此结构体:
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
但其实:
hProcess As Long
hThread As Long
在64位机器环境下均定义错误。
因为 64 位环境下, VBA 中 Long 仅为4字节,如果按照这个宏样本去运行,取第三个参数的值,就会发现并不是正确的 PID:
Debug.Print pInfo.dwProcessId
但 CS 的默认宏样本却能正确运行,这是因为 hProcess 不过是一个进程句柄,是可以被4字节容纳下的。但是错误的内存大小定义会影响到占位和第三个参数的值,所以应该根据系统选择性的定义:
#If VBA7 And Win64 Then
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
#Else
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
#End If
注: LongPtr
在64位系统中为8字节。
什么是纤程(Fibers)?
用一个类比:线程是进程内部的实体,那么纤程则又是纤程内部的实体。
在《Windows Internals(第六版)》一书中提到:
因为将 CPU 的执行从一个线程切换到另一个线程,将不可避免地涉及内核调度器,所以,这可能是一个开销昂贵的操作,如果两个线程经常频繁地来回切换则尤其如此。 Windows 实现了两种机制来降低这一开销:纤程(fiber)和用户模式调度( UMS , user-mode scheduling )。
纤程使得一个应用程序可以调度它自己的“线程”的执行过程,而不必依赖于 Windows 内置的基于优先级的调度机制。纤程也常被称为“轻量”线程:从调度的角度来看,它们对于内核是不可见的,因为它们是在用户模式下在 Kemel32.dll 中实现的。为了使用纤程,首先要调用 Windows 的 ConvertThreadToFiber 函数。该函数将当前线程转变成一个正在运行的纤程。之后,在转变得到的纤程中,通过调用 CreateFiber 函数,又可以创建额外的纤程(每个纤程可以有它自己的一组纤程)。然而,与线程不同的是,纤程不会自动执行,它必须由 SwitchToFiber 函数手工选中,然后才能执行。新的纤程会一直运行,直到退出,或者调用SwitchToFiber再次选择运行另一个纤程。
总结一下,纤程是执行单元,其必须由应用程序进行手动调度。纤程在对其进行调度的线程的上下文中运行。
本文中,企图通过利用和纤程相关的 Win32 API,来在本地进程中执行 shellcode。绕过一些防御设备对常见进程注入 API 的拦截。
为了使用纤程执行 Shellcode,调用链为:
ConvertThreadToFiber
函数。VirtualAlloc
+ memcpy
函数。CreateFiber
函
- 因为蚂蚁笔记官方图片服务器极其不稳定,如果本文出现图片加载不出的问题,请转到此链接查看:
https://shimo.im/docs/8XJKxH9PxqgKp33c/ 《傀儡进程执行 Shellcode 的小坑》,可复制链接后用石墨文档 App 或小程序打开
三好学生在傀儡进程的实现与检测一文里面提到了傀儡进程。但是在那篇文章中,其实是 process hollowing/RunPE 技术。众所周知,所谓进程,就是pe文件的执行在内存中的映射。那么 process hollowing/RunPE 技术就是把一个挂起进程的内存数据清空,然后将一个 PE 文件映射到内存,再将进程的入口点替换为 PE 文件在内存中的起始地址,最后恢复进程状态,执行 PE 文件。
但这种所谓的 process hollowing/RunPE 技术对于 shellcode 注入来说有点过于重量级,因为镂空一个 PE 文件写入 shellcode 动静较大。所谓的镂空就是使用 NtUnmapViewOfSection 卸载正在执行的PE文件在内存中的映像。所以在进程 shellcode 注入的时候,更好地选择可能是不镂空进程,而直接修改进程的 EIP/RIP ,指向 shellcode 在内存中的起始地址。
所以调用链大概是这样的:
CREATE_SUSPENDED
标志调用 CreateProcess
API);或者使用 SuspendThread 函数挂起目标线程。总之也就是远线程注入+修改远线程的 EIP/RIP。
其中要注意的是,我们要通过GetThreadContext获得所有寄存器的信
看到了很多关于钩子注入 Hook inject
的文章,但主要是 DLL 注入。本文将尝试实现通过 Hooking 技术在远程进程中注入 Shellcode。
主要使用的是 SetWindowsHookEx
这个 API。
SetWindowsHookEx 用于将应用程序定义的挂钩过程安装到挂钩链中。您将安装一个挂钩过程来监视系统中的某些类型的事件。这些事件与特定线程或与调用线程在同一桌面上的所有线程相关联。
HHOOK SetWindowsHookEx(
int idHook,
HOOKPROC lpfn,
HINSTANCE hmod,
DWORD dwThreadId
);
调用示例:
HMODULE library = LoadLibraryA("dllhook.dll");
HOOKPROC hookProc = (HOOKPROC)GetProcAddress(library, "spotlessExport");
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, hookProc, library, 0);
可以看到第二个参数 HOOKPROC lpfn
是指向钩子程序的指针。在这里所谓的钩子程序的具体内容是:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" __declspec(dlle
- 因为蚂蚁笔记官方图片服务器极其不稳定,如果本文出现图片加载不出的问题,请转到此链接查看:
https://shimo.im/docs/rKXY8V9rrYPt3xKK/ 《Bypass DACL 注入进程(二)》
在 Bypass MIC & DACL 注入进程 一文中,有几个需要注意的地方。
调用链是:
1. OpenProcessToken
2. LookupPrivilegevalue
3. AdjustTokenPrivileges
目的是使用 OpenProcess 使用写权限去打开远程进程句柄,通过对当前进程启用 SeDebugPrivilege
特权绕过远程进程内存保护的限制。
但是启用此特权的前提是,当前进程具有此特权。
普通用户特权:
Administrator 组用户特权:
可以看到,Administrator 组的用户才具备此特权,只是被禁用了。所以我们需要通过调用 AdjustTokenPrivileges
API 来启用此特权。
AdjustTokenPrivileges 函数(securitybaseapi.h)
该 AdjustTokenPrivileges 功能启用或禁用特权在指定的访问令牌。启用或禁用访问令牌中的特权需要 TOKEN_ADJUST_PRIVILEGES 访问。
在以普通用户运行的 Visual Studio 中执行以下测试代码,发现不启用 SeDebugPrivilege 时,无法打开 pid 为 5192 的进程句柄。
此进程为 Session 0 中的宿迁进程 svchost.exe
。
尝试启用 SeDebugPrivilege,发现依然无法打开 pid 为 5192 的进程句柄。
这是符合以上我们所说的 token 特权问题的。也就是普通用户根本就无 SeDebugPrivilege
这一特权,所以启用此特权也是无效的。
所以结论是:
Administrator 组成员的 access token 中会含有一些可以执行系统级操作的特权(privileges) ,如终止任意进程、关闭/重启系
在远程进程注入之 shellcode 注入的时候,常规方案是:
VirtualAllocEx
→ WriteProcessMemory
注意这里必须是
VirtualAllocEx
而非VirtualAlloc
,因为 VirtualAlloc 是给调用进程分配内存;而 VirtualAllocEx 才是给另一个进程的地址空间中分配内存。
现在这两个函数都挺敏感的,在下曾经遇到过天擎拦截 WriteProcessMemory
。当然我们可以对 API 作精修,比如替换为内核函数,这样可以绕过 uerland hook。就算是 inline hook,也可以使用 syscall 进行绕过。
但其实要完成[在远程进程中分配内存并将shellcode复制进去]这一任务,还可以使用一套API链,也就是所谓的映射注入 File Mapping
。
我不是故意想搞名词,名词是为了概括,东西并不难,我只是记录一条链,不喜勿喷。
CreateFileMapping
→ MapViewOfFile
→ MapViewOfFile2
原理见我的亲兄弟idiotc4t的文章 Mapping Injection。
CreateFileMapping
创建或打开指定文件的命名或未命名文件映射对象。
MapViewOfFile
将文件映射的视图映射到调用进程的地址空间。
MapViewOfFile2
将文件或页面文件支持的部分的视图映射到指定进程的地址空间。
概括一下注入流程:
CreateFileMapping
API)CreateFileMapping
API)memcpy
库函数)MapViewOfFile2
API)代码实现(我把这个过程自己封成了一个函数 MappingShellcodeFile):
#include <windows.h>
#include <stdio.h>
#
//可以在startinfo里面指定
si.wShowWindow=SW_HIDE;
si.dwFlags=STARTF_USESHOWWINDOW
//也可以在createflags
里面指定
2、 MSSQL使用CLR程序集来执行命令
这里面有命令执行的CLR程序集。
更多功能的程序集,如文件下载可以参考此github项目:MSSQL-Fileless-Rootkit-WarSQLKit