Skip to content

Commit 94183e3

Browse files
authored
Merge pull request #24 from GDATAAdvancedAnalytics/fix-confuserex-native
ConfuserEx: Fix issues with native method emulation
2 parents 83174a9 + 68df25d commit 94183e3

File tree

17 files changed

+265
-236
lines changed

17 files changed

+265
-236
lines changed

.github/workflows/build.yml

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ on:
88
branches: [ "master" ]
99
workflow_dispatch:
1010

11+
env:
12+
DOTNET_RUNTIME: net8.0
13+
SETUP_DOTNET_VERSION: 8.0.x
14+
15+
BEAENGINE_TAG: v5.3.0
16+
1117
jobs:
1218
build-windows:
1319
name: Build Windows artifacts
@@ -27,20 +33,70 @@ jobs:
2733
if ($LASTEXITCODE) { exit $LASTEXITCODE }
2834
Remove-Item Release\net48\*.pdb, Release\net48\*.xml, Release\net48\Test.Rename.*
2935
30-
dotnet publish -c Release -f net8.0 -o publish-net8.0 de4dot
31-
Remove-Item publish-net8.0\*.pdb, publish-net8.0\*.xml
36+
dotnet publish -c Release -f $env:DOTNET_RUNTIME -o publish-$env:DOTNET_RUNTIME de4dot
37+
Remove-Item publish-$env:DOTNET_RUNTIME\*.pdb, publish-$env:DOTNET_RUNTIME\*.xml
38+
39+
- name: Build BeaEngine
40+
shell: pwsh
41+
run: |
42+
$ErrorActionPreference = 'Stop'
43+
44+
git clone --depth 1 --branch $env:BEAENGINE_TAG https://github.com/BeaEngine/beaengine.git beaengine
45+
46+
# Remove cmake malpractice
47+
(Get-Content beaengine/CMakeLists.txt) |
48+
Where-Object { $_ -notmatch '\s*list\s*\(APPEND\s+BEA_FLAGS\s+/M' } |
49+
Set-Content beaengine/CMakeLists.txt
50+
51+
# Win32 for .NET 4.8
52+
cmake -S beaengine -B build-beaengine-32 `
53+
-A Win32 `
54+
-DoptBUILD_DLL=ON `
55+
-DoptHAS_OPTIMIZED=ON `
56+
-DoptHAS_SYMBOLS=OFF `
57+
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
58+
59+
cmake --build build-beaengine-32 --config Release
60+
61+
$dll32 = Get-ChildItem -Recurse build-beaengine-32 -Filter BeaEngine*.dll | Select-Object -First 1
62+
63+
if (-not $dll32) {
64+
throw "32-bit BeaEngine.dll not found"
65+
}
66+
67+
Copy-Item $dll32.FullName Release/net48/BeaEngine.dll
68+
Copy-Item beaengine/src/COPYING.txt Release/net48/LICENSES/LICENSE.BeaEngine.GPL.txt
69+
Copy-Item beaengine/src/COPYING.LESSER.txt Release/net48/LICENSES/LICENSE.BeaEngine.LGPL.txt
70+
71+
# Win64 for .NET 8+
72+
cmake -S beaengine -B build-beaengine-64 `
73+
-A x64 `
74+
-DoptBUILD_DLL=ON `
75+
-DoptHAS_OPTIMIZED=ON `
76+
-DoptHAS_SYMBOLS=OFF `
77+
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
78+
79+
cmake --build build-beaengine-64 --config Release
80+
81+
$dll64 = Get-ChildItem -Recurse build-beaengine-64 -Filter BeaEngine*.dll | Select-Object -First 1
82+
83+
if (-not $dll64) {
84+
throw "64-bit BeaEngine.dll not found"
85+
}
86+
87+
Copy-Item $dll64.FullName publish-$env:DOTNET_RUNTIME/BeaEngine.dll
88+
Copy-Item beaengine/src/COPYING.txt publish-$env:DOTNET_RUNTIME/LICENSES/LICENSE.BeaEngine.GPL.txt
89+
Copy-Item beaengine/src/COPYING.LESSER.txt publish-$env:DOTNET_RUNTIME/LICENSES/LICENSE.BeaEngine.LGPL.txt
3290
3391
- uses: actions/upload-artifact@v4
34-
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
3592
with:
3693
name: de4dotEx-net48
3794
path: Release/net48
3895

