不知道是国外哪个壳,用自己之前写的控制流分析库把控制流混淆清掉了(不是de4dot.blocks!!!!!!!是自己写的,最后附上的代码是清理控制流混淆的,可能你不能直接套给de4dot用)
脱完了才发现原来里面还有代码虚拟化,虚拟化前的代码还有许多是无效代码,用来防止分析的
懒得弄了,.NET的CrackMe都快变成了UnpackMe。
这种CM其实真没太大意思的,全是靠壳的强度。
这个虚拟化也不是特别难,2个数组,一个数组储存局部变量,一个数组负责对访问局部变量数组的下标,也就是index,进行解密操作。
虚拟化只是虚拟化了控制流,没把指令也虚拟化掉,说白了就是加强版控制流混淆。可还原性上来说比Agile.NET的虚拟化好还原的多,这个只需要干掉控制流混淆,恢复下局部变量的访问就清掉虚拟化了。而Agile.NET的代码虚拟化是真的把所有指令都虚拟化了,还原起来很麻烦,虽然反编译正常,但是不一定能正常运行,如果研究过Agile.NET虚拟机的应该都清楚,就不多说了。
有时间我再看看把这个虚拟化的还原写出来。
[C#] 纯文本查看 复制代码 class Program {
static void Main(string[] args) {
new Thread(Execute, 4096 * 1000).Start();
}
private static void Execute() {
using (ModuleDefMD moduleDef = ModuleDefMD.Load(@"I:\Downloads\CrackMe-J4\CrackMe-J4-cleaned.exe")) {
foreach (MethodDef methodDef in moduleDef.EnumerateAllMethodDefs())
if (methodDef.HasBody && !methodDef.Body.HasExceptionHandlers) {
BlockParser parser;
MethodBlock methodBlock;
BlockDeparser deparser;
methodDef.SimplifyMacros();
if (!IsObfuscated(methodDef.Body.Instructions))
continue;
parser = new BlockParser(methodDef.Body.Instructions, methodDef.Body.ExceptionHandlers, methodDef.Body.Variables);
parser.Parse(true);
methodBlock = parser.MethodBlock;
Decflow(methodBlock);
BlockSorter.Sort(methodBlock);
deparser = new BlockDeparser(methodBlock);
deparser.Deparse();
methodDef.Body.Instructions.Clear();
foreach (Instruction instruction in deparser.Instructions)
methodDef.Body.Instructions.Add(instruction);
methodDef.Body.Variables.Clear();
foreach (Local variable in deparser.Variables)
methodDef.Body.Variables.Add(variable);
}
moduleDef.Write(@"I:\Downloads\CrackMe-J4\CrackMe-J4-cf.exe");
}
}
private static bool IsObfuscated(IList<Instruction> instructions) {
if (instructions.Count < 3)
return false;
if (instructions[0].OpCode.Code != Code.Ldc_I4 ||
instructions[1].OpCode.Code != Code.Stloc ||
instructions[2].OpCode.Code != Code.Br)
return false;
return true;
}
private static void Decflow(MethodBlock methodBlock) {
Local ipVariable;
int maxCaseIp;
BasicBlock[] jumpTable;
ipVariable = (Local)ToBasicBlock(methodBlock.FirstBlock).Instructions[1].Operand;
foreach (IBlock block in methodBlock.Blocks) {
bool isCase;
int targetIp;
BasicBlock body;
isCase = IsCase(ToBasicBlock(block), ipVariable, out targetIp, out body);
block.PushExtraData(new BlockInfo() {
IsCase = isCase,
TargetIp = targetIp,
Body = body
});
}
// set extra-data
maxCaseIp = methodBlock.Blocks.Max(block => ToBasicBlock(block).PeekExtraData<BlockInfo>().TargetIp);
jumpTable = new BasicBlock[maxCaseIp + 1];
foreach (IBlock block in methodBlock.Blocks) {
BasicBlock basicBlock;
BlockInfo blockInfo;
basicBlock = ToBasicBlock(block);
blockInfo = basicBlock.PeekExtraData<BlockInfo>();
if (blockInfo.IsCase)
jumpTable[blockInfo.TargetIp] = blockInfo.Body;
}
// build jump-table
foreach (IBlock block in methodBlock.Blocks) {
BasicBlock basicBlock;
BlockInfo blockInfo;
int targetIp;
List<Instruction> instructions;
int maxIndex;
basicBlock = ToBasicBlock(block);
blockInfo = basicBlock.PeekExtraData<BlockInfo>();
if (blockInfo.IsCase)
continue;
if (!HasIpSetter(basicBlock, ipVariable, out targetIp))
continue;
instructions = basicBlock.Instructions;
maxIndex = instructions.Count - 1;
SetNop(instructions[maxIndex - 2]);
SetNop(instructions[maxIndex - 1]);
basicBlock.LastInstruction.Operand = jumpTable[targetIp].FirstInstruction;
basicBlock.FallThrough = jumpTable[targetIp];
}
// fix jump
foreach (IBlock block in methodBlock.Blocks)
block.PopExtraData();
// clean extra-data
}
private static void SetNop(Instruction instruction) {
instruction.OpCode = OpCodes.Nop;
instruction.Operand = null;
}
private static bool IsCase(BasicBlock basicBlock, Local ipVariable, out int targetIp, out BasicBlock body) {
List<Instruction> instructions;
targetIp = 0;
body = null;
instructions = basicBlock.Instructions;
if (instructions.Count < 4)
return false;
if (instructions[0].OpCode.Code != Code.Ldloc || instructions[0].Operand != ipVariable ||
instructions[1].OpCode.Code != Code.Ldc_I4 ||
instructions[2].OpCode.Code != Code.Ceq ||
(instructions[3].OpCode.Code != Code.Brfalse && instructions[3].OpCode.Code != Code.Brtrue))
return false;
targetIp = (int)instructions[1].Operand;
if (instructions[3].OpCode.Code == Code.Brfalse)
body = basicBlock.FallThrough;
else
body = basicBlock.ConditionalTarget;
return true;
}
private static bool HasIpSetter(BasicBlock basicBlock, Local ipVariable, out int targetIp) {
List<Instruction> instructions;
int maxIndex;
targetIp = 0;
instructions = basicBlock.Instructions;
if (instructions.Count < 3)
return false;
maxIndex = instructions.Count - 1;
if (instructions[maxIndex - 2].OpCode.Code != Code.Ldc_I4 ||
instructions[maxIndex - 1].OpCode.Code != Code.Stloc || instructions[maxIndex - 1].Operand != ipVariable ||
instructions[maxIndex].OpCode.Code != Code.Br)
return false;
targetIp = (int)instructions[maxIndex - 2].Operand;
return true;
}
private static BasicBlock ToBasicBlock(IBlock block) {
return (BasicBlock)block;
}
private sealed class BlockInfo {
private bool _isCase;
private int _targetIp;
private BasicBlock _body;
public bool IsCase {
get => _isCase;
set => _isCase = value;
}
public int TargetIp {
get => _targetIp;
set => _targetIp = value;
}
public BasicBlock Body {
get => _body;
set => _body = value;
}
}
} |