• 多数恶意代码以windows平台为目标,并且与操作系统进行紧密交互
  • 恶意代码可能使用操作系统来执行代码而不是用一个跳转或调用指令
  • 恶意代码趋向于执行预期之外的动作

1、windows API

  • 类型和匈牙利表达法
    • 使用前缀命名模式
      21.jpg
  • 句柄
    • 在操作系统中被打开或被创建的项(窗口、进程、模块、菜单、文件等)
    • 句柄不能被用来做数学操作,并且不总是表示对象地址;能对句柄做的唯一的事就是保存它并在后续函数调用中使用它来引用同一个对象
  • 文件系统函数
    • 恶意代码与系统交互的最常用方式就是创建或修改文件
    • 独特文件名或修改为既有的文件名是明显基于主机大的感染迹象
    • 微软提供多个函数访问文件系统:
      • CreateFile:创建和打开文件。参数dwCreationDisposition控制是否创建一个新文件或打开一个已存在文件
      • ReadFile & WriteFile:对文件读写,将文件作为流来操作。每次调用都操作的是上次调用后后续字节,不能在一个文件里灵活跳转
      • CreateFileMapping & MapViewOfFile:
        • CreateFileMapping:从磁盘上加载一个文件到内存
        • MapViewOfFile:返回一个指向映射的基地址指针,可以被用来访问内存中的文件,在文件中的任意位置进行读取和写入。
        • 文件映射被用来复制windows加载器的功能,在获得一个文件的映射以后,恶意代码可以解析PE头,并对内存中的文件进行修改,使PE文件像被操作系统加载器加载一样执行。
  • 特殊文件
    • 比普通文件更隐蔽,在列目录时不会被显示出来
    • 特殊文件可以作为字符串参数被传递给任何文件操作函数中,像普通文件一样操作
      • 共享文件
        • 以\serverName\share or \?\serverName\share开头命名的特殊文件,用来访问保存在共享目录中的目录或文件。\?\告诉操作系统禁用所有的字符串解析,并允许访问长文件名
      • 通过名字空间访问的文件
        • 名字空间可以被认为是固定数目的文件夹,每一个文件夹中保存不同类型的对象
        • 底层的名字空间是NT名字空间,以前缀\开始。NT名字空间可以访问所有的设备,已经所有在NT名字空间中存在的其他名字空间
        • 以.\开始的win32设备名字空间被恶意代码用来直接访问物理设备,并且像一个文件一样进行读写操作。这允许程序通过普通API不可能做到的方式来修改磁盘。(使用该方法恶意代码可以读写数据到一个未分配扇区,而无需创建或访问文件)
        • 例:恶意代码通过\Device\PjysicalDisk1使受害者文件系统崩溃
          恶意代码使用呢\Device\PhysicalMemory来直接访问内存
      • 备用数据流(ADS)
        • 允许附加数据被添加到一个已存在的NTFS文件中,相当于添加一个文件到另一个文件中。
        • 额外数据在列一个目录时不会被显示出来,并且当显示文件内容时也不显示,只有在访问流时才是可见的。
        • ADS数据流根据约定normalFile.txt:Stream:$Data来命名,这允许程序去读写一个流

