恶意代码原理与防护笔记

发布时间 2023-05-21 10:58:48作者: 不负春光不负卿

病毒

行为

  • 欺骗
  • 隐蔽
  • 自启动
  • 自我复制
  • 自我删除
  • 传播
  • 感染

与反病毒的简单较量

  • 显示与隐藏拓展名
  • U盘传播

进程与线程

进程是一个具有独立功能的程序关于某个数据集合的一次运行活动,是程序的一次动态执行,它可以申请和拥有系统资源。把进程看成一个容器,程序运行时,首先将程序代码装入容器,然后可继续加入程序执行所需要的变量数据等。

线程是进程中的一个实体,是CPU调度和分派的基本单位,一个进程可以有多个独立运行的线程。进程为线程提供生存空间和所需资源,线程执行任务。

程序一次执行结束,则进程退出,进程产生的所有线程也都被强制退出并清除。

DLL是作为共享函数库的可执行文件(PE格式),但它不能独立运行,只能通过其他可运行的程序加载到内存中执行功能。

共享函数库:DLL是一个包含可供多个程序同时使用的代码和数据的库。DLL提供了一种方法,使进程可以调用不属于其可执行代码的函数。

PE文件

PE (Portable Executable):可移植的执行体

文件结构

image-20230520103548101

入口点

PE文件执行时的入口点,也就是程序在执行的第一条语句

文件偏移地址(File Offset)

PE文件储存在磁盘上的时候,各数据的地址称为文件的偏移地址

从第一个字节开始计数,起始值为0

基地址

文件执行时候被映射到指定的内存地址中,这个初始内存地址称为基地址。

默认设置,用Visual C++建立的EXE文件基地址是0040 0000H,DDL文件的基地址为1000,0000H。

虚拟地址(VA)

由于Windows程序运行在保护模式下,所以应用程序访问存储器所使用的逻辑地址称为虚拟地址(因为他不是真正的物理地址,真正的物理地址被windows的保护机制保护起来了)

相对虚拟地址(RVA)

是相对于可执行文件映射到内存的基地址的偏移量

如:可执行文件映射到内存的基地址:0x40,0000H, RVA=0x1000H,则虚拟地址VA=0x40,1000H

PE文件结构图

image-20230520184824252

image-20230520184309487

PE

常见的区块名字

  • .text 是在编译或汇编结束时产生的一种块,它的内容全是指令代码。 
  • .idata 导入表,包含其它外来DLL的函数及数据信息 
  • .data是初始化的数据块  
  • .rsrc 包含模块的全部资源:如图标、菜单、位图等

文件结构详解

MS-DOS

image-20230520185825598

image-20230520190302132

image-20230520190330526

PE头

PE文件头(PE Header) 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着很多PE装载器用到的重要字段。

拓展PE头

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    //
    // NT additional fields.
    //
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;                                //<--- 文件对齐
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;                                //<--- 决定块的起始位置
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

节表(区块表,块表)

PE文件中所有节的属性都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER结构排列而成,每个结构用来描述一个节,结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束

节表总是被存放在紧接在PE文件头的地方。

另外,节表中 IMAGE_SECTION_HEADER 结构的总数总是由PE文件头 IMAGE_NT_HEADERS 结构中的 FileHeader.NumberOfSections 字段来指定的。

所以节表中总的IMAGE_SECTION_HEADER结构数量等于节的数量加一

块表在c的定义

#define IMAGE_SIZEOF_SHORT_NAME              8
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;                        //<--- 块的大小
    DWORD   PointerToRawData;                //<--- 块在磁盘文件中的偏移
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

image-20230520190640387

Name:区块名。

这是一个由8位的ASCII 码名,用来定义区块的名称。多数区块名都习惯性以一个“.”作为开头。

当我们要从PE 文件中读取需要的区块时候,不能以区块的名称作为定位的标准和依据,正确的方法是按照 IMAGE_OPTIONAL_HEADER32 结构中的数据目录字段结合进行定位。

Virtual Size:该表对应的区块的大小,可以是区块的数据在没有进行对齐处理前的实际大小,也可以是对齐后的大小

Virtual Address:该区块装载到内存中的RVA 地址。这个地址是按照内存页来对齐的,因此它的数值总是 SectionAlignment 的值的整数倍。

SizeOfRawData:该区块在磁盘中所占的大小,可以是被FileAlignment 对齐过的长度,也可以是对齐处理前的实际大小。

PointerToRawData:该区块在磁盘中的偏移。这个数值是从文件头开始算起的偏移量。

Characteristics:该区块的属性。该字段是按位来指出区块的属性(如代码/数据/可读/可写等)的标志

小总结

在非运行态下:

  • DOS部首和PE文件头及块表连续存储,中间没有空隙
  • 而块表和块之间由于文件对齐可能会存在空隙
  • 块和块之间也由于文件对齐可能会存在空隙

非运行态和运行态映射图

image-20210316163800340

PE装载器的工作

