前言:
在一次样本分析病毒释放的过程中,捕捉到下图,
样本尝试写入信息到内存中,并企图利用暴力P解的方式,来攻破计算机中的某些验证机制。
不免有以下疑问:
- 如何获取文件?
- 获取哪些指定文件?
- 如何写入病毒信息?
- 写入了哪些信息?
- 绕过了哪些验证机制?
绕过异常检测:
循环五次对 eax
的值操作,并将 esp
减去一个非常大的偏移量,主要目的应该是为了给这个函数分配足够的内存来进行下面的操作。
接着看,
这段代码的主要作用是初始化一些变量,然后调用sub_4040BC
进行上锁,确保操作的安全性。
接下来,设置了两个嵌套的SEH(结构化异常处理)处理程序,
push offset loc_407A36
将异常处理程序地址(loc_407A36
)压入栈顶;
mov fs:[eax], esp
设置新的异常处理程序(通过 fs
段寄存器)进入 SEH 链中。
所谓的结构化异常处理(SEH)是用于处理异常的一个函数列表,倘若遇上异常,会按照次序依次调用SEH当中的函数进行处理。当最后一个函数也无法处理的时候会弹出对话框进行报错,滥用结构化异常处理是指在SEH链当中加入自己定义的异常处理函数来实现控制流混淆。
通过这种方式设置的异常处理例程可以捕获函数内的错误并转移控制权到特定的错误处理代码中。同时可以通过利用SEH实现变相控制程序流,来对抗反汇编。
利用IDA将子例程强行转换为代码进行分析,
第一个异常处理,
参数传递:
-
mov eax, [esp+arg_0]
: 将第一个参数(指针)存入eax
。 -
mov edx, [esp+arg_4]
: 将第二个参数存入edx
。
检查异常状态:
-
test dword ptr [eax+4], 6
: 检查eax
指向的结构中的某个字段,判断是否符合特定条件。 -
jz short loc_403694
: 如果条件不满足,跳转到loc_403694
。
设置异常处理函数:
-
mov ecx, [edx+4]
: 从第二个参数读取处理函数的地址。 -
mov dword ptr [edx+4], offset loc_403694
: 将异常处理函数的地址设置为一个特定的跳转地址。
调用异常处理逻辑:
-
call sub_4034D4
: 调用一个函数进行异常处理逻辑的设置。
执行异常处理:
-
call ecx
: 调用之前设置的处理函数。
返回处理结果:
- 如果一切正常,返回
1
。
异常跳转:
- 如果未满足之前的条件,跳转到
loc_403694
,并返回1
。
第二个异常处理,
第二个异常处理函数比较复杂,可以看到在必要时还调用系统级的异常处理程序UnhandledExceptionFilter
。
获取异常信息:
-
mov eax, [esp+ExceptionInfo.ExceptionRecord]
: 将异常记录结构的指针存入eax
。
检查异常状态:
-
test dword ptr [eax+4], 6
: 检查异常记录的某个字段是否满足条件。 -
jnz loc_40365C
: 如果条件满足,跳转到处理逻辑。
获取异常代码:
-
cmp dword ptr [eax], 0EEDFADEh
: 检查异常代码是否为特定值(0EEDFADEh)。 -
mov edx, [eax+18h]
和mov ecx, [eax+14h]
: 获取异常记录中的其他重要字段。
调用处理逻辑:
-
call sub_40326C
: 调用一个处理函数,可能用于进一步分析异常信息。 -
mov edx, ds:dword_40E00C
: 获取另一个处理函数的指针。 -
test edx, edx
: 检查这个指针是否为空。 -
call edx
: 如果不为空,调用这个处理函数。
判断异常类型:
-
cmp dword ptr [esp+arg_8], 0EEFFACEh
: 比较某个值以确定异常的类型。 -
call sub_403464
: 如果不匹配,调用另一个处理函数。
处理返回值:
-
cmp ds:byte_40D01C, 0
: 检查一个全局状态标志,决定是否调用UnhandledExceptionFilter
。 -
call UnhandledExceptionFilter
: 如果需要,调用这个系统函数处理未处理的异常。
异常过滤和处理:
- 根据条件,可能会再次调用
UnhandledExceptionFilter
来处理异常。 -
or dword ptr [eax+4], 2
: 更新异常记录的状态。
设置SEH链:
- 保存当前的SEH链,并设置新的处理函数。
-
call sub_40499C
: 可能是一个函数,用于处理特定的异常或状态。 -
mov dword ptr [edi+4], offset loc_40363C
: 设置新的异常处理地址。
跳转到处理逻辑:
-
jmp ebx
: 跳转到处理逻辑,继续执行异常处理。
第一个SEH处理常规错误,第二个则处理更严重或特殊的异常,这些操作可以绕过计算机的某些错误检测。
读取指定文件:
接着往下看,有个经常出现的函数sub_402614
通过TlsGetValue
检索调用线程的线程本地存储中的值。
在获取tls值之前都会先call一个函数,通过对每个函数的分析,
sub_402EB8
获取文件的句柄,
sub_402D28
用于处理和检查文件的大小,
sub_402CBC
获取指定文件的文件指针。
通过这些操作,可以读取病毒所需要的特定的文件并读取其中的内容。
接下来,进入了一个循环,通过函数sub_403D34
和sub_403ED
4将病毒信息写入内存,
写入病毒信息:
sub_403D34:
可以看到这个函数主要通过sub_402650
进行写入操作,
jz short loc_40268D
:如果目标地址和源地址相等,则跳转到loc_40268D
,不执行任何操作。
sar ecx, 2
:将ecx
右移2位,计算出可以使用movsd
指令复制的双字数量(每次4字节)。
js short loc_40268D
:如果右移后的结果是负数,跳转到loc_40268D
。
rep movsd
:使用rep movsd
指令循环地将双字从源地址复制到目标地址。
此函数通过支持对齐的复制和处理剩余的字节,将数据从一个内存位置复制到另一个内存位置。
经过一点点调试查看寄存器数据和堆栈数据的变化,分别写入了“xboy”、“whboy”以及未解密的“++戊+缓叛聋+??删"蚊 苜+兆++”等数据。
同理函数sub_403ED4
也是写入函数,
这两个函数区别不大,主要在于sub_403ED4是对处理的数据进行复制,比如前面提到的加解密之类的数据,而sub_402650主要是负责实际的数据复制。
总结:
这些信息有助于我们获取病毒的行为信息,只有当我们深入分析其原理和实现过程,才有助于我们获取病毒的行为信息。