3996
- uses: actions/upload-artifact@v4
40-
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
4197
with:
42-
name: de4dotEx-net8.0-win-x64
43-
path: publish-net8.0
98+
name: de4dotEx-${{ env.DOTNET_RUNTIME }}-win-x64
99+
path: publish-${{ env.DOTNET_RUNTIME }}
44100

45101
build-linux:
46102
name: Build Linux artifacts & package .deb
@@ -52,19 +108,38 @@ jobs:
52108
- name: Set up .NET
53109
uses: actions/setup-dotnet@v4
54110
with:
55-
dotnet-version: 8.0.x
111+
dotnet-version: ${{ env.SETUP_DOTNET_VERSION }}
112+
113+
- name: Publish ${{ env.DOTNET_RUNTIME }}
114+
run: |
115+
dotnet publish -c Release -f "${DOTNET_RUNTIME}" -o publish-${DOTNET_RUNTIME} de4dot
116+
rm -rf publish-${DOTNET_RUNTIME}/*.pdb publish-${DOTNET_RUNTIME}/*.xml
56117
57-
- name: Publish net8.0
118+
- name: Build BeaEngine
58119
run: |
59-
dotnet publish -c Release -f net8.0 -o publish-net8.0 de4dot
60-
rm -rf publish-net8.0/*.pdb publish-net8.0/*.xml
120+
set -euo pipefail
121+
122+
git clone --depth 1 --branch "$BEAENGINE_TAG" https://github.com/BeaEngine/beaengine.git
123+
124+
cmake -S beaengine -B build64 \
125+
-DoptBUILD_DLL=ON \
126+
-DoptHAS_OPTIMIZED=ON \
127+
-DoptHAS_SYMBOLS=OFF
128+
129+
cmake --build build64
130+
131+
so64=$(find build64 -name "libBeaEngine*.so" | head -n 1)
132+
[[ -n "$so64" ]] || { echo "libBeaEngine not found"; exit 1; }
133+
134+
cp "$so64" publish-${DOTNET_RUNTIME}/libBeaEngine.so
135+
cp beaengine/src/COPYING.txt publish-${DOTNET_RUNTIME}/LICENSES/LICENSE.BeaEngine.GPL.txt
136+
cp beaengine/src/COPYING.LESSER.txt publish-${DOTNET_RUNTIME}/LICENSES/LICENSE.BeaEngine.LGPL.txt
61137
62138
- name: Upload publish folder
63-
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
64139
uses: actions/upload-artifact@v4
65140
with:
66-
name: de4dotEx-net8.0-linux-x64
67-
path: publish-net8.0
141+
name: de4dotEx-${{ env.DOTNET_RUNTIME }}-linux-x64
142+
path: publish-${{ env.DOTNET_RUNTIME }}
68143

69144
- name: Extract version from Git tag
70145
if: startsWith(github.ref, 'refs/tags/')
@@ -77,7 +152,7 @@ jobs:
77152
if: startsWith(github.ref, 'refs/tags/')
78153
run: |
79154
mkdir -p deb-root/opt/de4dotEx
80-
cp -r publish-net8.0/* deb-root/opt/de4dotEx/
155+
cp -r "publish-$DOTNET_RUNTIME/*" deb-root/opt/de4dotEx/
81156
82157
mkdir -p deb-root/usr/local/bin
83158
ln -s /opt/de4dotEx/de4dot deb-root/usr/local/bin/de4dot
@@ -90,7 +165,7 @@ jobs:
90165
Priority: optional
91166
Architecture: amd64
92167
Maintainer: G DATA Advanced Analytics GmbH <mwa@gdata-adan.de>
93-
Depends: libicu77 | libicu76 | libicu74 | libicu72 | libicu70 | libicu67 | libicu66
168+
Depends: libicu78 | libicu77 | libicu76 | libicu74 | libicu72 | libicu70 | libicu67 | libicu66
94169
Description: .NET deobfuscator and unpacker
95170
de4dot is a .NET deobfuscator and unpacker. It will try its best to
96171
restore a packed and obfuscated assembly to almost the original
@@ -106,5 +181,5 @@ jobs:
106181
- uses: actions/upload-artifact@v4
107182
if: startsWith(github.ref, 'refs/tags/')
108183
with:
109-
name: de4dotEx-${{ steps.get_version.outputs.VERSION }}-net8.0-x64-deb
184+
name: de4dotEx-${{ steps.get_version.outputs.VERSION }}-${{ env.DOTNET_RUNTIME }}-x64-deb
110185
path: de4dotEx-${{ steps.get_version.outputs.VERSION }}.deb

de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,30 +139,28 @@ protected override void GetCallInfo(object context, FieldDef field, out IMethod
139139
}
140140

141141
result *= GetMagicNumber(field.CustomAttributes[0]);
142-
calledMethod = module.ResolveMemberRef(new MDToken(result).Rid);
142+
calledMethod = module.ResolveToken(result) as IMethod;
143143

144144
if (calledMethod == null)
145-
throw new Exception();
145+
throw new Exception($"Resolved token is wrong: {result:X8}");
146146

147147
var charNum = GetCharNum(instructions, parameters.Last());
148148
callOpcode = GetCallOpCode(calledMethod, charNum, ctx.ByteNum);
149149

150150
ctx.CreateMethod.Body = originalMethod.Body; // restore
151151
}
152152

153-
private OpCode GetCallOpCode(IMethod calledMethod, int charNum, int byteNum)
154-
{
155-
if (calledMethod.ResolveMethodDef().IsStatic) return OpCodes.Call;
153+
private OpCode GetCallOpCode(IMethod calledMethod, int charNum, int byteNum) {
154+
if (calledMethod is not MemberRef && calledMethod.ResolveMethodDef().IsStatic) return OpCodes.Call;
156155

157156
var charOpCode = (byte) (charNum ^ byteNum);
158157

159-
if (charOpCode == 0x28)
160-
return OpCodes.Call;
161-
if (charOpCode == 0x6F)
162-
return OpCodes.Callvirt;
163-
if (charOpCode == 0x73)
164-
return OpCodes.Newobj;
165-
throw new Exception();
158+
return charOpCode switch {
159+
0x28 => OpCodes.Call,
160+
0x6F => OpCodes.Callvirt,
161+
0x73 => OpCodes.Newobj,
162+
_ => throw new Exception($"Invalid charOpCode {charOpCode:X2}")
163+
};
166164
}
167165

168166
private int EmulateNativeMethod(MethodDef externalMethod, int parameter)
@@ -256,7 +254,7 @@ private int GetCharNum(IList<Instruction> instructions, Parameter byteParam)
256254
if ((Parameter) instrs[0].Operand != byteParam)
257255
continue;
258256

259-
return (int) instructions[i - 5].Operand;
257+
return (int) instructions[i - 5].Operand; // ldc.i4 placed by ReplaceFieldNameChar()
260258
}
261259
throw new Exception();
262260
}
@@ -413,4 +411,4 @@ private void ReplaceMetadataToken(ref IList<Instruction> instructions, int metad
413411
}
414412
}
415413
}
416-
}
414+
}

de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace de4dot.Bea
33
{
44
public static class BeaConstants
55
{
6-
public static int INSTRUCT_LENGTH = 80;
6+
public const int INSTRUCT_LENGTH = 80;
77

88
public enum SegmentRegister : byte
99
{
Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,21 @@
1-
using System.IO;
2-
using System.Runtime.InteropServices;
3-
4-
namespace de4dot.Bea
5-
{
6-
public static class BeaEngine
7-
{
8-
// 'de4dot\bin\de4dot.blocks.dll' -> 'de4dot\bin\'
9-
private static string _executingPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
10-
11-
static BeaEngine()
12-
{
13-
//TODO: Better handle native DLL discovery
14-
SetDllDirectory(_executingPath);
15-
}
16-
17-
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
18-
private static extern bool SetDllDirectory(string lpPathName);
19-
20-
[DllImport("BeaEngine")]
21-
public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm);
22-
23-
[DllImport("BeaEngine")]
24-
private static extern string BeaEngineVersion();
25-
26-
[DllImport("BeaEngine")]
27-
private static extern string BeaEngineRevision();
28-
29-
public static string Version
30-
{
31-
get
32-
{
33-
return BeaEngineVersion();
34-
}
35-
}
36-
37-
public static string Revision
38-
{
39-
get
40-
{
41-
return BeaEngineRevision();
42-
}
43-
}
44-
}
45-
}
1+
using System;
2+
using System.Runtime.InteropServices;
3+
4+
namespace de4dot.Bea
5+
{
6+
public static class BeaEngine
7+
{
8+
[DllImport("BeaEngine")]
9+
public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm);
10+
11+
[DllImport("BeaEngine")]
12+
private static extern IntPtr BeaEngineVersion(); // returning string would call free() on a const char*
13+
14+
[DllImport("BeaEngine")]
15+
private static extern IntPtr BeaEngineRevision();
16+
17+
public static string Version => Marshal.PtrToStringAnsi(BeaEngineVersion());
18+
19+
public static string Revision => Marshal.PtrToStringAnsi(BeaEngineRevision());
20+
}
21+
}

de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class REX_Struct
1313
public byte state;
1414
}
1515

16-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
16+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
1717
public class PrefixInfo
1818
{
1919
public int Number;
@@ -32,7 +32,7 @@ public class PrefixInfo
3232
public byte BranchTaken;
3333
public byte BranchNotTaken;
3434
public REX_Struct REX;
35-
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
35+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
3636
public string alignment;
3737
}
3838

@@ -53,7 +53,7 @@ public class EFLStruct
5353
public byte alignment;
5454
}
5555

56-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
56+
[StructLayout(LayoutKind.Sequential, Pack = 4)]
5757
public class RegisterType
5858
{
5959
public Int64 type;
@@ -74,16 +74,16 @@ public class RegisterType
7474
}
7575

7676

77-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
77+
[StructLayout(LayoutKind.Sequential, Pack = 4)]
7878
public class MemoryType
7979
{
80-
public Int32 BaseRegister;
81-
public Int32 IndexRegister;
80+
public Int64 BaseRegister;
81+
public Int64 IndexRegister;
8282
public Int32 Scale;
8383
public Int64 Displacement;
8484
}
8585

86-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
86+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
8787
public class InstructionType
8888
{
8989
public Int32 Category;
@@ -98,12 +98,12 @@ public class InstructionType
9898
public RegisterType ImplicitUsedRegs;
9999
}
100100

101-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
101+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
102102
public class ArgumentType
103103
{
104104
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
105105
public string OpMnemonic;
106-
public Int32 OpType;
106+
public Int64 OpType;
107107
public Int32 OpSize;
108108
public Int32 OpPosition;
109109
public UInt32 AccessMode;
@@ -112,13 +112,13 @@ public class ArgumentType
112112
public UInt32 SegmentReg;
113113
}
114114

115-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
115+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
116116
public class Disasm
117117
{
118118
public IntPtr EIP;
119119
public UInt64 VirtualAddr;
120120
public UInt32 SecurityBlock;
121-
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
121+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BeaConstants.INSTRUCT_LENGTH)]
122122
public string CompleteInstr;
123123
public UInt32 Archi;
124124
public UInt64 Options;

de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
using System.Collections.Generic;
22
using de4dot.Bea;
3-
using de4dot.code.deobfuscators.ConfuserEx.x86;
43

5-
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
4+
namespace de4dot.code.deobfuscators.ConfuserEx.x86.Instructions
65
{
76
class X86ADD : X86Instruction
87
{
9-
public X86ADD(Disasm rawInstruction) : base()
8+
public X86ADD(Disasm rawInstruction)
109
{
1110
Operands = new IX86Operand[2];
1211
Operands[0] = GetOperand(rawInstruction.Operand1);
13-
Operands[1] = GetOperand(rawInstruction.Operand2);
12+
Operands[1] = GetOperand(rawInstruction.Operand2);
1413
}
1514

1615
public override X86OpCode OpCode { get { return X86OpCode.ADD; } }

0 commit comments

Comments
 (0)