From c0cf50c6f72d05edd1b8d473d21da266a8c7b78f Mon Sep 17 00:00:00 2001 From: Augs Date: Tue, 3 Dec 2019 17:01:59 +0000 Subject: [PATCH] Added flow control analysis to decompiler --- sbdump/CodeGraph.cs | 172 +++++++++++++++++++++++++++++++++++++++++++ sbdump/CodeVertex.cs | 24 ++++++ sbdump/Enums.cs | 16 ++++ sbdump/main.cs | 158 +++++++++------------------------------ sbdump/settings.cs | 4 +- 5 files changed, 247 insertions(+), 127 deletions(-) create mode 100644 sbdump/CodeGraph.cs create mode 100644 sbdump/CodeVertex.cs create mode 100644 sbdump/Enums.cs diff --git a/sbdump/CodeGraph.cs b/sbdump/CodeGraph.cs new file mode 100644 index 0000000..bdd7e87 --- /dev/null +++ b/sbdump/CodeGraph.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace arookas +{ + class CodeGraph + { + public List Graph; + + public CodeGraph() + { + Graph = new List(); + } + + public void AddVertex(CodeVertex NewVertex)//Adds a vertex + { + Graph.Add(NewVertex); + } + + + private int IndexFromAddr(long Address) + { + for (int i = 0; i < Graph.Count; i++) + { + if (Graph[i].Addr < Address) + { + continue; + } + return i; + } + return -1;//-1 indicates end of the code + } + + public string OutputCode(int IndentL) + { + for (int i = 0; i < Graph.Count; i++) + { + if(Graph[i].Type == VertexType.Branch) + { + int BranchIndex = IndexFromAddr(Graph[i].BranchTo); + if(BranchIndex == -1) + { + Graph[i].Code = "break " + Graph[i].BranchTo.ToString() + ";";//Branch to label + long EndAddr = Graph[Graph.Count - 1].Addr; + CodeVertex Label = new CodeVertex(VertexType.Label, -1, Graph[i].BranchTo.ToString() + ":", EndAddr+1); + Graph.Add(Label); + } + else + { + VertexType DestType = Graph[BranchIndex].Type; + + if (DestType == VertexType.CodeBlock || DestType == VertexType.Branch) + { + Graph[i].Code = "break " + Graph[i].BranchTo.ToString() + ";";//Branch to label + CodeVertex NewLine = new CodeVertex(VertexType.Label, -1, Graph[i].BranchTo.ToString() + ":",Graph[i].BranchTo); + Graph.Insert(BranchIndex, NewLine); + if(BranchIndex <= i) + { + i++; + } + } + else if(DestType == VertexType.Label) + { + string LabelName = Graph[BranchIndex].Code.Replace(':',' '); + Graph[i].Code = "break " + LabelName + ";";//Branch to label + } + else if(DestType == VertexType.ConditionalBranch) + { + //While Loop Detected + Graph[BranchIndex].Code = "while(" + Graph[BranchIndex].Code + ")" + Environment.NewLine + "{";//Put while loop + int ClosingIndex = IndexFromAddr(Graph[BranchIndex].BranchTo); + if (ClosingIndex == -1) + { + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[BranchIndex].BranchTo); + Graph.Add(NewLine); + } + else + { + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[BranchIndex].BranchTo); + Graph.Insert(ClosingIndex, NewLine); + if (ClosingIndex <= i) + { + i++; + } + } + } + } + } + } + + for (int i = 0; i < Graph.Count; i++) + { + if (Graph[i].Type == VertexType.ConditionalBranch && !Graph[i].Code.StartsWith("while")) + { + if (Graph[i].Addr < Graph[i].BranchTo) + { + //If Statement Detected + Graph[i].Code = "if(" + Graph[i].Code + ")" + Environment.NewLine + "{";//Put if + int ClosingIndex = IndexFromAddr(Graph[i].BranchTo); + if (ClosingIndex == -1) + { + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[i].BranchTo); + Graph.Add(NewLine); + } + else + { + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[i].BranchTo); + Graph.Insert(ClosingIndex, NewLine); + if (ClosingIndex <= i) + { + i++; + } + } + } + else + { + //Do loop detected + Graph[i].Code = "} while(" + Graph[i].Code + ")";//Put do loop + int ClosingIndex = IndexFromAddr(Graph[i].BranchTo); + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "do" + Environment.NewLine + "{", Graph[i].BranchTo); + Graph.Insert(ClosingIndex, NewLine); + if (ClosingIndex <= i) + { + i++; + } + } + } + } + + string DecompiledCode = ""; + for (int i = 0; i < Graph.Count; i++) + { + DecompiledCode = DecompiledCode + Environment.NewLine + Graph[i].Code; + } + + string[] lines = DecompiledCode.Split(new[] { Environment.NewLine },StringSplitOptions.None); + DecompiledCode = ""; + + int IndentLevel = IndentL; + for (int i = 0; i < lines.Length; i++) + { + if (lines[i] == "") + continue; + string IndentedString = new String(' ', IndentLevel * 4) + lines[i]; + if (lines[i].Contains("{")) + { + IndentLevel++; + } + else if (lines[i].Contains("}")) + { + IndentLevel = System.Math.Max(0, IndentLevel - 1); + IndentedString = new String(' ', IndentLevel * 4) + lines[i]; + } + if(DecompiledCode == "") + { + DecompiledCode = IndentedString; + } + else + { + DecompiledCode = DecompiledCode + Environment.NewLine + IndentedString; + } + + } + + return DecompiledCode; + } + + } +} diff --git a/sbdump/CodeVertex.cs b/sbdump/CodeVertex.cs new file mode 100644 index 0000000..1ec5f41 --- /dev/null +++ b/sbdump/CodeVertex.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace arookas +{ + class CodeVertex + { + public VertexType Type; + public long BranchTo = -1; + public string Code; + public long Addr; + + public CodeVertex(VertexType Typ, long BrTo,string Cde,long Adr) + { + Type = Typ; + BranchTo = BrTo; + Code = Cde; + Addr = Adr; + } + } +} diff --git a/sbdump/Enums.cs b/sbdump/Enums.cs new file mode 100644 index 0000000..553cdcf --- /dev/null +++ b/sbdump/Enums.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace arookas +{ + enum VertexType + { + CodeBlock = 0, + Branch = 1, + ConditionalBranch = 2, + Label = 3, + } +} diff --git a/sbdump/main.cs b/sbdump/main.cs index ffc88db..1ed902e 100644 --- a/sbdump/main.cs +++ b/sbdump/main.cs @@ -23,30 +23,6 @@ namespace arookas { "ret", "ret0", "jne", "jmp", "pop", "int0", "int1", "end", }; - public struct CodeLine - { - public string Text; - public long Address; - - - public CodeLine(string txt, long addr) - { - Text = txt; - Address = addr; - } - } - - public struct JumpStatement - { - public long StartAddress; - public long EndAddress; - public JumpStatement(long addr1, long addr2) - { - StartAddress = addr1; - EndAddress = addr2; - } - } - static int Main(string[] args) { #if !DEBUG try { @@ -276,7 +252,7 @@ namespace arookas { static void WriteSun() { Console.WriteLine("Decompiling sb file..."); - DecompFunction(0u);//Decompile main part. + var symbols = new Symbol[sSymCount]; for (var i = 0; i < sSymCount; ++i) { @@ -285,21 +261,25 @@ namespace arookas { foreach (var symbol in symbols.Where(i => i.Type == SymbolType.Function).OrderBy(i => i.Data)) { - sWriter.WriteLine("{0}: ", FetchSymbolName(symbol)); - DecompFunction(symbol.Data); - } + sWriter.WriteLine(); + sWriter.WriteLine("function "+ FetchSymbolName(symbol)+ "(...)" + Environment.NewLine+ "{"); + DecompFunction(symbol.Data, 1); + sWriter.WriteLine("}"); + sWriter.WriteLine(); + } + + DecompFunction(0u, 0);//Decompile main part. + } - static void DecompFunction(uint ofs) + static void DecompFunction(uint ofs, int IndentL) { byte command; sReader.Keep(); sReader.Goto(sTextOffset + ofs); var maxofs = 0u; Stack Stack = new Stack(); - List DecompiledCode = new List(); - List IfStatements = new List(); - List JmpStatements = new List(); + CodeGraph FuncCodeGraph = new CodeGraph(); do { var pos = sReader.Position - sTextOffset; @@ -355,8 +335,8 @@ namespace arookas { } case 0x08: //add { - string Op1 = Stack.Pop(); string Op2 = Stack.Pop(); + string Op1 = Stack.Pop(); Stack.Push("(" + Op1 + " + " + Op2 + ")"); break; } @@ -369,8 +349,8 @@ namespace arookas { } case 0x0A: //mul { - string Op1 = Stack.Pop(); string Op2 = Stack.Pop(); + string Op1 = Stack.Pop(); Stack.Push("(" + Op1 + " * " + Op2 + ")"); break; } @@ -399,21 +379,21 @@ namespace arookas { case 0: VariableName = FetchSymbolName(FetchSymbol(i => i.Type == SymbolType.Variable && i.Data == data)); break; case 1: VariableName = "local" + data.ToString(); break; } - CodeLine NewLine = new CodeLine(VariableName + " = " + Stack.Pop() + ";", pos); - DecompiledCode.Add(NewLine); + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock , -1, VariableName + " = " + Stack.Pop() + ";", pos); + FuncCodeGraph.AddVertex(NewLine); break; } case 0x0E: //eq { - string Op1 = Stack.Pop(); string Op2 = Stack.Pop(); + string Op1 = Stack.Pop(); Stack.Push("(" + Op1 + " == " + Op2 + ")"); break; } case 0x0F: //ne { - string Op1 = Stack.Pop(); string Op2 = Stack.Pop(); + string Op1 = Stack.Pop(); Stack.Push("(" + Op1 + " != " + Op2 + ")"); break; } @@ -546,14 +526,15 @@ namespace arookas { case 0x20: //ret { string Op = Stack.Pop(); - CodeLine NewLine = new CodeLine("return " + Op + ";", pos); - DecompiledCode.Add(NewLine); + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock , -1, "return " + Op + ";", pos); + FuncCodeGraph.AddVertex(NewLine); + break; } case 0x21: //ret0 { - CodeLine NewLine = new CodeLine("return 0;", pos); - DecompiledCode.Add(NewLine); + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "return 0;", pos); + FuncCodeGraph.AddVertex(NewLine); break; } case 0x22: //jne @@ -561,23 +542,24 @@ namespace arookas { var dest = sReader.Read32(); nextofs = dest; string Op = Stack.Pop(); - CodeLine NewLine = new CodeLine("if(" + Op + "){",pos); - IfStatements.Add(new JumpStatement(pos, dest)); - DecompiledCode.Add(NewLine); + CodeVertex NewLine = new CodeVertex(VertexType.ConditionalBranch, dest, Op, pos); + FuncCodeGraph.AddVertex(NewLine); + break; } case 0x23: //jmp { var dest = sReader.Read32(); nextofs = dest; - JmpStatements.Add(new JumpStatement(pos, dest)); + CodeVertex NewLine = new CodeVertex(VertexType.Branch, dest, "", pos); + FuncCodeGraph.AddVertex(NewLine); break; } case 0x24: //pop { string Op = Stack.Pop(); - CodeLine NewLine = new CodeLine(Op + ";", pos); - DecompiledCode.Add(NewLine); + CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, Op + ";", pos); + FuncCodeGraph.AddVertex(NewLine); break; } case 0x25: //int0 @@ -597,90 +579,16 @@ namespace arookas { } } while (!IsReturnCommand(command) || sReader.Position <= sTextOffset + maxofs); - WriteCode(DecompiledCode, IfStatements, JmpStatements); + WriteCode(FuncCodeGraph, IndentL); sWriter.WriteLine(); sReader.Back(); } - static void WriteCode(List DecompCode, List Ifs, List Jumps) + static void WriteCode(CodeGraph FuncCodeGraph, int Indent) { - - //Close if statements - foreach (JumpStatement IfStatement in Ifs) - { - CodeLine NewLine; - for (int i = DecompCode.Count-1; i > 0; i--) - { - if (IfStatement.EndAddress < DecompCode[i].Address)//close address - { - continue; - } - NewLine = new CodeLine("}", IfStatement.EndAddress); - DecompCode.Insert(i + 1, NewLine); - break; - } - } - - - //ImplementBreaks - for (int i = 0; i < DecompCode.Count; i++) - { - if (i < DecompCode.Count - 1) - { - foreach (JumpStatement Jump in Jumps) - { - bool AddLabel = false; - bool AddBreak = false; - CodeLine Label = new CodeLine("", 0); - CodeLine Break = new CodeLine("", 0); - if (Jump.EndAddress >= DecompCode[i].Address && Jump.EndAddress < DecompCode[i + 1].Address)//add label - { - Label = new CodeLine(Jump.EndAddress.ToString("X8") + ":", Jump.EndAddress); - AddLabel = true; - } - if (Jump.StartAddress >= DecompCode[i].Address && Jump.StartAddress < DecompCode[i + 1].Address)//add break - { - Break = new CodeLine("break " + Jump.EndAddress.ToString("X8") + ";", Jump.StartAddress); - AddBreak = true; - } - if (AddLabel) - { - DecompCode.Insert(i + 1, Label); - i++; - } - if (AddBreak) - { - DecompCode.Insert(i + 1, Break); - i++; - } - } - } - } - - int Indent = 0; - //Indentation - for (int i = 0; i < DecompCode.Count; i++) - { - string IndentedString = new String(' ', Indent * 4) + DecompCode[i].Text; - if (DecompCode[i].Text.StartsWith("if")) - { - Indent++; - } - else if (DecompCode[i].Text.StartsWith("}")) - { - Indent = System.Math.Max(0, Indent - 1); - IndentedString = new String(' ', Indent * 4) + DecompCode[i].Text; - } - DecompCode[i] = new CodeLine(IndentedString, DecompCode[i].Address); - - } - - for (int i = 0; i < DecompCode.Count; i++) - { - sWriter.WriteLine(DecompCode[i].Text); - } + sWriter.Write(FuncCodeGraph.OutputCode(Indent)); } static uint FetchData(int i) { diff --git a/sbdump/settings.cs b/sbdump/settings.cs index 4a6e257..7d2eb4c 100644 --- a/sbdump/settings.cs +++ b/sbdump/settings.cs @@ -39,8 +39,8 @@ namespace arookas { } } ///Debug/// - //mInput = "C:\\Users\\August\\Downloads\\Sunshine ROM hacking\\ssc\\Decompile\\talkevent.sb"; - //mDecomp = true; + mInput = "C:\\Users\\August\\Downloads\\Sunshine ROM hacking\\ssc\\Decompile\\talkevent.sb"; + mDecomp = true; ///Debug/// if (mInput == null) { throw new Exception("Missing input file setting.");