2、windows注册表

  • 用来保存操作系统与程序的配置信息(设置、选项),是基于主机迹象的很好来源,能揭示恶意代码功能
  • 恶意代码使用注册表来完成持久驻留或存储配置数据,开机自动运行
  • 注册表术语
    • 根健:注册表被划分为称为根键的5个顶层节(HKEY),每一个HKEY有一个特定的目的
    • 子健:像是一个文件夹中的子文件夹
    • 键:一个键是一个注册表中的文件夹,可以包含额外的文件夹或键值
    • 值项:一个值项是一个配对的名字和值
    • 值或数据:存储在注册表项中的数据
  • 注册表根键
    • HKEY_LOCAL_MACHINE(HKLM) :保存对本地机器全局设置
    • HKEY_CURRENT_USER(HKCU):保存当前用户特定设置
    • HKEY_CLASSES_ROOT:保存定义的类型信息
    • HKEY_CURRENT_CONFIG:保存关于当前硬件配置的设置,特别是与标准配置不符的部分
    • HKEY_USERS:定义默认用户、新用户、当前用户配置
    • 例:自启动项 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  • Regedit(注册表编辑器)
    • windows内建用来查看和编辑注册表的工具
  • 自启动程序
    • Autorun工具:列举了在操作系统启动时会自动启动运行的代码
    • 自启动的可执行文件,加载到Internet Explorer和其他程序中的DLL程序,加载到内核中的驱动
  • 常用注册表函数
    • RegOpenKeyEx:打开一个注册表进行编辑和查询
    • RegSetValueEx:添加一个新值到注册表并设置它的数值
    • RegGetValue:返回注册表中一个值项的数值
  • 练习分析注册表操作代码
    • 从注册表打开Run键并添加了一个键值。RegSetValueEx函数使用6个参数,用来编辑一个注册表值项,或在一个新项不存在时创建它。
  • 使用.reg文件的注册表脚本
    • .reg文件包含一个人类可读的注册表数据,可被用来修改注册表
      22.jpg

3、 网络API:识别和理解常见的网络函数

  • 伯克利兼容套接字
    • 伯克利兼容套接字的网络功能在windows系统中是由Winsock库实现的,主要在ws2_32.dll中。
    • socket、connect、bind、listen、accept、send、recv函数最常用
      23.jpg
    • WSAStartup函数必须在其他网络函数之前被调用,以便为这些网络库分配资源,网络入口在其后不远处
  • 网络的服务器和客户端
    • 服务器端:维护一个打开套接字并等待入栈连接(socket->bind->listen->accept)
    • 客户端:连接到一个正在等待的套接字(socket->connect->send/recv)
  • WinINet API
    • 保存在Wininet.dll中,是比Winsock API更高一级
    • 实现了应用层协议,如HTTP和FTP
    • InternetOpen:初始化一个到互联网的连接
    • InernetOpenUrl:被用来访问一个url(可以是HTTP页面或一个FTP资源)
    • InternetReadFile:允许程序从一个来自互联网的下载文件中读取数据

4、跟踪恶意代码的运行

  • 恶意代码如何调用其他代码运行

  • DLL

    • 使用库在多个应用程序之间共享代码(windows特有)
    • 被DLL程序使用的内存可以在正运行的进程之间共享
    • 能最小化发布软件的规模
    • 有用的代码复用方式(通用功能的DLL)
    • 恶意代码作者如何使用DLL
      • 保存恶意代码: 将恶意代码保存在dll文件中,会附加到其他进程中
      • 通过使用Windows DLL:调用系统API实现功能
      • 通过第三方DLL:利用第三方dll的导入函数
    • 基本DLL结构
      • 使用PE文件格式,只有一个单一标志指示该文件是一个DLL
      • DLL经常有更多导出函数,导入函数较少
      • DLLL主函数为dllMain,文件的主入口,进程加载或卸载库的时候调用该函数通知dll
  • 进程
    • windows正在执行的程序(管理资源的容器),每一个进程管理自己的资源,一个进程包含一个或多个由CPU执行的线程
    • 新型恶意代码普遍将自身代码作为其他进程的一部分执行
    • 多个进程能共享系统内存。每一块进程被给予一块与所有其他进程隔离的内存空间。有时可能虚拟内存相同但物理内存不同
    • 创建一个新进程
      • CreateProcess:创建一个进程执行恶意代码\访问恶意内容\创建简单远程shell(STARTUPINFO设置标准输出为套接字)
        24.jpg

