2011-09-22 10:55:30 +08:00
/ *
Copyright ( C ) 2011 de4dot @gmail . com
This file is part of de4dot .
de4dot is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
de4dot is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with de4dot . If not , see < http : //www.gnu.org/licenses/>.
* /
using System ;
using System.Collections.Generic ;
using Mono.Cecil ;
using Mono.Cecil.Cil ;
using de4dot.blocks ;
2011-12-09 16:02:06 +08:00
namespace de4dot.code.deobfuscators {
2011-09-22 10:55:30 +08:00
abstract class ProxyDelegateFinderBase {
2011-10-23 19:43:32 +08:00
protected ModuleDefinition module ;
2011-11-06 19:16:30 +08:00
protected List < MethodDefinition > delegateCreatorMethods = new List < MethodDefinition > ( ) ;
2011-12-28 20:33:10 +08:00
protected Dictionary < TypeDefinition , bool > delegateTypesDict = new Dictionary < TypeDefinition , bool > ( ) ;
FieldDefinitionAndDeclaringTypeDict < DelegateInfo > fieldToDelegateInfo = new FieldDefinitionAndDeclaringTypeDict < DelegateInfo > ( ) ;
2011-10-23 19:43:32 +08:00
Dictionary < MethodDefinition , FieldDefinition > proxyMethodToField = new Dictionary < MethodDefinition , FieldDefinition > ( ) ;
2011-11-28 18:45:48 +08:00
int errors = 0 ;
public int Errors {
get { return errors ; }
}
2011-09-22 10:55:30 +08:00
2011-12-28 20:33:10 +08:00
protected class DelegateInfo {
2011-09-22 10:55:30 +08:00
public MethodReference methodRef ; // Method we should call
public FieldDefinition field ; // Field holding the Delegate instance
2011-10-23 19:43:32 +08:00
public OpCode callOpcode ;
public DelegateInfo ( FieldDefinition field , MethodReference methodRef , OpCode callOpcode ) {
2011-09-22 10:55:30 +08:00
this . field = field ;
this . methodRef = methodRef ;
2011-10-23 19:43:32 +08:00
this . callOpcode = callOpcode ;
2011-09-22 10:55:30 +08:00
}
}
public int RemovedDelegateCreatorCalls { get ; set ; }
2011-10-23 19:43:32 +08:00
2011-09-22 10:55:30 +08:00
public IEnumerable < TypeDefinition > DelegateTypes {
get { return delegateTypesDict . Keys ; }
}
2011-10-23 19:43:32 +08:00
public IEnumerable < TypeDefinition > DelegateCreatorTypes {
get {
foreach ( var method in delegateCreatorMethods )
yield return method . DeclaringType ;
}
2011-09-22 10:55:30 +08:00
}
public bool Detected {
2011-10-23 19:43:32 +08:00
get { return delegateCreatorMethods . Count ! = 0 ; }
2011-09-22 10:55:30 +08:00
}
2011-10-09 01:33:12 +08:00
public ProxyDelegateFinderBase ( ModuleDefinition module ) {
2011-09-22 10:55:30 +08:00
this . module = module ;
}
public void setDelegateCreatorMethod ( MethodDefinition delegateCreatorMethod ) {
2011-10-23 19:43:32 +08:00
if ( delegateCreatorMethod = = null )
return ;
delegateCreatorMethods . Add ( delegateCreatorMethod ) ;
}
protected bool isDelegateCreatorMethod ( MethodDefinition method ) {
foreach ( var m in delegateCreatorMethods ) {
if ( m = = method )
return true ;
}
return false ;
2011-09-22 10:55:30 +08:00
}
public void find ( ) {
2011-10-23 19:43:32 +08:00
if ( delegateCreatorMethods . Count = = 0 )
2011-09-22 10:55:30 +08:00
return ;
Log . v ( "Finding all proxy delegates" ) ;
foreach ( var type in module . Types ) {
if ( type . BaseType = = null | | type . BaseType . FullName ! = "System.MulticastDelegate" )
continue ;
2012-01-04 02:22:45 +08:00
var cctor = DotNetUtils . getMethod ( type , ".cctor" ) ;
2011-09-22 10:55:30 +08:00
if ( cctor = = null | | ! cctor . HasBody )
continue ;
if ( ! type . HasFields )
continue ;
2011-10-23 19:43:32 +08:00
object context = checkCctor ( type , cctor ) ;
if ( context = = null )
2011-09-22 10:55:30 +08:00
continue ;
Log . v ( "Found proxy delegate: {0} ({1:X8})" , type , type . MetadataToken . ToUInt32 ( ) ) ;
RemovedDelegateCreatorCalls + + ;
2011-10-23 19:43:32 +08:00
onFoundProxyDelegate ( type ) ;
2011-09-22 10:55:30 +08:00
Log . indent ( ) ;
foreach ( var field in type . Fields ) {
2012-01-04 02:22:45 +08:00
if ( ! field . IsStatic )
2011-09-22 10:55:30 +08:00
continue ;
2011-10-23 19:43:32 +08:00
MethodReference calledMethod ;
OpCode callOpcode ;
getCallInfo ( context , field , out calledMethod , out callOpcode ) ;
if ( calledMethod = = null )
2012-01-04 02:22:45 +08:00
continue ;
2011-12-28 20:33:10 +08:00
addDelegateInfo ( new DelegateInfo ( field , calledMethod , callOpcode ) ) ;
2011-10-23 19:43:32 +08:00
Log . v ( "Field: {0}, Opcode: {1}, Method: {2} ({3:X8})" , field . Name , callOpcode , calledMethod , calledMethod . MetadataToken . ToUInt32 ( ) ) ;
2011-09-22 10:55:30 +08:00
}
Log . deIndent ( ) ;
delegateTypesDict [ type ] = true ;
}
}
2011-12-28 20:33:10 +08:00
protected void addDelegateInfo ( DelegateInfo di ) {
fieldToDelegateInfo . add ( di . field , di ) ;
}
2011-10-23 19:43:32 +08:00
protected virtual void onFoundProxyDelegate ( TypeDefinition type ) {
}
protected abstract object checkCctor ( TypeDefinition type , MethodDefinition cctor ) ;
protected abstract void getCallInfo ( object context , FieldDefinition field , out MethodReference calledMethod , out OpCode callOpcode ) ;
protected void add ( MethodDefinition proxyMethod , FieldDefinition proxyField ) {
if ( proxyMethod = = null | | proxyField = = null )
return ;
proxyMethodToField [ proxyMethod ] = proxyField ;
}
2011-09-22 10:55:30 +08:00
DelegateInfo getDelegateInfo ( FieldReference field ) {
if ( field = = null )
return null ;
2011-12-28 20:33:10 +08:00
return fieldToDelegateInfo . find ( field ) ;
2011-09-22 10:55:30 +08:00
}
class BlockInstr {
public Block Block { get ; set ; }
public int Index { get ; set ; }
}
class RemoveInfo {
public int Index { get ; set ; }
public DelegateInfo DelegateInfo { get ; set ; }
public bool IsCall {
get { return DelegateInfo ! = null ; }
}
}
public void deobfuscate ( Blocks blocks ) {
2011-12-28 20:33:10 +08:00
if ( blocks . Method . DeclaringType ! = null & & delegateTypesDict . ContainsKey ( blocks . Method . DeclaringType ) )
return ;
2011-09-22 10:55:30 +08:00
var removeInfos = new Dictionary < Block , List < RemoveInfo > > ( ) ;
var allBlocks = blocks . MethodBlocks . getAllBlocks ( ) ;
foreach ( var block in allBlocks ) {
var instrs = block . Instructions ;
for ( int i = 0 ; i < instrs . Count ; i + + ) {
2011-10-23 19:43:32 +08:00
var instr = instrs [ i ] ;
if ( instr . OpCode = = OpCodes . Ldsfld ) {
var di = getDelegateInfo ( instr . Operand as FieldReference ) ;
if ( di = = null )
continue ;
var visited = new Dictionary < Block , bool > ( ) ;
var callInfo = findProxyCall ( di , block , i , visited , 1 ) ;
if ( callInfo ! = null ) {
add ( removeInfos , block , i , null ) ;
add ( removeInfos , callInfo . Block , callInfo . Index , di ) ;
}
else {
2011-11-28 18:45:48 +08:00
errors + + ;
2011-10-23 19:43:32 +08:00
Log . w ( "Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})" ,
blocks . Method , blocks . Method . MetadataToken . ToInt32 ( ) ,
di . field . DeclaringType , di . field . DeclaringType . MetadataToken . ToInt32 ( ) ) ;
}
2011-09-22 10:55:30 +08:00
}
2011-10-23 19:43:32 +08:00
else if ( instr . OpCode = = OpCodes . Call ) {
var method = instr . Operand as MethodDefinition ;
if ( method = = null )
continue ;
FieldDefinition field ;
if ( ! proxyMethodToField . TryGetValue ( method , out field ) )
continue ;
var di = getDelegateInfo ( field ) ;
if ( di = = null )
continue ;
add ( removeInfos , block , i , di ) ;
2011-09-22 10:55:30 +08:00
}
}
}
foreach ( var block in removeInfos . Keys ) {
var list = removeInfos [ block ] ;
var removeIndexes = new List < int > ( list . Count ) ;
foreach ( var info in list ) {
if ( info . IsCall ) {
2011-10-23 19:43:32 +08:00
var opcode = info . DelegateInfo . callOpcode ;
2011-09-22 10:55:30 +08:00
var newInstr = Instruction . Create ( opcode , info . DelegateInfo . methodRef ) ;
block . replace ( info . Index , 1 , newInstr ) ;
}
else
removeIndexes . Add ( info . Index ) ;
}
block . remove ( removeIndexes ) ;
}
fixBrokenCalls ( blocks . Method , allBlocks ) ;
}
void add ( Dictionary < Block , List < RemoveInfo > > removeInfos , Block block , int index , DelegateInfo di ) {
List < RemoveInfo > list ;
if ( ! removeInfos . TryGetValue ( block , out list ) )
removeInfos [ block ] = list = new List < RemoveInfo > ( ) ;
list . Add ( new RemoveInfo {
Index = index ,
DelegateInfo = di ,
} ) ;
}
BlockInstr findProxyCall ( DelegateInfo di , Block block , int index , Dictionary < Block , bool > visited , int stack ) {
if ( visited . ContainsKey ( block ) )
return null ;
if ( index < = 0 )
visited [ block ] = true ;
var instrs = block . Instructions ;
for ( int i = index + 1 ; i < instrs . Count ; i + + ) {
if ( stack < = 0 )
return null ;
var instr = instrs [ i ] ;
2011-10-17 06:22:22 +08:00
DotNetUtils . updateStack ( instr . Instruction , ref stack , false ) ;
2011-09-22 10:55:30 +08:00
if ( stack < 0 )
return null ;
if ( instr . OpCode ! = OpCodes . Call & & instr . OpCode ! = OpCodes . Callvirt )
continue ;
var method = DotNetUtils . getMethod ( module , instr . Operand as MethodReference ) ;
if ( method = = null )
continue ;
if ( stack ! = ( DotNetUtils . hasReturnValue ( method ) ? 1 : 0 ) )
continue ;
if ( method . DeclaringType ! = di . field . DeclaringType )
continue ;
return new BlockInstr {
Block = block ,
Index = i ,
} ;
}
if ( stack < = 0 )
return null ;
foreach ( var target in block . getTargets ( ) ) {
var info = findProxyCall ( di , target , - 1 , visited , stack ) ;
if ( info ! = null )
return info ;
}
return null ;
}
// The obfuscator could be buggy and call a proxy delegate without pushing the
// instance field. SA has done it, so let's fix it.
void fixBrokenCalls ( MethodDefinition obfuscatedMethod , IList < Block > allBlocks ) {
foreach ( var block in allBlocks ) {
var instrs = block . Instructions ;
for ( int i = 0 ; i < instrs . Count ; i + + ) {
var call = instrs [ i ] ;
if ( call . OpCode ! = OpCodes . Call & & call . OpCode ! = OpCodes . Callvirt )
continue ;
var methodRef = call . Operand as MethodReference ;
if ( methodRef . Name ! = "Invoke" )
continue ;
var method = DotNetUtils . getMethod ( module , methodRef ) ;
if ( method = = null )
continue ;
var declaringType = DotNetUtils . getType ( module , method . DeclaringType ) ;
if ( declaringType = = null )
continue ;
if ( ! delegateTypesDict . ContainsKey ( declaringType ) )
continue ;
// Oooops!!! The obfuscator is buggy. Well, let's hope it is, or it's my code. ;)
Log . w ( "Holy obfuscator bugs, Batman! Found a proxy delegate call with no instance push in {0:X8}. Replacing it with a throw..." , obfuscatedMethod . MetadataToken . ToInt32 ( ) ) ;
block . insert ( i , Instruction . Create ( OpCodes . Ldnull ) ) ;
block . replace ( i + 1 , 1 , Instruction . Create ( OpCodes . Throw ) ) ;
i + + ;
}
}
}
}
}