/// <summary> /// Get instructions from stream /// </summary> /// <param name="initClass">Init class</param> /// <param name="result">Result</param> public virtual bool TryParse(object initClass, ref ReverseResult result) { uint insNumber = 0; uint offset = 0; if (result == null) { result = new ReverseResult() { } } ; if (initClass == null || !(initClass is IInitClassStream ics)) { return(false); } byte[] all; OffsetRelationCache offsetCache = new OffsetRelationCache(); List <Instruction> recallJump = new List <Instruction>(); List <Instruction> calls = new List <Instruction>(); PrapareResultForOcurrences(result); using (MemoryStream ms = new MemoryStream()) { foreach (StreamModule module in ics.GetStream()) { Types.Module mAdd = new Types.Module() { Name = module.Name, Start = new IndexOffset() { Offset = (uint)ms.Position, Index = (uint)result.Instructions.Count }, Color = module.Color }; Method mEntryPoint = new Method() { Start = mAdd.Start, Name = "EntryPoint of " + module.Name, }; Instruction lastIns = null; try { if (module.Stream is FileStream fi && Path.GetExtension(fi.Name).ToLowerInvariant() == ".json") { #region load file if are json FileStream long originalPos = module.Stream.Position; all = new byte[module.Stream.Length - originalPos]; module.Stream.Read(all, 0, all.Length); string json = Encoding.UTF8.GetString(all, 0, all.Length); result = JsonHelper.Deserialize <ReverseResult>(json, true); if (result != null) { // Prepare ocurrences PrapareResultForOcurrences(result); // Fill cache offsetCache.FillWith(result.Instructions); // Process instructions (Jumps) using (MemoryStream msX = new MemoryStream()) { foreach (Instruction i in result.Instructions) { ProcessInstruction(result.Instructions, i, offsetCache); // Recall jumps if (i.Jump != null && i.Jump.To != null && offsetCache.TryGetValue(i.Jump.To.Offset, out uint index, OffsetIndexRelation.OffsetToIndex)) { i.Jump.To.Index = index; } i.Write(msX); } result.Bytes = msX.ToArray(); } // Regenerate borders result.StyleMethodBorders(); // Regenerate ocurrences result.GenerateOcurrences(); return(result.Instructions.Count > 0); } module.Stream.Seek(originalPos, SeekOrigin.Begin); #endregion } long max = module.Stream.Length; int percent = 0, newPercent = 0; while (true) { byte[] opCode = new byte[OpCodeSize]; if (module.Stream.Read(opCode, 0, OpCodeSize) != OpCodeSize) { break; } string key = opCode.ToHexString(); if (!OpCodeCache.TryGetValue(key, out OpCodeArgumentAttribute read) || read == null) { throw (new OpCodeNotFoundException() { Offset = offset, OpCode = opCode, }); } OpCodeEmptyArgument arg = read.Create(); uint rBytes = arg.Read(module.Stream); lastIns = new Instruction() { OpCode = new OpCode() { RawValue = opCode, Name = read.OpCode, Description = read.Description, Flags = read.Flags }, Argument = arg.GetType() == typeof(OpCodeEmptyArgument) ? null : arg, Comment = arg.ASCIIValue, Color = mAdd.Color, }; lastIns.Location.Index = insNumber; lastIns.Location.Offset = offset; offsetCache.Add(lastIns.Location); ProcessInstruction(result.Instructions, lastIns, offsetCache); if (lastIns.OpCode.Flags.HasFlag(OpCodeFlag.IsCall)) { calls.Add(lastIns); } else { if (mEntryPoint != null && lastIns.OpCode.Flags.HasFlag(OpCodeFlag.IsRet)) { mEntryPoint.End = lastIns.Location; mAdd.Methods.Add(mEntryPoint); mEntryPoint = null; } } // Recall jumps if (lastIns.Jump != null && !lastIns.Jump.IsDynamic && lastIns.Jump.To.Index == uint.MaxValue) { recallJump.Add(lastIns); } result.Instructions.Add(lastIns); offset += (uint)(rBytes + OpCodeSize); insNumber++; newPercent = (int)((module.Stream.Position * 100) / max); if (percent != newPercent) { percent = newPercent; OnParseProgress?.Invoke(this, percent); } lastIns.Write(ms); } if (mEntryPoint != null) { mEntryPoint.End = lastIns.Location; mAdd.Methods.Add(mEntryPoint); mEntryPoint = null; } } catch (Exception er) { result = null; throw (er); } finally { if (module != null) { module.Dispose(); } } long pos = ms.Position; mAdd.End = lastIns.Location; mAdd.Size = (uint)(pos - mAdd.Start.Offset); ms.Seek(mAdd.Start.Offset, SeekOrigin.Begin); all = new byte[mAdd.Size]; ms.Read(all, 0, all.Length); using (SHA1 sha = SHA1.Create()) mAdd.Hash = sha.ComputeHash(all, 0, all.Length).ToHexString(); result.Modules.Add(mAdd); } result.Bytes = ms.ToArray(); } // Recall jumps foreach (Instruction j in recallJump) { if (offsetCache.TryGetValue(j.Jump.To.Offset, out uint index, OffsetIndexRelation.OffsetToIndex)) { j.Jump.To.Index = index; } else { // If enter here, there will be an error j.Jump = null; } }
/// <summary> /// Fill jumps /// </summary> /// <param name="bag">Bag</param> /// <param name="ins">Instruction</param> /// <param name="offsetToIndexCache">Cache</param> public override void ProcessInstruction(InstructionCollection bag, Instruction ins, OffsetRelationCache offsetToIndexCache) { if (ins.OpCode == null) { return; } switch (ins.OpCode.Name) { // Detect bad property optimization case nameof(NeoOpCode.DROP): { /* * 0x03C5 PUSH0 Method 0x03C5 [R4S] * 0x03C6 NEWARRAY * 0x03C7 TOALTSTACK * That is the only valid > 0x03C8 PUSHBYTES3 0x523453 R4S * 0x03CC NOP * 0x03CD FROMALTSTACK * > 0x03CE DROP */ int y = 1; for (int x = (int)ins.Location.Index - 1; x >= 0 && y <= 6; x--, y++) { Instruction i = bag[x]; if (i == null || i.OpCode == null) { return; } switch (y) { case 1: { if (i.OpCode.Name != nameof(NeoOpCode.FROMALTSTACK)) { // Detect Push / Drop if (i.OpCode.Name.StartsWith("PUSH")) { ins.Flags = InstructionFlag.UnusableCode; ins.ApplyColorForFlags(); i.Flags = InstructionFlag.UnusableCode; i.ApplyColorForFlags(); } return; } break; } case 2: { if (i.OpCode.Name != nameof(NeoOpCode.NOP)) { return; } break; } case 3: { if (!i.OpCode.Name.StartsWith("PUSH")) { return; } break; } case 4: { if (i.OpCode.Name != nameof(NeoOpCode.TOALTSTACK)) { return; } break; } case 5: { if (i.OpCode.Name != nameof(NeoOpCode.NEWARRAY)) { return; } break; } case 6: { if (i.OpCode.Name != nameof(NeoOpCode.PUSH0)) { return; } break; } } } if (y == 7) { ins.Flags = InstructionFlag.UnusableCode; ins.ApplyColorForFlags(); y = 1; for (int x = (int)ins.Location.Index - 1, m = x - 6; x > m; x--, y++) { if (y == 3) { continue; } Instruction i = bag[x]; i.Flags = InstructionFlag.UnusableCode; i.ApplyColorForFlags(); } } break; } // Detect NOP case nameof(NeoOpCode.NOP): { ins.Flags = InstructionFlag.UnusableCode; ins.ApplyColorForFlags(); break; } // Detect To/From ALTSTACK case nameof(NeoOpCode.FROMALTSTACK): { if (ins.Location.Index != 0) { Instruction prev = bag[ins.Location.Index - 1]; if (prev != null && prev.OpCode != null && prev.OpCode.Name == nameof(NeoOpCode.TOALTSTACK)) { prev.Flags = InstructionFlag.UnusableCode; ins.Flags = InstructionFlag.UnusableCode; prev.ApplyColorForFlags(); ins.ApplyColorForFlags(); } } break; } case nameof(NeoOpCode.RET): { ins.Jump = new Jump(new OnJumpDelegate( (d, i) => { if (d == null || d.CurrentInstructionIndex != i.Location.Index || !(d is NeoDebugger neodebug)) { return(null); } try { return((uint)neodebug.Engine.InvocationStack.Peek().InstructionPointer); } catch { } return(null); }) ); break; } case nameof(NeoOpCode.CALL): case nameof(NeoOpCode.CALL_E): case nameof(NeoOpCode.CALL_ED): case nameof(NeoOpCode.CALL_EDT): case nameof(NeoOpCode.CALL_ET): case nameof(NeoOpCode.CALL_I): case nameof(NeoOpCode.JMP): { uint offset; if (ins.Argument is OpCodeShortArgument a) { offset = (uint)a.Value; } else { if (ins.Argument is OpCodeCall_IArgument c) { // TODO: Check this offset = (uint)c.Value - 2; } else { return; } } if (offset == 3) { // Detect JMP to next line ins.Flags = InstructionFlag.UnusableCode; ins.ApplyColorForFlags(); } offset = ins.Location.Offset + offset; if (offsetToIndexCache.TryGetValue(offset, out uint ix, OffsetIndexRelation.OffsetToIndex)) { ins.Jump = new Jump(offset, ix); }