25.jpg

  • 恶意代码经常在一个程序的资源节存储另一个程序,并创建一个新进程。从PE头中提取附加的可执行文件,将它写到磁盘上,可以使用CreateProcrss等方式执行文件。【你用Resource Hacker工具分析】
    • 线程
  • 执行代码的容器,windows操作系统真正要执行的内容,被CPU执行的独立指令序列,一个进程中的所有线程共享同样的内存空间,但是每一个有自己的处理器、寄存器和栈
  • 线程上下文
    • 线程对CPU有完全控制,一个线程改变CPU某个寄存器的值时,不会影响其他线程
    • 操作系统在线程间切换之前,在CPU中的所有值会被保存到一个称为线程上下文的结构体中;操作修通加载线程上下文到一个新的线程中,cpu开始执行
  • 创建一个线程
    • CreateThread:函数调用者指定起始地址(start函数),执行从这个起始地址开始直到这个函数返回。
      • 使用CreateThread加载一个新的恶意库文件到进程中:将start设置为LoadLibrary的地址,传递给CreateThread的参数是被加载库的名字
      • 恶意代码可以为输入输出创建两个线程:一个用来在套接字或管道上监听并输出到一个进程的标准输入里;另一个用来从标准输出读取数据,发送到套接字或管道上,和运行的应用程序进行无缝通信。
        • 使用互斥量(mutex)的进程间协作
  • 互斥量是全局对象,用于协调多个进程和线程
  • 控制共享资源的访问
  • 互斥量经常使用硬编码的名字【保证进程间不论通信方式如何名字一致都能使用】,可以被用来作为基于主机的感染迹象
  • OpenMutex:进程获取另一个进程中互斥量的句柄,用来检查互斥量是否存在
  • CreateMutex:如果互斥量存在,创建互斥量
  • WaitForSingleObject:线程获取对互斥量的访问,检测互斥量状态,如果有其他线程使用则挂起
  • ReleaseMutex:线程完成对互斥量的使用
    • 服务
  • 恶意代码执行附加代码的另一种方式是将它作为服务安装,作为后台应用程序运行
  • 代码被windows服务管理器调度和运行,但没有用户输入
  • 恶意代码通常作为SYSTEM或其他特权账户运行
  • 服务在系统上维护持久化驻留的方式:设置成操作系统启动时自动运行
  • 相关API
    • OpenSCManager:返回一个服务控制管理器的句柄,被用来进行所有后续与服务相关的函数调用
    • CreateService:添加一个新服务到服务控制管理器
    • StartService:启动一个服务
  • 恶意代码最常使用的服务类型
    • WIN32_SHARE_PROCESS
      • 将服务代码保存在DLL中
      • 在一个共享的进程中组合多个不同的服务
    • WIN32_OWN_PROCESS
      • 在一个.exe文件中保存代码
      • 作为一个独立进程运行
    • KERNEL_DRIVER:加载代码到内核中执行
  • 注册表中的服务表项:HKLM\SYSTEM\CurrentControlSet\Services\
  • SC命令:调查和操作服务【sc qc “服务名”】
    • 组建对象模型(COM)???
  • 一个接口标准,使不同软件组件在不知道其他组件代码的接口规范时相互之间可以调用
  • 客户-服务器框架
  • 使用COM的线程在调用任何其他COM库函数之前至少调用一次OleInitializeCoInitializeEx函数,若恶意代码使用来COM功能,可以通过查找这两个函数发现
  • CLSID、IID以及COM对象的使用
    • COM对象通过全局唯一标识符(GUID),分为类型标识符(CLSID)和接口标识符(IID)来访问
    • COCreateInstance:获取对COM对象的访问,操作系统使用注册表信息判断哪个文件包含被请求的COM代码
    • Navigate函数
  • COM服务器恶意代码
    • 通过浏览器帮助对象(BHO)在Internet Explorer进程中运行代码,允许他们监控互联网流量,跟踪浏览器使用,与互联网通信,并且不使用他们自己的进程
    • 检测实现COM服务器的恶意代码:导出了几个函数-DllCanUnloadNow、DllGetClassObject、DllInstall、DllRegiserServer、DllUnregisterServer
      • 异常:当事情出错时
  • 在普通执行流程之外处理事件
  • 硬件抛出的异常:比如除零异常
  • 操作系统抛出的异常:无效内存访问
    。。。
  • 使用RaiseException显式抛出一个异常
  • windows异常处理机制:结构化异常处理(SEH),32位系统中SEH信息保存在栈上
    26.jpg
  • 异常处理器可以让恶意代码获得执行机会:一个指向异常处理信息的指针被保存在栈上,栈溢出时攻击者可以覆盖指针。