①读取IMAGE_FILE_HEADER的NumberOfSections域,知道文件的节数目

② SizeOfHeaders域值作为节表的文件偏移量,并以此定位节表

③ 遍历整个结构数组检查各成员值

将VirtualAddress域值加上ImageBase域值等于节起始的虚拟地址,然后就准备将节映射进内存,并根据Characteristics域值设置属性

遍历整个数组,直至所有节都已处理完毕。

文件感染

EPO技术-未修改入口点

Ø病毒没有改变宿主文件中的入口地址,而是在宿主文件的中部记录跳转到病毒本身的指令。

Ø 当运行宿主文件后,病毒没有立即得到控制权,而是当宿主例程调用包含病毒跳转指令时才获得系统的控制权。

Ø 可以一定程度上逃避虚拟机检测。

image-20230520194719278

添加新节感染(修改入口点)

image-20230520194752279

空间隙感染(修改入口点)

image-20230520194830005

插入感染(未修改入口点)

image-20230520194858784

image-20230520194907401

捆绑式感染

image-20230520194953237

重定位

病毒感染HOST程序后,由于它依附到HOST程序中的位置各不相同,因此病毒随着HOST载入内存后,它的各个变量(常量)在内存中的位置会随着HOST程序的位置而发生变化,因此病毒程序要能正常使用变量,必须用重定位技术。

image-20230520195318945

image-20230520195330470

获取API函数

系统中绝大部分API函数都集中在3个动态链接库Kernel 32.dll,User32.dll,Gdi32.dll中,所以一般病毒使用的所有API函数地址都可以从相关的DLL中获得。

Kernel 32.dll:32位动态链接库文件,属于内核级文件。
它控制着系统的内存管理、数据的输入输出操作和中断处理,当Windows启动时,Kernel32.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域。

User32.dll:是Windows用户界面相关应用程序接口,如创建窗口和发送消息。

Gdi32.dll:是Windows GDI图形用户界面相关程序,包含的函数用来绘制图像和显示文字。

LoadLibraryA:可以加载一个DLL库,如果成功,则返回该模块的句柄。
GetProcAddress:从加载的DLL库中搜索指定的API函数的地址。
一旦得到了Kernel 32.dll的句柄,也就可以达到使用任意动态库中的任意API函数的目的。

获取API函数地址的步骤:

1.首先获得Kernel32.dll的模块加载基地址

  • 方法1:利用程序的返回地址,在其附近搜索Kernel32模块基地址。

  • 方法2:通过SEH链获得Kernel32.dll模块基地址

  • 方法3:通过PEB获取Kernel32.dll模块基地址

2.然后通过Kernel32.dll定位具体函数的地址

  • 通过导出表查找
  • 通过函数GetProcAddress、LoadLibraryA查找

搜索目标文件

  • 使用FindFile函数查找指定路径;
  • 对当前路径使用FindNextFile函数获得下一个文件句柄;
  • 通过IsDirectory函数判断文件是否为目录,如果是目录,则用GetFilePath得到文件路径再递归调用查找函数;如果不是目录就用Find(“.exe”)函数判断文件的后缀名是否为.exe。

返回HOST

image-20230521101627235

宏病毒

VBA( Visual Basic for Applications),意思是开发环境被整合到了某个应用程序的 Visual Basic 语言。可以认为VBA是Visual Basic的子集.

原理

如何获得控制权

(1)用户对当前文档执行有些操作时,Word会查找指定的“内建宏”,例如:
打开文件之前首先查找“FileOpen”宏;
关闭文件之前查找“FileClose”宏;
打印文件之前首先查找“FilePrint”宏
改名存文件之前首先查找“FileSaveAS”宏

(2)全局宏—“Auto”开始的宏

image-20230521101837675

自我保护

① 禁止可能引起用户警觉的提示信息(减少异常)
② 设置工程口令,设置查看障碍(封锁入口)
③ 屏蔽命令菜单,不允许查看宏(封锁入口)
④ 编码混淆(让你看不懂)
⑤ 隐藏关键宏代码等(让你找不到)

传播

病毒宏的传染通常是Word在打开一个带宏病毒的文档或模板时,激活了病毒宏,病毒宏将自身复制至Word的通用(Normal)模板中,以后每次Word进行打开、新建等操作时,就可能会调用病毒代码,并且将病毒代码写到刚才打开或新建的文件中,以达到传播的目的。

对抗

文档工程被加密无法进入宏代码编辑器怎么办?
VBA_Password_Bypasser
AOPR(Advanced Office Password Recovery)

在不打开文档的情况下如何查看宏代码?
oledump.py :可以使用命令提取文档中的宏代码。

编码混淆之后看不懂,怎么办?
解码工具(scrdec.exe等)
动态调试
模板被感染且无法进入宏编辑器,怎么办?删除原始模板文件,Office将自动生成新模板

文档“宏查看”相关操作的宏被接管,无法进 入宏代码编辑器,怎么办?
先打开非感染文档进入宏代码编辑器,然后打开目标文档切换。

