├── 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)
--------------------------------------------------------------------------------