1.Win32 API介绍

这个内容在win 32汇编里面有。这个API就是函数。

这些API函数是封装起来的,在动态链接库中,对于API函数的调用就需要加载动态链接库【.dll】

API函数是开发win32应用程序的基础,即使使用MFC库来开发win32应用程序,也要对API函数有一定的了解。MFC封装了API函数,但并没有封装所有的API函数,比如发送消息函数:

SHBrowseForFolder

Win32 API的⼀些对系统,进程,线程,内存等操作的API上⾯与常⻅常⽤的⼀些windows api。

大概了解一下API

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Winodw
GetClassName // 获取窗⼝类名
SendMessage // 发送消息
FindWindow // 查找窗⼝
EnumWindows // 枚举所有窗⼝
GetVersionEx // 获取系统版本
GetSystemInfo // 获取硬件信息
GetSystemDirectory // 获取系统⽬录
GetWindowsDirectory // 获取Windows 安装⽬录
GetUserName // 获取⽤户名
GetComputerName // 获取计算机名
SystemParametersInfo // 外设信息
(时间函数经常会出现在某某试⽤软件中。)
GetLocalTime // 获取本地时间
SetLocalTime // 设置本地时间
GetTickCount // 获取开机到现在的时间(毫秒)
GetTickCount64 // 64位
(病毒开机⾃启动会对注册表进⾏⼀些敏感操作就会调⽤这些API。)
// 注册表
RegCreateKey RegCreateKeyEx // 创建新项
RegOpenKey RegOpenKeyEx // 打开⼀个项
RegQueryValue RegQueryValueEx // 访问项的值
RegDeleteKey RegDeleteKeyEx // 删除⼀个项
RegCloseKey // 关闭句柄

1
2
3
4
5
6
7
8
9
10
Memory
(病毒为了更好的隐藏⾃⼰,经常在内存加载运⾏。VirualAlloc再常⽤不过了。。。)
VirtualAlloc // 开辟私有内存 (进程独有的内存空间) Private
VirtualFree // 私有内存释放
CreateFileMapping // 开辟物理⻚ 不与虚拟内存链接
MapViewOfFile // 链接
UnMapViewOfFile // 关闭⽂件资源
FlushMapViewOfFile // 刷新缓冲区
OpenFileMapping // 打开⼀个共享
// 真正的开辟内存只有这两个函数,molloc new 在分配好的内存上 再分配 假分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
⽂件系统
(感染形病毒经常对⽂件做操作哦)
⼀、卷相关API
GetLogicalDrives // 获取逻辑驱动器 获取卷
GetLogicalDriveStrings // 获取所有盘符的字符串
GetDriveType // 获取驱动器的类型
GetVolumeInformation // 获取驱动器的信息
⼆、⽬录相关 API
CreateDirectory // 创建⽬录
RemoveDirectory // 删除⽬录
MoveFile // 修改⽬录名称
SetCurrentDirectory // 设置当前⽬录所在位置
GetCurrentDirectory // 获取当前⽬录名称
三、⽂件相关 API
CreateFile // 创建⼀个⽂件
GetFileSize // 获取⽂件⼤⼩
GetFileAttributesEx // 获取⽂件属性
FileTimeToSystem // ⽂件时间 系统时间
SetFilePointer // ⽂件索引位置
ReadFile // 读取⽂件
WriteFile // 写⼊⽂件
CopyFile // 拷⻉⽂件
DeleteFile // 删除⽂件
FindFirstFile // 搜索第⼀个⽂件
FindNextFile // 搜索下⼀个⽂件
FindClose // 关闭查找句柄
1
2
3
4
环境变量
GetEnvironmentStrings // 获取系统中的所有环境变量
GetEnvironmentVarlable // 获取某⼀个环境变量
SetEnvironmentVarlable // 增加、修改、删除某⼀个环境变量

2.进程,进程之间的通信,令牌,令牌环,UAC提权

进程

什么是进程

进程是指系统中正在运行的一个应用程序,程序一旦运行,就是进程。进程是系统进行资源分配的独立实体,并且每一个进程拥有独立的地址空间。一个进程可以拥有多个线程,每一个线程使用其所属进程的空间。进行之间的通信 进程间通信IPC(管道,信号量,共享内存,消息队列)

进程和线程的区别

1.定义不一样,进程是执行中的一段程序,而一个进程中执行的每一个任务就是线程

2.一个线程只可以属于一个进程,但是一个进程能包含很多线程