5、内核与用户模式

  • windows两种处理器特权级别:内核模式和用户模式
  • 除了操作系统和硬件驱动,其他都运行在用户模式
  • 在用户模式中操作硬件或改变内核中的状态必须依赖于API
  • 反汇编中:SYSENTER、SYSCALL、INT 0x2E 指明一个调用被使用进入内核。直接通过跳转进入内核模式是不可能的,这些指令使用查找表定位一个预定义函数,从而在内核中执行代码。
  • 运行在内核的进程共享资源和内存地址。一旦执行无效指令就会蓝屏。
  • 大多数安全程序都运行在内核模式
  • rootkit几乎都会运行在内核的编码:在内核空间没有用户特权的区别、操作系统的审计特性也不应用于内核,开发调试困难,容易崩溃。

6、原生API

  • 和windows进行交互的底层API,调用原生API可以绕过普通API
  • 用户应用程序被基于对用户API的访问(比如kernel32.dll),这些DLL会调用ntdll.dll(管理用户空间和内核的交互),然后处理器切换到内核模式,执行内核函数(通常位于ntoskrnl.exe中)
  • 设计很差的安全程序可能只在kernal32.dll层面监控
  • 原生API调用获取系统信息(NtQuerySystemInformation)、进程(NtQueryInformationProcess)、线程(NtQueryInformationThread)、句柄(NtQueryInformationFile)、其他项目(NtQueryInformationKey)等
  • NtContinue:从一个异常处理返回,意图是在一个异常被处理后转移执行回到一个程序的主线程,要返回的位置在异常上下文中被指定且可以修改。恶意代码使用这个函数以复杂的方式转移执行,增加调试难度。

7、实验

Lab 7-1

  • OpenSCManager,CreateService:创建一个服务保证重启后运行
  • StartServiceCtrlDispatcherA:实现一个服务
  • InternetOpen,InternetOpenUrl:连接url并下载内容
  • OpenMutexA:调用之前能找到互斥量的名字,调用保证可执行程序任意给定时刻只有一份实例在系统上运行。
  • GetModuleFileName:返回当前正在运行的可执行成宿或一个被加载dll的全路径名
  • CreateServiceA:BinaryPath(和GetModuleFileName调用得到的当前正在运行的可执行程序路径是一样的)、dwStartType(有暗示自启动的参数)、dwServiceType
  • WaitForSingleObject:代码进入等待
  • dec esi:esi被设计为计数器
  • 把自己作为一个任务循环下载页面,启动DDOS攻击

Lab 7-2

  • 导入函数为COM相关:CoCreateInstance(获得一个COM对象)、OleInitialize(初始化COM)
  • 判断使用什么COM功能:检查接口标识符(IID)和类标识符(CLSID)
  • 遇到简单的程序应考虑可疑性,可能随额外恶意代码一起打包作为其中一个组件

Lab 7-3

  • 只有搜索目录遍历文件映射到内存,没有使用LoadLibrary等加载dll,需要注意,进一步检查
  • dll导入表:通过网络发送和接收数据,这个程序可能创建另一个进程
  • dll没有导出函数(strange)
  • 分析dll:仅查看call指令,获得功能视图。实现后门功能(发送恢复80端口的一个数据包,启动一个系统上的可执行文件)
  • 分析exe:[eax+4]- argv[1],分析时可以先跳过内存操作函数
    • repne scasb:相当于strlen
    • rep movsd:相当于memcpy,用于使用一个字符串覆盖另一个字符串