1+ using HarmonyLib ;
2+ using System ;
3+ using System . Collections . Generic ;
4+ using System . Globalization ;
5+ using System . Linq ;
6+ using System . Reflection ;
7+ using System . Reflection . Emit ;
8+ using VMUnprotect . Utils ;
9+
10+ namespace VMUnprotect . Hooks . Methods
11+ {
12+ /// <summary>
13+ /// This class holds target functions.
14+ /// </summary>
15+ internal class Targets
16+ {
17+ /// <summary>
18+ /// This function is used by current VMProtect version.
19+ /// </summary>
20+ /// <param name="obj">
21+ /// The object on which to invoke the method or constructor. If a method is static, this argument is
22+ /// ignored. If a constructor is static, this argument must be <see langword="null" /> or an instance of the class that
23+ /// defines the constructor.
24+ /// </param>
25+ /// <param name="invokeAttr">
26+ /// A bitmask that is a combination of 0 or more bit flags from
27+ /// <see cref="T:System.Reflection.BindingFlags" />. If <paramref name="binder" /> is <see langword="null" />, this
28+ /// parameter is assigned the value <see cref="F:System.Reflection.BindingFlags.Default" />; thus, whatever you pass in
29+ /// is ignored.
30+ /// </param>
31+ /// <param name="binder">
32+ /// An object that enables the binding, coercion of argument types, invocation of members, and
33+ /// retrieval of <see langword="MemberInfo" /> objects via reflection. If <paramref name="binder" /> is
34+ /// <see langword="null" />, the default binder is used.
35+ /// </param>
36+ /// <param name="parameters">
37+ /// An argument list for the invoked method or constructor. This is an array of objects with the same number, order,
38+ /// and type as the parameters of the method or constructor to be invoked. If there are no parameters, this should be
39+ /// <see langword="null" />.
40+ /// If the method or constructor represented by this instance takes a ByRef parameter, there is no special attribute
41+ /// required for that parameter in order to invoke the method or constructor using this function. Any object in this
42+ /// array that is not explicitly initialized with a value will contain the default value for that object type. For
43+ /// reference-type elements, this value is <see langword="null" />. For value-type elements, this value is 0, 0.0, or
44+ /// <see langword="false" />, depending on the specific element type.
45+ /// </param>
46+ /// <param name="culture">
47+ /// An instance of <see langword="CultureInfo" /> used to govern the coercion of types. If this is
48+ /// <see langword="null" />, the <see langword="CultureInfo" /> for the current thread is used. (This is necessary to
49+ /// convert a <see langword="String" /> that represents 1000 to a <see langword="Double" /> value, for example, since
50+ /// 1000 is represented differently by different cultures.)
51+ /// </param>
52+ /// <returns>
53+ /// An <see langword="Object" /> containing the return value of the invoked method, or <see langword="null" /> in
54+ /// the case of a constructor, or <see langword="null" /> if the method's return type is <see langword="void" />.
55+ /// Before calling the method or constructor, <see langword="Invoke" /> checks to see if the user has access permission
56+ /// and verifies that the parameters are valid.
57+ /// </returns>
58+ public object HookedInvoke ( object obj , BindingFlags bindingFlags , Binder binder , object [ ] parameters , CultureInfo culture , MethodBase methodBase )
59+ {
60+ try
61+ {
62+ // Indicate this method was called by newer version of VMP.
63+ ConsoleLogger . Warn ( "============================================= HookedInvoke =============================================\n " ) ;
64+
65+ // Invoke the method and get return value.
66+ var returnValue = methodBase . Invoke ( obj , bindingFlags , binder , parameters , culture ) ;
67+
68+ // Route the arguments and return value to our middleman function where they can be manipulated or logged.
69+ MiddleMan . VmpMethodLogger ( obj , bindingFlags , binder , ref parameters , culture , methodBase , ref returnValue ) ;
70+
71+ // Return logged return value.
72+ return returnValue ;
73+ }
74+ catch ( Exception ex )
75+ {
76+ // Log the exception.
77+ ConsoleLogger . Error ( ex . StackTrace ) ;
78+ return null ;
79+ }
80+ }
81+
82+ /// <summary>
83+ /// This function is used by older VMProtect version.
84+ /// </summary>
85+ /// <param name="obj">
86+ /// The object on which to invoke the method or constructor. If a method is static, this argument is
87+ /// ignored. If a constructor is static, this argument must be <see langword="null" /> or an instance of the class that
88+ /// defines the constructor.
89+ /// </param>
90+ /// <param name="parameters">
91+ /// An argument list for the invoked method or constructor. This is an array of objects with the same number, order,
92+ /// and type as the parameters of the method or constructor to be invoked. If there are no parameters,
93+ /// <paramref name="parameters" /> should be <see langword="null" />.
94+ /// If the method or constructor represented by this instance takes a <see langword="ref" /> parameter (
95+ /// <see langword="ByRef" /> in Visual Basic), no special attribute is required for that parameter in order to invoke
96+ /// the method or constructor using this function. Any object in this array that is not explicitly initialized with a
97+ /// value will contain the default value for that object type. For reference-type elements, this value is
98+ /// <see langword="null" />. For value-type elements, this value is 0, 0.0, or <see langword="false" />, depending on
99+ /// the specific element type.
100+ /// </param>
101+ /// <returns>
102+ /// An object containing the return value of the invoked method, or <see langword="null" /> in the case of a
103+ /// constructor.
104+ /// </returns>
105+ public object HookedInvokeOld ( object obj , object [ ] parameters , MethodBase methodBase )
106+ {
107+ try
108+ {
109+ // Indicate this method was called by older version of VMP.
110+ ConsoleLogger . Warn ( "============================================= HookedInvokeOld =============================================\n " ) ;
111+
112+ // Invoke the method and get return value.
113+ var returnValue = methodBase . Invoke ( obj , parameters ) ;
114+
115+ // Route the arguments and return value to our middleman function where they can be manipulated or logged.
116+ MiddleMan . VmpMethodLogger ( obj , null , null , ref parameters , null , methodBase , ref returnValue ) ;
117+
118+ // Return logged return value.
119+ return returnValue ;
120+ }
121+ catch ( Exception ex )
122+ {
123+ // Log the exception.
124+ ConsoleLogger . Error ( ex . StackTrace ) ;
125+ return null ;
126+ }
127+ }
128+ }
129+
130+ /// <summary>
131+ /// This class contains Harmony Patches
132+ /// </summary>
133+ public static class VmProtectDumper
134+ {
135+ /// <summary>A transpiler that replaces all occurrences of a given method with another with additional Ldarg_1 instruction</summary>
136+ /// <param name="instructions">The enumeration of <see cref="T:HarmonyLib.CodeInstruction" /> to act on</param>
137+ /// <param name="from">Method to search for</param>
138+ /// <param name="to">Method to replace with</param>
139+ /// <returns>Modified enumeration of <see cref="T:HarmonyLib.CodeInstruction" /></returns>
140+ private static IEnumerable < CodeInstruction > ReplaceVmpInvoke ( this IEnumerable < CodeInstruction > instructions , MethodBase from , MethodBase to )
141+ {
142+ if ( ( object ) from == null ) throw new ArgumentException ( "Unexpected null argument" , nameof ( from ) ) ;
143+ if ( ( object ) to == null ) throw new ArgumentException ( "Unexpected null argument" , nameof ( to ) ) ;
144+
145+ var code = new List < CodeInstruction > ( instructions ) ;
146+
147+ for ( var x = 0 ; x < code . Count ; x ++ )
148+ {
149+ var ins = code [ x ] ;
150+ if ( ins . operand as MethodBase != from ) continue ;
151+
152+ // replace callvirt Invoke with our debug invoke.
153+ ins . opcode = OpCodes . Callvirt ;
154+ ins . operand = to ;
155+
156+ // insert additional Ldarg_1 which corresponds to MethodBase of invoked function.
157+ // TODO: Improve this, can be easily broken by obfuscation or future VMP updates
158+ code . Insert ( x , new CodeInstruction ( OpCodes . Ldarg_1 ) ) ;
159+ ConsoleLogger . Info ( "Replaced with custom Invoke and injected MethodBase argument at {0}." , x ) ;
160+ }
161+
162+ return code . AsEnumerable ( ) ;
163+ }
164+
165+ /// <summary>A transpiler that alters instructions that calls specific method</summary>
166+ /// <param name="instructions">The enumeration of <see cref="T:HarmonyLib.CodeInstruction" /> to act on</param>
167+ /// <returns>Modified enumeration of <see cref="T:HarmonyLib.CodeInstruction" /></returns>
168+ public static IEnumerable < CodeInstruction > Transpiler ( IEnumerable < CodeInstruction > instructions )
169+ {
170+ ConsoleLogger . Debug ( "VMP Function Handler Transpiler" ) ;
171+
172+ var codeInstructions = instructions . ToList ( ) ;
173+
174+ // Replace all occurrences of MethodBase.Invoke with our debug version.
175+ return codeInstructions . ReplaceVmpInvoke ( AccessTools . Method ( typeof ( MethodBase ) ,
176+ "Invoke" ,
177+ new [ ]
178+ {
179+ typeof ( object ) , typeof ( BindingFlags ) , typeof ( Binder ) , typeof ( object [ ] ) ,
180+ typeof ( CultureInfo )
181+ } ) ,
182+ AccessTools . Method ( typeof ( Targets ) , nameof ( Targets . HookedInvoke ) ) )
183+ // Older version of VMP
184+ . ReplaceVmpInvoke ( AccessTools . Method ( typeof ( MethodBase ) , "Invoke" , new [ ] { typeof ( object ) , typeof ( object [ ] ) } ) ,
185+ AccessTools . Method ( typeof ( Targets ) , nameof ( Targets . HookedInvokeOld ) ) ) ;
186+ }
187+ }
188+ }
0 commit comments