3.线程无地址空间,它包括在进程的地址空间里面

4.线程的开销或代价比线程小

相当于,进程是一个容器,容器里面的线程才是真正做事情的。

如何在WIN32中去操控进程呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
OpenProcess	//打开进程
CreateProcess //创建进程
WriteProcessMemory //写进程中的数据
ResumeThread(pi.hThread); //重新启动线程
termilateprocess //终止进程
GetModuleFileName //获取模块路劲
GetCurrentDirectory //获取工作路劲
GetCurrentProcessId //获取当前进程的id
GetCurrentProcess //获取当前进程句柄(伪句柄)
GetCommandLine //获取命令行
GetStarupInfo //获取启动信息
EnumProcesses //遍历进程id
....

先看如何打开记事本,用API函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <Windows.h>	//windows 编程
#include <iostream>


int main() {
STARTUPINFO si = { sizeof(STARTUPINFO) };//在产生子进程的时候,子进程的窗口相关信息
PROCESS_INFORMATION pi;
DWORD returnCode;//用于保存子进程的返回值

wchar_t commandLine1[] = L"notepad.exe";//测试命令行参数

BOOL bRet = CreateProcess( //按下F1//调用失败,返回0;调用成功
NULL, //一般都是空
commandLine1,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi
);

if (bRet) {
std::cout << "process is running" << std::endl;
//等待子程序进程结束
WaitForSingleObject(pi.hProcess, -1);
std::cout << "process is finished" << std::endl;
//获取子程序进程的返回值
GetExitCodeProcess(pi.hProcess, &returnCode);
std::cout << "Create return code:" << returnCode<<std::endl;
}
else {
std::cout << "Create Process error!" << std::endl;
return 0;
}

CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);

}

在运行上面的C++的时候,我们可以看到记事本已经被执行了。打开notepad。这里主要靠CreateProcess这个函数API执行,当然如果只是打开程序的话也可以调用ShellExecute打开一个程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
ShellExcute(NULL,"open","EXE路劲",NULL,NULL,SW_SHOW);

SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = strExe;
ShExecInfo.lpParameters = sParams;
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_HIDE:
ShExecInfo.hInstApp = NULL;
B00L ret = ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess,INFINITE);

程序之间的通讯

进程通讯方法非常之多,病毒和木马进程会和其他进程进行通讯。想办法获取信息,甚至有一些会基于系统层去拦截一些信息

文件映射

文件映射能使文件内容当作进程地址区间一块内存那样来对待,因此,进程不必使用文件I/O操作,只需要指针操作就可以读取和修改文件内容

Win32 API允许多个进程访问同一个文件映射对象,各个进程在它自己的地址空间接受内存的指针。通过使用这些指针,不同进制就可以读或修改文件的内容,实现了对文件中数据的共享。

应用程序有3种方法来使多个进程共享一个文件映射对象。

  1. 继承:第一个进程建立文件映射对象,它的子进程继承改对象的句柄。
  2. 命名文件映射:第一个进程在建立文件映射对象的时候可以给该对象指定一个名字【可于文件名字不同】。第二个进程可通过这个名字打开次文件映射对象。另外,第一个进程也可以通过一些其他IPC机制【有名管道,邮件槽等..】把名字传给第二个进程
  3. 句柄复制:第一个进程建立文件映射对象,然后通过其他IPC鸡翅【有名管道,邮件槽等..】把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。

文件映射在多个进程间共享数据的方法非常有效,有较好的安全性。但文件映射只能用于本地机器的进程之间,不用于网络,而开发者还必须控制进程间的同步。

共享内存

Win 32 API 中共享内存 实际就是文件映射的一种特殊情况。进程在创建文件映射对象时使用0xFFFFFFFF来代替文件句柄,就表示了对应的文件映射对象是从操作系统页面文件访问内存,其他进程打开文件映射对象就可以访问该内存快。由于共享内存是用文件映射时间的,所以它的安全性也good,也只能运行于同一个计算机上的进程之间

匿名管道

管道是一种具有两个端点的通信通道:有一端句柄的进程可以和另一端句柄的进程通信,管道可以是单向- 一端只能读,另一段只能写,也可以是双向的。

匿名管道是在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通向由父进程创建管道,然后由要通信的子进程继承管道的读端点句柄或写断点句柄,然后实现通信。父进程还可以建立两个或者更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通讯,不需要通过父进程

