免杀系列会在公众号中进行从简单入门到精通,让你看完整个系列也能成为免杀工作者中的一员。但是免杀和其他的行业区别在于需要你对操作系统、杀软规则、杀软特性、免杀手法等都要有一定的了解,只有打好基础才可以轻松自如的使用第一性原理快速解决掉你在与杀软对抗中的难题。所以整个免杀系列会一直更新,需要学习免杀的同学可以持续关注。
虽然系统篇是非常非常的无聊,在我编写的时候也是一样,但是我觉得这篇文章也是非常非常的重要,因为如果不了解系统或者说了解不够,对于做免杀来说局限性会非常非常大。并且系统篇对于白加黑方式、或者工具编写或者免杀的层面来说会扩展多的思路。
有些东西只需要了解即可,但是像内存管理、进程与线程、用户态、
Intel CPU特权级别这些必须要掌握。
我的免杀基础了解部分暂定如下:
每个人讲知识的方式不同,对于我自己的教学输出方式和知识拆分方式我有自己的想法和看法,不喜勿喷请看别的师傅。谢谢!
1.1 操作系统的定义
操作系统(Operating System,简称OS)是一个大型的程序系统,他负责计算机的全部软、硬件资源的分配、调度工作,控制并协调多个任务的活动,实现信息的存取和保护。他提供了用户接口,使用户获得良好的工作环境。
1.2 操作系统的组成
操作系统应具备的核心功能 (1)作业管理(系统调用接口) (2)进程管理 (3)内存管理 (4)文件系统管理 (5)设备管理
1.3 用户态与内核态
用户态与内核态是操作系统中两种不同的执行模式,它们对程序的权限和访问资源的能力有着根本的区别。以下是对用户态与内核态的详细介绍:
(1). 用户态(User Mode)
定义:用户态是操作系统中的一种执行级别,通常用于运行普通的应用程序。在这个状态下,程序的权限受到限制,以保护系统安全和稳定性。由于每个应用程序的虚拟地址空间都是专用的,因此一个应用程序无法修改另一个应用程序的数据每个应用程序单独运行,确保如果一个应用程序崩溃,不会影响其他应用程序或操作系统。
权限限制:
用户态程序不能直接访问硬件设备,如CPU、内存、磁盘等。
用户态程序不能执行某些特权指令,如改变内存管理单元(MMU)的设置、直接访问I/O端口等。
这里是重中之重:
用户态对于程序的运行有严格的要求,如:
取证工具:
截屏工具、键盘记录工具、U盘马、浏览器抓密码、剪切板监听工具等取证工具,如果你运行的权限是SYSTEM,那么这些工具将无法获取到你想要拿到的东西。
但是也不一定,如获取浏览器密码,SYSTEM想要获取,可以指定程序的安装目录如:
Chrome的浏览器密码就存储在:
C:\Users\<username>\AppData\Local\Google\Chrome\User Data\Default\Login Data
这样就绕过了用户态的限制,但是只适用于部分工具。
系统调用:
用户态程序需要请求操作系统内核提供的服务时,必须通过系统调用。系统调用是一种软件中断,它允许用户态程序请求内核态服务。
安全性:
权限的限制有助于防止恶意软件或错误程序破坏系统或访问其他程序的数据。
应用场景:
几乎所有的应用程序都在用户态下运行,包括文本编辑器、浏览器、游戏等。
(2). 内核态(Kernel Mode)
定义:内核态是操作系统中的另一种执行级别,通常用于运行操作系统内核和设备驱动程序。在这个状态下,程序拥有对硬件和系统资源的完全访问权限。因此,内核模式驱动程序不会与其他驱动程序或操作系统隔离。如果内核模式驱动程序错误地写入错误的虚拟地址,则可能会泄露属于操作系统或其他驱动程序的数据。如果内核模式驱动程序崩溃,则会导致整个操作系统崩溃。
权限:
内核态程序可以执行所有CPU指令,包括特权指令。
内核态程序可以直接访问所有硬件设备和内存资源。
系统资源管理:
内核态程序负责管理进程、内存、文件系统和设备驱动等系统资源。
安全性:
由于内核态程序具有高权限,它们对系统的安全性和稳定性至关重要。内核态程序的错误或恶意行为可能导致整个系统的崩溃。
应用场景:
操作系统内核和设备驱动程序在内核态下运行。此外,某些系统服务和守护进程也可能在内核态下执行。
(3). 用户态与内核态的转换
系统调用:用户态程序通过系统调用请求内核态服务时,会触发一个上下文切换,从用户态切换到内核态。
中断:当硬件设备需要操作系统注意时,如键盘输入或网络数据到达,会触发中断,导致从用户态切换到内核态。
异常:用户态程序执行非法操作时,如除以零或访问无效内存地址,会触发异常,操作系统将处理这些异常并可能切换到内核态。
(4). 内核态和用户态有什么不同
这么一看,非常清晰内核态所对应的内核空间就是一个,而用户态所对应的用户空间就随着进程而有多个了。果然是一图胜千言。而最高位1G是一样的,剩下的7G才是自己进程使用。
(1). CPU指令集
计算机中有一个用于控制CPU完成各项功能的命令系统,它叫做“指令系统”,指令系统将指令分为特权指令与普通指令。对于一些危险的指令(即绝大部分特权指令),只允许操作系统及其相关模块使用,而普通的应用程序只能使用那些不会造成灾难的指令(即普通指令)。
(2). Intel CPU特权级别
Intel的CPU将特权级别分为4个级别--从R0到R3,R0是系统核心态,在核心态运行,拥有最高权限;R1和R2运行的是设备驱动程序;R3是用户代码态,在用户态下运行,拥有最低权限,每一层支持访问本层以及权限更低层的数据。
如果用户态运行的系统要控制系统或者运行系统代码,就要提权至R0,也就是从用户态进入核心态中,中间的切换就是syscall的作用,以notepad.exe为例,如下图:
(3). 系统调用
系统调用是用户态程序请求操作系统内核提供的服务的一种机制。以下是系统调用原理的详细介绍:
系统调用的定义
系统调用是操作系统提供给用户态程序的一种接口,它允许用户态程序请求内核态服务,如文件操作、进程控制、网络通信等。
系统调用的工作原理
请求服务:用户态程序通过系统调用请求内核态服务。
参数传递:用户态程序将请求的参数传递给系统调用。
上下文切换:操作系统将控制权从用户态切换到内核态。
执行服务:内核根据请求执行相应的服务,如读取文件、创建进程等。
返回结果:内核将执行结果返回给用户态程序。
系统调用的实现
中断或陷阱:系统调用通常通过软件中断或陷阱指令实现。用户态程序执行这些指令时,CPU会触发中断或陷阱,将控制权交给操作系统内核。
系统调用表:操作系统内核维护一个系统调用表,该表将系统调用号映射到具体的内核函数。
参数传递:系统调用的参数通常通过寄存器或堆栈传递给内核。
系统调用的类型
文件系统调用:如open、read、write等,用于文件的打开、读取和写入。
进程控制调用:如fork、exec、wait等,用于创建、执行和等待进程。
网络通信调用:如socket、bind、connect等,用于网络连接和数据传输。
设备驱动调用:如ioctl,用于设备控制和配置。
系统调用的优势
抽象性:系统调用为用户态程序提供了统一的接口,隐藏了内核实现的细节。
安全性:通过系统调用,操作系统可以控制用户态程序对资源的访问,防止潜在的安全风险。
可扩展性:操作系统可以通过添加新的系统调用来扩展功能,而不影响现有的用户态程序。
系统调用的优化
减少系统调用次数:频繁的系统调用可能导致性能下降,因此需要优化程序设计,减少不必要的系统调用。
批量处理:对于需要多次请求相同服务的情况,操作系统可以提供批量处理的系统调用,如readv和writev。
系统调用的安全性
权限检查:内核在执行系统调用时,需要检查用户态程序的权限,确保其有权请求相应的服务。
错误处理:内核需要正确处理系统调用中的错误情况,如资源不足、权限不足等。
系统调用的示例
假设用户态程序需要从磁盘读取文件内容,它可以通过以下步骤进行系统调用:
程序调用open系统调用,传入文件路径和打开模式。
操作系统将控制权从用户态切换到内核态,并查找文件系统。
内核打开文件,并将文件描述符返回给用户态程序。
程序调用read系统调用,传入文件描述符和缓冲区地址。
内核从文件中读取数据到缓冲区,并返回读取的字节数。
通过理解系统调用的原理,开发者可以更好地利用操作系统提供的服务,同时确保程序的安全性和性能。
在内核中除了内核模块ntoskrnl.exe和HAL以外,其他的模块几乎都以设备驱动程序的形式存在。Windows操作系统中的驱动程序既可以创建虚拟设备,也可以是内核的扩展模块(与设备无关)。
设备驱动程序(.sys文件,为PE文件)可动态加载到系统中。驱动程序中的代码运行在内核模式下,往往通过C/C++实现,以调用HAL中的函数与硬件打交道。设备驱动程序的三种基本类型如下:
即插即用驱动程序(WDM驱动程序):这类驱动程序通常是为了驱动硬件设备而由硬件产商提供,它们与Windows的I/O管理器、即插即用管理器和电源管理器一起工作。其职责是自动检测设备的插入和移除;动态地分配硬件资源;指示I/O管理器为设备加载正确的驱动程序;向内核及应用程序提供有关设备插入和移除的通知机制。
内核扩展驱动程序:扩展内核的功能。
文件系统驱动程序:接收针对文件的请求,再进一步将请求转变为真正对于存储设备或网络设备的I/O请求,以满足原始的文件请求。
进程和线程是操作系统中用于任务调度和执行的基本单位。它们在多任务操作系统中扮演着核心角色,允许多个任务同时进行。以下是对进程和线程的详细介绍:
(1). 进程(Process)
定义:进程是操作系统进行资源分配和调度的一个独立单位,是程序在执行时的实体。
特征:
独立性:进程拥有独立的地址空间。
动态性:进程在系统中的生命周期是动态变化的。
并发性:进程可以与其他进程并发执行。
异步性:进程的执行是不可预测的,由操作系统调度。
组成:
代码段:程序的指令集合。
数据段:程序的全局变量和静态变量。
堆:动态分配的内存区域。
栈:局部变量和调用栈。
状态:
运行态:进程正在执行。
就绪态:进程等待分配CPU。
阻塞态:进程等待某个事件完成。
创建和结束:
通过系统调用如
fork()
创建新的进程。通过
exit()
或return
结束进程。进程间通信(IPC):
进程间通过共享内存、管道、消息队列等方式通信。
(2). 线程(Thread)
定义:线程是进程中的一个执行流,是程序执行的最小单位,有时被称为轻量级进程。
特征:
轻量级:线程共享进程的资源,创建和切换开销小。
独立调度:线程可以独立于其他线程被调度。
组成:
寄存器集合:线程的执行状态。
栈:线程的调用栈。
状态:
运行态:线程正在执行。
就绪态:线程等待分配CPU。
阻塞态:线程等待某个资源。
创建和结束:
通过系统调用如
clone()
或库函数如pthread_create()
创建线程。通过
exit()
或pthread_exit()
结束线程。线程间通信:
线程间通过共享内存进行通信,无需额外的IPC机制。
(3). 进程与线程的比较
资源分配:进程是资源分配的基本单位,线程共享进程的资源。
开销:进程创建和切换的开销比线程大。
独立性:进程拥有独立的地址空间,线程共享进程的地址空间。
通信:进程间通信需要IPC机制,线程间通信更简单,因为它们共享内存。
(4). 多线程的优势
提高资源利用率:线程可以更高效地利用多核处理器。
简化设计:线程共享资源,减少了数据传递的复杂性。
提高响应性:多线程可以提高程序的响应性,如GUI程序。
(5). 多线程的挑战
同步问题:需要同步机制来避免竞态条件。
死锁:线程可能因为资源争夺而陷入死锁。
资源限制:过多的线程可能导致资源耗尽。
理解进程和线程的概念对于编写高效的并发程序至关重要。
(1). 内存管理的概念
内存的分类:
物理内存(RAM):计算机的物理存储空间。
虚拟内存:通过硬盘空间扩展的内存,允许操作系统使用比物理内存更多的内存资源。
内存管理的目的:
为程序分配内存空间。
保护程序的内存空间不被其他程序干扰。
回收不再使用的内存空间。
(2). 内存管理的关键技术
内存分配:
静态分配:编译时确定内存大小。
动态分配:运行时根据需要分配内存。
内存回收:
手动回收:程序员负责释放内存。
自动回收:如垃圾回收,由系统自动管理。
内存保护:
通过硬件和软件机制确保程序不能访问不属于它的内存区域。
内存映射:
将文件或设备映射到内存地址空间,提供对文件或设备的快速访问。
(3). 内存管理的策略
连续内存分配:
程序的内存需求在物理内存中连续存放。
包括固定分区、动态分区等策略。
非连续内存分配:
程序的内存需求分散在物理内存的不同部分。
包括页式管理、段式管理、段页式管理等。
文件分类
按文件类型分为:
1、文本文件 - 文件以文本的ASCII码形式存储在计算机中
2、二进制文件 - 文件以文本的二进制形式存储在计算机中
按类分为:
ofstream类:写操作
ifstream类:读操作
fstream类:读写操作
创建文件
分为五个步骤:
引入头文件
#include <fstream>
2. 创建文件流
ofstream 对象名;
3. 打开文件
对象名.open(“文件路径”,打开方式);
4. 写数据
对象名 << “待写入的数据”;
5. 关闭文件
对象名.close();
文件打开方式:
打开方式 | 用法 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意:文件打开方式可以利用 | 操作符配合使用
例如:用二进制方式写文件 ios::binary | ios:: out
代码示例
#include <iostream>
#include <fstream>
using namespace std;
//写文件操作
void xiefile() {
ofstream file;
file.open("test1.txt",ios::out);
file << "Vipersec" << endl;
file << "TeamSecret 安全团队" << endl;
file.close();
}
int main()
{
xiefile();
return 0;
}
读取文件
读文件与写文件步骤相似,但是读取方式相对于比较多
读文件步骤如下:
1.引入头文件
#include <fstream>
2. 创建文件流
ifstream 对象名;
3.打开文件并判断文件是否打开成功
对象名.open(“文件路径”,打开方式);
4.读数据
四种方式读取
5.关闭文件
对象名.close();
代码示例
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void readfiles() {
ifstream file;
file.open("test1.txt", ios::in);
if (!file.is_open())
{
cout << "文件打开失败,请检查文件路径!" << endl;
return;
}
std::string line;
while (getline(file, line)) { // 逐行读取文件内容
std::cout << line << std::endl;
}
file.close();
}
int main()
{
readfiles();
return 0;
}
推荐本站淘宝优惠价购买喜欢的宝贝:
本文链接:https://zblog.hqyman.cn/post/9689.html 非本站原创文章欢迎转载,原创文章需保留本站地址!
休息一下~~