├── samples ├── add_opaque │ ├── .gitignore │ ├── generate.sh │ └── opaque.c ├── encode_data │ ├── .gitignore │ ├── generate.sh │ ├── litterals.c │ └── check_modular_inverse.cpp └── encode_arithmetic │ ├── .gitignore │ ├── patterns.sh │ ├── patterns.c │ └── generate.sh ├── .gitignore ├── report ├── report.pdf ├── .gitignore ├── projectreport.cls ├── refs.bib └── report.tex ├── README.md └── simplifier ├── README.md ├── MBASimplifier ├── MBASimplifier.fsproj ├── Input.fs ├── UserInterface.fs ├── Main.fs ├── Engine.fs ├── Ast.fs ├── .gitignore └── Simplifier.fs ├── MBASimplifier.Tests ├── MBASimplifier.Tests.fsproj ├── Program.fs ├── .gitignore └── SimplifierTests.fs └── MBASimplifier.sln /samples/add_opaque/.gitignore: -------------------------------------------------------------------------------- 1 | out -------------------------------------------------------------------------------- /samples/encode_data/.gitignore: -------------------------------------------------------------------------------- 1 | out -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ionide 2 | .vs 3 | .vscode -------------------------------------------------------------------------------- /samples/encode_arithmetic/.gitignore: -------------------------------------------------------------------------------- 1 | out -------------------------------------------------------------------------------- /report/report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adutilleul/tigress-deobfuscation/HEAD/report/report.pdf -------------------------------------------------------------------------------- /report/.gitignore: -------------------------------------------------------------------------------- 1 | report.aux 2 | report.bbl 3 | report.blg 4 | report.fdb_latexmk 5 | report.fls 6 | report.log 7 | report.out 8 | report.synctex.gz 9 | _minted-report -------------------------------------------------------------------------------- /samples/add_opaque/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -R out 4 | mkdir out 5 | 6 | tigress --Seed=0 --Environment=x86_64:Linux:Gcc:9.3 --Inputs='+2:int:1?2147483647' --Transform=InitEntropy --Transform=InitOpaque --Functions=* --Transform=AddOpaque --Functions=* --AddOpaqueCount=10 --AddOpaqueKinds=call,bug,true,junk,question --InitOpaqueStructs=* --out=out/opaque.c opaque.c 7 | 8 | rm a.out -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tigress Deobfuscation 2 | A documentation of several Tigress obfuscation passes and an attempt to simplify Mixed Boolean-Arithmetic (MBA) expressions. 3 | 4 | # Tool 5 | The tool for simplifying MBA expressions is available in the folder **simplify**, its use is described in the secondary README.md. 6 | 7 | 8 | # Scripts and miscellaneous 9 | The scripts that were used to generate the obfuscated code for the analysis are available in the **sample** folder. -------------------------------------------------------------------------------- /simplifier/README.md: -------------------------------------------------------------------------------- 1 | # Tool 2 | ## Building 3 | ### Build Prerequisites 4 | * [.NET Core SDK 6.0.0 or later](https://dotnet.microsoft.com/download/dotnet/6.0) 5 | ## Tests 6 | Just use the ```dotnet test``` command to launch the unit tests. 7 | ## How to use 8 | Use ```dotnet run``` inside the **MBASimplifier** folder. 9 | It is possible to see all the commands available in the tool in interactive mode by typing the ```help``` command. The expressions entered must be a subset of C99, without any casts or (pre/post)(in/de)crementations. For a precise idea of the grammar, please see the **Engine.Fs** file. 10 | -------------------------------------------------------------------------------- /samples/encode_data/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -R out 4 | mkdir out 5 | encodingtypes=( 6 | "poly1" 7 | "xor" 8 | "add" 9 | ) 10 | 11 | for i in "${!encodingtypes[@]}" 12 | do 13 | tigress --Environment=x86_64:Linux:Gcc:9.3 \ 14 | --Transform=EncodeData \ 15 | --Functions=main \ 16 | --LocalVariables='main:argc,loop,res,res2,i' \ 17 | --GlobalVariables='global_res' \ 18 | --EncodeDataCodecs="${encodingtypes[i]}" \ 19 | --out=./out/"${encodingtypes[i]}".c \ 20 | --Seed=0 ./litterals.c 21 | done 22 | 23 | rm a.out -------------------------------------------------------------------------------- /simplifier/MBASimplifier/MBASimplifier.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /samples/encode_data/litterals.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TYPE int 6 | #define LITTERAL 66 7 | #define LITTERAL2 900 8 | 9 | TYPE global_res = LITTERAL; 10 | TYPE global_res2 = LITTERAL; 11 | 12 | static const TYPE foo = LITTERAL; 13 | 14 | TYPE tigress_obf() 15 | { 16 | return LITTERAL; 17 | } 18 | 19 | int main(TYPE argc) 20 | { 21 | TYPE res = tigress_obf(); 22 | TYPE res2 = res + 4LL; 23 | TYPE loop[5] = {LITTERAL2, LITTERAL2, LITTERAL2, LITTERAL2}; 24 | for(TYPE i = 0; i < (TYPE) sizeof(loop); i++) { 25 | res2 -= loop[i]; 26 | res--; 27 | switch(res2++) { 28 | case 4: 29 | res2++; 30 | break; 31 | default: 32 | break; 33 | } 34 | } 35 | 36 | TYPE check = 5; 37 | while(check = (--check)) {} 38 | global_res = res; 39 | return res2 * loop[0]; 40 | } 41 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier.Tests/MBASimplifier.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier.Tests/Program.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | module Program = 26 | 27 | [] 28 | let main _ = 0 29 | -------------------------------------------------------------------------------- /samples/encode_arithmetic/patterns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # launch Tigress 4 | tigress --Seed=0 --Environment=x86_64:Linux:Gcc:9.3 \ 5 | --Transform=EncodeArithmetic\ 6 | --Functions=equal,not_equal,greater,lesser,greater_or_equal,lesser_or_equal,main,add,sub,mul,bdiv,mod,logic_and,logic_or,logic_neg,bit_or,bit_xor,bit_neg,bit_and,bit_lshift,bit_rshift,add3,sub3 \ 7 | --out=patterns_out.c \ 8 | patterns.c 9 | 10 | patterns=( 11 | "int bit_xor(int a , int b , int c , int d )" 12 | "int bit_and(int a , int b )" 13 | "int bit_or(int a , int b )" 14 | "int add(int a , int b )" 15 | "int sub(int a , int b )" 16 | "int mul(int a , int b , int c )" 17 | "int add3(int a , int b , int c )" 18 | "int sub3(int a , int b , int c )" 19 | "int not_equal(int a , int b )" 20 | "int equal(int a , int b )" 21 | "int greater(int a , int b )" 22 | "int greater_or_equal(int a , int b )" 23 | "int lesser_or_equal(int a , int b )" 24 | ) 25 | 26 | fnames=( 27 | "bit_xor" 28 | "bit_and" 29 | "bit_or" 30 | "add" 31 | "sub" 32 | "mul" 33 | "add3" 34 | "sub3" 35 | "not_equal" 36 | "equal" 37 | "greater" 38 | "greater_or_equal" 39 | "lesser_or_equal" 40 | ) 41 | 42 | # get the obfuscated expression and save it to a temporary file 43 | for i in "${!fnames[@]}" 44 | do 45 | (grep -A 5 "^${patterns[i]} $" patterns_out.c | tail -1 | tr -d ";" | sed 's/return*//' | sed 's/^ *//g') >> "out/${fnames[i]}.mba" 46 | done 47 | 48 | rm a.out 49 | rm patterns_out.c -------------------------------------------------------------------------------- /simplifier/MBASimplifier.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31717.71 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MBASimplifier", "MBASimplifier\MBASimplifier.fsproj", "{881F4FC9-8D29-48F8-B042-EE26393BE085}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MBASimplifier.Tests", "MBASimplifier.Tests\MBASimplifier.Tests.fsproj", "{C2E4B854-7D1C-4013-9A6B-392950E79359}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {881F4FC9-8D29-48F8-B042-EE26393BE085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {881F4FC9-8D29-48F8-B042-EE26393BE085}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {881F4FC9-8D29-48F8-B042-EE26393BE085}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {881F4FC9-8D29-48F8-B042-EE26393BE085}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C2E4B854-7D1C-4013-9A6B-392950E79359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C2E4B854-7D1C-4013-9A6B-392950E79359}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C2E4B854-7D1C-4013-9A6B-392950E79359}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C2E4B854-7D1C-4013-9A6B-392950E79359}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {585AE2DF-EED0-4DA5-A13C-256FE955B50F} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /samples/encode_arithmetic/patterns.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TYPE int 6 | 7 | TYPE equal(TYPE a, TYPE b) 8 | { 9 | return a == b; 10 | } 11 | 12 | TYPE not_equal(TYPE a, TYPE b) 13 | { 14 | return a != b; 15 | } 16 | 17 | TYPE greater(TYPE a, TYPE b) 18 | { 19 | return a > b; 20 | } 21 | 22 | TYPE lesser(TYPE a, TYPE b) 23 | { 24 | return a < b; 25 | } 26 | 27 | TYPE greater_or_equal(TYPE a, TYPE b) 28 | { 29 | return a >= b; 30 | } 31 | 32 | TYPE lesser_or_equal(TYPE a, TYPE b) 33 | { 34 | return a <= b; 35 | } 36 | 37 | TYPE add(TYPE a, TYPE b) 38 | { 39 | return (a + b) | b; 40 | } 41 | 42 | TYPE add3(TYPE a, TYPE b, TYPE c) 43 | { 44 | return (a + b + c + 5) * c; 45 | } 46 | 47 | TYPE sub(TYPE a, TYPE b) 48 | { 49 | return (a - b) & a; 50 | } 51 | 52 | TYPE sub3(TYPE a, TYPE b, TYPE c) 53 | { 54 | return (a - b - c) * (a * b); 55 | } 56 | 57 | TYPE mul(TYPE a, TYPE b, TYPE c) 58 | { 59 | return a * b; 60 | } 61 | 62 | TYPE bdiv(TYPE a, TYPE b) 63 | { 64 | return a/b; 65 | } 66 | 67 | TYPE mod(TYPE a, TYPE b) { 68 | return a%b; 69 | } 70 | 71 | TYPE logic_and(TYPE a, TYPE b) 72 | { 73 | return a && b; 74 | } 75 | 76 | TYPE logic_or(TYPE a, TYPE b) 77 | { 78 | return a || b; 79 | } 80 | 81 | TYPE logic_neg(TYPE a, TYPE b) 82 | { 83 | return !a; 84 | } 85 | 86 | TYPE bit_or(TYPE a, TYPE b) 87 | { 88 | return a | b; 89 | } 90 | 91 | TYPE bit_xor(TYPE a, TYPE b, TYPE c, TYPE d) 92 | { 93 | return a ^ b; 94 | } 95 | 96 | TYPE bit_neg(TYPE a) 97 | { 98 | return ~a; 99 | } 100 | 101 | TYPE bit_and(TYPE a, TYPE b) 102 | { 103 | return a & b; 104 | } 105 | 106 | 107 | TYPE bit_lshift(TYPE a, TYPE b) 108 | { 109 | return a << b; 110 | } 111 | 112 | TYPE bit_rshift(TYPE a, TYPE b) 113 | { 114 | return a >> b; 115 | } 116 | 117 | int main() 118 | { 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier/Input.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | 26 | module MBASimplifier.Input 27 | 28 | let private whitespace = [| ' '; '\t' |] 29 | 30 | type Command = 31 | | Exit 32 | | Help 33 | | ReadFile of string 34 | | ReadInput of string 35 | | ClearConsole 36 | 37 | static member parse (commandString:string) = 38 | let input = ReadInput commandString 39 | let tokens = commandString.Split(whitespace, 2, System.StringSplitOptions.RemoveEmptyEntries) |> List.ofArray 40 | match tokens with 41 | // No arguments 42 | | [command] -> 43 | match command with 44 | | "quit" | "exit" -> Exit 45 | | "help" -> Help 46 | | "clear" -> ClearConsole 47 | | _ -> input 48 | 49 | // One argument 50 | | [command; arg] -> 51 | match command with 52 | | "read" -> ReadFile arg 53 | | _ -> input 54 | 55 | | _ -> input 56 | 57 | -------------------------------------------------------------------------------- /samples/encode_data/check_modular_inverse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template< 7 | typename T, 8 | typename std::enable_if< 9 | std::is_integral::value>::type* = nullptr, 10 | uint32_t M = 66666> 11 | T fast_modular_inverse(T x) { 12 | const T rest = x & ~1; 13 | T acc = 1; 14 | for(uint32_t i = 0; i < M; i++) { 15 | if(acc & (1 << i)) 16 | acc -= rest << i; 17 | } 18 | const T mask = ~(~static_cast(0) << M); 19 | return acc & mask; 20 | } 21 | 22 | template< 23 | typename T, 24 | typename std::enable_if< 25 | std::is_integral::value>::type* = nullptr> 26 | T gcd(T a, T b, T& x, T& y) { 27 | if (b == 0) { 28 | x = 1; 29 | y = 0; 30 | return a; 31 | } 32 | T x1, y1; 33 | T d = gcd(b, a % b, x1, y1); 34 | x = y1; 35 | y = x1 - y1 * (a / b); 36 | return d; 37 | } 38 | 39 | template< 40 | typename T, 41 | typename std::enable_if< 42 | std::is_integral::value>::type* = nullptr, 43 | uint32_t M = 66666> 44 | T naive_mod_inverse(T a) { 45 | T x, y; 46 | T g = gcd(a, M, x, y); 47 | if (g != 1) 48 | return -1; 49 | else 50 | return { (x%M + M) % M }; 51 | } 52 | 53 | int main(int argc, char** argv) { 54 | uint64_t n; 55 | if(argc > 1) { 56 | n = std::strtoull(argv[1], NULL, 10); 57 | } else { 58 | return -1; 59 | } 60 | 61 | uint64_t ainv = fast_modular_inverse(n); 62 | std::chrono::high_resolution_clock::time_point t1_fast = 63 | std::chrono::high_resolution_clock::now(); 64 | volatile const auto _fast = fast_modular_inverse(n); 65 | const auto duration_fast = std::chrono::duration_cast( 66 | std::chrono::high_resolution_clock::now()-t1_fast).count(); 67 | 68 | std::chrono::high_resolution_clock::time_point t1_slow = 69 | std::chrono::high_resolution_clock::now(); 70 | volatile const auto _slow = fast_modular_inverse(n); 71 | const auto duration_slow = std::chrono::duration_cast( 72 | std::chrono::high_resolution_clock::now()-t1_slow).count(); 73 | 74 | std::cout << duration_slow << " ns vs " << duration_fast << " ns" << std::endl; 75 | std::cout << _slow << std::endl; 76 | std::cout << _fast << std::endl; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /samples/add_opaque/opaque.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int isqrt_newton(int n) { 6 | int x = 1; 7 | int decreased = 0; 8 | for (;;) { 9 | int nx = (x + n / x) >> 1; 10 | if (x == nx || nx > x && decreased) 11 | break; 12 | decreased = nx < x; 13 | x = nx; 14 | } 15 | return x; 16 | } 17 | 18 | double pow(double x, int n) { 19 | double result = 1; 20 | int minus = 1; 21 | 22 | if (n < 0) { 23 | minus = -1; 24 | n = -n; 25 | } 26 | 27 | if (0 == n) { 28 | return 1; 29 | } else if (0 == x) { 30 | return 0; 31 | } 32 | 33 | while (n) { 34 | if (n & 1) { 35 | result *= x; 36 | } 37 | x *= x; 38 | n /= 2; 39 | } 40 | 41 | if (minus < 0) { 42 | return 1.0 / result; 43 | } else { 44 | return result; 45 | } 46 | } 47 | 48 | static int reversible_count(int d) { 49 | switch (d % 4) { 50 | case 1: 51 | return 0; 52 | case 0: 53 | case 2: 54 | return 20 * (int)pow(30 , d / 2 - 1); 55 | case 3: 56 | return 100 * (int)pow(500, (d - 3) / 4); 57 | } 58 | return 0; 59 | } 60 | 61 | int findKthLargest(int* nums, int numsSize, int k) { 62 | if (k < 1 || k > numsSize) return 0; 63 | 64 | int pivot = nums[0]; 65 | int i = 0, j = numsSize - 1; 66 | 67 | while (i < j) { 68 | while (i < j && nums[j] >= pivot) 69 | j--; 70 | 71 | nums[i] = nums[j]; 72 | 73 | while (i < j && nums[i] <= pivot) 74 | i++; 75 | 76 | nums[j] = nums[i]; 77 | } 78 | 79 | nums[i] = pivot; 80 | 81 | int rightSize = numsSize - i - 1; 82 | 83 | if (rightSize + 1 == k) { 84 | return nums[i]; 85 | } 86 | 87 | if (rightSize >= k) { 88 | return findKthLargest(nums + i + 1, rightSize, k); 89 | } 90 | else { 91 | return findKthLargest(nums, i, k - rightSize - 1); 92 | } 93 | } 94 | 95 | int* productExceptSelf(int* nums, int numsSize, int* returnSize) { 96 | if (nums == NULL || numsSize == 0) return NULL; 97 | 98 | int *output = (int *)malloc(numsSize * sizeof(int)); 99 | *returnSize = numsSize; 100 | 101 | int i; 102 | for (i = 0; i < numsSize; i++) { 103 | output[i] = 1; 104 | } 105 | 106 | int prodLeft = 1; 107 | for (i = 1; i < numsSize; i++) { 108 | prodLeft *= nums[i - 1]; 109 | output[i] *= prodLeft; 110 | } 111 | 112 | int prodRight = 1; 113 | for (i = numsSize - 2; i >= 0; i--){ 114 | prodRight *= nums[i + 1]; 115 | output[i] *= prodRight; 116 | } 117 | 118 | return output; 119 | } 120 | 121 | int main(int argc) { 122 | for(int i = 0; i < 100; i++) { 123 | if(argc == 5) 124 | return reversible_count(66666) && isqrt_newton(2); 125 | } 126 | } -------------------------------------------------------------------------------- /simplifier/MBASimplifier/UserInterface.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | module MBASimplifier.UserInterface 26 | 27 | open Spectre.Console 28 | open RadLine 29 | 30 | let printHeader() = 31 | FigletText "MBA Simplifier" 32 | |> AlignableExtensions.Centered 33 | |> (fun p -> FigletTextExtensions.Color(p, Color.LightCyan1)) 34 | |> AnsiConsole.Console.Write 35 | 36 | let printHelp() = 37 | Table() 38 | |> (fun p -> TableExtensions.AddColumn(p, "Name")) 39 | |> (fun p -> TableExtensions.AddColumn(p, "What it does")) 40 | |> (fun p -> TableExtensions.AddRow(p, "clear", "Resets the console to its default display")) 41 | |> (fun p -> TableExtensions.AddRow(p, "read {path}", "Executes all instructions in a file")) 42 | |> (fun p -> TableExtensions.AddRow(p, "quit", "Exit the interactive mode of the console")) 43 | |> (fun p -> TableExtensions.AddRow(p, "help", "Display this message")) 44 | |> ExpandableExtensions.Expand 45 | |> HasTableBorderExtensions.RoundedBorder 46 | |> (fun p -> HasBorderExtensions.BorderColor(p, Color(byte 0, byte 128, byte 0))) 47 | |> AnsiConsole.Console.Write 48 | 49 | let colorGreen = Color(byte 0, byte 128, byte 0) 50 | let colorRed = Color(byte 196, byte 0, byte 0) 51 | let colorYellow = Color(byte 255, byte 255, byte 0) 52 | 53 | let writeText (ansiConsole: IAnsiConsole) (input: string) (header: string) (color: Color)= 54 | printf "\n" 55 | input 56 | |> Markup.Escape 57 | |> Panel 58 | |> (fun p -> PanelExtensions.Header(p, header)) 59 | |> ExpandableExtensions.Expand 60 | |> HasBoxBorderExtensions.RoundedBorder 61 | |> (fun p -> HasBorderExtensions.BorderColor(p, color)) 62 | |> ansiConsole.Write 63 | 64 | let writeLine (ansiConsole: IAnsiConsole) (input: string) = 65 | writeText ansiConsole input "Ok" colorGreen 66 | 67 | let writeLineError (ansiConsole: IAnsiConsole) (input: string) = 68 | writeText ansiConsole input "Error" colorRed 69 | 70 | let writeLineInfo (ansiConsole: IAnsiConsole) (input: string) = 71 | writeText ansiConsole input "Info" colorYellow 72 | 73 | let private getWordHighlighter () = 74 | let rec addWord words style (highlighter : WordHighlighter) = 75 | match words with 76 | | hd::tl -> 77 | highlighter.AddWord(hd, style) |> ignore 78 | addWord tl style highlighter 79 | | _ -> () 80 | 81 | let highlighter = WordHighlighter () 82 | 83 | let keywords = ["let"] 84 | let operators = ["("; ")"] 85 | 86 | addWord keywords (Style Color.LightSlateBlue) highlighter 87 | addWord operators (Style Color.Pink1) highlighter 88 | 89 | highlighter 90 | 91 | let getLineEditor (ansiConsole : IAnsiConsole) = 92 | let lineEditor = LineEditor (ansiConsole, null) 93 | lineEditor.Highlighter <- getWordHighlighter () 94 | lineEditor 95 | 96 | let readLine (lineEditor : LineEditor) = 97 | lineEditor.ReadLine(System.Threading.CancellationToken.None).Result 98 | 99 | let writeLineAnsi text = writeLine AnsiConsole.Console text 100 | let writeLineErrorAnsi text = writeLineError AnsiConsole.Console text 101 | let writeLineInfoAnsi text = writeLineInfo AnsiConsole.Console text -------------------------------------------------------------------------------- /simplifier/MBASimplifier/Main.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | module MBASimplifier.Main 26 | 27 | open System 28 | open Spectre.Console 29 | 30 | open MBASimplifier.Ast 31 | open MBASimplifier.Implementation 32 | open MBASimplifier.Input 33 | open MBASimplifier.UserInterface 34 | 35 | let lineEditor = getLineEditor AnsiConsole.Console 36 | 37 | let initialState = 38 | let bitcount (n : int64) = 39 | let count2 = n - ((n >>> 1) &&& 0x5555555555555555L) 40 | let count4 = (count2 &&& 0x3333333333333333L) + ((count2 >>> 2) &&& 0x3333333333333333L) 41 | let count8 = (count4 + (count4 >>> 4)) &&& 0x0f0f0f0f0f0f0f0fL 42 | (count8 * 0x0101010101010101L) >>> 56 |> int 43 | { 44 | ExpressionsCache = Map.empty 45 | Exploration = Set.empty 46 | MemoryMap = Map.ofSeq( 47 | seq { 48 | yield "inc", Function (fun e -> e + Constant 1) 49 | yield "dec", Function (fun e -> e - Constant 1) 50 | yield "popcnt", Function (fun e -> match e with Constant c -> Constant(bitcount c) | _ -> failwith "popcnt(int) is excepted") 51 | } 52 | ); 53 | Debug = false 54 | } 55 | 56 | let runSimplification remind state line = 57 | let parseResult = (parseLine state line) 58 | match parseResult with 59 | | ParseError msg -> failwith msg 60 | | ParseSuccess statement -> 61 | if remind then 62 | match statement with 63 | | Single expression -> 64 | writeLineInfoAnsi (expression.humanize()) 65 | | Assignment expressions -> 66 | for (_, expr) in expressions do 67 | writeLineInfoAnsi (expr.humanize()) 68 | let result = executeStatement state statement 69 | match result with 70 | | SingleResult (newState, value) -> 71 | writeLineAnsi (value.humanize()) 72 | writeLineInfoAnsi (sprintf "%A" value) 73 | newState 74 | | AssignmentResult (newState, assignments) -> 75 | for (_, value) in assignments do 76 | writeLineAnsi (value.humanize()) 77 | newState 78 | 79 | let runFile state filename = 80 | use reader = new System.IO.StreamReader(System.IO.File.OpenRead filename) 81 | let fileInput = seq { 82 | while not reader.EndOfStream do 83 | let line = reader.ReadLine().Trim() 84 | // Don't process comments 85 | if line.Length > 0 && not (line.StartsWith "//") then 86 | yield line 87 | } 88 | fileInput |> Seq.fold (runSimplification true) state 89 | 90 | let rec userInput = seq { 91 | while true do 92 | printf "> " 93 | let input = (readLine lineEditor).Trim() 94 | if input.Length > 0 then 95 | yield input 96 | } 97 | 98 | let dispatchCommand state command = 99 | let none unit = None 100 | match command with 101 | | Exit -> none(exit 0) 102 | | Help -> none(printHelp()) 103 | | ClearConsole -> none (try System.Console.Clear(); printHeader() with ex -> ()) 104 | | ReadInput input -> Some(runSimplification false state input) 105 | | ReadFile filename -> Some (runFile state filename) 106 | 107 | let userMain state input = 108 | try 109 | let command = Command.parse input 110 | match dispatchCommand state command with 111 | | Some newState -> newState 112 | | None -> state 113 | with ex -> 114 | writeLineErrorAnsi ex.Message 115 | state 116 | 117 | [] 118 | let main argv = 119 | Console.Title <- "MBA Simplifier - Interactive" 120 | printHeader() 121 | userInput |> Seq.fold userMain initialState |> ignore 122 | 0 123 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier/Engine.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | module MBASimplifier.Engine 26 | 27 | open FParsec 28 | open MBASimplifier.Ast 29 | 30 | let private ws = spaces 31 | let private ws1 = spaces1 32 | let private str_ws1 s = skipString s >>? ws1 33 | let private ws_str_ws s = ws >>. skipString s .>> ws 34 | let private betweenBrackets p = between (ws_str_ws "(") (ws_str_ws ")") p 35 | 36 | let private pname = regexL "[a-zA-Z_][a-zA-Z_0-9]*" "name" |>> (fun name -> { Key = name }) 37 | let pvariable = pname 38 | let pexpr, private pexprRef = createParserForwardedToRef () 39 | let pbracketedExpr = betweenBrackets pexpr "bracketed expression" 40 | let pterm = (pint64 |>> Constant "constant") <|> (pvariable |>> Variable "variable") 41 | 42 | let pfunction = 43 | let functionArguments = (ws1 >>? pterm) <|> pbracketedExpr 44 | attempt (pname .>>. functionArguments) |>> FunctionCall "function call" 45 | let passignment = 46 | let binding = pipe2 (pvariable .>> ws_str_ws "=") pexpr (fun name expr -> (name, expr)) 47 | let manyBindings = sepBy1 binding (ws_str_ws ",") 48 | str_ws1 "let" >>. manyBindings "variable binding" 49 | let pnegationExpr = 50 | let negSymbols = (many1Chars (pchar '-')) <|> (many1Chars (pchar '~')) <|> (many1Chars (pchar '!')) 51 | let expression = (spaces >>? pterm) <|> (spaces >>? pbracketedExpr) 52 | let rec build_expr s e = match (s) with 53 | | '-'::[] -> Negative e 54 | | '~'::[] -> BitwiseNegation e 55 | | '!'::[] -> LogicNegation e 56 | | '-'::q -> build_expr q (Negative e) 57 | | '~'::q -> build_expr q (BitwiseNegation e) 58 | | '!'::q -> build_expr q (LogicNegation e) 59 | | [] -> failwith "pnegationExpr() - Empty buffer" 60 | | c::_ -> failwith (sprintf "pnegationExpr() - Unexcepted negation character %c" c) 61 | pipe2 negSymbols expression (fun s e -> build_expr (s |> Seq.toList) e) "negate expression" 62 | 63 | // https://en.cppreference.com/w/c/language/operator_precedence 64 | do pexprRef := 65 | let opp = new OperatorPrecedenceParser () 66 | let expr = opp.ExpressionParser 67 | opp.TermParser <- (pfunction <|> pterm <|> pnegationExpr <|> pbracketedExpr) .>> ws 68 | 69 | opp.AddOperator(InfixOperator("*", ws, 3, Associativity.Left, fun x y -> Multiply (x, y))) 70 | opp.AddOperator(InfixOperator("/", ws, 3, Associativity.Left, fun x y -> Divide (x, y))) 71 | opp.AddOperator(InfixOperator("%", ws, 3, Associativity.Left, fun x y -> Modulo (x, y))) 72 | 73 | opp.AddOperator(InfixOperator("-", ws, 4, Associativity.Left, fun x y -> Subtract (x, y))) 74 | opp.AddOperator(InfixOperator("+", ws, 4, Associativity.Left, fun x y -> Add (x, y))) 75 | 76 | opp.AddOperator(InfixOperator(">>", ws, 5, Associativity.Left, fun x y -> BitwiseRightShift (x, y))) 77 | opp.AddOperator(InfixOperator("<<", ws, 5, Associativity.Left, fun x y -> BitwiseLeftShift (x, y))) 78 | 79 | opp.AddOperator(InfixOperator(">", ws, 6, Associativity.Left, fun x y -> GreaterThan (x, y))) 80 | opp.AddOperator(InfixOperator("<", ws, 6, Associativity.Left, fun x y -> LessThan (x, y))) 81 | opp.AddOperator(InfixOperator(">=", ws, 6, Associativity.Left, fun x y -> GreaterOrEqual (x, y))) 82 | opp.AddOperator(InfixOperator("<=", ws, 6, Associativity.Left, fun x y -> LessOrEqual (x, y))) 83 | 84 | opp.AddOperator(InfixOperator("==", ws, 7, Associativity.Left, fun x y -> Equal (x, y))) 85 | opp.AddOperator(InfixOperator("!=", ws, 7, Associativity.Left, fun x y -> NotEqual (x, y))) 86 | 87 | opp.AddOperator(InfixOperator("&", ws, 8, Associativity.Left, fun x y -> BitwiseAnd (x, y))) 88 | opp.AddOperator(InfixOperator("^", ws, 9, Associativity.Left, fun x y -> BitwiseXor (x, y))) 89 | opp.AddOperator(InfixOperator("|", ws, 10, Associativity.Left, fun x y -> BitwiseOr (x, y))) 90 | opp.AddOperator(InfixOperator("&&", ws, 11, Associativity.Left, fun x y -> LogicAnd (x, y))) 91 | opp.AddOperator(InfixOperator("||", ws, 12, Associativity.Left, fun x y -> LogicOr (x, y))) 92 | 93 | expr 94 | 95 | let pcommand_eof = ((passignment |>> Assignment) <|> (pexpr |>> Single)) .>> eof -------------------------------------------------------------------------------- /samples/encode_arithmetic/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # generate all base patterns 4 | if [ "$#" -ne 1 ]; then 5 | echo "Usage: ./generate.sh [tigress_runs]" 6 | exit 2 7 | fi 8 | 9 | rm -r out 10 | mkdir out 11 | mkdir out/sorted 12 | chmod u+x out 13 | 14 | for ((i=1; i<=$1; i++)); do 15 | ./patterns.sh > /dev/null 2>&1; 16 | echo "[+] Tigress output generated [n=$i]!" 17 | done 18 | 19 | for entry in "out"/*.mba 20 | do 21 | [ -e "$entry" ] || continue 22 | # get all unique expressions 23 | sort -u $entry > "out/sorted/$(basename "$entry" .mba)" 24 | done 25 | 26 | tigress --Seed=0 --Environment=x86_64:Linux:Gcc:9.3 \ 27 | --Transform=EncodeArithmetic \ 28 | --Functions=target_0,target_1,target_2,target_3,target_4,target_5,target_6,target_7,target_8,target_9,target_10,target_11,target_12,target_13,target_14,target_15,target_16,target_17,target_18,target_19,target_20,target_21,target_22,target_23,target_24,target_25,target_26,target_27,target_28,target_29,target_30,target_31,target_32,target_33,target_34,target_35,target_36,target_37,target_38,target_39,target_40,target_41,target_42,target_43,target_44,target_45,target_46,target_47,target_48,target_49,target_50,target_51,target_52,target_53,target_54,target_55,target_56,target_57,target_58,target_59,target_60,target_61,target_62,target_63,target_64,target_65,target_66,target_67,target_68,target_69,target_70,target_71,target_72,target_73,target_74,target_75,target_76,target_77,target_78,target_79,target_80,target_81,target_82,target_83,target_84,target_85,target_86,target_87,target_88,target_89,target_90,target_91,target_92,target_93,target_94,target_95,target_96,target_97,target_98,target_99,target_100,target_101,target_102,target_103,target_104,target_105,target_106,target_107,target_108,target_109,target_110,target_111,target_112,target_113,target_114,target_115,target_116,target_117,target_118,target_119,target_120,target_121,target_122,target_123,target_124,target_125,target_126,target_127,target_128,target_129,target_130,target_131,target_132,target_133,target_134,target_135,target_136,target_137,target_138,target_139,target_140,target_141,target_142,target_143,target_144,target_145,target_146,target_147,target_148,target_149,target_150,target_151,target_152,target_153,target_154,target_155,target_156,target_157,target_158,target_159,target_160,target_161,target_162,target_163,target_164,target_165,target_166,target_167,target_168,target_169,target_170,target_171,target_172,target_173,target_174,target_175,target_176,target_177,target_178,target_179,target_180,target_181,target_182,target_183,target_184,target_185,target_186,target_187,target_188,target_189,target_190,target_191,target_192,target_193,target_194,target_195,target_196,target_197,target_198,target_199,target_200,target_201,target_202,target_203,target_204,target_205,target_206,target_207,target_208,target_209,target_210,target_211,target_212,target_213,target_214,target_215,target_216,target_217,target_218,target_219,target_220,target_221,target_222,target_223,target_224,target_225,target_226,target_227,target_228,target_229,target_230,target_231,target_232,target_233,target_234,target_235,target_236,target_237,target_238,target_239,target_240,target_241,target_242,target_243,target_244,target_245,target_246,target_247,target_248,target_249,target_250,target_251,target_252,target_253,target_254,target_255,target_256,target_257,target_258,target_259,target_260,target_261,target_262,target_263,target_264,target_265,target_266,target_267,target_268,target_269,target_270,target_271,target_272,target_273,target_274,target_275,target_276,target_277,target_278,target_279,target_280,target_281,target_282,target_283,target_284,target_285,target_286,target_287,target_288,target_289,target_290,target_291,target_292,target_293,target_294,target_295,target_296,target_297,target_298,target_299,target_300,target_301,target_302,target_303,target_304,target_305,target_306,target_307,target_308,target_309,target_310,target_311,target_312,target_313,target_314,target_315,target_316,target_317,target_318,target_319,target_320,target_321,target_322,target_323,target_324,target_325,target_326,target_327,target_328,target_329,target_330,target_331,target_332,target_333,target_334,target_335,target_336,target_337,target_338,target_339,target_340,target_341,target_342,target_343,target_344,target_345,target_346,target_347,target_348,target_349,target_350,target_351,target_352,target_353,target_354,target_355,target_356,target_357,target_358,target_359,target_360,target_361,target_362,target_363,target_364,target_365,target_366,target_367,target_368,target_369,target_370,target_371,target_372,target_373,target_374,target_375,target_376,target_377,target_378,target_379,target_380,target_381,target_382,target_383,target_384,target_385,target_386,target_387,target_388,target_389,target_390,target_391,target_392,target_393,target_394,target_395,target_396,target_397,target_398,target_399,target_400,target_401,target_402,target_403,target_404,target_405,target_406,target_407,target_408,target_409,target_410,target_411,target_412,target_413,target_414,target_415,target_416,target_417,target_418,target_419,target_420,target_421,target_422,target_423,target_424,target_425,target_426,target_427,target_428,target_429,target_430,target_431,target_432,target_433,target_434,target_435,target_436,target_437,target_438,target_439,target_440,target_441,target_442,target_443,target_444,target_445,target_446,target_447,target_448,target_449,target_450,target_451,target_452,target_453,target_454,target_455,target_456,target_457,target_458,target_459,target_460,target_461,target_462,target_463,target_464,target_465,target_466,target_467,target_468,target_469,target_470,target_471,target_472,target_473,target_474,target_475,target_476,target_477,target_478,target_479,target_480,target_481,target_482,target_483,target_484,target_485,target_486,target_487,target_488,target_489,target_490,target_491,target_492,target_493,target_494,target_495,target_496,target_497,target_498,target_499 \ 29 | --out=out/dataset1.c dataset1.c 30 | 31 | for i in {0..499}; 32 | do 33 | function_name="int target_$i(int a , int b , int c , int d , int e )" 34 | (grep -A 5 "^$function_name $" out/dataset1.c | tail -1 | tr -d ";" | sed 's/return*//' | sed 's/^ *//g') >> "out/dataset1.mba" 35 | done 36 | 37 | rm a.out 38 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier/Ast.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | module MBASimplifier.Ast 26 | 27 | [] 28 | type Name = { Key : string } 29 | with 30 | member private t.StructuredFormatDisplay = t.Key 31 | and Expr = 32 | | Constant of int64 33 | | Variable of Name 34 | 35 | | FunctionCall of Name * Expr 36 | 37 | // Arithmetic 38 | | Add of Expr * Expr 39 | | Multiply of Expr * Expr 40 | | Subtract of Expr * Expr 41 | | Divide of Expr * Expr 42 | | Modulo of Expr * Expr 43 | | Negative of Expr 44 | 45 | // Bitwise 46 | | BitwiseLeftShift of Expr * Expr 47 | | BitwiseRightShift of Expr * Expr 48 | | BitwiseAnd of Expr * Expr 49 | | BitwiseXor of Expr * Expr 50 | | BitwiseOr of Expr * Expr 51 | | BitwiseNegation of Expr 52 | 53 | // Relational 54 | | Equal of Expr * Expr 55 | | NotEqual of Expr * Expr 56 | | GreaterOrEqual of Expr * Expr 57 | | LessOrEqual of Expr * Expr 58 | | GreaterThan of Expr * Expr 59 | | LessThan of Expr * Expr 60 | 61 | // Logic 62 | | LogicAnd of Expr * Expr 63 | | LogicOr of Expr * Expr 64 | | LogicNegation of Expr 65 | 66 | member this.humanize() = 67 | let format_binary_op (e1: Expr) (e2: Expr) op = (e1.stringize(e1.Priority < this.Priority)) + " " + op + " " + (e2.stringize(e1.Priority < this.Priority)) 68 | let format_unary_op (e: Expr) op = op + (e.stringize(e.Priority < this.Priority)) 69 | match this with 70 | | Constant c -> sprintf "%d" c 71 | | Variable e -> e.Key 72 | | FunctionCall(n, e) -> sprintf "%s %s" n.Key (e.stringize(e.Priority < this.Priority)) 73 | 74 | | LogicOr(e1, e2) -> format_binary_op e1 e2 "||" 75 | | LogicAnd(e1, e2) -> format_binary_op e1 e2 "&&" 76 | | LogicNegation(e) -> format_unary_op e "!" 77 | 78 | | Subtract(e1, e2) -> format_binary_op e1 e2 "-" 79 | | Add(e1, e2) -> format_binary_op e1 e2 "+" 80 | | Multiply(e1, e2) -> format_binary_op e1 e2 "*" 81 | | Divide(e1, e2) -> format_binary_op e1 e2 "/" 82 | | Modulo(e1, e2) -> format_binary_op e1 e2 "%" 83 | | Negative(e) -> format_unary_op e "-" 84 | 85 | | BitwiseAnd(e1, e2) -> format_binary_op e1 e2 "&" 86 | | BitwiseOr(e1, e2) -> format_binary_op e1 e2 "|" 87 | | BitwiseXor(e1, e2) -> format_binary_op e1 e2 "^" 88 | | BitwiseLeftShift(e1, e2) -> format_binary_op e1 e2 "<<" 89 | | BitwiseRightShift(e1, e2) -> format_binary_op e1 e2 ">>" 90 | | Equal(e1, e2) -> format_binary_op e1 e2 "==" 91 | | NotEqual(e1, e2) -> format_binary_op e1 e2 "!=" 92 | | BitwiseNegation(e) -> format_unary_op e "~" 93 | 94 | | GreaterOrEqual(e1, e2) -> format_binary_op e1 e2 ">=" 95 | | GreaterThan(e1, e2) -> format_binary_op e1 e2 ">" 96 | | LessOrEqual(e1, e2) -> format_binary_op e1 e2 "<=" 97 | | LessThan(e1, e2) -> format_binary_op e1 e2 "<" 98 | 99 | member this.stringize(parenthesesRequired) = 100 | let exprStr = (this.humanize()) in 101 | match this with 102 | | Constant _ | Variable _ -> exprStr 103 | | _ -> if parenthesesRequired then sprintf "(%s)" exprStr else exprStr 104 | 105 | member this.Priority with get() : int = 106 | match this with 107 | | Constant _ | Variable _ -> 1 108 | | LogicOr(e1, e2) -> 2 109 | | LogicAnd(e1, e2) -> 3 110 | | BitwiseOr(e1, e2) -> 4 111 | | BitwiseXor(e1, e2) -> 5 112 | | BitwiseAnd(e1, e2) -> 6 113 | | NotEqual(e1, e2) -> 7 114 | | Equal(e1, e2) -> 7 115 | | LessOrEqual(_, _) | LessThan(_, _) | GreaterOrEqual(_, _) | GreaterThan(_, _) -> 8 116 | | BitwiseLeftShift(_, _)| BitwiseRightShift(_, _) -> 9 117 | | Add(_, _) | Subtract(_, _) -> 10 118 | | Modulo(_, _)| Divide(_, _) | Multiply(_, _) -> 11 119 | | BitwiseNegation(_) | LogicNegation(_) | Negative(_) -> 12 120 | | FunctionCall(n, e) -> 13 121 | 122 | (* Helpers *) 123 | static member (~-) (e1) = Negative(e1) 124 | static member (+) (e1,e2) = Add(e1,e2) 125 | static member (*) (e1,e2) = Multiply(e1,e2) 126 | static member (/) (e1,e2) = Divide(e1,e2) 127 | static member (-) (e1,e2) = Subtract(e1,e2) 128 | static member (%) (e1,e2) = Modulo(e1,e2) 129 | static member (&&&) (e1,e2) = BitwiseAnd(e1,e2) 130 | static member (|||) (e1,e2) = BitwiseOr(e1,e2) 131 | static member (^^^) (e1,e2) = BitwiseXor(e1,e2) 132 | static member (~~~) (e1) = BitwiseNegation(e1) 133 | static member (===) (e1, e2) = Equal(e1, e2) 134 | static member (<=>) (e1, e2) = NotEqual(e1, e2) 135 | 136 | member this.Complexity with get() : int = 137 | let rec count expr = match expr with 138 | | Constant _ -> 1 139 | | Variable _ -> 2 140 | | Negative e | BitwiseNegation e | LogicNegation e -> 4 + count e 141 | | Add(e1, e2) | Multiply(e1, e2) | Subtract (e1, e2) | Divide(e1, e2) | Modulo(e1, e2) 142 | | BitwiseLeftShift(e1, e2) | BitwiseRightShift(e1, e2) | BitwiseAnd(e1, e2) | BitwiseXor(e1, e2) 143 | | BitwiseOr(e1, e2) | Equal(e1, e2) | NotEqual(e1, e2) | GreaterOrEqual(e1, e2) | LessOrEqual(e1, e2) 144 | | GreaterThan(e1, e2) | LessThan(e1, e2) | LogicAnd(e1, e2) | LogicAnd(e1, e2) | LogicOr(e1, e2) -> 8 + count e1 + count e2 145 | | FunctionCall(_,e) -> 20 + count e 146 | count this 147 | and Statement = 148 | | Single of Expr 149 | | Assignment of VariableAssignment list 150 | 151 | and VariableAssignment = Name * Expr -------------------------------------------------------------------------------- /simplifier/MBASimplifier/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Nuget personal access tokens and Credentials 210 | # nuget.config 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio LightSwitch build output 301 | **/*.HTMLClient/GeneratedArtifacts 302 | **/*.DesktopClient/GeneratedArtifacts 303 | **/*.DesktopClient/ModelManifest.xml 304 | **/*.Server/GeneratedArtifacts 305 | **/*.Server/ModelManifest.xml 306 | _Pvt_Extensions 307 | 308 | # Paket dependency manager 309 | .paket/paket.exe 310 | paket-files/ 311 | 312 | # FAKE - F# Make 313 | .fake/ 314 | 315 | # CodeRush personal settings 316 | .cr/personal 317 | 318 | # Python Tools for Visual Studio (PTVS) 319 | __pycache__/ 320 | *.pyc 321 | 322 | # Cake - Uncomment if you are using it 323 | # tools/** 324 | # !tools/packages.config 325 | 326 | # Tabs Studio 327 | *.tss 328 | 329 | # Telerik's JustMock configuration file 330 | *.jmconfig 331 | 332 | # BizTalk build output 333 | *.btp.cs 334 | *.btm.cs 335 | *.odx.cs 336 | *.xsd.cs 337 | 338 | # OpenCover UI analysis results 339 | OpenCover/ 340 | 341 | # Azure Stream Analytics local run output 342 | ASALocalRun/ 343 | 344 | # MSBuild Binary and Structured Log 345 | *.binlog 346 | 347 | # NVidia Nsight GPU debugger configuration file 348 | *.nvuser 349 | 350 | # MFractors (Xamarin productivity tool) working folder 351 | .mfractor/ 352 | 353 | # Local History for Visual Studio 354 | .localhistory/ 355 | 356 | # BeatPulse healthcheck temp database 357 | healthchecksdb 358 | 359 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 360 | MigrationBackup/ 361 | 362 | # Ionide (cross platform F# VS Code tools) working folder 363 | .ionide/ 364 | 365 | # Fody - auto-generated XML schema 366 | FodyWeavers.xsd 367 | 368 | # VS Code files for those working on multiple tools 369 | .vscode/* 370 | !.vscode/settings.json 371 | !.vscode/tasks.json 372 | !.vscode/launch.json 373 | !.vscode/extensions.json 374 | *.code-workspace 375 | 376 | # Local History for Visual Studio Code 377 | .history/ 378 | 379 | # Windows Installer files from build outputs 380 | *.cab 381 | *.msi 382 | *.msix 383 | *.msm 384 | *.msp 385 | 386 | # JetBrains Rider 387 | .idea/ 388 | *.sln.iml 389 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier.Tests/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Nuget personal access tokens and Credentials 210 | # nuget.config 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio LightSwitch build output 301 | **/*.HTMLClient/GeneratedArtifacts 302 | **/*.DesktopClient/GeneratedArtifacts 303 | **/*.DesktopClient/ModelManifest.xml 304 | **/*.Server/GeneratedArtifacts 305 | **/*.Server/ModelManifest.xml 306 | _Pvt_Extensions 307 | 308 | # Paket dependency manager 309 | .paket/paket.exe 310 | paket-files/ 311 | 312 | # FAKE - F# Make 313 | .fake/ 314 | 315 | # CodeRush personal settings 316 | .cr/personal 317 | 318 | # Python Tools for Visual Studio (PTVS) 319 | __pycache__/ 320 | *.pyc 321 | 322 | # Cake - Uncomment if you are using it 323 | # tools/** 324 | # !tools/packages.config 325 | 326 | # Tabs Studio 327 | *.tss 328 | 329 | # Telerik's JustMock configuration file 330 | *.jmconfig 331 | 332 | # BizTalk build output 333 | *.btp.cs 334 | *.btm.cs 335 | *.odx.cs 336 | *.xsd.cs 337 | 338 | # OpenCover UI analysis results 339 | OpenCover/ 340 | 341 | # Azure Stream Analytics local run output 342 | ASALocalRun/ 343 | 344 | # MSBuild Binary and Structured Log 345 | *.binlog 346 | 347 | # NVidia Nsight GPU debugger configuration file 348 | *.nvuser 349 | 350 | # MFractors (Xamarin productivity tool) working folder 351 | .mfractor/ 352 | 353 | # Local History for Visual Studio 354 | .localhistory/ 355 | 356 | # BeatPulse healthcheck temp database 357 | healthchecksdb 358 | 359 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 360 | MigrationBackup/ 361 | 362 | # Ionide (cross platform F# VS Code tools) working folder 363 | .ionide/ 364 | 365 | # Fody - auto-generated XML schema 366 | FodyWeavers.xsd 367 | 368 | # VS Code files for those working on multiple tools 369 | .vscode/* 370 | !.vscode/settings.json 371 | !.vscode/tasks.json 372 | !.vscode/launch.json 373 | !.vscode/extensions.json 374 | *.code-workspace 375 | 376 | # Local History for Visual Studio Code 377 | .history/ 378 | 379 | # Windows Installer files from build outputs 380 | *.cab 381 | *.msi 382 | *.msix 383 | *.msm 384 | *.msp 385 | 386 | # JetBrains Rider 387 | .idea/ 388 | *.sln.iml 389 | -------------------------------------------------------------------------------- /report/projectreport.cls: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | \ProvidesClass{projectreport}[Project Report] 3 | 4 | % default = false 5 | \newif\if@altquants 6 | \newif\if@localnums \@localnumstrue 7 | \newif\if@narrowmargins \@narrowmarginstrue 8 | \newif\if@officialeuro 9 | 10 | \DeclareOption{altquants}{\@altquantstrue} % while https://github.com/alerque/libertinus/issues/346 remains open 11 | \DeclareOption{globalnums}{\@localnumsfalse} 12 | \DeclareOption{officialeuro}{\@officialeurotrue} 13 | \DeclareOption{widemargins}{\@narrowmarginsfalse} 14 | 15 | \DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}} 16 | \ProcessOptions\relax 17 | 18 | \LoadClass[12pt, a4paper, twocolumn]{article} 19 | 20 | % extrasp=0pt disables extra space after sentence-ending period 21 | % mono disables space stretching and shrinking 22 | % scale=.94 scales size to roughly match Libertinus's x-height 23 | % varqu replaces slanted by upright quotes (for code) 24 | \RequirePackage[extrasp=0pt, mono, scale=.94, varqu]{inconsolata} 25 | 26 | % mono=false disables Libertinus Mono (which would replace Inconsolata) 27 | \RequirePackage[mono=false]{libertinus-type1} 28 | 29 | % lcgreekalpha enables e.g. \mathbf for lower case Greek letters 30 | \RequirePackage[lcgreekalpha]{libertinust1math} 31 | 32 | % load fonts before fontenc: https://tex.stackexchange.com/a/2869 33 | \RequirePackage[T1]{fontenc} 34 | \RequirePackage[utf8]{inputenc} 35 | 36 | % load early: https://tex.stackexchange.com/a/151864 37 | \RequirePackage[english]{babel} 38 | 39 | % Typesets the title etc. in Libertinus Display. These declarations were copied 40 | % from ltsect.dtx and modified. Since hyperref also redefines them (to make the 41 | % pdfusetitle option work, among others), we do it before hyperref is loaded. 42 | % TODO: could be applied to sections as well 43 | \DeclareRobustCommand\title[1]{\gdef\@title{\LibertinusDisplay#1}} 44 | \DeclareRobustCommand*\author[1]{\gdef\@author{\LibertinusDisplay#1}} 45 | \DeclareRobustCommand*\date[1]{\gdef\@date{\LibertinusDisplay#1}} 46 | \date\today % reinitializes \date with default value, so correct font is used 47 | 48 | \RequirePackage{aliascnt} 49 | \RequirePackage{amsmath, amssymb, amsthm} 50 | \RequirePackage{mathtools} 51 | \RequirePackage{microtype} 52 | \RequirePackage{mleftright} 53 | \RequirePackage{parskip} 54 | \RequirePackage{scalerel} 55 | 56 | \if@officialeuro 57 | \RequirePackage[left]{eurosym} 58 | \let\@euro\euro 59 | \def\euro{\scalerel*{$\@euro$}{C}} 60 | \DeclareUnicodeCharacter{20AC}{\euro} 61 | \fi 62 | 63 | % load last 64 | \RequirePackage[pdfusetitle]{hyperref} % 5.1 of http://mirrors.ctan.org/macros/latex/contrib/hyperref/doc/paper.pdf 65 | \if@narrowmargins 66 | \RequirePackage[margin=1in]{geometry} % after hyperref, per manual 67 | \fi 68 | 69 | \addto\extrasamerican{ 70 | \let\subsectionautorefname\sectionautorefname 71 | \let\subsubsectionautorefname\sectionautorefname 72 | \let\paragraphautorefname\sectionautorefname 73 | \let\subparagraphautorefname\sectionautorefname 74 | } 75 | 76 | \hypersetup{pdfcreator={LaTeX Project}} 77 | 78 | % \left and \right introduce extra space around the delimiters. To remove this, 79 | % we need to insert opening (\mathopen) and closing (\mathclose) atoms. The 80 | % package mleftright defines commands that do this automatically (\mleft and 81 | % \mright). The command below redefines the normal \left and \right as well. 82 | % https://tex.stackexchange.com/a/2610 83 | \mleftright 84 | 85 | % removes \, from all text when used for pdf fields (e.g. author) 86 | \pdfstringdefDisableCommands{\def\,{}} 87 | 88 | % Without this patch, there is too much vertical spacing above and below the 89 | % proof environment. I've found no other environments that suffer from this, 90 | % yet. This solution (copying & modifying the definition in amsthm.sty) was 91 | % chosen because it requires no additional packages. I think the combination of 92 | % parskip and the reassignment of \topsep in the original \proof is the cause. 93 | % 192722, 339440, 522809 on https://tex.stackexchange.com/q/ 94 | \renewenvironment{proof}[1][\proofname]{% 95 | \par\pushQED{\qed}\normalfont% removed: \topsep6\p@\@plus6\p@\relax 96 | \trivlist\item[\hskip\labelsep\itshape#1\@addpunct{.}]\ignorespaces% 97 | }{% 98 | \popQED\endtrivlist\@endpefalse% 99 | } 100 | 101 | \renewcommand*{\P}{\mathbb P} % for primes or probability, overwrites shorthand for \textparagraph 102 | \newcommand*{\N}{\mathbb N} 103 | \newcommand*{\Z}{\mathbb Z} 104 | \newcommand*{\Q}{\mathbb Q} 105 | \newcommand*{\R}{\mathbb R} 106 | \newcommand*{\C}{\mathbb C} 107 | 108 | \if@localnums 109 | \counterwithin{equation}{section} % resets equation counter for each section 110 | \fi 111 | 112 | \newtheoremstyle{pr-plain}{}{}{\itshape}{}{\bfseries}{ }{0pt}{} 113 | \newtheoremstyle{pr-definition}{}{}{}{}{\bfseries}{ }{0pt}{} 114 | \newtheoremstyle{pr-remark}{}{}{}{}{\itshape}{ }{0pt}{} % unused 115 | % Algorithms 116 | \RequirePackage{algorithm} 117 | \RequirePackage[noend]{algpseudocode} 118 | \algrenewcommand\Return{\State \algorithmicreturn{} } 119 | \algnewcommand\algorithmicforeach{\textbf{for each}} 120 | \algdef{S}[FOR]{ForEach}[1]{\algorithmicforeach\ #1\ \algorithmicdo} 121 | \algnewcommand{\LineComment}[1]{\State \(\triangleright\) #1} 122 | 123 | % The string used by \autoref (e.g. 'Lemma') depends on the counter of the 124 | % command. Since all theorem-type commands use the equation counter, you'd get 125 | % the wrong string (i.e. 'Equation'). We fool hyperref by defining an alias 126 | % counter, and we define the right string for it (e.g. \lemmaautorefname). 127 | % https://tex.stackexchange.com/a/113540 128 | % TODO: add \expandafter to \MakeUppercase? 129 | \newcommand*{\NewTheorem}[1]{% 130 | \expandafter\providecommand\csname#1autorefname\endcsname{\MakeUppercase#1}% 131 | \newaliascnt{#1}{equation}% 132 | \newtheorem{#1}[#1]{\MakeUppercase#1}% 133 | \aliascntresetthe{#1}% 1.2 of http://mirrors.ctan.org/macros/latex/contrib/oberdiek/aliascnt.pdf 134 | } 135 | 136 | \theoremstyle{pr-plain} 137 | \NewTheorem{lemma} 138 | \NewTheorem{theorem} 139 | 140 | \theoremstyle{pr-definition} 141 | \NewTheorem{definition} 142 | \NewTheorem{example} 143 | 144 | \theoremstyle{pr-break} 145 | 146 | % libertinust1math.sty 147 | \DeclareMathSymbol{*}{\mathbin}{symbols}{"0C} % defines * as \cdot (use \ast for asterisk symbol) 148 | \DeclareMathSymbol{\epsilon}{\libus@lcgc}{letters}{"22} % swaps definition of \epsilon .. 149 | \DeclareMathSymbol{\varepsilon}{\libus@lcgc}{operators}{"0F} % .. and \varepsilon 150 | 151 | % https://tex.stackexchange.com/a/254626 and fonttable package 152 | \DeclareFontEncoding{LS1}{}{} 153 | \DeclareFontSubstitution{LS1}{stix2}{m}{n} 154 | 155 | \DeclareSymbolFont{stix2-symbols3}{LS1}{stix2bb}{m}{n} 156 | \DeclareMathSymbol{\@bbone}{\mathord}{stix2-symbols3}{"31} 157 | \def\bbone{\scalerel*{\@bbone}{1}} 158 | 159 | % after amssymb is loaded, since it defines \nexists 160 | \if@altquants 161 | \DeclareSymbolFont{stix2-operators}{LS1}{stix2}{m}{n} 162 | \DeclareMathSymbol{\forall} {\mathord}{stix2-operators}{"C5} 163 | \DeclareMathSymbol{\exists} {\mathord}{stix2-operators}{"C7} 164 | \DeclareMathSymbol{\nexists}{\mathord}{stix2-operators}{"C8} 165 | \else 166 | \DeclareMathSymbol{\nexists}{\mathord}{operators}{"C8} 167 | \fi 168 | 169 | % fixes inconsistencies with libertinust1math (mathtools's conventions are used) 170 | \renewcommand*{\vcentcolon}{\!:\!} % dirty fix: both vertical and horizontal spacing is off 171 | \DeclareMathSymbol{\coloneqq}{\mathrel}{symbols}{"65} % := 172 | \DeclareMathSymbol{\eqqcolon}{\mathrel}{symbols}{"66} % =: 173 | \renewcommand*{\coloneq}{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} % :- (missing in Libertinus?) 174 | \DeclareMathSymbol{\eqcolon}{\mathrel}{operators}{"EA} % -: 175 | 176 | % 3.6 of http://mirrors.ctan.org/macros/latex/contrib/mathtools/mathtools.pdf 177 | % \mid is of type \mathrel, so \; is used. In (script)script style \, is used. 178 | % TODO: \delimsize vs \middle? add \allowbreak? \mathopen, \mathclose correct? 179 | \newcommand*{\@renewmid}{\renewcommand*{\mid}{% 180 | \mathclose{}% 181 | \mathchoice{\;}{\;}{\,}{\,}% 182 | \delimsize\vert% 183 | \mathchoice{\;}{\;}{\,}{\,}% 184 | \mathopen{}% 185 | }} 186 | 187 | % https://tex.stackexchange.com/a/43009 188 | \DeclarePairedDelimiter{\abs}{\lvert}{\rvert} 189 | \DeclarePairedDelimiter{\ceil}{\lceil}{\rceil} 190 | \DeclarePairedDelimiter{\floor}{\lfloor}{\rfloor} 191 | \DeclarePairedDelimiter{\inner}{\langle}{\rangle} % bad name 192 | \DeclarePairedDelimiter{\norm}{\lVert}{\rVert} 193 | \DeclarePairedDelimiterX{\set}[1]{\{}{\}}{\@renewmid#1} 194 | \DeclarePairedDelimiterX{\Set}[1]{\{}{\}}{\@renewmid\nonscript\,#1\nonscript\,} % \nonscript suppresses \, in (script)script style 195 | 196 | \let\@abs\abs 197 | \let\@ceil\ceil 198 | \let\@floor\floor 199 | \let\@inner\inner 200 | \let\@norm\norm 201 | \let\@set\set 202 | \let\@Set\Set 203 | 204 | \def\abs{\@ifstar{\@abs}{\@abs*}} 205 | \def\ceil{\@ifstar{\@ceil}{\@ceil*}} 206 | \def\floor{\@ifstar{\@floor}{\@floor*}} 207 | \def\inner{\@ifstar{\@inner}{\@inner*}} 208 | \def\norm{\@ifstar{\@norm}{\@norm*}} 209 | \def\set{\@ifstar{\@set}{\@set*}} 210 | \def\Set{\@ifstar{\@Set}{\@Set*}} 211 | -------------------------------------------------------------------------------- /report/refs.bib: -------------------------------------------------------------------------------- 1 | %% LaTeX2e file `refs.bib' 2 | %% generated by the `filecontents' environment 3 | %% from source `report' on 2021/12/01. 4 | %% 5 | 6 | @book{HackersDelight, 7 | author = {Warren, Henry S.}, 8 | title = {Hacker's Delight}, 9 | year = {2012}, 10 | isbn = {0321842685}, 11 | publisher = {Addison-Wesley Professional}, 12 | edition = {2nd}, 13 | abstract = {In Hackers Delight, Second Edition, Hank Warren once again compiles an irresistible collection of programming hacks: timesaving techniques, algorithms, and tricks that help programmers build more elegant and efficient software, while also gaining deeper insights into their craft. Warrens hacks are eminently practical, but theyre also intrinsically interesting, and sometimes unexpected, much like the solution to a great puzzle. They are, in a word, a delight to any programmer who is excited by the opportunity to improve. Extensive additions in this edition include A new chapter on cyclic redundancy checking (CRC), including routines for the commonly used CRC-32 code A new chapter on error correcting codes (ECC), including routines for the Hamming code More coverage of integer division by constants, including methods using only shifts and adds Computing remainders without computing a quotient More coverage of population count and counting leading zeros Array population count New algorithms for compress and expand An LRU algorithm Floating-point to/from integer conversions Approximate floating-point reciprocal square root routine A gallery of graphs of discrete functions Now with exercises and answers} 14 | } 15 | 16 | @inproceedings{Guinet2016AryboMC, 17 | title={Arybo: Manipulation, Canonicalization and Identification of Mixed Boolean-Arithmetic Symbolic Expressions}, 18 | author={Adrien Guinet and Ninon Eyrolles and Marion Videau}, 19 | year={2016} 20 | } 21 | 22 | @inproceedings {syntia, 23 | author = {Tim Blazytko and Moritz Contag and Cornelius Aschermann and Thorsten Holz}, 24 | title = {Syntia: Synthesizing the Semantics of Obfuscated Code}, 25 | booktitle = {26th {USENIX} Security Symposium ({USENIX} Security 17)}, 26 | year = {2017}, 27 | isbn = {978-1-931971-40-9}, 28 | address = {Vancouver, BC}, 29 | pages = {643--659}, 30 | url = {https://www.usenix.org/conference/usenixsecurity17/technical-sessions/presentation/blazytko}, 31 | publisher = {{USENIX} Association}, 32 | month = aug, 33 | } 34 | 35 | @inproceedings{eyrolles, 36 | TITLE = {{Defeating MBA-based Obfuscation}}, 37 | AUTHOR = {Eyrolles, Ninon and Goubin, Louis and Videau, Marion}, 38 | URL = {https://hal.archives-ouvertes.fr/hal-01388109}, 39 | BOOKTITLE = {{2nd International Workshop on Software PROtection}}, 40 | ADDRESS = {Vienna, Austria}, 41 | EDITOR = {ACM}, 42 | YEAR = {2016}, 43 | MONTH = Oct, 44 | DOI = {10.1145/2995306.2995308}, 45 | KEYWORDS = {pattern matching ; expression simplification ; mixed boolean-arithmetic expressions ; reverse engineering ; Obfuscation}, 46 | PDF = {https://hal.archives-ouvertes.fr/hal-01388109/file/spro05.pdf}, 47 | HAL_ID = {hal-01388109}, 48 | HAL_VERSION = {v1}, 49 | } 50 | 51 | @inproceedings{decidability, 52 | author = {Richardson, Dan and Fitch, John}, 53 | title = {The Identity Problem for Elementary Functions and Constants}, 54 | year = {1994}, 55 | isbn = {0897916387}, 56 | publisher = {Association for Computing Machinery}, 57 | address = {New York, NY, USA}, 58 | url = {https://doi.org/10.1145/190347.190429}, 59 | doi = {10.1145/190347.190429}, 60 | abstract = {A solution for a version of the identify problem is proposed for a class of functions including the elementary functions. Given f(x), g(x), defined at some point β we decide whether or not f(x) = g(x) in some neighbourhood of β. This problem is first reduced to a problem about zero equivalence of elementary constants. Then a semi algorithm is given to solve the elementary constant problem. This semi algorithm is guaranteed to give the correct answer whenever it terminates, and it terminates unless the problem being considered contains a counterexample to Schanuel's conjecture.}, 61 | booktitle = {Proceedings of the International Symposium on Symbolic and Algebraic Computation}, 62 | pages = {285–290}, 63 | numpages = {6}, 64 | location = {Oxford, United Kingdom}, 65 | series = {ISSAC '94} 66 | } 67 | 68 | @book{termrewriting, 69 | author = {Baader, Franz and Nipkow, Tobias}, 70 | title = {Term Rewriting and All That}, 71 | year = {1998}, 72 | isbn = {0521455200}, 73 | publisher = {Cambridge University Press}, 74 | address = {USA} 75 | } 76 | 77 | @inproceedings{z3solver, 78 | author = {De Moura, Leonardo and Bj\o{}rner, Nikolaj}, 79 | title = {Z3: An Efficient SMT Solver}, 80 | year = {2008}, 81 | isbn = {3540787992}, 82 | publisher = {Springer-Verlag}, 83 | address = {Berlin, Heidelberg}, 84 | abstract = {Satisfiability Modulo Theories (SMT) problem is a decision problem for logical first order formulas with respect to combinations of background theories such as: arithmetic, bit-vectors, arrays, and uninterpreted functions. Z3 is a new and efficient SMT Solver freely available from Microsoft Research. It is used in various software verification and analysis applications.}, 85 | booktitle = {Proceedings of the Theory and Practice of Software, 14th International Conference on Tools and Algorithms for the Construction and Analysis of Systems}, 86 | pages = {337–340}, 87 | numpages = {4}, 88 | location = {Budapest, Hungary}, 89 | series = {TACAS'08/ETAPS'08} 90 | } 91 | 92 | @MISC{collberg, 93 | author = {Christian Collberg and Clark Thomborson and Douglas Low}, 94 | title = {A Taxonomy of Obfuscating Transformations}, 95 | year = {1997} 96 | } 97 | 98 | @book{knuth, 99 | author = {Knuth, Donald E.}, 100 | title = {The Art of Computer Programming, Volume 2 (3rd Ed.): Seminumerical Algorithms}, 101 | year = {1997}, 102 | isbn = {0201896842}, 103 | publisher = {Addison-Wesley Longman Publishing Co., Inc.}, 104 | address = {USA} 105 | } 106 | 107 | @inproceedings{abstract, 108 | author = {Cousot, Patrick and Cousot, Radhia}, 109 | title = {Abstract Interpretation: A Unified Lattice Model for Static Analysis of Programs by Construction or Approximation of Fixpoints}, 110 | year = {1977}, 111 | isbn = {9781450373500}, 112 | publisher = {Association for Computing Machinery}, 113 | address = {New York, NY, USA}, 114 | url = {https://doi.org/10.1145/512950.512973}, 115 | doi = {10.1145/512950.512973}, 116 | abstract = {A program denotes computations in some universe of objects. Abstract interpretation of programs consists in using that denotation to describe computations in another universe of abstract objects, so that the results of abstract execution give some information on the actual computations. An intuitive example (which we borrow from Sintzoff [72]) is the rule of signs. The text -1515 * 17 may be understood to denote computations on the abstract universe {(+), (-), (±)} where the semantics of arithmetic operators is defined by the rule of signs. The abstract execution -1515 * 17 → -(+) * (+) → (-) * (+) → (-), proves that -1515 * 17 is a negative number. Abstract interpretation is concerned by a particular underlying structure of the usual universe of computations (the sign, in our example). It gives a summary of some facets of the actual executions of a program. In general this summary is simple to obtain but inaccurate (e.g. -1515 + 17 → -(+) + (+) → (-) + (+) → (±)). Despite its fundamentally incomplete results abstract interpretation allows the programmer or the compiler to answer questions which do not need full knowledge of program executions or which tolerate an imprecise answer, (e.g. partial correctness proofs of programs ignoring the termination problems, type checking, program optimizations which are not carried in the absence of certainty about their feasibility, …).}, 117 | booktitle = {Proceedings of the 4th ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages}, 118 | pages = {238–252}, 119 | numpages = {15}, 120 | location = {Los Angeles, California}, 121 | series = {POPL '77} 122 | } 123 | 124 | @InProceedings{abstract_detection, 125 | author="Dalla Preda, Mila 126 | and Madou, Matias 127 | and De Bosschere, Koen 128 | and Giacobazzi, Roberto", 129 | editor="Johnson, Michael 130 | and Vene, Varmo", 131 | title="Opaque Predicates Detection by Abstract Interpretation", 132 | booktitle="Algebraic Methodology and Software Technology", 133 | year="2006", 134 | publisher="Springer Berlin Heidelberg", 135 | address="Berlin, Heidelberg", 136 | pages="81--95", 137 | abstract="Code obfuscation and software watermarking are well known techniques designed to prevent the illegal reuse of software. Code obfuscation prevents malicious reverse engineering, while software watermarking protects code from piracy. An interesting class of algorithms for code obfuscation and software watermarking relies on the insertion of opaque predicates. It turns out that attackers based on a dynamic or an hybrid static-dynamic approach are either not precise or time consuming in eliminating opaque predicates. We present an abstract interpretation-based methodology for removing opaque predicates from programs. Abstract interpretation provides the right framework for proving the correctness of our approach, together with a general methodology for designing efficient attackers for a relevant class of opaque predicates. Experimental evaluations show that abstract interpretation based attacks significantly reduce the time needed to eliminate opaque predicates.", 138 | isbn="978-3-540-35636-3" 139 | } 140 | 141 | @InProceedings{concolic, 142 | author={Bardin, Sébastien and David, Robin and Marion, Jean-Yves}, 143 | booktitle={2017 IEEE Symposium on Security and Privacy (SP)}, 144 | title={Backward-Bounded DSE: Targeting Infeasibility Questions on Obfuscated Codes}, 145 | year={2017}, 146 | volume={}, 147 | number={}, 148 | pages={633-651}, 149 | doi={10.1109/SP.2017.36}} 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier.Tests/SimplifierTests.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | namespace MBASimplifier.Tests 26 | 27 | open System 28 | open NUnit.Framework 29 | 30 | open MBASimplifier.Ast 31 | open MBASimplifier.Implementation 32 | 33 | [] 34 | type TestClass () = 35 | 36 | let mutable testState = 37 | let bitcount (n : int64) = 38 | let count2 = n - ((n >>> 1) &&& 0x5555555555555555L) 39 | let count4 = (count2 &&& 0x3333333333333333L) + ((count2 >>> 2) &&& 0x3333333333333333L) 40 | let count8 = (count4 + (count4 >>> 4)) &&& 0x0f0f0f0f0f0f0f0fL 41 | (count8 * 0x0101010101010101L) >>> 56 |> int 42 | { 43 | ExpressionsCache = Map.empty 44 | Exploration = Set.empty 45 | MemoryMap = Map.ofSeq( 46 | seq { 47 | yield "inc", Function (fun e -> e + Constant 1) 48 | yield "dec", Function (fun e -> e - Constant 1) 49 | yield "popcnt", Function (fun e -> match e with Constant c -> Constant(bitcount c) | _ -> failwith "popcnt(int) is excepted") 50 | } 51 | ); 52 | Debug = false 53 | } 54 | 55 | let assertSimplification line (exprs: Expr list) = 56 | let parseResult = (parseLine testState line) 57 | match parseResult with 58 | | ParseError msg -> Assert.True(false); 59 | | ParseSuccess statement -> 60 | let result = executeStatement testState statement 61 | match result with 62 | | SingleResult (state, expr') -> Assert.AreEqual(List.head exprs, expr') 63 | | AssignmentResult (state, exprs') -> Assert.AreEqual(exprs, exprs') 64 | 65 | [] 66 | member this.TestBitwiseOperatorsSimplification() = 67 | // AND Tests 68 | assertSimplification "(((~ a | b) + a) + 1)" [BitwiseAnd(Variable ({Key="a"}), Variable {Key="b"})] 69 | assertSimplification "((~ a | b) - ~ a)" [BitwiseAnd(Variable ({Key="a"}), Variable {Key="b"})] 70 | 71 | // XOR Tests 72 | assertSimplification "((a | b) - (a & b))" [BitwiseXor(Variable ({Key="a"}), Variable {Key="b"})] 73 | assertSimplification "(((a - b) - ((a | ~ b) + (a | ~ b))) - 2)" [BitwiseXor(Variable ({Key="a"}), Variable {Key="b"})] 74 | assertSimplification "(((a - b) - ((a | ~ b) << 1)) - 2)" [BitwiseXor(Variable ({Key="a"}), Variable {Key="b"})] 75 | 76 | // OR Tests 77 | assertSimplification "(((a + b) + 1) + ((- a - 1) | (- b - 1)))" [BitwiseOr(Variable ({Key="a"}), Variable {Key="b"})] 78 | assertSimplification "((a & ~ b) + b)" [BitwiseOr(Variable ({Key="a"}), Variable {Key="b"})] 79 | 80 | // NEG Tests 81 | assertSimplification "(-a - 1)" [BitwiseNegation(Variable ({Key="a"}))] 82 | 83 | 84 | [] 85 | member this.TestArithmeticOperatorsSimplification() = 86 | // ADD Tests 87 | assertSimplification "(((a ^ ~ b) + ((a | b) + (a | b))) + 1)" [Add(Variable ({Key="a"}), Variable {Key="b"})] 88 | assertSimplification "(((a ^ ~ b) + ((a | b) << 1)) + 1)" [Add(Variable ({Key="a"}), Variable {Key="b"})] 89 | assertSimplification "(((a | b) + (a | b)) - (a ^ b))" [Add(Variable ({Key="a"}), Variable {Key="b"})] 90 | assertSimplification "(((a | b) << 1) - (a ^ b))" [Add(Variable ({Key="a"}), Variable {Key="b"})] 91 | assertSimplification "((a - ~ b) - 1)" [Add(Variable ({Key="a"}), Variable {Key="b"})] 92 | assertSimplification "((a ^ b) + ((a & b) + (a & b)))" [Add(Variable ({Key="a"}), Variable {Key="b"})] 93 | assertSimplification "((a ^ b) + ((a & b) << 1))" [Add(Variable ({Key="a"}), Variable {Key="b"})] 94 | assertSimplification "((a | b) + (a & b))" [Add(Variable ({Key="a"}), Variable {Key="b"})] 95 | 96 | // Sub Tests 97 | assertSimplification "(((a & ~ b) + (a & ~ b)) - (a ^ b))" [Subtract(Variable ({Key="a"}), Variable {Key="b"})] 98 | assertSimplification "(((a & ~ b) << 1) - (a ^ b))" [Subtract(Variable ({Key="a"}), Variable {Key="b"})] 99 | assertSimplification "((a & ~ b) - (~ a & b))" [Subtract(Variable ({Key="a"}), Variable {Key="b"})] 100 | assertSimplification "((a + ~ b) + 1)" [Subtract(Variable ({Key="a"}), Variable {Key="b"})] 101 | assertSimplification "((a ^ b) - ((~ a & b) + (~ a & b)))" [Subtract(Variable ({Key="a"}), Variable {Key="b"})] 102 | assertSimplification "((a ^ b) - ((~ a & b) << 1))" [Subtract(Variable ({Key="a"}), Variable {Key="b"})] 103 | 104 | // MULT Tests 105 | assertSimplification "((a & b) * (a | b) + (a & ~ b) * (~ a & b))" [Multiply(Variable ({Key="a"}), Variable {Key="b"})] 106 | 107 | [] 108 | member this.TestComplexMixedOperatorsSimplification() = 109 | let complexExpr1 = "(((((((a + ~ b) + 1) & ~ c) << 1) - (((a + ~ b) + 1) ^ c)) & ((a & b) * (a | b) + (a & ~ b) * (~ a & b))) * ((((((a + ~ b) + 1) & ~ c) << 1) - (((a + ~ b) + 1) ^ c)) | ((a & b) * (a | b) + (a & ~ b) * (~ a & b))) + ((((((a + ~ b) + 1) & ~ c) << 1) - (((a + ~ b) + 1) ^ c)) & ~ ((a & b) * (a | b) + (a & ~ b) * (~ a & b))) * (~ (((((a + ~ b) + 1) & ~ c) << 1) - (((a + ~ b) + 1) ^ c)) & ((a & b) * (a | b) + (a & ~ b) * (~ a & b))))" 110 | let complexExpr2 = "((((((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) | 5) + (((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) & 5)) & c) * (((((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) | 5) + (((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) & 5)) | c) + (((((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) | 5) + (((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) & 5)) & ~ c) * (~ ((((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) | 5) + (((((((a ^ ~ b) + ((a | b) + (a | b))) + 1) ^ ~ c) + (((((a ^ ~ b) + ((a | b) + (a | b))) + 1) | c) << 1)) + 1) & 5)) & c))" 111 | let complexExpr3 = "((((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) | (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) + ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & ~ (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * (~ (- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2))) & ((d | ((~ d | d) - ~ d)) - (d & ((~ d | d) - ~ d)))) * ((((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) | (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) + ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & ~ (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * (~ (- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2))) | ((d | ((~ d | d) - ~ d)) - (d & ((~ d | d) - ~ d)))) + ((((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) | (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) + ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & ~ (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * (~ (- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2))) & ~ ((d | ((~ d | d) - ~ d)) - (d & ((~ d | d) - ~ d)))) * (~ (((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) | (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) + ((- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & ~ (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2)) * (~ (- ((d & e) * (d | e) + (d & ~ e) * (~ d & e)) - 1) & (((d - (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) - ((d | ~ (((d - (((~ e | e) + e) + 1)) - ((d | ~ (((~ e | e) + e) + 1)) + (d | ~ (((~ e | e) + e) + 1)))) - 2)) << 1)) - 2))) & ((d | ((~ d | d) - ~ d)) - (d & ((~ d | d) - ~ d))))" 112 | let complexExpr4 = "(((~ ((e + ~ e) + 1) | (((e & ~ d) + (e & ~ d)) - (e ^ d))) - ~ ((e + ~ e) + 1)) ^ d) - ((~ ((~ ((e + ~ e) + 1) | (((e & ~ d) + (e & ~ d)) - (e ^ d))) - ~ ((e + ~ e) + 1)) & d) << 1)" 113 | let complexExpr5 = "((((((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e) + (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e)) - (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) ^ e)) & (((((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ~ ((((c - ~ e) - 1) & ~ d) + d)) - (~ (((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ((((c - ~ e) - 1) & ~ d) + d)))) * ((((((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e) + (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e)) - (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) ^ e)) | (((((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ~ ((((c - ~ e) - 1) & ~ d) + d)) - (~ (((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ((((c - ~ e) - 1) & ~ d) + d)))) + ((((((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e) + (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e)) - (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) ^ e)) & ~ (((((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ~ ((((c - ~ e) - 1) & ~ d) + d)) - (~ (((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ((((c - ~ e) - 1) & ~ d) + d)))) * (~ (((((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e) + (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) & ~ e)) - (((((~ c | e) - ~ c) | d) - (((~ c | e) - ~ c) & d)) ^ e)) & (((((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ~ ((((c - ~ e) - 1) & ~ d) + d)) - (~ (((((c ^ ~ c) + ((c | c) + (c | c))) + 1) & ~ ((d | d) + (d & d))) + ((d | d) + (d & d))) & ((((c - ~ e) - 1) & ~ d) + d))))" 114 | let complexExpr6 = "(((~ d | d) - ~ d) + ~ ((d + ~ (- d - 1)) + 1)) + 1" 115 | 116 | // b * (a * (a - b - c)) 117 | assertSimplification complexExpr1 [Multiply(Variable {Key="b"}, Multiply(Variable {Key="a"}, Subtract (Subtract (Variable {Key="a"}, Variable {Key="b"}), Variable {Key="c"})))] 118 | 119 | // (5 + a + c + b) * c 120 | assertSimplification complexExpr2 [Multiply(Add (Add (Constant 5L, Variable {Key="a"}), Add (Variable {Key="c"}, Variable {Key="b"})), Variable {Key="c"})] 121 | 122 | // ((((~(d*e))*(d^(d^(e&e))))*(d^(d&d)))) = 0 123 | assertSimplification complexExpr3 [Constant 0] 124 | 125 | // ((((e-e)&(e-d))-d)) = -d 126 | assertSimplification complexExpr4 [Negative(Variable {Key="d"})] 127 | 128 | // ((c & e ^ d) - e) * ((2 * c | 2 * d) - (c + e | d)) 129 | assertSimplification complexExpr5 [Multiply(Subtract(BitwiseXor (BitwiseAnd (Variable {Key="c"}, Variable {Key="e"}), Variable {Key="d"}), Variable {Key="e"}),Subtract(BitwiseOr(Multiply (Constant 2L, Variable {Key="c"}), Multiply (Constant 2L, Variable {Key="d"})), BitwiseOr (Add (Variable {Key="c"}, Variable {Key="e"}), Variable {Key="d"})))] 130 | 131 | // (((d&d)-(d-(~d))) = ~d 132 | assertSimplification complexExpr6 [BitwiseNegation(Variable {Key="d"})] 133 | 134 | -------------------------------------------------------------------------------- /report/report.tex: -------------------------------------------------------------------------------- 1 | \documentclass{projectreport} 2 | \usepackage{lipsum} 3 | \usepackage[newfloat]{minted} 4 | \usemintedstyle{vs} 5 | \usepackage[toc,page]{appendix} 6 | \usepackage{tikz,varwidth} 7 | \usepackage{booktabs} 8 | \usepackage{ltablex} 9 | \usepackage{hyperref} 10 | \usepackage{setspace} 11 | \usepackage{enumitem}% http://ctan.org/pkg/enumitem 12 | \usetikzlibrary{matrix,decorations.markings} 13 | \title{Code Deobfuscation} 14 | 15 | \author{ 16 | Alban Dutilleul\\ 17 | \texttt{\href{mailto:alban.dutilleul@ens-rennes.fr}{alban.dutilleul@ens-rennes.fr}} 18 | \and 19 | Gauvain Thomas\\ 20 | \texttt{\href{mailto:gauvain.thomas@ens-rennes.fr}{gauvain.thomas@ens-rennes.fr}} 21 | } 22 | 23 | %%%%%%%%%%%%%% 24 | % References % 25 | %%%%%%%%%%%%%% 26 | 27 | % If changing the name of the bib file, change \bibliography{test} at the bottom 28 | \begin{filecontents}{refs.bib} 29 | 30 | @book{HackersDelight, 31 | author = {Warren, Henry S.}, 32 | title = {Hacker's Delight}, 33 | year = {2012}, 34 | isbn = {0321842685}, 35 | publisher = {Addison-Wesley Professional}, 36 | edition = {2nd}, 37 | abstract = {In Hackers Delight, Second Edition, Hank Warren once again compiles an irresistible collection of programming hacks: timesaving techniques, algorithms, and tricks that help programmers build more elegant and efficient software, while also gaining deeper insights into their craft. Warrens hacks are eminently practical, but theyre also intrinsically interesting, and sometimes unexpected, much like the solution to a great puzzle. They are, in a word, a delight to any programmer who is excited by the opportunity to improve. Extensive additions in this edition include A new chapter on cyclic redundancy checking (CRC), including routines for the commonly used CRC-32 code A new chapter on error correcting codes (ECC), including routines for the Hamming code More coverage of integer division by constants, including methods using only shifts and adds Computing remainders without computing a quotient More coverage of population count and counting leading zeros Array population count New algorithms for compress and expand An LRU algorithm Floating-point to/from integer conversions Approximate floating-point reciprocal square root routine A gallery of graphs of discrete functions Now with exercises and answers} 38 | } 39 | 40 | @inproceedings{Guinet2016AryboMC, 41 | title={Arybo: Manipulation, Canonicalization and Identification of Mixed Boolean-Arithmetic Symbolic Expressions}, 42 | author={Adrien Guinet and Ninon Eyrolles and Marion Videau}, 43 | year={2016} 44 | } 45 | 46 | @inproceedings {syntia, 47 | author = {Tim Blazytko and Moritz Contag and Cornelius Aschermann and Thorsten Holz}, 48 | title = {Syntia: Synthesizing the Semantics of Obfuscated Code}, 49 | booktitle = {26th {USENIX} Security Symposium ({USENIX} Security 17)}, 50 | year = {2017}, 51 | isbn = {978-1-931971-40-9}, 52 | address = {Vancouver, BC}, 53 | pages = {643--659}, 54 | url = {https://www.usenix.org/conference/usenixsecurity17/technical-sessions/presentation/blazytko}, 55 | publisher = {{USENIX} Association}, 56 | month = aug, 57 | } 58 | 59 | @inproceedings{eyrolles, 60 | TITLE = {{Defeating MBA-based Obfuscation}}, 61 | AUTHOR = {Eyrolles, Ninon and Goubin, Louis and Videau, Marion}, 62 | URL = {https://hal.archives-ouvertes.fr/hal-01388109}, 63 | BOOKTITLE = {{2nd International Workshop on Software PROtection}}, 64 | ADDRESS = {Vienna, Austria}, 65 | EDITOR = {ACM}, 66 | YEAR = {2016}, 67 | MONTH = Oct, 68 | DOI = {10.1145/2995306.2995308}, 69 | KEYWORDS = {pattern matching ; expression simplification ; mixed boolean-arithmetic expressions ; reverse engineering ; Obfuscation}, 70 | PDF = {https://hal.archives-ouvertes.fr/hal-01388109/file/spro05.pdf}, 71 | HAL_ID = {hal-01388109}, 72 | HAL_VERSION = {v1}, 73 | } 74 | 75 | @inproceedings{decidability, 76 | author = {Richardson, Dan and Fitch, John}, 77 | title = {The Identity Problem for Elementary Functions and Constants}, 78 | year = {1994}, 79 | isbn = {0897916387}, 80 | publisher = {Association for Computing Machinery}, 81 | address = {New York, NY, USA}, 82 | url = {https://doi.org/10.1145/190347.190429}, 83 | doi = {10.1145/190347.190429}, 84 | abstract = {A solution for a version of the identify problem is proposed for a class of functions including the elementary functions. Given f(x), g(x), defined at some point β we decide whether or not f(x) = g(x) in some neighbourhood of β. This problem is first reduced to a problem about zero equivalence of elementary constants. Then a semi algorithm is given to solve the elementary constant problem. This semi algorithm is guaranteed to give the correct answer whenever it terminates, and it terminates unless the problem being considered contains a counterexample to Schanuel's conjecture.}, 85 | booktitle = {Proceedings of the International Symposium on Symbolic and Algebraic Computation}, 86 | pages = {285–290}, 87 | numpages = {6}, 88 | location = {Oxford, United Kingdom}, 89 | series = {ISSAC '94} 90 | } 91 | 92 | @book{termrewriting, 93 | author = {Baader, Franz and Nipkow, Tobias}, 94 | title = {Term Rewriting and All That}, 95 | year = {1998}, 96 | isbn = {0521455200}, 97 | publisher = {Cambridge University Press}, 98 | address = {USA} 99 | } 100 | 101 | @inproceedings{z3solver, 102 | author = {De Moura, Leonardo and Bj\o{}rner, Nikolaj}, 103 | title = {Z3: An Efficient SMT Solver}, 104 | year = {2008}, 105 | isbn = {3540787992}, 106 | publisher = {Springer-Verlag}, 107 | address = {Berlin, Heidelberg}, 108 | abstract = {Satisfiability Modulo Theories (SMT) problem is a decision problem for logical first order formulas with respect to combinations of background theories such as: arithmetic, bit-vectors, arrays, and uninterpreted functions. Z3 is a new and efficient SMT Solver freely available from Microsoft Research. It is used in various software verification and analysis applications.}, 109 | booktitle = {Proceedings of the Theory and Practice of Software, 14th International Conference on Tools and Algorithms for the Construction and Analysis of Systems}, 110 | pages = {337–340}, 111 | numpages = {4}, 112 | location = {Budapest, Hungary}, 113 | series = {TACAS'08/ETAPS'08} 114 | } 115 | 116 | @MISC{collberg, 117 | author = {Christian Collberg and Clark Thomborson and Douglas Low}, 118 | title = {A Taxonomy of Obfuscating Transformations}, 119 | year = {1997} 120 | } 121 | 122 | @book{knuth, 123 | author = {Knuth, Donald E.}, 124 | title = {The Art of Computer Programming, Volume 2 (3rd Ed.): Seminumerical Algorithms}, 125 | year = {1997}, 126 | isbn = {0201896842}, 127 | publisher = {Addison-Wesley Longman Publishing Co., Inc.}, 128 | address = {USA} 129 | } 130 | 131 | @inproceedings{abstract, 132 | author = {Cousot, Patrick and Cousot, Radhia}, 133 | title = {Abstract Interpretation: A Unified Lattice Model for Static Analysis of Programs by Construction or Approximation of Fixpoints}, 134 | year = {1977}, 135 | isbn = {9781450373500}, 136 | publisher = {Association for Computing Machinery}, 137 | address = {New York, NY, USA}, 138 | url = {https://doi.org/10.1145/512950.512973}, 139 | doi = {10.1145/512950.512973}, 140 | abstract = {A program denotes computations in some universe of objects. Abstract interpretation of programs consists in using that denotation to describe computations in another universe of abstract objects, so that the results of abstract execution give some information on the actual computations. An intuitive example (which we borrow from Sintzoff [72]) is the rule of signs. The text -1515 * 17 may be understood to denote computations on the abstract universe {(+), (-), (±)} where the semantics of arithmetic operators is defined by the rule of signs. The abstract execution -1515 * 17 → -(+) * (+) → (-) * (+) → (-), proves that -1515 * 17 is a negative number. Abstract interpretation is concerned by a particular underlying structure of the usual universe of computations (the sign, in our example). It gives a summary of some facets of the actual executions of a program. In general this summary is simple to obtain but inaccurate (e.g. -1515 + 17 → -(+) + (+) → (-) + (+) → (±)). Despite its fundamentally incomplete results abstract interpretation allows the programmer or the compiler to answer questions which do not need full knowledge of program executions or which tolerate an imprecise answer, (e.g. partial correctness proofs of programs ignoring the termination problems, type checking, program optimizations which are not carried in the absence of certainty about their feasibility, …).}, 141 | booktitle = {Proceedings of the 4th ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages}, 142 | pages = {238–252}, 143 | numpages = {15}, 144 | location = {Los Angeles, California}, 145 | series = {POPL '77} 146 | } 147 | 148 | @InProceedings{abstract_detection, 149 | author="Dalla Preda, Mila 150 | and Madou, Matias 151 | and De Bosschere, Koen 152 | and Giacobazzi, Roberto", 153 | editor="Johnson, Michael 154 | and Vene, Varmo", 155 | title="Opaque Predicates Detection by Abstract Interpretation", 156 | booktitle="Algebraic Methodology and Software Technology", 157 | year="2006", 158 | publisher="Springer Berlin Heidelberg", 159 | address="Berlin, Heidelberg", 160 | pages="81--95", 161 | abstract="Code obfuscation and software watermarking are well known techniques designed to prevent the illegal reuse of software. Code obfuscation prevents malicious reverse engineering, while software watermarking protects code from piracy. An interesting class of algorithms for code obfuscation and software watermarking relies on the insertion of opaque predicates. It turns out that attackers based on a dynamic or an hybrid static-dynamic approach are either not precise or time consuming in eliminating opaque predicates. We present an abstract interpretation-based methodology for removing opaque predicates from programs. Abstract interpretation provides the right framework for proving the correctness of our approach, together with a general methodology for designing efficient attackers for a relevant class of opaque predicates. Experimental evaluations show that abstract interpretation based attacks significantly reduce the time needed to eliminate opaque predicates.", 162 | isbn="978-3-540-35636-3" 163 | } 164 | 165 | @InProceedings{concolic, 166 | author={Bardin, Sébastien and David, Robin and Marion, Jean-Yves}, 167 | booktitle={2017 IEEE Symposium on Security and Privacy (SP)}, 168 | title={Backward-Bounded DSE: Targeting Infeasibility Questions on Obfuscated Codes}, 169 | year={2017}, 170 | volume={}, 171 | number={}, 172 | pages={633-651}, 173 | doi={10.1109/SP.2017.36}} 174 | 175 | 176 | 177 | 178 | \end{filecontents} 179 | 180 | \begin{document} 181 | 182 | \setstretch{1,0} 183 | 184 | \maketitle 185 | 186 | %%%%%%%%%%% 187 | % Article % 188 | %%%%%%%%%%% 189 | 190 | \section{Introduction} 191 | 192 | Code obfuscation is the process of modifying a program, in order to make it unclear and unintelligible (mostly for humans), while remaining functional. It can be achieved for several goals such as hiding the purpose of the software, as well as its behavior and logic, which makes its reverse engineering harder. Either a source code or a binary can be obfuscated. 193 | One way to obfuscate one's code is to use a automated dedicated tool, such as Tigress. 194 | Tigress is a software, allowing direct obfuscation of C source code in many several ways -of which will be described in this paper- and even allowing combinations and recursive uses of those. 195 | Even though these modifications makes it difficult to understand and reverse engineering a code, it is however not impossible to do the obfuscation inverse operation, deobfuscation, which we'll focus on. 196 | This project will focus on studying three different ways of Tigress to obfuscate C code, and providing tools to undo them. 197 | 198 | \section{Analysis and attacks of several Tigress transformations} 199 | 200 | \subsection{Encode Data} 201 | \subsubsection{General comments} 202 | The \textit{EncodeData} transformation replaces integer variables with non-standard representations. 203 | 204 | Through the \verb|--EncodeDataCodecs| option, it is possible to choose between several representations, although we are only interested in the first one (\textbf{\textsf{poly1}}). 205 | Indeed the other transformations are not very interesting, they use trivial identities and are therefore very easy to detect. 206 | 207 | \begin{description}[font=\sffamily\bfseries, leftmargin=1cm, style=nextline] 208 | \item[poly1] 209 | Linear transformation using invertible affine functions $f(x) = ax +b$ (with $a \neq 0$) and $f^{-1}(x) = a^{-1}x - ba^{-1}$ in $\Z/2^n\Z$ 210 | \item[xor] 211 | Exclusive-or with a constant using the identity $A \oplus C \oplus C = A$. 212 | \item[add] 213 | Add a constant and promote to next largest integer type. It is not supported for the largest integer type. 214 | \end{description} 215 | 216 | If a variable is global an initialization function will be generated and called at the very beginning of the \verb|main|. In the case of the initialization of an array, the first initialized constants are unrolled and the rest will be initialized in a loop to a default value. 217 | 218 | \begin{minted}[breaklines]{c} 219 | loop[0] = 1956833575U; 220 | loop[1] = 1956833575U; 221 | loop[2] = 1956833575U; 222 | loop[3] = 1956833575U; 223 | tmp___0 = 4U; 224 | while (! (tmp___0 >= 5U)) { 225 | loop[tmp___0] = 1206696158U; 226 | tmp___0 ++; 227 | } 228 | \end{minted} 229 | 230 | All local variables that are encoded in the same scope must have the same type otherwise Tigress raises an error. 231 | 232 | \subsubsection{In-depth analysis of the poly1 encoding} 233 | 234 | \textbf{\textsf{poly1}} computes the modular inverse $f^{-1}$ of an affine function $f$ such that $f^{-1}(f(x)) = x$. Given an integer $a$ and a modulus $m$, its modular multiplicative inverse 235 | is an integer $u$ such that $au$ is congruent to $1$ modulo $m$. A standard way to compute $u$ is to use Euclid's extended algorithm. These operations take place on unsigned integers and are reconverted as needed. 236 | 237 | \begin{itemize} 238 | \item One way to find the original constant of \textbf{\textsf{poly1}} in our code is to find the first place where our variable $v$ is initialized. If the expression has an affine pattern, we can calculate the modular inverse such as $a' \gets a^{-1} \mod 2^n$ and save $b' \gets b$. It is possible that an initialization does not contain an affine expression but a constant, in this case we will look further in the code for an affine use as in (2) and we will use it directly in it. 239 | \item As soon as we find a use of $v$ within another affine expression, we check that the calculated modular inverse is the right one by comparing it to $a'$, we also check that $(b' * a') \mod 2^n = b$. 240 | \item If both conditions are verified, we can then directly replace with the expected value because it was indeed an obfuscated expression. If the expression is nested, we just do a depth-first search by repeating the same process. 241 | \end{itemize} 242 | 243 | \subsubsection{Closing remarks} 244 | 245 | Combining this transformation with \textit{EncodeArithmetic} (which we will present next) makes the problem much more complicated as the expression of the affine function is also modified. 246 | 247 | \subsection{Encode Arithmetic} 248 | 249 | \subsubsection{Mixed Boolean-Arithmetic expression} 250 | 251 | This transformation allows to transform a standard arithmetic expression into a Mixed Boolean-Arithmetic (\textsf{MBA}) expression. 252 | 253 | \begin{definition}[\textsf{MBA} expression] An expression $E$ of the form 254 | \begin{equation} 255 | E = \sum_{i \in I} a_i (\prod_{j \in J_i} e_{i, j}(x_1, \ldots, x_t )) 256 | \end{equation} 257 | where the arithmetic sum and product are modulo $2^n$, $a_i$ are constants in $\Z/2^n\Z$, $e_{i,j}$ are bitwise 258 | expressions (using the following operators : $\wedge, \neg, \lor, \oplus, \ll, \gg$) of variables $x_1, \ldots, x_t $ in $\{0, 1\}^n$, $I \subset \Z $ and for all $i \in I$, $J_i \subset Z$ are finite index sets is a Mixed Boolean-Arithmetic (\textsf{MBA}) expression. 259 | 260 | \end{definition} 261 | 262 | \begin{example} 263 | For instance a simple addition is transformed into the following \textsf{MBA} expression by Tigress 264 | \begin{equation} 265 | (x \oplus y) + 2 * (x \land y) 266 | \end{equation} 267 | \end{example} 268 | 269 | To generate these expressions, Tigress uses a set of identities that are randomly chosen. They are then applied recursively to a certain depth. It is possible to explore these rules exhaustively by generating a large number of expressions based on the basic operators (see the \hyperref[app:rules]{appendix}). \\ 270 | Most of these identities are extracted from \cite{HackersDelight}. All integer comparison operations are represented by \textsf{MBA} expressions with results in their most significant bit (\textsf{MSB}). 271 | 272 | \subsubsection{Symbolic simplification of MBAs} 273 | 274 | We will detail here our approach to simplify the expressions generated by Tigress. The state of the art proposes several types of solutions to simplify generic \textsf{MBA} expressions: pattern matching \cite{eyrolles}, bit-blasting \cite{Guinet2016AryboMC}, and program synthesis \cite{syntia}. \\ 275 | The scope of our work being limited to Tigress which has only a limited number of known rewriting rules led us to choose the pattern matching option. This technique is based on the term rewriting theory that we will briefly describe here. 276 | 277 | \begin{definition}[Term Rewriting System] 278 | A Term Rewrite System (\textsf{TRS}) \cite{termrewriting} is composed of a set $\Sigma$ of function symbols and a set $\mathcal{R}$ of rewrite rules over $\mathcal{T}$ (set of terms). A reduction $x \mapsto y$ corresponds to the application of a rule on the term $x$. A term is said to be normalizing if no other rules can be applied to it. 279 | 280 | \end{definition} 281 | \begin{definition}[TRS Properties] 282 | A \textsf{TRS} can have several properties, termination, confluence and convergence. 283 | \begin{enumerate} 284 | \item \textbf{Terminating: } There is no infinite descending chain $a_0 \mapsto a_1 \mapsto \cdots$ 285 | \item \textbf{Confluent: } When there are different rules to apply from a term $t$, leading to two different terms $y_1$ and $y_2$, we always find a common term $z$ that can be reached from both $y_1$ and $y_2$ by successive applications of rewrite rules. 286 | \item \textbf{Convergent: } It is both confluent and terminating. 287 | \end{enumerate} 288 | \end{definition} 289 | 290 | It is therefore relatively straightforward to construct our rewriting system from the Tigress identities, however it is difficult to bring together more than one of the properties stated above. One of the first problems arises from the associativity and commutativity of some arithmetic and bitwise operators ($\{+, \times, \land, \lor, \oplus$\}). We can't include these rules in our system unless we create cycles. The solutions proposed in the literature are not easy to implement \cite{termrewriting}, based for instance on solving homogeneous linear diophantine equations in the non-negatives integers. \\ 291 | If we want to have the convergence property, it implies to determine normal forms, but the complexity of the operations in this TRS makes this difficult to consider. According to Richardson's theorem \cite{decidability}, checking the equivalence of two arithmetic expressions for a certain fairly natural class of expressions is actually an undecidable problem. \\ 292 | It is nevertheless possible thanks to \textsf{SMT} solvers like Z3 \cite{z3solver} to determine in some cases if two expressions are equivalent (for instance: $\neg (a \oplus 23) \equiv (a \oplus 232)$) but it is very costly in performance. This is why we have chosen not to use one. \\ 293 | The rewriting system we propose uses only one structural equality, which increases the number of rules because a new one must be created as soon as an operation is commutative. 294 | 295 | \subsubsection{The simplification tool: MBA Simplifier} 296 | We have implemented our rewriting system in a tool named \href{https://github.com/adutilleul/tigress-deobfuscation}{\textbf{MBASimplifier}}. It has been realized in the F\# functional language, which proposes a powerful pattern matching and a type system facilitating the modeling of a symbolic expression (through sum types). 297 | 298 | \paragraph{Project Structure} 299 | 300 | \begin{itemize} 301 | \item \textbf{Simplifier.fs} Simplify.fs contains the term rewriting system 302 | \item \textbf{Ast.fs} The representation of a symbolic MBA expression 303 | \item \textbf{Engine.fs} The parser of an expression corresponding to a subset of the C99 standard 304 | \item \textbf{Input.fs}, \textbf{UserInterface.fs} Interactions with the user on the standard input 305 | \item \textbf{Main.fs} Program entry point 306 | \end{itemize} 307 | 308 | The simplification algorithm corresponds to the following pseudocode, a cycle detection algorithm was used to verify that the rule system does not prevent termination. 309 | 310 | \begin{algorithm} 311 | \caption{Eval function pseudocode}\label{alg:cap} 312 | \begin{algorithmic} 313 | \State $expr', \mu, \lambda \gets$ \Call{floydCycle}{$expr$, $simp$} 314 | \LineComment $\lambda$ is the length of the cycle 315 | \If{$\lambda > 1$} 316 | \Call{Exception}{} 317 | \Else 318 | \LineComment Termination condition. 319 | \If{$expr' \equiv expr $} 320 | \Return $expr$ 321 | \Else 322 | \Return \Call{eval}{$expr'$} 323 | \EndIf 324 | \EndIf 325 | \end{algorithmic} 326 | \end{algorithm} 327 | 328 | Classical arithmetic and binary simplification identities have been used in parallel with Tigress specific rules (e.g. $A \lor A \equiv A$). \\ 329 | Since the reduction paths can be long, a hash table of simplifications is used for performance reasons. 330 | \paragraph{Results} 331 | In order to propose consistent and reproducible results we propose unit tests. Moreover, a script is available to generate an arbitrary number of expressions obfuscated by Tigress, for the moment they have to be compared manually because of the equivalence problem of the expressions mentioned above. 332 | However, we propose a measure of the complexity of expressions to quantify the simplification. Expressions are represented as a tree, so we can weight the nodes according to the type of operation. 333 | 334 | \begin{table}[!ht] 335 | \centering 336 | \begin{tabular}{ll} 337 | \hline 338 | \multicolumn{1}{|l|}{Type} & \multicolumn{1}{l|}{Weight} \\ \hline 339 | \multicolumn{1}{|l|}{Constant} & \multicolumn{1}{l|}{1} \\ \hline 340 | \multicolumn{1}{|l|}{Variable} & \multicolumn{1}{l|}{2} \\ \hline 341 | \multicolumn{1}{|l|}{Unary operator} & \multicolumn{1}{l|}{4} \\ \hline 342 | \multicolumn{1}{|l|}{Binary operator} & \multicolumn{1}{l|}{8} \\ \hline 343 | \end{tabular} 344 | \end{table} 345 | 346 | It also is possible to use the dataset named dataset1.c to compare the results on randomly generated expressions, they come from this \href{https://github.com/werew/qsynth-artifacts/blob/master/datasets/custom_EA_ED/original.c}{paper}. 347 | 348 | \paragraph{Limits of our tool} 349 | 350 | \begin{enumerate} 351 | \item The lack of genericity, if new identities were added, it would be less able to simplify. 352 | \item The implementation of associativity and commutativity which are not optimal. 353 | \end{enumerate} 354 | 355 | \subsection{Add opaque} 356 | 357 | This transformation inject superfluous branches (also known as dead branches) in order to complexify the control flow graph (\textsf{CFG}). 358 | 359 | \subsubsection{Opaque predicates} 360 | 361 | \textit{Collberg et al.} introduced the notation of opaque predicates in 1997 \cite{collberg}. 362 | 363 | \begin{definition}[Opaque predicate] An opaque predicate is an invariant expression that 364 | always evaluates to true or false with the intention of hiding the fact they are constant. 365 | \end{definition} 366 | \textit{Collberg} differentiates these predicates into several categories according to the difficulty to detect them automatically. 367 | 368 | \begin{description}[font=\sffamily\bfseries, leftmargin=1cm, style=nextline] 369 | \item[Trivial] 370 | A \textit{trivial} opaque predicate is constructed inside a basic block so its invariant expression can be identified at a basic block level. 371 | \item[Weak] 372 | A \textit{weak} opaque predicate is constructed throughout a function so it requires intra-procedural analysis to identify its invariant 373 | expression. 374 | \item[Strong] 375 | A \textit{strong} opaque predicate is constructed across multiple functions so it requires inter-procedural analysis to identify its invariant expression. 376 | \item[Full] 377 | A \textit{full} opaque predicate is constructed across multiple processes so it requires inter-process analysis to identify its invariant expression. 378 | \end{description} 379 | 380 | We will use this classification on the different types of invariants proposed by Tigress. 381 | 382 | \subsubsection{Invariants generated by Tigress} 383 | 384 | Tigress allows through the \verb|--AddOpaqueKinds| option to choose among the following invariants: 385 | 386 | \begin{description}[font=\sffamily\bfseries, leftmargin=1cm, style=nextline] 387 | \item[True] 388 | Places a branch that evaluates all the time because the predicate is true. 389 | \item[Junk] 390 | Places into a dead branch a sequence of random bytes into the assembly code. 391 | \item[Bug] 392 | Places a buggified version of its sibling authentic basic block in the unreachable basic block. 393 | \item[Question] 394 | Places copy of sibling authentic basic block in the unreachable basic block. 395 | \item[Fake] 396 | Places a function call to a non-existing function in the unreachable basic block. 397 | \item[Call] 398 | Places a function call to a random function existing in executable binary in the unreachable basic. 399 | \end{description} 400 | 401 | In addition to that, invariants can use complex predicates via the --AddOpaqueStructs option. Either by using chained lists, or arrays, entropy generated from an external thread but also from the program input. \\ 402 | 403 | If we only consider the options on classical data structures, the complexity varies from trivial to weak since the invariants only depend on the function. 404 | \begin{example} 405 | Input-based invariants are not much more complicated to detect as long as we know the inputs. 406 | With the following inputs \verb|--Inputs='+2:int:1?2147483647'|, here is the original and generated obfuscated code. 407 | \begin{minted}[linenos,tabsize=2,breaklines]{c} 408 | str [i] = (( str [ i ] - 'a' + num_shift ) % 26) + 'a'; 409 | \end{minted} 410 | \begin{minted}[linenos,tabsize=2,breaklines]{c} 411 | atoi_result7 = atoi (*( _ obf_2_ main_ _argv + 2) ) ; 412 | if (( atoi_result7 - 1 < 0) + ( atoi_result7 - 2147483647 >0) ) { 413 | *( str + i ) = ( char ) 2; 414 | } else { 415 | *( str + i ) = ( char ) (((( int ) *( str + i ) - 97) + num_shift ) % 26 + 97) ; 416 | } 417 | \end{minted} 418 | \end{example} 419 | 420 | However, thread-based entropy invariants are much more difficult to detect. Tigress uses a Linear congruential generator (\textsf{LCG}) of \textit{Donald Knuth}, the values of the 421 | modulus, multiplier and increment are as defined as: $a = 6364136223846793005, c = 1442695040888963407$ \cite{knuth}. 422 | 423 | \begin{minted}[linenos,tabsize=2,breaklines]{c} 424 | case 3: 425 | __asm__ volatile ("cpuid\n" 426 | "rdtsc\n": "=a" (low6), "=d" (high7)); 427 | newValue4 = ((unsigned long )high7 << 32) | (unsigned long )low6; 428 | break; 429 | } 430 | _1_entropy = (newValue4 + 3) | _1_entropy; 431 | _1_entropy = 6364136223846793005UL * _1_entropy + 1442695040888963407UL; 432 | \end{minted} 433 | 434 | The \verb|_1_entropy| value used in the \textsf{LCG} is the number of cycles that were required to execute the \textbf{x86} instruction \verb|cpuid| on the processor. 435 | 436 | Using the environment (as with this entropy function) moves an invariant into the strong category. 437 | 438 | \subsubsection{Proposed Lifting Approach} 439 | 440 | Since reasoning about invariants is complicated at a high level as in C code, it is reasonable to assume that our analysis will be performed on an intermediate representation to avoid noise related to semantics. 441 | 442 | We propose a two-phase approach for Tigress opaque predicates to be removed. Due to its complexity and lack of time, we were unable to implement it. 443 | 444 | \begin{enumerate} 445 | \item Since invariants can be relatively simple as we have pointed out, it is possible to first statically analyze the code to remove unnecessary branches through the \textbf{abstract interpretation} technique (see \cite{abstract}). This technique has already been used in the literature for the detection of opaque predicates \cite{abstract_detection}. 446 | \item Since the evaluation of some branches is particularly difficult (typically with the entropy thread), if the static analysis was not sufficient, we can use the \textbf{concolic execution} technique to determine which branches are never taken (an example of implementation is proposed in the paper \cite{concolic}). 447 | \end{enumerate} 448 | 449 | \section{Conclusion} 450 | 451 | In our effort, we managed to analyse a small part of the Tigress transformations and to partially lift the expressions generated in \textbf{EncodeArithmetic}. However, we realised that it is difficult to completely deobfuscate even a single transformation and even more so when combining them. The literature on the subject is still rather limited and leaves many room for improvement. It is also regrettable that an academic project such as Tigress is not available in open-source, which limits the understanding of some of the techniques it uses. 452 | 453 | %%%%%%%%%%%%%% 454 | % References % 455 | %%%%%%%%%%%%%% 456 | 457 | \nocite{*} 458 | \bibliographystyle{plain} 459 | \bibliography{refs} 460 | 461 | \newpage 462 | \appendix 463 | \onecolumn 464 | 465 | \label{app:rules} 466 | \begin{tabularx}{\linewidth}{lcc@{}} 467 | \toprule 468 | & \textbf{MBA Expressions} & \textbf{Identity} \\ \midrule 469 | \multicolumn{2}{@{}l}{$a \land b$}\\ 470 | 1 & $(((\neg a \lor b) + a) + 1)$ & $((\neg a \lor b) - \neg a)$ \\ 471 | 2 & $((\neg a \lor b) - \neg a)$ \\ 472 | \midrule 473 | \multicolumn{2}{@{}l}{$a \lor b$}\\ 474 | 1 & $(((a + b) + 1) + ((- a - 1) \lor (- b - 1)))$ & $((a \land \neg b) + b)$ \\ 475 | 2 & $((a \land \neg b) + b)$ \\ 476 | \midrule 477 | \multicolumn{2}{@{}l}{$a \oplus b$}\\ 478 | 1 & $(((a - b) - ((a \lor \neg b) + (a \lor \neg b))) - 2)$ \\ 479 | 2 & $(((a - b) - ((a \lor \neg b) \ll 1)) - 2)$ \\ 480 | 3 & $((a \lor b) - (a \land b))$ \\ 481 | \midrule 482 | \multicolumn{2}{@{}l}{$a \oplus b$}\\ 483 | 1 & $(((a - b) - ((a \lor \neg b) + (a \lor \neg b))) - 2)$ \\ 484 | 2 & $(((a - b) - ((a \lor \neg b) \ll 1)) - 2)$ \\ 485 | 3 & $((a \lor b) - (a \land b))$ \\ 486 | \midrule 487 | \multicolumn{2}{@{}l}{$a + b$}\\ 488 | 1 & $(((a \oplus \neg b) + ((a \lor b) + (a \lor b))) + 1)$ \\ 489 | 2 & $(((a \oplus \neg b) + ((a \lor b) \ll 1)) + 1)$ \\ 490 | 3 & $(((a \lor b) + (a \lor b)) - (a \oplus b))$ \\ 491 | 4 & $(((a \lor b) \ll 1) - (a \oplus b))$ \\ 492 | 5 & $((a - \neg b) - 1)$ \\ 493 | 6 & $((a \oplus b) + ((a \land b) + (a \land b)))$ \\ 494 | 7 & $((a \oplus b) + ((a \land b) \ll 1))$ \\ 495 | 8 & $((a \lor b) + (a \land b))$ \\ 496 | \midrule 497 | \multicolumn{2}{@{}l}{$a - b$}\\ 498 | 1 & $(((a \land ~ b) + (a \land ~ b)) - (a \oplus b))$ \\ 499 | 2 & $(((a \land ~ b) \ll 1) - (a \oplus b))$ \\ 500 | 3 & $((a \land ~ b) - (~ a \land b))$ \\ 501 | 4 & $(((a \lor b) \ll 1) - (a \oplus b))$ \\ 502 | 5 & $((a + ~ b) + 1)$ \\ 503 | 6 & $((a \oplus b) - ((~ a \land b) + (~ a \land b)))$ \\ 504 | 7 & $((a \oplus b) - ((~ a \land b) \ll 1))$ \\ 505 | \midrule 506 | \multicolumn{2}{@{}l}{$a \times b$}\\ 507 | 1 & $((a \land b) \times (a \lor b) + (a \land \neg b) \times (\neg a \land b))$ \\ 508 | \midrule 509 | \multicolumn{2}{@{}l}{$a = b$}\\ 510 | 1 & $(\neg (a - b) \lor (b - a)) \gg b) \land 1$ \\ 511 | \midrule 512 | \multicolumn{2}{@{}l}{$a \neq b$}\\ 513 | 1 & $((a - b) \lor (b - a)) \gg b) \land 1$ \\ 514 | \midrule 515 | \multicolumn{2}{@{}l}{$a \geq b$}\\ 516 | 1 & $((\neq (b \oplus a) \gg 1) + (b \land \neq a)) \gg b) \land 1)$ \\ 517 | \midrule 518 | \multicolumn{2}{@{}l}{$a \leq b$}\\ 519 | 1 & $((\neq (a \oplus b) \gg 1) + (a \land \neq b)) \gg b) \land 1)$ \\ 520 | \midrule 521 | \multicolumn{2}{@{}l}{$a * 2$}\\ 522 | 1 & $a \ll 1$ \\ 523 | \midrule 524 | \multicolumn{2}{@{}l}{$\neg a$}\\ 525 | 1 & $(- a - 1) $ \\ 526 | \bottomrule \\ 527 | \caption{Rewrite rules used by Tigress for generating MBA ex-pressions} 528 | \end{tabularx} 529 | 530 | It is possible to reproduce these results by going to the \verb|samples/encode_arithmetic| folder and running the bash script \verb|generate.sh| with a large number of iterations ($n \geq 500$). A \verb|out\sorted| folder will be generated with the corresponding identities. 531 | 532 | \end{document} 533 | -------------------------------------------------------------------------------- /simplifier/MBASimplifier/Simplifier.fs: -------------------------------------------------------------------------------- 1 | (* 2 | MIT License 3 | 4 | Copyright (c) 2021 Alban DUTILLEUl, Gauvain THOMAS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | *) 24 | 25 | module MBASimplifier.Implementation 26 | 27 | open MBASimplifier.Ast 28 | open MBASimplifier.Engine 29 | open FParsec.CharParsers 30 | 31 | type Stored = 32 | | Value of Expr 33 | | Function of (Expr -> Expr) 34 | 35 | type State = { 36 | MemoryMap : Map 37 | Debug: bool 38 | // The results of intermediate simplifications are stored for performance reasons 39 | mutable ExpressionsCache : Map 40 | mutable Exploration: Set 41 | } 42 | 43 | type ParseResult = 44 | | ParseSuccess of Statement 45 | | ParseError of string 46 | 47 | type CommandResult = 48 | | SingleResult of State * Expr 49 | | AssignmentResult of State * (string * Expr) list 50 | 51 | let parseLine state line = 52 | let parserResult = runParserOnString pcommand_eof () "Input" line 53 | match parserResult with 54 | | Success (expr, state, pos) -> ParseSuccess expr 55 | | Failure (msg, err, state) -> ParseError msg 56 | 57 | let (|StorageSymbol|StorageUnknown|) (state, name) = 58 | match (Map.tryFind name.Key state.MemoryMap) with 59 | | Some s -> StorageSymbol(s) 60 | | None _ -> StorageUnknown(name.Key) 61 | 62 | (* Cycle detection algorithm, see https://en.wikipedia.org/wiki/Cycle_detection *) 63 | let tortoise_and_hare (x0: 'a) (fn: 'a -> 'a) : ('a * int * int) = 64 | let rec find_cycle n t h = if t = h then n, t else find_cycle (n+1) (fn t) (fn (fn h)) in 65 | let x1 = fn x0 in 66 | let n, xn = find_cycle 1 x1 (fn x1) in 67 | let rec get_cycle_length i xi xni lam = 68 | if xi = xni then 69 | i, lam 70 | else 71 | get_cycle_length (i+1) (fn xi) (fn xni) 72 | (if lam = 0 && i > 0 && xni = xn then i else lam) 73 | in 74 | let mu, lam = get_cycle_length 0 x0 xn 0 in 75 | x1, mu, if lam = 0 then n else lam 76 | 77 | let rec eval (state: State) (expr: Expr) : Expr = 78 | let expr', mu, lam = tortoise_and_hare expr (simp state) 79 | if lam > 1 then 80 | failwith (sprintf "" lam) 81 | else 82 | if expr' = expr then expr else eval state expr' 83 | and simp (state: State) (expr: Expr) : Expr = 84 | let expr' = 85 | match (Map.tryFind expr state.ExpressionsCache) with 86 | | Some e -> e 87 | | None _ -> 88 | match (state, expr) with 89 | | StoredSimplification e' -> e' 90 | | BeforeAssociativity e' -> e' 91 | | AssociativitySimplification e' -> e' 92 | | ArithmeticSimplification e' -> e' 93 | | BooleanSimplification e' -> e' 94 | | BitwiseGenericSimplification e' -> e' 95 | | TigressSimplification e' -> e' 96 | | RelationalSimplification e' -> e' 97 | | BaseSimplification e' -> e' 98 | | _ -> expr 99 | in state.ExpressionsCache <- Map.add expr expr' state.ExpressionsCache; expr' 100 | and (|StoredSimplification|_|) (state, expr) = 101 | match expr with 102 | | Constant c -> Some(Constant c) 103 | | Variable n -> Some(evalVariable state n) 104 | | FunctionCall (name, e) -> Some(evalFunction state name e) 105 | | _ -> None 106 | and (|BeforeAssociativity|_|) (state, expr) = 107 | match expr with 108 | // ((A + ~ B) + 1) = A - B 109 | | Add (Add (e1, BitwiseNegation (e2)), Constant 1L) -> Some((simp state e1) - (simp state e2)) 110 | | Add (Constant 1L, Add (e1, BitwiseNegation (e2))) -> Some((simp state e1) - (simp state e2)) 111 | | Add (Constant 1L, Add (BitwiseNegation (e2), e1)) -> Some((simp state e1) - (simp state e2)) 112 | 113 | // Constant folding 114 | | Subtract(BitwiseXor (e1, Constant c1), BitwiseNegation (Multiply (Constant 2L, BitwiseOr (e2, Constant c2)))) 115 | when (e1 = e2) && (c1 < 0) && (c2 > 0) && (c2 = abs(c1) - 1L ) -> Some((simp state e1) + Constant (c2)) 116 | | Subtract(Multiply (Constant 2L, BitwiseOr (e1, Constant c1)), BitwiseNegation (BitwiseXor (e2, Constant c2))) 117 | when (e1 = e2) && (c1 > 0) && (c2 < 0) && (abs(c2) = c1 + 1L) -> Some((simp state e1) + Constant (c1)) 118 | | Subtract(Multiply (Constant 2L, BitwiseAnd (e1, Constant c1)), BitwiseXor (e2, Constant c2)) 119 | when (e1 = e2) && (c1 < 0) && (c2 > 0) && (c2 = abs(c1) - 1L ) -> Some((simp state e1) - Constant (c2)) 120 | | Subtract(Multiply (Constant 2L, BitwiseOr (e1, e2)), BitwiseNegation (BitwiseXor (e3, BitwiseNegation (e4)))) 121 | when e1 = e3 && e2 = e4 -> Some((simp state e1) + (simp state e2)) 122 | 123 | | Subtract(BitwiseXor (e1, BitwiseNegation (e2)),BitwiseNegation (Multiply (Constant 2L, BitwiseOr (e3, e4)))) 124 | when e1 = e3 && e2 = e4 -> Some((simp state e1) + (simp state e2)) 125 | 126 | // Multiplication simplification. 127 | // ((A & B) * (A | B) + (A & ~ B) * (~ A & B)) = A * B 128 | | Multiply(Multiply(BitwiseAnd (BitwiseNegation (e1), e2), Add(BitwiseOr (e3, e4), BitwiseAnd (e5, BitwiseNegation (e6)))), BitwiseAnd (e7, e8)) 129 | when (e1 = e3 && e3 = e5 && e5 = e7) && (e2 = e4 && e4 = e6 && e6 = e8) -> Some((simp state e1) * (simp state e2)) 130 | | Multiply(Multiply(BitwiseAnd (e1, Constant c1), Add (BitwiseOr (e2, Constant c2), BitwiseAnd (e3, BitwiseNegation (Constant c3)))), BitwiseAnd (BitwiseNegation (e4), Constant c4)) 131 | when (e1 = e2) && (e2 = e3) && (e3 = e4) && (c1 = c2) && (c2 = c3) && (c3 = c4) -> Some((simp state e1) * (Constant c4)) 132 | 133 | // A + B + A, A + A + B, ... = 2 * A + B 134 | | Add(e1, Add(e2, e3)) when e1 = e3 -> Some(Constant 2 * e1 + e2) 135 | | Add(e1, Add(e3, e2)) when e1 = e3 -> Some(Constant 2 * e1 + e2) 136 | | Add(Add(e2, e1), e3) when e1 = e3 -> Some(Constant 2 * e1 + e2) 137 | | Add(Add(e1, e2), e3) when e1 = e3 -> Some(Constant 2 * e1 + e2) 138 | 139 | // Tigress OR Simplification 140 | | Add(Negative (BitwiseAnd (e1, e2)), Add (e3, e4)) when e1 = e3 && e2 = e4 -> Some(BitwiseOr(simp state e1, simp state e2)) 141 | 142 | | _ -> None 143 | and (|AssociativitySimplification|_|) (state, expr) = 144 | let permute list = 145 | let rec inserts e = function 146 | | [] -> [[e]] 147 | | x::xs as list -> (e::list)::[for xs' in inserts e xs -> x::xs'] 148 | List.fold (fun accum x -> List.collect (inserts x) accum) [[]] list 149 | 150 | (* Managing associativity in a term rewriting system is far from trivial, so we will simply avoid cycles through a hash table. *) 151 | match expr with 152 | | Add(e1, Add(e2, e3)) -> 153 | Some( 154 | let visitedExpressions = 155 | permute [e1; e2; e3] 156 | |> List.map (fun e -> Add(simp state e[0], simp state (Add(e[1], e[2])))) 157 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 158 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 159 | if visitedExpressions = [] then 160 | Add(simp state e1, simp state (Add(e2, e3))) 161 | else 162 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 163 | ) 164 | 165 | | Add(Add(e1, e2), e3) -> 166 | Some( 167 | let visitedExpressions = 168 | permute [e1; e2; e3] 169 | |> List.map (fun e -> Add(simp state (Add(e[0], e[1])), simp state e[2])) 170 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 171 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 172 | if visitedExpressions = [] then 173 | Add(simp state (Add(e1, e2)), simp state e3) 174 | else 175 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 176 | ) 177 | 178 | | BitwiseOr(e1, BitwiseOr(e2, e3)) -> 179 | Some( 180 | let visitedExpressions = 181 | permute [e1; e2; e3] 182 | |> List.map (fun e -> BitwiseOr(simp state e[0], simp state (BitwiseOr(e[1], e[2])))) 183 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 184 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 185 | if visitedExpressions = [] then 186 | BitwiseOr(simp state e1, simp state (BitwiseOr(e2, e3))) 187 | else 188 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 189 | ) 190 | 191 | | BitwiseOr(BitwiseOr(e2, e3), e1) -> 192 | Some( 193 | let visitedExpressions = 194 | permute [e1; e2; e3] 195 | |> List.map (fun e -> BitwiseOr(simp state (BitwiseOr(e[1], e[2])), simp state e[0])) 196 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 197 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 198 | if visitedExpressions = [] then 199 | BitwiseOr(simp state (BitwiseOr(e2, e3)), simp state e1) 200 | else 201 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 202 | ) 203 | 204 | | BitwiseAnd(e1, BitwiseAnd(e2, e3)) -> 205 | Some( 206 | let visitedExpressions = 207 | permute [e1; e2; e3] 208 | |> List.map (fun e -> BitwiseAnd(simp state e[0], simp state (BitwiseAnd(e[1], e[2])))) 209 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 210 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 211 | if visitedExpressions = [] then 212 | BitwiseAnd(simp state e1, simp state (BitwiseAnd(e2, e3))) 213 | else 214 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 215 | ) 216 | 217 | | BitwiseAnd(BitwiseAnd(e2, e3), e1) -> 218 | Some( 219 | let visitedExpressions = 220 | permute [e1; e2; e3] 221 | |> List.map (fun e -> BitwiseAnd(simp state (BitwiseAnd(e[1], e[2])), simp state e[0])) 222 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 223 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 224 | if visitedExpressions = [] then 225 | BitwiseAnd(simp state (BitwiseAnd(e2, e3)), simp state e1) 226 | else 227 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 228 | ) 229 | 230 | | BitwiseXor(e1, BitwiseXor(e2, e3)) -> 231 | Some( 232 | let visitedExpressions = 233 | permute [e1; e2; e3] 234 | |> List.map (fun e -> BitwiseXor(simp state e[0], simp state (BitwiseXor(e[1], e[2])))) 235 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 236 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 237 | if visitedExpressions = [] then 238 | BitwiseXor(simp state e1, simp state (BitwiseXor(e2, e3))) 239 | else 240 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 241 | ) 242 | 243 | | BitwiseXor(BitwiseXor(e2, e3), e1) -> 244 | Some( 245 | let visitedExpressions = 246 | permute [e1; e2; e3] 247 | |> List.map (fun e -> BitwiseXor(simp state (BitwiseXor(e[1], e[2])), simp state e[0])) 248 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 249 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 250 | if visitedExpressions = [] then 251 | BitwiseXor(simp state (BitwiseXor(e2, e3)), simp state e1) 252 | else 253 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 254 | ) 255 | 256 | | Multiply(e1, Multiply(e2, e3)) -> 257 | Some( 258 | let visitedExpressions = 259 | permute [e1; e2; e3] 260 | |> List.map (fun e -> Multiply(simp state e[0], simp state (Multiply(e[1], e[2])))) 261 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 262 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 263 | if visitedExpressions = [] then 264 | Multiply(simp state e1, simp state (Multiply(e2, e3))) 265 | else 266 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 267 | ) 268 | 269 | | Multiply(Multiply(e1, e2), e3) -> 270 | Some( 271 | let visitedExpressions = 272 | permute [e1; e2; e3] 273 | |> List.map (fun e -> Multiply(simp state (Multiply(e[0], e[1])), simp state e[2])) 274 | |> List.filter(fun e -> not (state.Exploration.Contains(e))) 275 | visitedExpressions |> List.iter(fun e -> state.Exploration <- state.Exploration.Add(e)) 276 | if visitedExpressions = [] then 277 | Multiply(simp state (Multiply(e1, e2)), simp state e3) 278 | else 279 | visitedExpressions |> List.minBy (fun e -> e.Complexity) 280 | ) 281 | 282 | | _ -> None 283 | and (|ArithmeticSimplification|_|) (state, expr) = 284 | match expr with 285 | // Constants result. 286 | | Add (Constant l, Constant r) -> Some(Constant (l + r)) 287 | | Subtract(Constant l, Constant r) -> Some(Constant (l - r)) 288 | | Multiply(Constant l, Constant r) -> Some(Constant(l * r)) 289 | | Divide(Constant l, Constant r) -> Some(Constant (l / r)) 290 | | Modulo(Constant l, Constant r) -> Some(Constant (l % r)) 291 | | Negative(Constant e) -> Some(Constant(-e)) 292 | 293 | // Involution. 294 | // -(-(A)) = A 295 | | Negative(Negative(e)) -> Some(simp state e) 296 | 297 | // Misc 298 | // A + A = 2 * A 299 | | Add(e1, e2) when e1 = e2 -> Some(Constant 2 * simp state e1) 300 | // A - (B * A) = A * (1 - B), ... 301 | | Subtract(e1, Multiply(e2, e3)) when e1 = e3 -> Some((simp state e1) * ((Constant 1) - (simp state e2))) 302 | | Subtract(e1, Multiply(e3, e2)) when e1 = e3 -> Some((simp state e1) * ((Constant 1) - (simp state e2))) 303 | // (A + B) - A = B, ... 304 | | Subtract(Add(e1, e2), e3) when e1 = e3 -> Some((simp state e2)) 305 | | Subtract(Add(e2, e1), e3) when e1 = e3 -> Some((simp state e2)) 306 | // A - (B + A) = -B, ... 307 | | Subtract(e1, Add(e2, e3)) when e1 = e3 -> Some(-(simp state e2)) 308 | | Subtract(e1, Add(e3, e2)) when e1 = e3 -> Some(-(simp state e2)) 309 | // A + (B - A) = B, ... 310 | | Add(e1, Subtract(e2, e3)) when e1 = e3 -> Some((simp state e2)) 311 | | Add(e1, Subtract(e3, e2)) when e1 = e3 -> Some((Constant 2) * (simp state e1) - (simp state e2)) 312 | | Add(Subtract(e2, e1), e3) when e1 = e3 -> Some((simp state e2)) 313 | | Add(Subtract(e1, e2), e3) when e1 = e3 -> Some((Constant 2) * (simp state e1) - (simp state e2)) 314 | // (A - B) - A = - B, ... 315 | | Subtract(e1, Subtract(e2, e3)) when e1 = e3 -> Some((Constant 2) * (simp state e1) - (simp state e2)) 316 | | Subtract(e1, Subtract(e3, e2)) when e1 = e3 -> Some(simp state e2) 317 | | Subtract(Subtract(e2, e1), e3) when e1 = e3 -> Some((simp state e2) - (Constant 2) * (simp state e1)) 318 | | Subtract(Subtract(e1, e2), e3) when e1 = e3 -> Some(-(simp state e2)) 319 | // {1} * {2} + {1} * {3} = {1} * ({2} + {3}) 320 | | Add(Multiply(e1, e2), Multiply(e3, e4)) when e1 = e3 -> Some((simp state e1) * ((simp state e2) + e4)) 321 | | Add(Multiply(e2, e1), Multiply(e3, e4)) when e1 = e3 -> Some((simp state e1) * ((simp state e2) + e4)) 322 | | Add(Multiply(e1, e2), Multiply(e4, e3)) when e1 = e3 -> Some((simp state e1) * ((simp state e2) + e4)) 323 | | Add(Multiply(e2, e1), Multiply(e4, e3)) when e1 = e3 -> Some((simp state e1) * ((simp state e2) + e4)) 324 | | Add(e1, Multiply(e3, Constant e2)) when e1 = e3 -> Some(Constant(1L + e2) * (simp state e1)) 325 | | Add(e1, Multiply(Constant e2, e3)) when e1 = e3 -> Some(Constant (1L + e2) * (simp state e1)) 326 | | Add(Multiply(e1, Constant e2), e3) when e1 = e3 -> Some(Constant (1L + e2) * (simp state e1)) 327 | | Add(Multiply(Constant e2, e1), e3) when e1 = e3 -> Some(Constant (1L + e2) * (simp state e1)) 328 | 329 | | Subtract(Multiply(e1, e2), Multiply(e3, e4)) when e1 = e3 -> Some(((simp state e2) - (simp state e4)) * (simp state e1)) 330 | | Subtract(Multiply(e2, e1), Multiply(e3, e4)) when e1 = e3 -> Some(((simp state e2) - (simp state e4)) * (simp state e1)) 331 | | Subtract(Multiply(e1, e2), Multiply(e4, e3)) when e1 = e3 -> Some(((simp state e2) - (simp state e4)) * (simp state e1)) 332 | | Subtract(Multiply(e2, e1), Multiply(e4, e3)) when e1 = e3 -> Some(((simp state e2) - (simp state e4)) * (simp state e1)) 333 | | Subtract(e1, Multiply(e3, Constant e2)) when e1 = e3 -> Some(Constant (1L - e2) * (simp state e1)) 334 | | Subtract(e1, Multiply(Constant e2, e3)) when e1 = e3 -> Some(Constant (1L - e2) * (simp state e1)) 335 | | Subtract(Multiply(e1, Constant e2), e3) when e1 = e3 -> Some(Constant (e2 - 1L) * (simp state e1)) 336 | | Subtract(Multiply(Constant e2, e1), e3) when e1 = e3 -> Some(Constant (e2 - 1L) * (simp state e1)) 337 | 338 | // Identity and absorbing elements 339 | // A - A = 0 340 | | Subtract(e1, e2) when e1=e2 -> Some(Constant 0) 341 | // 0 - A = -A 342 | | Subtract(Constant 0L, e1) -> Some(Negative(simp state e1)) 343 | // -(0)=0 344 | | Negative(Constant(0L)) -> Some(Constant(0)) 345 | // A - 0 = A 346 | | Subtract(e, Constant(0L)) -> Some(simp state e) 347 | // A + 0 = A 348 | | Add(e, Constant(0L)) -> Some(simp state e) 349 | // 0 + A = A 350 | | Add(Constant(0L), e) -> Some(simp state e) 351 | // A * 1 = A 352 | | Multiply(e, Constant(1L)) -> Some(simp state e) 353 | // 1 * A = A 354 | | Multiply(Constant(1L), e) -> Some(simp state e) 355 | // A * 0 = 0 356 | | Multiply(e, Constant(0L)) -> Some(Constant(0)) 357 | // 0 * A = 0 358 | | Multiply(Constant(0L), e) -> Some(Constant(0)) 359 | // 0 / A = 0 360 | | Divide(Constant(0L), e) -> Some(Constant(0)) 361 | // A / 1 362 | | Divide(e, Constant(1L)) -> Some(simp state e) 363 | // A / A = 1 364 | | Divide(v1, v2) when v1 = v2 -> Some(Constant 1) 365 | // A % A = 0 366 | | Modulo(e1, e2) when e1 = e2 -> Some(Constant 0) 367 | 368 | | _ -> None 369 | and (|BooleanSimplification|_|) (state, expr) = 370 | let is_predicate_true pred = pred > 0L 371 | let is_predicate_false pred = pred <= 0L 372 | match expr with 373 | // Constant result. 374 | // !T = F, !F = T 375 | | LogicNegation(Constant p) -> Some(if is_predicate_true p then Constant 0 else Constant 1) 376 | // T || T = T, T || F = T, ... 377 | | LogicOr(Constant l, Constant r) -> Some(if (is_predicate_true l || is_predicate_true r) then Constant 1 else Constant 0) 378 | // T && T = T, T && F = F, ... 379 | | LogicAnd(Constant l, Constant r) -> Some(if (is_predicate_true l && is_predicate_true r) then Constant 1 else Constant 0) 380 | 381 | // Identity constants. 382 | // A || F = A, F || A = A 383 | | LogicOr(e, Constant p) when is_predicate_false p -> Some(simp state e) 384 | | LogicOr(Constant p, e) when is_predicate_false p -> Some(simp state e) 385 | 386 | // A && T = A, T && A = A 387 | | LogicAnd(e, Constant p) when is_predicate_true p -> Some(simp state e) 388 | | LogicAnd(Constant p, e) when is_predicate_true p -> Some(simp state e) 389 | 390 | // A || T = T, T && A = T 391 | | LogicOr(e, Constant p) when is_predicate_true p -> Some(Constant 1) 392 | | LogicOr(Constant p, e) when is_predicate_true p -> Some(Constant 1) 393 | 394 | // A && 0 = F, 0 && A = F 395 | | LogicAnd(e, Constant p) when is_predicate_false p -> Some(Constant 0) 396 | | LogicAnd(Constant p, e) when is_predicate_false p -> Some(Constant 0) 397 | 398 | // Involution 399 | // !(!A)) = A 400 | | LogicNegation(LogicNegation(e)) -> Some(simp state e) 401 | 402 | | _ -> None 403 | 404 | and (|BitwiseGenericSimplification|_|) (state, expr) = 405 | match expr with 406 | // Identity constants. 407 | // A|A = A 408 | | BitwiseOr(l, r) when l = r -> Some(simp state l) 409 | // A|0 = A 410 | | BitwiseOr(l, Constant 0L) -> Some(simp state l) 411 | | BitwiseOr(Constant 0L, e1) -> Some((simp state e1)) 412 | // A&A = A 413 | | BitwiseAnd(l, r) when l = r -> Some(simp state l) 414 | // A^0 = A 415 | | BitwiseXor(l, Constant 0L) -> Some(simp state l) 416 | | BitwiseXor(Constant 0L, l) -> Some(simp state l) 417 | 418 | // A&-1 = A 419 | | BitwiseAnd(l, Constant -1L) -> Some(simp state l) 420 | | BitwiseAnd(Constant -1L, l) -> Some(simp state l) 421 | 422 | // A<<0 = A 423 | | BitwiseLeftShift(l, Constant 0L) -> Some(simp state l) 424 | // A>>0 = A 425 | | BitwiseRightShift(l, Constant 0L) -> Some(simp state l) 426 | 427 | // Constant result. 428 | // A&0 = 0 429 | | BitwiseAnd(l, Constant 0L) -> Some(Constant 0) 430 | | BitwiseAnd(Constant 0L, l) -> Some(Constant 0) 431 | // A^A = A 432 | | BitwiseXor(l, r) when l = r -> Some(Constant 0) 433 | // A&~A = 0 434 | | BitwiseAnd(BitwiseNegation(e2), e1) when e1 = e2 -> Some(Constant 0) 435 | | BitwiseAnd(e1, BitwiseNegation(e2)) when e1 = e2 -> Some(Constant 0) 436 | // A|-1 = -1 437 | | BitwiseOr(e1, Constant -1L) -> Some(Constant -1) 438 | | BitwiseOr(Constant -1L, e1) -> Some(Constant -1) 439 | // A+(~A) = -1 440 | | Add(e1, BitwiseNegation e2) when e1 = e2 -> Some(Constant -1) 441 | | Add(BitwiseNegation e2, e1) when e1 = e2 -> Some(Constant -1) 442 | // A^(~A) = -1 443 | | BitwiseXor(e1, BitwiseNegation e2) when e1 = e2 -> Some(Constant -1) 444 | | BitwiseXor(BitwiseNegation e2, e1) when e1 = e2 -> Some(Constant -1) 445 | // A|(~A) = -1 446 | | BitwiseOr(e1, BitwiseNegation e2) when e1 = e2 -> Some(Constant -1) 447 | | BitwiseOr(BitwiseNegation e2, e1) when e1 = e2 -> Some(Constant -1) 448 | // ~(~(A)) = A 449 | | BitwiseNegation(BitwiseNegation(e)) -> Some(simp state e) 450 | 451 | // NOT conversion. 452 | // A^-1 = ~A 453 | | BitwiseXor(e1, Constant -1L) -> Some(BitwiseNegation(simp state e1)) 454 | | BitwiseXor(Constant -1L, e1) -> Some(BitwiseNegation(simp state e1)) 455 | 456 | // Simplify shifts 457 | // A << C = A * (2^C) 458 | // Does not reduce complexity but provides a single representation for other simplifications 459 | | BitwiseLeftShift(e, Constant c1) -> Some(Multiply(Constant (pown 2 (int(c1))), simp state e)) 460 | 461 | // (~A|~B) = ~(A&B) 462 | | BitwiseOr (BitwiseNegation (e1), BitwiseNegation (e2)) -> Some(BitwiseNegation(BitwiseAnd(simp state e1, simp state e2))) 463 | | _ -> None 464 | and (|TigressSimplification|_|) (state, expr) = 465 | let repeat n fn = List.init (abs(n)) (fun _ -> fn) |> List.reduce (>>) 466 | match expr with 467 | // A-C = ~(-(...(A))) 468 | | Subtract(a, Constant 1L) -> 469 | Some( 470 | [simp state a |> repeat ((int) 1) (fun e -> BitwiseNegation(Negative(e)))] 471 | |> List.minBy (fun e -> e.Complexity) 472 | ) 473 | // A+C = -(~(...(A))) 474 | | Add(a, Constant 1L) -> 475 | Some( 476 | [simp state a |> repeat ((int) 1) (fun e -> Negative(BitwiseNegation(e))); Add(Constant 1, simp state a)] 477 | |> List.minBy (fun e -> e.Complexity) 478 | ) 479 | // C+A = -(~(...(A))) 480 | | Add(Constant 1L, a) -> 481 | Some( 482 | [simp state a |> repeat ((int) 1) (fun e -> Negative(BitwiseNegation(e))); Add(Constant 1, simp state a)] 483 | |> List.minBy (fun e -> e.Complexity) 484 | ) 485 | 486 | // NEG Simplification 487 | // -~A + ~-B = A + B 488 | | Add(Negative (BitwiseNegation (e1)), BitwiseNegation (Negative (e2))) -> Some((simp state e1) + (simp state e2)) 489 | | Add(BitwiseNegation (Negative (e2)), Negative (BitwiseNegation (e1))) -> Some((simp state e1) + (simp state e2)) 490 | 491 | // OR Simplification. 492 | // ((A&~B)+B) = A|B 493 | | Add (BitwiseAnd (e1, BitwiseNegation (e2)), e3) when e2 = e3 -> Some(BitwiseOr(simp state e1, simp state e2)) 494 | | Add (e3, BitwiseAnd (e1, BitwiseNegation (e2))) when e2 = e3 -> Some(BitwiseOr(simp state e1, simp state e2)) 495 | | Add (e1, Subtract (e2, BitwiseAnd (e3, e4))) when e1=e3 && e2=e4 -> Some(BitwiseOr(simp state e1, simp state e2)) 496 | 497 | // ((-~A) + ~(A & B)) = A&~B 498 | | Add(Negative (BitwiseNegation (e1)), BitwiseNegation (BitwiseAnd (e3, e4))) when e1=e3 -> Some(BitwiseAnd(simp state e1, BitwiseNegation(simp state e4))) 499 | 500 | // XOR Simplification. 501 | // ((A|B) - (A&B)) = A^B 502 | | Subtract(BitwiseOr (e1, e2), BitwiseAnd (e3, e4)) when e1=e3 && e2=e4 -> Some(BitwiseXor(simp state e1, simp state e2)) 503 | // (((A-B) - ((A|~B) * 2))) = (A^B)+2 504 | | Subtract(Subtract (e1, e2), Multiply (BitwiseOr (e3, BitwiseNegation (e4)), Constant 2L)) when e1=e3 && e2=e4 -> Some(Add(BitwiseXor(simp state e1, simp state e2), Constant 2L)) 505 | // (((A-B) - 2 * ((A|~B)))) = (A^B)+2 506 | | Subtract(Subtract (e1, e2), Multiply (Constant 2L, BitwiseOr (e3, BitwiseNegation (e4)))) when e1=e3 && e2=e4 -> Some(Add(BitwiseXor(simp state e1, simp state e2), Constant 2L)) 507 | 508 | // AND simplification. 509 | // (~A|B) - ~A = A&B 510 | | Subtract(BitwiseOr (BitwiseNegation (e1), e2), BitwiseNegation (e3)) when e1 = e3 -> Some(BitwiseAnd ((simp state e1), simp state e2)) 511 | // A + -(~(~A|B)) = A&B 512 | | Add(e1, Negative(BitwiseNegation (BitwiseOr (BitwiseNegation (e2), e3)))) when e1 = e2 -> Some(BitwiseAnd(simp state e1, simp state e3)) 513 | // -~A + ~A | B = A&B 514 | | Add(Negative (BitwiseNegation (e1)), BitwiseOr (BitwiseNegation (e2), e3)) when e1 = e2 -> Some((simp state e1) &&& (simp state e3)) 515 | 516 | // Sub simplification 517 | // ((A ^ B) - 2 * ((~ A & B))) = A - B 518 | | Subtract(BitwiseXor (e1, e2),Multiply (Constant 2L, BitwiseAnd (BitwiseNegation (e3), e4))) when e1=e3 && e2=e4 -> Some((simp state e1) - (simp state e2)) 519 | // ((A & ~ B) - (~ A & B)) = A - B 520 | | Subtract(BitwiseAnd (e1, BitwiseNegation (e2)), BitwiseAnd (BitwiseNegation (e3), e4)) when e1=e3 && e2=e4 -> Some((simp state e1) - (simp state e2)) 521 | // ((2 * (A & ~ B)) - (A ^ B)) = A - B 522 | | Subtract(Multiply (Constant 2L, BitwiseAnd (e1, BitwiseNegation (e2))), BitwiseXor (e3, e4)) when e1=e3 && e2=e4 -> Some((simp state e1) - (simp state e2)) 523 | 524 | // Addition simplification. 525 | // ~(-(A - ~B)) = A + B 526 | | BitwiseNegation (Negative (Subtract (e1, BitwiseNegation (e2)))) -> Some((simp state e1) + (simp state e2)) 527 | | BitwiseNegation (Negative (Subtract (e1, Constant c1))) when c1 < 0 -> Some((simp state e1) + Constant (abs(c1) - 1L)) 528 | 529 | // (A^B) + (A & B) * 2, (A & B) * 2 + (A^B) = A + B 530 | | Add(BitwiseXor (e1, e2), Multiply (Constant 2L, BitwiseAnd (e3, e4))) when e1=e3 && e2=e4 -> Some((simp state e1) + (simp state e2)) 531 | | Add(Multiply (Constant 2L, BitwiseAnd (e3, e4)), BitwiseXor (e1, e2)) when e1=e3 && e2=e4 -> Some((simp state e1) + (simp state e2)) 532 | | Add(BitwiseXor (e1, BitwiseNegation (e2)), Multiply (Constant 2L, BitwiseOr (e3, e4))) when e1=e3 && e2=e4 -> Some(((simp state e1) + (simp state e2)) - Constant 1L) 533 | | Add(Multiply (Constant 2L, BitwiseOr (e3, e4)), BitwiseXor (e1, BitwiseNegation (e2))) when e1=e3 && e2=e4 -> Some(((simp state e1) + (simp state e2)) - Constant 1L) 534 | | Add(Multiply (Constant 2L, BitwiseOr (e1, e2)), Negative (BitwiseNegation (BitwiseXor (e3, BitwiseNegation (e4))))) when e1=e3 && e2=e4 -> Some((simp state e1) + (simp state e2)) 535 | 536 | // (((A | B) << 1) - (A ^ B)) = A + B 537 | | Add(Negative (BitwiseNegation (BitwiseXor (e3, BitwiseNegation (e4)))), Multiply (Constant 2L, BitwiseOr (e1, e2))) when e1=e3 && e2=e4 -> Some((simp state e1) + (simp state e2)) 538 | | Subtract(Multiply (Constant 2L, BitwiseOr (e1, e2)), BitwiseXor (e3, e4)) when e1=e3 && e2=e4 -> Some((simp state e1) + (simp state e2)) 539 | 540 | // ((A | B) + (A & B)) = A + B 541 | | Add (BitwiseOr (e1, e2), BitwiseAnd (e3, e4)) when e1=e3 && e2=e4 -> Some((simp state e1) + (simp state e2)) 542 | | Add(Negative (BitwiseNegation (BitwiseXor (a, Constant c1))), Multiply (Constant 2L, BitwiseOr (b, Constant c2))) 543 | when a = b && c1 < 0 && c2 > 0 && (c1 = ~~~c2) -> Some((simp state a) + (Constant c2)) 544 | | Add(Multiply (Constant 2L, BitwiseOr (b, Constant c2)), Negative (BitwiseNegation (BitwiseXor (a, Constant c1)))) 545 | when a = b && c1 < 0 && c2 > 0 && (c1 = ~~~c2) -> Some((simp state a) + (Constant c2)) 546 | | Add(Negative(BitwiseNegation(Multiply (Constant 2L, BitwiseOr (a, Constant c2)))),BitwiseXor (b, Constant c1)) 547 | when a = b && c1 < 0 && c2 > 0 && (c1 = ~~~c2) -> Some((simp state a) + (Constant c2)) 548 | | Add(BitwiseXor (b, Constant c1), Negative(BitwiseNegation(Multiply (Constant 2L, BitwiseOr (a, Constant c2))))) 549 | when a = b && c1 < 0 && c2 > 0 && (c1 = ~~~c2) -> Some((simp state a) + (Constant c2)) 550 | | Add(BitwiseXor (b, BitwiseNegation(e1)), Negative(BitwiseNegation(Multiply (Constant 2L, BitwiseOr (a, e2))))) 551 | when a = b && e1=e2 -> Some((simp state a) + simp state e1) 552 | | Add(Negative(BitwiseNegation(Multiply (Constant 2L, BitwiseOr (a, e2)))), BitwiseXor (b, BitwiseNegation(e1))) 553 | when a = b && e1=e2 -> Some((simp state a) + simp state e1) 554 | | Add(Negative (BitwiseNegation (BitwiseXor (a, BitwiseNegation(e1)))), Multiply (Constant 2L, BitwiseOr (b, e2))) 555 | when a = b && e1=e2 -> Some((simp state a) + simp state e1) 556 | | Add(Multiply (Constant 2L, BitwiseOr (b, e1)), Negative (BitwiseNegation (BitwiseXor (a, BitwiseNegation(e2))))) 557 | when a = b && e1=e2 -> Some((simp state a) + simp state e1) 558 | 559 | | _ -> None 560 | and (|RelationalSimplification|_|) (state, expr) = 561 | match expr with 562 | // Invert comparison. 563 | // ~(A>B) = A<=B 564 | | BitwiseNegation(GreaterThan(l, r)) -> Some(LessOrEqual(simp state l, simp state r)) 565 | // ~(A>=B) = A Some(LessThan(simp state l, simp state r)) 567 | // ~(A==B) = A!=B 568 | | BitwiseNegation(Equal(l, r)) -> Some(NotEqual(simp state l, simp state r)) 569 | // ~(A!=B) = A==B 570 | | BitwiseNegation(NotEqual(l, r)) -> Some(Equal(simp state l, simp state r)) 571 | // ~(A<=B) = A>B 572 | | BitwiseNegation(LessOrEqual(l, r)) -> Some(GreaterThan(simp state l, simp state r)) 573 | // ~(A= A>=B 574 | | BitwiseNegation(LessThan(l, r)) -> Some(GreaterOrEqual(simp state l, simp state r)) 575 | 576 | // Comparison result. 577 | (* a == a, a == b, a != a, a != b*) 578 | | Equal(l, r) when l = r -> Some(Constant(1)) 579 | | Equal(l, r) when l <> r -> Some(Constant(0)) 580 | | NotEqual(l, r) when l = r -> Some(Constant(0)) 581 | | NotEqual(l, r) when l <> r -> Some(Constant(1)) 582 | 583 | // Constant result. 584 | | GreaterThan(Constant l, Constant r) -> Some(if l > r then Constant 1 else Constant 0) 585 | | GreaterOrEqual(Constant l, Constant r) -> Some(if l >= r then Constant 1 else Constant 0) 586 | | LessOrEqual(Constant l, Constant r) -> Some(if l <= r then Constant 1 else Constant 0) 587 | | LessThan(Constant l, Constant r) -> Some(if l < r then Constant 1 else Constant 0) 588 | 589 | | _ -> None 590 | and (|BaseSimplification|_|) (state, expr) = 591 | match expr with 592 | // Arithmetic. 593 | | Add (l, r) -> Some((simp state l) + (simp state r)) 594 | | Subtract (l, r) -> Some((simp state l) - (simp state r)) 595 | | Multiply (l, r) -> Some((simp state l) * (simp state r)) 596 | | Divide (l, r) -> Some((simp state l) / (simp state r)) 597 | | Modulo (l, r) -> Some((simp state l) % (simp state r)) 598 | | Negative e -> Some(Negative(simp state e)) 599 | 600 | // Logic. 601 | | LogicNegation(e) -> Some(LogicNegation((simp state e))) 602 | | LogicAnd(l, r) -> Some(LogicAnd((simp state l), (simp state r))) 603 | | LogicOr(l, r) -> Some(LogicOr((simp state l), (simp state r))) 604 | 605 | // Relational. 606 | | Equal(l, r) -> Some((simp state l) === (simp state r)) 607 | | NotEqual(l, r) -> Some((simp state l) <=> (simp state r)) 608 | | GreaterThan(l, r) -> Some(GreaterThan((simp state l), (simp state r))) 609 | | GreaterOrEqual(l, r) -> Some(GreaterOrEqual((simp state l), (simp state r))) 610 | | LessOrEqual(l, r) -> Some(LessOrEqual((simp state l), (simp state r))) 611 | | LessThan(l, r) -> Some(LessThan((simp state l), (simp state r))) 612 | 613 | // Bitwise. 614 | | BitwiseLeftShift(Constant l, Constant r) -> Some(Constant (l <<< int(r))) 615 | | BitwiseRightShift(Constant l, Constant r) -> Some(Constant (l >>> int(r))) 616 | | BitwiseAnd(Constant l, Constant r) -> Some(Constant (l &&& r)) 617 | | BitwiseOr(Constant l, Constant r) -> Some(Constant (l ||| r)) 618 | | BitwiseXor(Constant l, Constant r) -> Some(Constant (l ^^^ r)) 619 | | BitwiseNegation(Constant e) -> Some(Constant(~~~e)) 620 | | BitwiseLeftShift(l, r) -> Some(BitwiseLeftShift((simp state l), (simp state r))) 621 | | BitwiseRightShift(l, r) -> Some(BitwiseRightShift((simp state l), (simp state r))) 622 | | BitwiseAnd(l, r) -> Some((simp state l) &&& (simp state r)) 623 | | BitwiseOr(l, r) -> Some((simp state l) ||| (simp state r)) 624 | | BitwiseXor(l, r) -> Some((simp state l) ^^^ (simp state r)) 625 | | BitwiseNegation e -> Some(~~~(simp state e)) 626 | 627 | | _ -> None 628 | 629 | and evalFunction state name expr = 630 | match (state, name) with 631 | | StorageUnknown _ -> failwith (sprintf "function %s doesn't exist" name.Key) 632 | | StorageSymbol v -> match v with 633 | | Function f -> f (eval state expr) 634 | | _ -> failwith (sprintf "%s is not a function" name.Key) 635 | 636 | and evalVariable state name = 637 | match (state, name) with 638 | | StorageUnknown _ -> Variable name 639 | | StorageSymbol v -> match v with 640 | | Value f -> f 641 | | _ -> failwith (sprintf "%s is not a value" name.Key) 642 | 643 | let executeStatement state statement = 644 | let setMem state key value = { state with MemoryMap = Map.add key value state.MemoryMap } 645 | 646 | match statement with 647 | | Single expression -> 648 | let result = (eval state expression) 649 | state.Exploration <- Set.empty 650 | state.ExpressionsCache <- Map.empty 651 | let newState = setMem state "_" (Value result) 652 | SingleResult (newState, result) 653 | 654 | | Assignment expressions -> 655 | let applyUpdate (state, results) (name, expr) = 656 | let result = (eval state expr) 657 | state.Exploration <- Set.empty 658 | state.ExpressionsCache <- Map.empty 659 | let newState = setMem state name.Key (Value result) 660 | (newState, (name.Key, result)::results) 661 | 662 | let newState, assignments = List.fold applyUpdate (state, []) expressions 663 | AssignmentResult (newState, List.rev assignments) --------------------------------------------------------------------------------