PS:如想复现本项目,请在虚拟机中操作!!!
".NET"因其原生于Windows操作系统,同时具有极高的开发效率,以及大量混淆手段(便于免杀),使其成为了众多Windows恶意软件开发者的首选语言,且其中绝大部分都为窃密木马,本文将以市面上非常常见的"AgnetTesla"为例,详细介绍如何利用powershell处理.NET程序中的加密函数,以减轻样本分析人员的工作量,开心干活,愉快狩猎!
PS: 本文将不再论述那些基础的.NET恶意样本分析流程,因为各位分析师都有自己的分析习惯,大家按自己喜欢的方法就好,此外本文的去混淆方法也仅是抛砖引玉,大家有什么常用的方法和改进意见,欢迎一起讨论!!!
通常来讲,字符串的存储方式无非两种,一种是以变量方式存储,而另一种则是将其直接存放在内存中。且这两种方式常常会同时出现,因而我们需要对这两种方式都进行相应的处理。
下图便是"存储在变量中"的情况:
面对这种情况,可以进行如下操作:
享受战果:
该样本中,又包含了大量的存储在内存中的字符串,如下图所示:
面对这种情况,往往我们会选择进行动态调试,但是当遇到如此大量的调用时,会浪费我们大量的时间和经历,才能找到我们感兴趣的地方,本人也被此困扰,因而急需一种自动化的解决方案。
通过查看IL,可以发现这种情况下存在相同的IL切片。随后,又把微软官方文档和官方论坛翻了又翻,本人决定通过将IL和ps1相结合的方式,来实现自动化处理。
步骤如下:
还有很多此处就不再一一列举了,对应的CSV文件本人也会发出来,到时大家有兴趣的话慢慢看就好了。到这也能非常轻松的看出来这是一个窃密样本了。
小结
以上两种方法,解决了我们在开头所提出了"需要进行大量重复操作"的痛点,但是这两种方法真的完美吗,真的优雅吗?答案一定是否定的。首先,其需要将"存储在变量中"和"存储在内存中"这两种情况分开处理,并且分析的时候需要根据所保存的立即数去定位相应的方法,再进行详细分析; 其次,第二个脚本中用了一个非常非常冗长的正则表达式,其一,虽然正则表达式非常强大,但他经常给人一种非常杂乱,并且难以阅读的感觉,对于本人而言能不用就不用,因为在我眼里这个手法太不优雅了,甚至有点丑陋; 其二,本人这个正则水平着实不高,为了匹配上上文中小小的代码切片,写了快半小时。基于上面所说的两点问题,下面我们来说一说优雅的解决方案。
在具体说明这个优雅的方式如何实现之前,我们不得不来看一看"dnlib"这个相当nice项目,它让这种优雅的方式成为现实,让我们可以摆脱正则表达式,同时还能够直接修改.NET程序,使我们的分析过程变得更加丝滑。
可以看到,该项目的功能是"读写.NET文件的程序集和模块",具体的用法把"readme"看一看,例子看一看,再配合ps1的Get-Member,我们就能动手了。
成果
随便举两个例子,可以看到,我们想要的效果已经实现了,这样分析起来就非常的流畅并且舒适了,最关键的是还可以正常进行动态调试。
但这真的完美了吗,还没有!我们可以更近一步,我们可以将"ldsfld $VAR",即"b.R"以及"b.s"这种变量直接替换为字符串,这样可以更加方便我们阅读,也同时解决了上文中提到的"以变量形式存储"和"以内存形式存储"这两种情况,这样才是一个趋近于完美的解决方案。
本文提供了三种利用powershell自动化处理.NET样本混淆的方案,可以极大的提高分析效率,降低重复工作,希望大家有所收获,读的开心,也欢迎大家讨论和分享自己平时所用的分析方法和技巧。最后,祝大家生活愉快!!!
$AgentTesla
=
[System.Reflection.Assembly]::LoadFile(
"C:\Users\g0mx\Desktop\RCData1.bin"
)
$
class
=
$AgentTesla.GetType(
"A.b"
)
$fields
=
$
class
.GetFields([System.Reflection.BindingFlags]::Static
-
bor [System.Reflection.BindingFlags]::Public) | Where
-
Object
{$_.MetadataToken
-
in
(
0x04000005
..
0x0400002B
)}
foreach ($field
in
$fields) {
$fieldValue
=
$field.GetValue($null)
$fieldValueString
=
[System.Convert]::ToString($fieldValue)
Write
-
Host
"The decrypted value is: $fieldValueString"
}
$AgentTesla
=
[System.Reflection.Assembly]::LoadFile(
"C:\Users\g0mx\Desktop\RCData1.bin"
)
$
class
=
$AgentTesla.GetType(
"A.b"
)
$fields
=
$
class
.GetFields([System.Reflection.BindingFlags]::Static
-
bor [System.Reflection.BindingFlags]::Public) | Where
-
Object
{$_.MetadataToken
-
in
(
0x04000005
..
0x0400002B
)}
foreach ($field
in
$fields) {
$fieldValue
=
$field.GetValue($null)
$fieldValueString
=
[System.Convert]::ToString($fieldValue)
Write
-
Host
"The decrypted value is: $fieldValueString"
}
$assemblyPath
=
"C:\Users\g0mx\Desktop\RCData1.bin"
$methodToken
=
"0x0600026F"
$filePath
=
"C:\Users\g0mx\Desktop\AT.il"
$pattern
=
"\s+(IL_\w+):\s+ldc\.i4\s+([0-9a-fx]+)\r?\n\s+IL_\w+:\s+call\s+string A\.V::A\(int32\)"
try
{
$assembly
=
[System.Reflection.Assembly]::LoadFile($assemblyPath)
}
catch {
Write
-
Host
"Failed to load assembly: $_"
return
}
try
{
$method
=
$assembly.ManifestModule.ResolveMethod([
int
]$methodToken)
}
catch {
Write
-
Host
"Failed to resolve method: $_"
return
}
$matches
=
@()
Get
-
Content $filePath
-
Raw | Select
-
String
-
Pattern $pattern
-
AllMatches | ForEach
-
Object
{
$_.Matches | ForEach
-
Object
{
$key
=
$_.Groups[
1
].Value
$value
=
$_.Groups[
2
].Value
$matches
+
=
[PSCustomObject]@{Key
=
$key; Value
=
$value}
}
}
$results
=
$matches | ForEach
-
Object
{
$value
=
[
int
]$_.Value
$hexValue
=
"0x{0:x}"
-
f $value
$string
=
$method.invoke($null, $value)
[PSCustomObject]@{
Value
=
$hexValue
String
=
$string
}
}
$results | Export
-
Csv
-
Path
"C:\Users\g0mx\Desktop\Output.csv"
-
NoTypeInformation
$results | Out
-
GridView
$assemblyPath
=
"C:\Users\g0mx\Desktop\RCData1.bin"
$methodToken
=
"0x0600026F"
$filePath
=
"C:\Users\g0mx\Desktop\AT.il"
$pattern
=
"\s+(IL_\w+):\s+ldc\.i4\s+([0-9a-fx]+)\r?\n\s+IL_\w+:\s+call\s+string A\.V::A\(int32\)"
try
{
$assembly
=
[System.Reflection.Assembly]::LoadFile($assemblyPath)
}
catch {
Write
-
Host
"Failed to load assembly: $_"
return
}
try
{
$method
=
$assembly.ManifestModule.ResolveMethod([
int
]$methodToken)
}
catch {
Write
-
Host
"Failed to resolve method: $_"
return
}
$matches
=
@()
Get
-
Content $filePath
-
Raw | Select
-
String
-
Pattern $pattern
-
AllMatches | ForEach
-
Object
{
$_.Matches | ForEach
-
Object
{
$key
=
$_.Groups[
1
].Value
$value
=
$_.Groups[
2
].Value
$matches
+
=
[PSCustomObject]@{Key
=
$key; Value
=
$value}
}
}
$results
=
$matches | ForEach
-
Object
{
$value
=
[
int
]$_.Value
$hexValue
=
"0x{0:x}"
-
f $value
$string
=
$method.invoke($null, $value)
[PSCustomObject]@{
Value
=
$hexValue
String
=
$string
}
}
$results | Export
-
Csv
-
Path
"C:\Users\g0mx\Desktop\Output.csv"
-
NoTypeInformation
$results | Out
-
GridView
function FindDecryptMethod($methods)
{
foreach($method
in
$methods)
{
if
(
-
not
$method.HasBody){
continue
}
if
($method.Parameters.Count
-
eq
1
-
and
$method.Parameters[
0
].
Type
.FullName
-
eq
"System.Int32"
-
and
$method.ReturnType.FullName
-
eq
"System.String"
)
{
return
$method
}
}
return
$null
}
$methods
=
$module_defmd.GetTypes().foreach{$_.Methods}
$decrypt_method
=
FindDecryptMethod
-
methods $methods
$Global:remove_method
=
@($decrypt_method)
function FindDecryptMethod($methods)
{
foreach($method
in
$methods)
{
if
(
-
not
$method.HasBody){
continue
}
if
($method.Parameters.Count
-
eq
1
-
and
$method.Parameters[
0
].
Type
.FullName
-
eq
"System.Int32"
-
and
$method.ReturnType.FullName
-
eq
"System.String"
)
{
return
$method
}
}
return
$null
}
$methods
=
$module_defmd.GetTypes().foreach{$_.Methods}
$decrypt_method
=
FindDecryptMethod
-
methods $methods
$Global:remove_method
=
@($decrypt_method)
foreach($method
in
$methods)
{
if
(
-
not
$method.HasBody){
continue
}
foreach($instr
in
$method.MethodBody.Instructions.ToArray())
{
if
($instr.OpCode.Name
-
like
"call"
-
and
$instr.Operand
-
eq $decrypt_method)
{
$index
=
$method.MethodBody.Instructions.IndexOf($instr)
$para_1_instr
=
$method.MethodBody.Instructions[$index
-
1
]
if
(
-
not
$para_1)
{
Write
-
Host
"Someting went wrong, para was not found!"
-
ForegroundColor Red;
Exit
}
$decrypted_string
=
$module_refl.ResolveMethod($instr.Operand.MDToken.ToInt32()).Invoke($null, $para_1_instr.Operand)
$method.MethodBody.Instructions[$index
-
1
].OpCode
=
[dnlib.DotNet.Emit.OpCodes]::Ldstr
$method.MethodBody.Instructions[$index
-
1
].Operand
=
$decrypted_string
$method.MethodBody.Instructions.RemoveAt($index)
}
}
$method.MethodBody.UpdateInstructionOffsets() | Out
-
Null
}
foreach($method
in
$methods)
{
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2023-8-4 17:41
被g0mx编辑
,原因: