吾爱破解 - LCG - LSG |安卓破解|病毒分析|破解软件|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 3708|回复: 27
上一主题 下一主题

[PC样本分析] 详细分析利用堆溢出攻击系统的病毒(分析windows10系统堆溢出过程、文件加密和查找...

  [复制链接]
跳转到指定楼层
楼主
buzhifou01 发表于 2020-2-16 18:44 回帖奖励
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
最近看到一个有意思的病毒,它可以让Windows 10系统发生堆溢出,进而注入shellcode,接下来给大家分享、分享,如有分析不对之处,还请赐教!
样本信息


从下图可以看出该病毒是PE文件,控制端程序,程序入口处、程序大小等信息

病毒运行
运行环境是:Windows 10 x64
1.病毒运行时,弹出cmd窗口,这时插入U盘,U盘会有病毒的可执行文件和启动文件




2.关掉病毒时,运行U盘中的病毒时,系统盘产生了病毒

3.注册表添加了键值,从这个键值可以看出,当U盘带有该病毒时,会自启动该病毒。桌面上的doc、docx文件全部删除并生成了几个exe文件,运行exe文件,发现无法运行,用编辑工具打开发现里面是exe文件所在路径,桌面上的txt文件被篡改,












4.安全软件和调式软件全部关闭,任务管理,注册表软件等也会关闭

脱壳和算法简单识别
1.查壳,发现是AsPack壳,接下来进行脱壳,脱壳的时候,一拖入OD病毒就跑了起来,那么用其它的方法,好像不行,接下来使用脱壳机进行脱壳。




2.接着单步,使用esp定律,单步到程序EP,下面是运行到程序EP脚本
[Asm] 纯文本查看 复制代码
 var address   //定义变量
  sto        //相当于f8
  //保存esp地址
  mov address,esp
  bphws address,"r" //当读取address的时候产生硬件中断
  run  //相当于f9
  sto
  sto
  sto
  BPHWC address //清除硬件断点
  sti  //相当于f7
  msg "Welcome To EP"

运行结果如下:

3.脱壳后,查看导入的函数,没发现加密的库函数,使用PEID的Kyrpto ANALyzer插件扫描病毒程序,同样也没发现什么加密算法。





4.由于TXT文件已被篡改,那么病毒很有可能使用了加密算法,那么接下来使用IDA脚本找到该病毒程序的算法函数,IDA脚本如下:
[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
def mul():
    return 1

def imul():
    return 1

def opand():
    return 1


def opor():
    return 1


def opnot():
    return 1


def div():
    return 1


def xor():
    return 1


def default():
    return 0

switch = {    'mul': mul,
			  'imul': imul,
			  'and': opand,
			  'or': opor,
			  'not': opnot,
			  'div': div,
			  'xor': xor,
                         }
FunAddress = []
OpAndTypeNum=dict()
FindFunc = dict()
def GetKeyFunc(Start, End):
	#把代码段中的所有函数存放在列表FunAddress中
	for function_ea in Functions(Start, End):
		FunAddress.append(function_ea)
	FunAddress.append(End)
	#遍历所有的函数
	for i in range(0, len(FunAddress)):
		#获取函数名
		FunctionName = GetFunctionName(FunAddress[i])
		#判断是否为用户函数
		if i + 1 != len(FunAddress) and FunctionName[0]=='s' and FunctionName[1]=='u' and FunctionName[2]=='b':
			OpNum=0
			#清空字典
			OpAndTypeNum.clear()
			#遍历所有函数中的指令
			for singfuc_ea in range(FunAddress[i], FunAddress[i + 1]):
				flag = GetFlags(singfuc_ea)
				#判断是否为操作码
				if isCode(flag):
					#获取汇编指令
					op = GetMnem(singfuc_ea)
					#使用switch判断是否为算术或逻辑指令
					OpAndTypeNum[op] = OpAndTypeNum.get(op,0)+switch.get(op, default)()
			#统计算术或逻辑指令的个数
			for OP,value in OpAndTypeNum.items():
				if value>0:
					OpNum+=1
					
			#如果算术或逻辑指令的个数大于2,则可以初步判断该函数为用户写的算法函数(有误差)
			if OpNum>2:
				FindFunc[FunctionName]=FunAddress[i]
				#print "i:",FunAddress[i],"i+1:",FunAddress[i+1]
				
	for Name, ea in FindFunc.items():
		print Name, ":", ea
for seg in Segments():  # 遍历所有的段
    if SegName(seg) == '.text':
        GetKeyFunc(seg, SegEnd(seg))

运行结果:

Windows 10堆溢出和堆的内核结构
1.我简单介绍一下Windows10系统中的堆:堆可以分为NT堆和段堆,NT堆又可以分为后端分配堆和低碎片化堆,段堆主要分为:可变大小分配堆和低碎片化堆,段堆一般是系统进程使用。通常在默认情况下,用户开发的程序往往使用的是NT堆,而要用户开发的程序也使用段堆,那么就要按如下方法在注册表中进行设置:
[Asm] 纯文本查看 复制代码
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Segment Heap
Enabled = (DWORD)
0 : Disable Segment Heap
(Not 0): Enable Segment Heap

段堆的内核结构如下:
[Asm] 纯文本查看 复制代码
_SEGMENT_HEAP
//堆的内存大小

 +0x000 TotalReservedPages : Uint8B
+0x008 TotalCommittedPages : Uint8B
//堆的标记

 +0x010 Signature : Uint4B
 +0x014 GlobalFlags : Uint4B
 +0x018 FreeCommittedPages : Uint8B
 +0x020 Interceptor : Uint4B
//堆在堆数组中的下标
+0x024 ProcessHeapListIndex : Uint2B
//堆内存锁住状态

 +0x026 GlobalLockCount : Uint2B
 +0x028 GlobalLockOwner : Uint4B
 +0x030 LargeMetadataLock : _RTL_SRWLOCK
 +0x038 LargeAllocMetadata : _RTL_RB_TREE
 +0x048 LargeReservedPages : Uint8B
 +0x050 LargeCommittedPages : Uint8B
 +0x058 SegmentAllocatorLock : _RTL_SRWLOCK
//子段链表

 +0x060 SegmentListHead : _LIST_ENTRY
//子段数量

 +0x070 SegmentCount : Uint8B
//空闲内存页面

 +0x078 FreePageRanges : _RTL_RB_TREE
 +0x088 StackTraceInitVar : _RTL_RUN_ONCE
 +0x090 ContextExtendLock : _RTL_SRWLOCK
 +0x098 AllocatedBase : Ptr64 UChar
 +0x0a0 UncommittedBase : Ptr64 UChar
 +0x0a8 ReservedLimit : Ptr64 UChar
 +0x0b0 VsContext : _HEAP_VS_CONTEXT
 +0x120 LfhContext : _HEAP_LFH_CONTEXT

在段堆的结构中我们可以看到VsContent和LFHContent,它们分别对应上面写的可变大小分配堆和低碎片化堆,这两个堆是段堆常用的堆。

2.在导入窗口看到CreateWindowExA函数,那么就有WinMain函数,进入OD,看到窗口处理函数40210D,进入40210D会看到触发堆溢出函数。








3.有时用OD打开程序直接进入ntdll模块,那么程序显然无法正常运行,接下来搜索地址401000,接着选中40100,右键点击此处新建EIP(修改EIP),那么就可以正常运行了。




4.病毒先是分配10个堆,然后释放其中一个块,然后在空闲块中分配shellcode,shellcode覆盖了下一块的头部及部分堆块,shellcode可在窗口中看到。











5.接下来看看堆溢出过程,在内存窗口可以看到已分配的块和释放的块,当释放一个块之后,病毒成功注入shellcode。




6.堆溢出往往发生在堆块中,思路一般是先分配几个块,然后释放一个块,在上一个块进行赋值操作时,赋值大于块的容量进而覆盖下一个块的头部及部分块身,进而发生重定向攻击
堆块分为两种:空闲块和已分配块,它们分别对应不同的块头,它们的块头如下所示:
[Asm] 纯文本查看 复制代码
空闲块的头部大小是32个字节
_HEAP_VS_CHUNK_FREE_HEADER
 +0x000 Header : _HEAP_VS_CHUNK_HEADER
 +0x000 Sizes : _HEAP_VS_CHUNK_HEADER_SIZE
 +0x000 MemoryCost : Pos 0, 16 Bits
 +0x000 UnsafeSize : Pos 16, 16 Bits
 +0x004 UnsafePrevSize : Pos 0, 16 Bits
 +0x004 Allocated : Pos 16, 8 Bits
 +0x000 KeyUShort : Uint2B
 +0x000 KeyULong : Uint4B
 +0x000 HeaderBits : Uint8B
 +0x008 EncodedSegmentPageOffset : Pos 0, 8 Bits
 +0x008 UnusedBytes : Pos 8, 1 Bit
 +0x008 SkipDuringWalk : Pos 9, 1 Bit
 +0x008 Spare : Pos 10, 22 Bits
 +0x008 AllocatedChunkBits : Uint4B
 +0x000 OverlapsHeader : Uint8B
 //Node结构的大小为24个字节
 +0x008 Node : _RTL_BALANCED_NODE

空闲块头如下:

[Asm] 纯文本查看 复制代码
已分配块的头部的大小是8个字节
_HEAP_VS_CHUNK_HEADER
 +0x000 Sizes : _HEAP_VS_CHUNK_HEADER_SIZE
 +0x000 MemoryCost : Pos 0, 16 Bits
 +0x000 UnsafeSize : Pos 16, 16 Bits
 +0x004 UnsafePrevSize : Pos 0, 16 Bits
 +0x004 Allocated : Pos 16, 8 Bits
 +0x000 KeyUShort : Uint2B
 +0x000 KeyULong : Uint4B
 +0x000 HeaderBits : Uint8B
 +0x008 EncodedSegmentPageOffset : Pos 0, 8 Bits
 +0x008 UnusedBytes : Pos 8, 1 Bit
 +0x008 SkipDuringWalk : Pos 9, 1 Bit
 +0x008 Spare : Pos 10, 22 Bits
 +0x008 AllocatedChunkBits : Uint4B

已分配块如下:

6.下面看看注入的shellcode,
[Asm] 纯文本查看 复制代码
"BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBB"
        "CCCCDDDD"
        //上面的字符填充空闲块
           
    "\x90\x90\x90\x90\x90\x90\xeb\x08" //覆盖空闲块中Node节点以上的头部字段