匿名管道是单机上实现子进程标准I/O重定向的有效的方法,不能在网上使用,也不能用于两个不相关的进程之间

命名管道

命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。

邮件槽

邮件槽(Mailslots)提 供进程间单向通信能力,任何进程都能建立邮件槽成为邮件槽服务器。其它进程,称为邮件槽客户,可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消 息一直放在邮件槽中,直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户,因此可建立多个邮件槽实现进程间的双向通信。

通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节,非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。

邮件槽与命名管道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的,一旦网络发生错误则无法保证消息正确地接收,而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一种选择。

远程过程调用

Win32 API提供的远程过程调用(RPC使应用程序可以使用玩程调用函数,这使在网络上用RPC进行进程通信就像函数调用那样简单RPC既可以在单机不同进程间使用也可以在网络中使用。

由于Win32 AP提供的RPC服从OSF-DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过Win32 APl编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程序。

Sockets

Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley Socket原有的库函数以外,还扩展了一组针对Windows的函数,使程序员可以充分利用Windows的消息机制进行编程。

现在通过Sockets实现进程通信的网络应用越来越多,这主要的原因是Sockets的跨平台性要比其它IPC机制好得多,另外WinSock2.0不仅支持TCP/IP协议,而且还支持其它协议(如IPX)。Sokets的唯一缺点是它支持的是底层通信操作,这使得在单机的进程间进行简单数据传递不太方便,这时使用下面将介绍的WM_COPYDATA消息将更合适些。

WM_COPYDATA消息

WM COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时,发送方只需使用调用SendMessage区数,参数是目的窗口的句柄、传递数据的起始地址、WM COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息这样收发双方就实现了数据共享。

WM_COPYDATA是一种非常简单的方法,它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高,并且它只能用于Windows平台的单机环境下。

进程的令牌

这个主要是我们后期进程提权需要,譬如在杀死一个进程中需要提权用到令牌环。

访问令牌

访问令牌(Access Tokens)是Windows操作系统安全性的一个概念。

当用户登陆时,系统创建一个访问令牌,里面包含登录进程返回的SID和由本地安全策略分配给用户和用户的安全组的特权列表。

系统使用令牌控制用户可以访问哪些安全对象,并控制用户执行相关系统操作的能力

有两种令牌:主令牌和模拟令牌。

主令牌是由windows内核创建并分配给进程的默认访问令牌,每一个进程有一个主令牌,它描述了与当前进程相关的用户账户的安全上下文

如果用sysinternal工具logonsessions查看的话,这两个令牌属于不同的Logon Session.

OpenProcessToken

函数用来打开与进程相关联的访问令牌;

要对一个任意进程(包括系统安全进程和服务进程)进指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以了。要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator账号对一个系统安全进程执行OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID)还是会遇到“访问拒绝”的错误。什么原因呢? 原来在默认的情况下进程的一些访问权限是没有被启用(Enabled)的,所以我们要做的首先是启用这些权限。与此相关的一些API函数有OpenProcessToken、LookupPrivilegevalue、AdjustTokenPrivileges。我们要修改一个进程的访问令牌,首先要获得进程访问令牌的句
柄,这可以通过OpenProcessToken得到,函数的原型如下:

1
2
3
4
5
BOOL OpenProcessToken(
__in HANDLE ProcessHandle,//要修改访问权限的进程句柄
__in DWORD DesiredAccess,//指定你要进行的操作类型
__out PHANDLE TokenHandle //返回的访问令牌指针
);

看看能关机的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<Windows.h>
#include <iostream>

int main(){
BOOL fResult;
TOKEN_PRIVILEGES tkp;
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, & hToken))
{
printf("OpenProcessToken failed!"); //获得进程句柄失败
}
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME, & tkp.Privileges[0].Luid); //获得本地机唯一的标识
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE, & tkp,0,(PTOKEN_PRIVILEGES)NULL,0); //调整获得的权限
if (GetLastError() != ERROR_SUCCESS)
{
printf("AdjustTokenPrivileges enable failed!"); //修改权限失败
}
fResult = InitiateSystemShutdown(
NULL, // 要关的计算机用户名
(LPWSTR)"由于系统不稳定,WINDOWS将在上面的时间内关机,请做好保存工作!",// 显示消息
0,// 关机所需的时间
FALSE,// 是否提示用户
FALSE //设为TRUE为重起,设为FALSE为关机
);
}

3.UAC提权