脚本病毒

脚本语言( Scripting language)是一种解释型的语言,一般内嵌在其他语言或者程序中,由解释器逐行解释执行;
脚本语言由一些ASCII码组成,并可以用“记事本”等文本编辑器直接对其进行编写。

脚本语言对系统的控制能力相对其他大型编程语言来说要弱,但是也可以利用Windows系统的开放性,通过调用一些现成的Windows对象和组件直接对文件系统、注册表等进行控制。

wsh

WSH 共有 14 个内置对象,它们各自有着明确分工。
Wscript 确定 WSH 执行文件名(wscript.exe 还是cscript.exe)
WshNetwork 的主要作用是开放或关闭网络共享,连接或断开网络打印机,映射或取消网络中的共享,获取当前登陆用户的信息;
WshShell 主要负责程序的本地运行,处理注册表项、创建快捷方式、获取系统文件夹信息,处理环境变量;

VBS

Visual Basic Script的简称。
微软环境下的轻量级解释型语言,是ASP(Active Server Page)默认脚本语言,也可在客户端作为独立程序(.vbs, .vbe)运行
可以高效地管理远程和本地计算机,如:

  • 读取及修改环境变量
  • 管理注册表、文件系统
  • 管理服务、进程、系统账户
  • 进行网络交互(文件上传下载、邮件发送等)

分析

如何获得控制权
  1. 修改注册表启动项

    WSH.RegWrite(strName,anyValue[,strType])

  2. 通过映射文件执行方式

    将exe文件的映射指向病毒代码。

  3. 欺骗用户,让用户自己执行:如双后缀名文件

如何搜索目标

image-20230521102411722

如何感染

直接通过自我复制感染文件,病毒中的绝大部分代码都可以直接附加在其他同类程序的中间。

VBS脚本病毒对抗反病毒软件的技巧
  1. 自加密

  2. 巧妙运用Execute函数

    改变某些对象的声明方法

  3. 直接关闭反病毒软件

恶意代码自我保护技术

反汇编算法

线性扫描

这个方法技术含量并不高,反汇编器只是依次将整个模块中的每一条指令都反汇编成汇编指令,没有对反汇编的内容进行任何判断,将遇到的机器码都作为代码来处理。因此无法正确的将代码和数据区分开,数据也将被当作代码来进行解码,从而导致反汇编出现错误

递归行进算法

该算法按照代码可能的执行顺序来反汇编程序,对每条可能的路径都进行扫描。当解码出分支指令后,反汇编器就将把这个地址记录下来,并分别反汇编各个分支中的指令,采用这种算法可以避免将代码中的数据作为指令来解码,比较灵活。

反静态分析技术

花指令(junk code)技术

为了达到迷惑破解者的目的,病毒作者会巧妙的构造代码和数据,在指令流中插入很多的“垃圾数
据”,干扰反汇编软件的判断,从而使它错误地确定
指令的起始位置,这类代码数据称为“花指令”

加密技术

Ø 目的:对抗静态反汇编
Ø 病毒的简单加密是指对病毒的某些主体代码采用固定的密钥进行加密,这样静态反汇编出来的代码就是经过加密处理过的,因此在某些程度上可以起到保护病毒程序的目的。

多态技术

Ø 目的:对抗静态扫描特征码的杀毒软件
Ø 病毒的多态是指一个病毒的每个样本的代码都不相同,表现为多种状态。
Ø 采用多态技术的病毒由于病毒代码不固定,所以就很难提取出该病毒的特征码。

变形技术

病毒的变形是病毒加密的最高状态,它与
多态技术不同的是每次加密的原始病毒代
码是变化

变形引擎一般可以使用如下方式生成病毒:
(1) 随机插入废指令
(2) 随机为相同的功能选择不同代码
(3) 随机选择寄存器
(4) 改变代码块顺序
从而生成功能相同但是代码截然不同的解密代码

指令位置变换模块

变换不影响执行效果的指令的前后位置。如:
Mov ebx, 23
Xor ecx,

寄存器变换模块

随机选取一些可以随意取代的中间寄存器,如:
mov reg, [123456]
mov [45678], reg
上述代码中的reg就可以在eax,ebx,ecx....等通用
寄存器之间进行随机选择,其不会影响执行的实
际效果

指令拓展模块

将一条指令替换为多条等价指令:
MOV EAX, EDX
PUSH EDX
POP EAX
POP EAX
MOV EAX, [ESP]
ADD ESP,

指令收缩模块
指令等价替换

将一条指令用另外一条等价指令替换
XOR EAX,EAX
SUB EAX, EAX
ADD EXX,1
INC EXX

无用指令随机插入模块
垃圾指令插入模块

加壳技术

为了保护软件不被非法修改或反编译,越来越多的软件都使用了加密的技术。壳是最早出现的一种专用加密软件技术。壳是指对PE文件具有压缩、加密、保护作用的程序