UAC是微软在windows vista以后版本引入后的一种安全机制,通过UAC,应用程序可始终在非管理员账户的安全上下文中运行,除非管理员特别授予管理员级别的系统访问权限。UAC可以阻止未经授权的应用程序自动进行安装,并防止无意中更改系统设置。

UAC需要授权的动作包括:配置windows update;增加或删除用户账户;改变用户账号类型;改变UAC设置;安装ActiveX;安装或移除程序;安装设备驱动程序;设置家长控制;将文件移动或复制到Program Files 或者 Windows 目录;查看其他用户文件夹等..

在触发 UAC 时,系统会创建一个consent.exe进程,该进程通过白名单程序和用户选择来判断是否创建管理员权限进程。请求进程将要请求的进程cmdline和进程路径通过LPC接口传递给appinfo的RAiLuanchAdminProcess函数,该函数首先验证路径是否在白名单中,并将结果传递给consent.exe进程,该进程验证被请求的进程签名以及发起者的权限是否符合要求,然后决定是否弹出UAC框让用户进行确认。这个UAC框会创建新的安全桌面,屏蔽之前的界面。同时这个UAC框进程是SYSTEM权限进程,其他普通进程也无法和其进行通信交互。用户确认之后,会调用CreateProcessAsUser函数以管理员权限启动请求的进程

所以,病毒木马想要实现更多权限操作,那么就不得不绕过UAC弹窗,在没有通知用户情况下,静默地将程序普通权限提升为管理员权限,从而程序可以实现一些需要权限的操作。目前实现Bypass UAC的方法主要有两种方法,一种是利用白名单提权机制,另一种是利用COM组件接口技术。

基于白名单程序 Bypass UAC

有些系统程序是直接获取管理员权限,而不会触发UAC弹框,这类程序称为白名单程序。例如,slui.exe、wusa.exe、taskmgr.exe、msra.exe、eudcedit.exe、eventwwr.exe、 CompMamtLauncherexe等等。可以通过对这些白名单程序进行DLL劫持、注入或是修改注册表执行命令的方式启动目标程序,实现Bypass UAC提权操作。

接下来,选取白名单程序CompMgmtLauncher.exe计算机管理程序进行详细分析,利用它实现Bypass UAC提权。下述的分析过程是在64位Windows 10操作系统上完成的,使用到的关键工具软件是进程监控器Procmon.exe.

首先,直接到System32目录下运行CompMgmtLauncher.exe程序,并没有出现UAC弹窗,直接显示计算机管理的窗口界面。其中,使用进程监控器Procmon.exe来监控CompMgmtLauncher.exe进程的所有操作行为,主要是监控注册表和文件的操作。通过分析Procmon.exe的监控数据发现,CompMgmtLauncherexe进程会先查询注册表HKCUSoftwarelClasses mscfileshellopencommand中数据,发现该路径不存在后,继续查询注册表HKCRmscflelshellopencommand/Detaut)中的数据并读取,该注册表路径中存储着mmc.exe进程的路径信息。

在CompMgmtLauncher.exe启动的过程中,有一个关键的操作就是它会先读取注册表HKCUSoftwarelClassesl凶scfilelshellopenlcommand的数据。打开系统注册表编辑器regedit.exe, 查看相应路径下的注册表,发现该注册表路径确实不存在。所以,如果自己构造该注册路径,写入启动程序的路径,这样,CompMamtLauncher.exe便会启动该程序。为了验证这个猜想,自己手动添加该注册表路径,并设置默认的数据为C:\Windows\System32\cmd.exe,然后使用Procmon.exe进行监控并运行CompMamtLauncher.exe,成功弹出cmd.exe命令行窗口,而且提示管理员权限

以上是最早的方式,我们举一反三换另外的思路
既然已经知道绕过UAC原理,我们试试fodhelper.exe。

因为%windir%iSystem32 fodhelper.exe运行时会检查打开注册表路径HKCU\SoftwarelClassesims-setinaslshellopencommand (不存在),检查注册表路径下是否有DelegateExecute键,如果满足这2个条件,就执行Default键值下保存的命令行。

如果想知道它一定会打开上述说的路径吗? 可以用Procmon工具监控看一下注册表那一栏就可以。

复现

UAC绕过(打开UAC默认权限,用户账户->更改用户账户控制设置)

1.我们创建这个注册表路径HKCU\SoftwarelClasses\ms-settingslshellopen\command以便来利用

image-20230511190014427 image-20230511190026121 image-20230511190036029