├── api_calls_viz ├── requirements.txt ├── README.md ├── api_calls_vis.py └── api_calls_vis_python3.py ├── pictures ├── Gootkit-CNC.png ├── Gootkit-query.png ├── api_calls_vis.png ├── Gootkit-batfile.png ├── Gootkit-envstr2.png ├── Gootkit-getcmd.png ├── Gootkit-regopen.png ├── Gootkit-strstr.png ├── gootkit-trololo.png ├── EmbusteBot-loadlib.png ├── Gootkit-allocmem.png ├── Gootkit-enumerate.png ├── Gootkit-executing.png ├── Gootkit-getcontext.png ├── Gootkit-newproc2.png ├── NotPetya-encrypt.png ├── NotPetya-look_path.png ├── NotPetya-privilege.png ├── gootkit-strstriw.png ├── DrLtrace-Performance.png ├── Gootkit-shellexecute.png ├── gootkit-injectmemory.png ├── EmbusteBot-getwindowtxt.png ├── EmbusteBot-uuidcreate.png ├── Gootkit-createsection.png ├── Gootkit-filteredscript.png ├── NotPetya-crypt_and_find.png ├── NotPetya-overwrite_mbr.png ├── EmbusteBot-getfileattrib.png ├── EmbusteBot-getwindowtxt2.png ├── EmbusteBot-setwindowhook.png ├── EmbusteBot-scan_class_name1.png ├── EmbusteBot-scan_class_name2.png ├── wannacry_truncated_log_html.png ├── EmbusteBot-take_window_screen.png └── Gootkit_list_of_dropped_files.png ├── .gitmodules ├── drltrace_src ├── scripts │ ├── remove_duplicates.py │ ├── find_duplicates.py │ ├── drltrace.ps1 │ ├── drltrace_test_expected.log │ ├── test_drltrace_out.py │ └── headers_parser.py ├── drltrace_options.h ├── drltrace_linux.config ├── drsyscall_app.c ├── runtest.cmake ├── filter.config ├── drltrace_utils.cpp ├── drltrace_options.cpp ├── test.cmake ├── drltrace.h ├── drltrace_utils.h ├── drltrace_libcalls.cpp ├── drltrace_frontend.cpp ├── CMakeLists.txt └── drltrace.cpp ├── .travis.yml ├── .appveyor.yml ├── LICENSE └── README.md /api_calls_viz/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | numpy 3 | Pillow 4 | argparse -------------------------------------------------------------------------------- /pictures/Gootkit-CNC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-CNC.png -------------------------------------------------------------------------------- /pictures/Gootkit-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-query.png -------------------------------------------------------------------------------- /pictures/api_calls_vis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/api_calls_vis.png -------------------------------------------------------------------------------- /pictures/Gootkit-batfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-batfile.png -------------------------------------------------------------------------------- /pictures/Gootkit-envstr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-envstr2.png -------------------------------------------------------------------------------- /pictures/Gootkit-getcmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-getcmd.png -------------------------------------------------------------------------------- /pictures/Gootkit-regopen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-regopen.png -------------------------------------------------------------------------------- /pictures/Gootkit-strstr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-strstr.png -------------------------------------------------------------------------------- /pictures/gootkit-trololo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/gootkit-trololo.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-loadlib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-loadlib.png -------------------------------------------------------------------------------- /pictures/Gootkit-allocmem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-allocmem.png -------------------------------------------------------------------------------- /pictures/Gootkit-enumerate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-enumerate.png -------------------------------------------------------------------------------- /pictures/Gootkit-executing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-executing.png -------------------------------------------------------------------------------- /pictures/Gootkit-getcontext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-getcontext.png -------------------------------------------------------------------------------- /pictures/Gootkit-newproc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-newproc2.png -------------------------------------------------------------------------------- /pictures/NotPetya-encrypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/NotPetya-encrypt.png -------------------------------------------------------------------------------- /pictures/NotPetya-look_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/NotPetya-look_path.png -------------------------------------------------------------------------------- /pictures/NotPetya-privilege.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/NotPetya-privilege.png -------------------------------------------------------------------------------- /pictures/gootkit-strstriw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/gootkit-strstriw.png -------------------------------------------------------------------------------- /pictures/DrLtrace-Performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/DrLtrace-Performance.png -------------------------------------------------------------------------------- /pictures/Gootkit-shellexecute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-shellexecute.png -------------------------------------------------------------------------------- /pictures/gootkit-injectmemory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/gootkit-injectmemory.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-getwindowtxt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-getwindowtxt.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-uuidcreate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-uuidcreate.png -------------------------------------------------------------------------------- /pictures/Gootkit-createsection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-createsection.png -------------------------------------------------------------------------------- /pictures/Gootkit-filteredscript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit-filteredscript.png -------------------------------------------------------------------------------- /pictures/NotPetya-crypt_and_find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/NotPetya-crypt_and_find.png -------------------------------------------------------------------------------- /pictures/NotPetya-overwrite_mbr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/NotPetya-overwrite_mbr.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-getfileattrib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-getfileattrib.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-getwindowtxt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-getwindowtxt2.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-setwindowhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-setwindowhook.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-scan_class_name1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-scan_class_name1.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-scan_class_name2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-scan_class_name2.png -------------------------------------------------------------------------------- /pictures/wannacry_truncated_log_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/wannacry_truncated_log_html.png -------------------------------------------------------------------------------- /pictures/EmbusteBot-take_window_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/EmbusteBot-take_window_screen.png -------------------------------------------------------------------------------- /pictures/Gootkit_list_of_dropped_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mxmssh/drltrace/HEAD/pictures/Gootkit_list_of_dropped_files.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "drltrace_src/dynamorio"] 2 | path = drltrace_src/dynamorio 3 | url = git://github.com/DynamoRIO/dynamorio.git 4 | -------------------------------------------------------------------------------- /drltrace_src/scripts/remove_duplicates.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | content = open(sys.argv[1], 'r').readlines() 6 | 7 | names = dict() 8 | 9 | for line in content: 10 | line = line[:-1] 11 | if "|" not in line or "#" in line: 12 | print(line) 13 | continue 14 | name = line.split("|")[1] 15 | try: 16 | names[name] += 1 17 | continue 18 | except: 19 | #print name 20 | names[name] = 1 21 | print(line) 22 | 23 | -------------------------------------------------------------------------------- /drltrace_src/scripts/find_duplicates.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | content = open(sys.argv[1], 'r').readlines() 6 | 7 | names = dict() 8 | 9 | for line in content: 10 | if "|" not in line or "#" in line: 11 | continue 12 | line = line.split("|") 13 | name = line[1] 14 | try: 15 | names[name] += 1 16 | except: 17 | #print name 18 | names[name] = 1 19 | result = sorted( ((v,k) for k,v in names.iteritems()), reverse=True) 20 | for count, element in result: 21 | if count > 1: 22 | print "\"%s\":\"%s\", " % (count, element) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: 2 | - c 3 | - cpp 4 | 5 | os: linux 6 | compiler: gcc 7 | 8 | sudo: required 9 | dist: bionic 10 | 11 | env: 12 | - BUILD=x86_64-linux-gnu_release 13 | CXXFLAGS=-m64 CFLAGS=-m64 LDFLAGS=-m64 14 | DEBUG_MODE=OFF 15 | - BUILD=x86_64-linux-gnu_debug 16 | CXXFLAGS=-m64 CFLAGS=-m64 LDFLAGS=-m64 17 | DEBUG_MODE=ON 18 | #- BUILD=i686-linux-gnu_release 19 | # CXXFLAGS=-m32 CFLAGS=-m32 20 | # DEBUG_MODE=OFF 21 | - BUILD=i686-linux-gnu_debug 22 | CXXFLAGS=-m32 CFLAGS=-m32 23 | DEBUG_MODE=ON 24 | 25 | install: 26 | - sudo apt-get install gcc-multilib g++-multilib 27 | 28 | before_script: 29 | - "mkdir build" 30 | - "cd build" 31 | - cmake -DBUILD_TOOL_TESTS=ON -DDEBUG=$DEBUG_MODE ../drltrace_src/ 32 | 33 | script: 34 | - "make" 35 | - "ctest -VV" 36 | -------------------------------------------------------------------------------- /api_calls_viz/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | The script allows to perform visualization of a trace of library calls provided in the following format 4 | (arguments are ignored): 5 | 6 | ```!``` 7 | 8 | Each unique library call is assigned with unique color from RGB or grayscale pallette. The tool generates an 9 | image where each pixel represents library call from a trace. 10 | 11 | **Example:** 12 | 13 | While the script was mainly developed to visualize an output of drltrace, the script is possible to use for custom traces provided in the appropriate format. 14 | 15 | # Dependencies 16 | 17 | matplotlib 18 | 19 | Pillow 20 | 21 | # Usage 22 | 23 | ```api_calls_vis.py -i [jpeg image name] -ht [html image name] -gr -t [trace_name] 24 | api_calls_vis.py -i calc.jpeg -ht calc.html -gr -t drltrace.calc.exe.00864.0000.log 25 | ``` 26 | 27 | Arguments: 28 | 29 | ``` 30 | -h, --help show the help message and exit 31 | -t TRACE, --trace TRACE A trace file with API calls 32 | -i IMAGE, --image IMAGE A name of image file 33 | -ht HTML, --html HTML A name of html page (slow) 34 | -gr, --grayscale Generate an image in grayscale 35 | ``` 36 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | # AppVeyor CI configuration: 2 | 3 | # We don't do a shallow clone of just the source archive as we want to get 4 | # the diff for source file checks. 5 | # We do limit to the last few commits to speed things up: 6 | clone_depth: 10 7 | 8 | # Don't run on pushes to feature branches: pull requests cover those. 9 | branches: 10 | only: 11 | - master 12 | 13 | platform: x64 14 | 15 | build: 16 | parallel: true 17 | 18 | environment: 19 | matrix: 20 | - GENERATOR: Visual Studio 12 21 | CONFIG: Debug 22 | MSVC_PLATFORM: x86 23 | 24 | - GENERATOR: Visual Studio 12 25 | CONFIG: RelWithDebInfo 26 | MSVC_PLATFORM: x86 27 | 28 | - GENERATOR: Visual Studio 12 Win64 29 | CONFIG: Debug 30 | MSVC_PLATFORM: x86_amd64 31 | 32 | - GENERATOR: Visual Studio 12 Win64 33 | CONFIG: RelWithDebInfo 34 | MSVC_PLATFORM: x86_amd64 35 | 36 | before_build: 37 | - call "c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %MSVC_PLATFORM% 38 | - cd c:\projects\drltrace 39 | - git submodule update --init --recursive 40 | - mkdir build 41 | - cd build 42 | 43 | build_script: 44 | #parallel: true 45 | - cmake "-G%GENERATOR%" -DCMAKE_BUILD_TYPE=%CONFIG% -DBUILD_TOOL_TESTS=ON ..\drltrace_src\ 46 | - cmake --build . --config "%CONFIG%" 47 | 48 | test_script: 49 | - ctest -VV 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Maksim Shudrak 4 | 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 met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (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 | -------------------------------------------------------------------------------- /drltrace_src/scripts/drltrace.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ## This script checks if the PE file given is compiled to 32 or 64-bits 3 | ## and call the right version of drltrace. 4 | ## It expects to have drltrace_win32 and drltrace_win64 folders at the same 5 | ## directory level. 6 | #> 7 | 8 | # Optional default parameters for drltrace 9 | #Set-Variable drltrace_params -option Constant '-print_ret_addr -only_from_app' 10 | 11 | function Get-ScriptDirectory { 12 | Split-Path -parent $PSCommandPath 13 | } 14 | 15 | function Get-PE-Arch($path) { 16 | $buf = New-Object byte[] 512 17 | $fs = New-Object IO.FileStream($path, [IO.FileMode]::Open, [IO.FileAccess]::Read) 18 | $num = 0 19 | $fs.Read($buf, $num, 512) > $null 20 | $fs.Close() 21 | 22 | # Check MZ header 23 | if ($buf[0] -ne 0x4d -or $buf[1] -ne 0x5a) { 24 | return "" 25 | } 26 | 27 | # DOS header has a pointer to the PE signature at 0x3c 28 | $pesig = [System.BitConverter]::ToUInt32($buf, 0x3c) 29 | # PE signature is 4 bytes long and it's followed by a 2-byte Machine ID 30 | $machine = [System.BitConverter]::ToUInt16($buf, $pesig + 4) 31 | 32 | if ($machine -eq 0x8664) { 33 | return 64 34 | } 35 | return 32 36 | } 37 | 38 | if ($args.count -lt 1) { 39 | cmd /c "$(Get-ScriptDirectory)\drltrace_win32\bin\drltrace.exe" 40 | exit 41 | } 42 | 43 | # Set $file to the *last* argument given 44 | $file = $args[$args.count - 1] 45 | 46 | if (![System.IO.File]::Exists($file)) { 47 | # If the file does not exist, call drltrace and let it handles that 48 | cmd /c "$(Get-ScriptDirectory)\drltrace_win32\bin\drltrace.exe $args" 49 | exit 50 | } 51 | 52 | $arch = Get-PE-Arch($file) 53 | 54 | if ($arch -eq 32) { 55 | cmd /c "$(Get-ScriptDirectory)\drltrace_win32\bin\drltrace.exe $drltrace_params $args" 56 | } elseif ($arch -eq 64) { 57 | cmd /c "$(Get-ScriptDirectory)\drltrace_win64\bin64\drltrace.exe $drltrace_params $args" 58 | } -------------------------------------------------------------------------------- /drltrace_src/drltrace_options.h: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | #include "droption.h" 34 | 35 | extern droption_t op_logdir; 36 | extern droption_t op_only_from_app; 37 | extern droption_t op_follow_children; 38 | extern droption_t op_print_ret_addr; 39 | extern droption_t op_unknown_args; 40 | extern droption_t op_max_args; 41 | extern droption_t op_config_file_default; 42 | extern droption_t op_filter_file; 43 | extern droption_t op_config_file; 44 | extern droption_t op_ignore_underscore; 45 | extern droption_t op_only_to_lib; 46 | extern droption_t op_help; 47 | extern droption_t op_version; 48 | extern droption_t op_verbose; 49 | extern droption_t op_use_config; 50 | extern droption_t op_ltracelib_ops; 51 | extern droption_t op_grepable; 52 | -------------------------------------------------------------------------------- /drltrace_src/drltrace_linux.config: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # Copyright (c) 2017 Google, Inc. All rights reserved. 3 | # *************************************************************************** 4 | # 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # * Neither the name of Google, Inc. nor the names of its contributors may be 17 | # used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | # DAMAGE. 31 | # 32 | 33 | # *************************************************************************** 34 | # drltrace default config file for library call arguments printing in Linux. 35 | 36 | # The syntax of this config file is simple. To add a new function, it's enough to specify 37 | # function's return type, function name and each argument of the function separated by 38 | # a pipe | symbol. A token __out is used to mark output arguments and __inout is used to 39 | # mark input+output arguments. 40 | # Example: int strcmp (char *, char *) -> int|strcmp|char *|char * 41 | # i#1948: The syntax now does not cover many other more complex function prototypes 42 | # e.g. atexit(). We need to improve that. 43 | # 44 | # NOTE: the syntax is not space sensitive. 45 | 46 | int|strcmp|char *|char * 47 | int|wcscmp|wchar *|wchar * 48 | int|printf|char * 49 | int|puts|char * 50 | int|open|char *|int 51 | -------------------------------------------------------------------------------- /drltrace_src/drsyscall_app.c: -------------------------------------------------------------------------------- 1 | /* ********************************************************** 2 | * Copyright (c) 2012-2014 Google, Inc. All rights reserved. 3 | * **********************************************************/ 4 | 5 | /* Dr. Memory: the memory debugger 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; 10 | * version 2.1 of the License, and no later version. 11 | 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | /* Test of the Dr. Syscall Extension */ 23 | 24 | #include 25 | #include 26 | #ifdef UNIX 27 | # include 28 | # include 29 | # include 30 | # include 31 | # include 32 | # ifdef MACOS 33 | # include 34 | # include 35 | # else 36 | # include 37 | # include 38 | # include 39 | # endif 40 | #else 41 | # include 42 | #endif 43 | 44 | static void 45 | syscall_test(void) 46 | { 47 | #ifdef UNIX 48 | int fd = open("/dev/null", O_WRONLY); 49 | int *uninit = (int *) malloc(sizeof(*uninit)); 50 | write(fd, uninit, sizeof(*uninit)); 51 | free(uninit); 52 | #else 53 | MEMORY_BASIC_INFORMATION mbi; 54 | void **uninit = (void **) malloc(sizeof(*uninit)); 55 | VirtualQuery(*uninit, &mbi, sizeof(mbi)); 56 | free(uninit); 57 | #endif 58 | } 59 | 60 | #ifdef UNIX 61 | static void 62 | socket_test(void) 63 | { 64 | int s; 65 | int i; 66 | socklen_t addrlen; 67 | 68 | s = socket(AF_INET, SOCK_STREAM, 0); 69 | struct sockaddr_in sa; 70 | addrlen = sizeof(sa)/2; /* test i#1119 */ 71 | getsockname(s, (struct sockaddr*)&sa, &addrlen); 72 | 73 | s = socket(AF_INET, SOCK_STREAM, 0); 74 | addrlen = sizeof(sa); 75 | getsockname(s, (struct sockaddr*)&sa, &addrlen); 76 | 77 | s = socket(AF_UNIX, SOCK_STREAM, 0); 78 | struct sockaddr_un sa2; 79 | addrlen = sizeof(sa2); 80 | getsockname(s, (struct sockaddr*)&sa2, &addrlen); 81 | 82 | s = socket(AF_INET6, SOCK_STREAM, 0); 83 | struct sockaddr_in6 sa3; 84 | addrlen = sizeof(sa3); 85 | getsockname(s, (struct sockaddr*)&sa3, &addrlen); 86 | } 87 | #endif 88 | 89 | int 90 | main(int argc, char **argv) 91 | { 92 | syscall_test(); 93 | #ifdef UNIX 94 | socket_test(); 95 | #endif 96 | printf("done\n"); 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /drltrace_src/scripts/drltrace_test_expected.log: -------------------------------------------------------------------------------- 1 | ~~~~ ADVAPI32.dll!RegOpenKeyExW 2 | arg 0: type= 3 | arg 1: SOFTWARE\Microsoft\Windows\CurrentVersion\Run type=wchar_t* 4 | arg 2: 0x0 type=DWORD 5 | arg 3: type= 6 | ~~~~ ADVAPI32.dll!RegQueryValueExW 7 | arg 0: type= 8 | arg 1: TestKey type=wchar_t* 9 | arg 2: 0x00000000 type=DWORD* 10 | arg 5: => 0x400 type=DWORD* 11 | ~~~~ ADVAPI32.dll!RegCloseKey 12 | arg 0: type= 13 | ~~~~ USER32.dll!RegisterClassW 14 | arg 0: type=* 15 | ~~~~ USER32.dll!CreateWindowExW 16 | arg 0: 0x0 type=DWORD 17 | arg 1: Sample Window Class type=wchar_t* 18 | arg 2: Test Window type=wchar_t* 19 | arg 3: type=DWORD 20 | arg 4: type=int 21 | arg 5: type=int 22 | ~~~~ USER32.dll!BeginPaint 23 | arg 0: type= 24 | ~~~~ GDI32.dll!CreateFontA 25 | arg 0: 0x30 type=int 26 | arg 1: 0x0 type=int 27 | arg 2: 0x0 type=int 28 | arg 3: 0x0 type=int 29 | arg 4: 0x0 type=int 30 | arg 5: 0x0 type=DWORD 31 | arg 6: 0x1 type=DWORD 32 | arg 7: 0x0 type=DWORD 33 | arg 8: 0x1 type=DWORD 34 | arg 9: 0x8 type=DWORD 35 | arg 10: type=DWORD 36 | arg 11: 0x5 type=DWORD 37 | arg 12: 0x2 type=DWORD 38 | arg 13: Test CreateFont type=char* 39 | ~~~~ GDI32.dll!SelectObject 40 | arg 0: type= 41 | arg 1: type= 42 | ~~~~ USER32.dll!SetRect 43 | arg 1: 0x64 type=int 44 | arg 2: 0x64 type=int 45 | arg 3: 0x2bc type=int 46 | arg 4: 0xc8 type=int 47 | ~~~~ GDI32.dll!SetTextColor 48 | arg 0: type= 49 | arg 1: type= 50 | ~~~~ USER32.dll!DrawTextW 51 | arg 0: type= 52 | arg 1: Test Text type=wchar_t* 53 | arg 2: 0xffffffff type=int 54 | arg 3: type=* 55 | arg 4: type=uint 56 | ~~~~ GDI32.dll!DeleteObject 57 | arg 0: type= 58 | ~~~~ USER32.dll!DestroyWindow 59 | arg 0: type= 60 | ~~~~ USER32.dll!UnregisterClassW 61 | arg 0: Sample Window Class type=wchar_t* 62 | arg 1: type= 63 | ~~~~ WININET.dll!InternetCreateUrlW 64 | arg 0: type=* 65 | arg 1: type=DWORD 66 | arg 3: => 0x400 type=DWORD* 67 | ~~~~ WININET.dll!InternetCanonicalizeUrlW 68 | arg 0: http://drmemory.org/ docs type=wchar_t* 69 | arg 2: => 0x400 type=DWORD* 70 | arg 3: 0x80000000 type=DWORD 71 | ~~~~ WS2_32.dll!gethostbyname 72 | arg 0: http://drmemory.org type=char* 73 | ~~~~ WS2_32.dll!WSAGetLastError 74 | arg 0: type=void size=0x0 75 | ~~~~ MSWSOCK.dll!GetTypeByNameA 76 | arg 0: TEST SERVER type=char* 77 | arg 1: type=* 78 | ~~~~ KERNEL32.dll!GetThreadLocale 79 | arg 0: type=void size=0x0 80 | ~~~~ OLEAUT32.dll!VarI4FromStr 81 | arg 0: 01453 type=wchar_t* 82 | arg 1: type=LCID 83 | arg 2: type= 84 | ~~~~ ole32.dll!OleInitialize 85 | arg 0: 0x00000000 => 0x00000000 type=void* 86 | ~~~~ ole32.dll!OleSetClipboard 87 | arg 0: type=* 88 | ~~~~ ole32.dll!OleUninitialize 89 | arg 0: 0x00000000 type=void 90 | ~~~~ SHELL32.dll!PathIsExe 91 | arg 0: C:\Windows\System32\calc.exe type=wchar_t* 92 | ~~~~ SHLWAPI.dll!ParseURLW 93 | arg 0: http://drmemory.org/docs type=wchar_t* 94 | arg 1: type=* -------------------------------------------------------------------------------- /drltrace_src/runtest.cmake: -------------------------------------------------------------------------------- 1 | # ********************************************************** 2 | # Copyright (c) 2013 Google, Inc. All rights reserved. 3 | # Copyright (c) 2010 VMware, Inc. All rights reserved. 4 | # ********************************************************** 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # * Neither the name of VMware, Inc. nor the names of its contributors may be 17 | # used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE 24 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | # DAMAGE. 31 | 32 | # Invoked by the test suite for testing this tool 33 | 34 | # input: 35 | # * cmd = command to run 36 | # should have intra-arg space=@@ and inter-arg space=@ and ;=! 37 | # * cmp = file containing output to compare app output to, to ensure app ran correctly 38 | 39 | # Intra-arg space=@@ and inter-arg space=@. 40 | # XXX i#1327: now that we have -c and other option passing improvements we 41 | # should be able to get rid of this @@ stuff. 42 | string(REGEX REPLACE "@@" " " cmd "${cmd}") 43 | string(REGEX REPLACE "@" ";" cmd "${cmd}") 44 | string(REGEX REPLACE "!" "\\\;" cmd "${cmd}") 45 | 46 | # run the cmd 47 | execute_process(COMMAND ${cmd} 48 | RESULT_VARIABLE cmd_result 49 | ERROR_VARIABLE cmd_err 50 | OUTPUT_VARIABLE cmd_out) 51 | if (cmd_result) 52 | message(FATAL_ERROR "*** ${cmd} failed (${cmd_result}): ${cmd_err}***\n") 53 | endif (cmd_result) 54 | 55 | # DR's tests write to stderr so combine stdout and stderr. 56 | # We distinguish drltrace's output via its prefx ~~~~. 57 | set(app_out "${cmd_out}${cmd_err}") 58 | string(REGEX MATCHALL "~~~~[^\n]*\n" tool_out "${app_out}") 59 | string(REGEX REPLACE "~~~~[^\n]*\n" "" app_out "${app_out}") 60 | 61 | # get expected app output 62 | # we assume it has already been processed w/ regex => literal, etc. 63 | file(READ "${cmp}" str) 64 | 65 | if (WIN32) 66 | # our test prep turned \n into \r?\n so revert 67 | string(REGEX REPLACE "\r\\?" "" str "${str}") 68 | endif (WIN32) 69 | 70 | # we don't support regex in output pattern 71 | if (NOT "${app_out}" STREQUAL "${str}") 72 | message(FATAL_ERROR "app output ${app_out} failed to match expected ${str}") 73 | endif () 74 | 75 | # Now check tool output 76 | if (UNIX) 77 | set(tomatch "~~~~ libc.so.*!.*printf") 78 | else (UNIX) 79 | set(tomatch "~~~~ KERNEL32.dll!WriteFile") 80 | endif (UNIX) 81 | if (NOT "${tool_out}" MATCHES "${tomatch}" ) 82 | message(FATAL_ERROR "tool output ${tool_out} failed to match expected ${tomatch}") 83 | endif () 84 | -------------------------------------------------------------------------------- /drltrace_src/filter.config: -------------------------------------------------------------------------------- 1 | # 2 | # This file contains module/functions to either filter in with a whitelist, 3 | # or filter out with a blacklist. They can be individually specified by 4 | # module name and function name, separated with '!', like so: 5 | # 6 | # ld-linux-x86-64.so.2!malloc 7 | # libc.so.6!free 8 | # ntdll.dll!RtlEnterCriticalSection 9 | # KERNELBASE.dll!GetLastError 10 | # ... etc ... 11 | # 12 | # Furthermore, an entire module's functions can be filtered simply by 13 | # writing its name without any function name appended. For example, putting 14 | # 'KERNEL32.dll' on a line on its own will match all functions defined in 15 | # KERNEL32.dll (note that it is case-sensitive). 16 | # 17 | # Lastly, a collection of functions can be filtered if the star wildcard is 18 | # appended. For example, 'ntdll.dll!Rtl*' will match all functions from 19 | # ntdll.dll that begin with 'Rtl', including 'RtlEnterCriticalSection', 20 | # 'RtlLeaveCriticalSection', and many, many others. Note that the star 21 | # _MUST_ appear at the end only! In other words, only function name prefixes 22 | # are supported; specifying 'mod!prefix*suffix' is not supported. 23 | # 24 | # Note that module-level filtering is more efficient (in terms of speed) than 25 | # filtering by function names. 26 | # 27 | 28 | 29 | # Add whitelisted module/functions here. If any entries here are found, then 30 | # *all* blacklist entries are ignored. 31 | [whitelist] 32 | #libc.so.6 33 | #ld-linux-x86-64.so.2 34 | 35 | #KERNEL32.dll 36 | #USER32.dll 37 | 38 | 39 | # Add blacklisted module/functions here. Note that these are ignored if *any* 40 | # whitelist entries are given above. 41 | [blacklist] 42 | #ld-linux-x86-64.so.2!_* 43 | #ld-linux-x86-64.so.2!__libc_memalign 44 | #ld-linux-x86-64.so.2!_dl_debug_state 45 | #ld-linux-x86-64.so.2!calloc 46 | #ld-linux-x86-64.so.2!malloc 47 | #libc.so.6!__errno_location 48 | #libc.so.6!__getdelim 49 | #libc.so.6!__getpagesize 50 | #libc.so.6!__strcoll_l 51 | #libc.so.6!free 52 | #libc.so.6!malloc 53 | #libc.so.6!memchr 54 | #libc.so.6!memcpy 55 | #libc.so.6!memmem 56 | #libc.so.6!strcoll 57 | #libc.so.6!strdup 58 | #libc.so.6!strlen 59 | #libc.so.6!strstr 60 | 61 | #ntdll.dll!Rtl* 62 | #ntdll.dll!bsearch 63 | #ntdll.dll!memcmp 64 | #ntdll.dll!memcpy 65 | #ntdll.dll!memmove 66 | #ntdll.dll!memset 67 | #ntdll.dll!strrchr 68 | #ntdll.dll!RtlActivateActivationContextUnsafeFast 69 | #ntdll.dll!RtlAcquireSRWLockShared 70 | #ntdll.dll!RtlAcquireSRWLockExclusive 71 | #ntdll.dll!RtlAllocateHeap 72 | #ntdll.dll!RtlAppendUnicodeStringToString 73 | #ntdll.dll!RtlAppendUnicodeToString 74 | #ntdll.dll!RtlCompareUnicodeStrings 75 | #ntdll.dll!RtlCopyUnicodeString 76 | #ntdll.dll!RtlDeactivateActivationContextUnsafeFast 77 | #ntdll.dll!RtlDecodeSystemPointer 78 | #ntdll.dll!RtlEnterCriticalSection 79 | #ntdll.dll!RtlEqualUnicodeString 80 | #ntdll.dll!RtlFreeHeap 81 | #ntdll.dll!RtlFreeUnicodeString 82 | #ntdll.dll!RtlImageNtHeaderEx 83 | #ntdll.dll!RtlInitializeCriticalSection 84 | #ntdll.dll!RtlInitializeCriticalSectionEx 85 | #ntdll.dll!RtlInitUnicodeStringEx 86 | #ntdll.dll!RtlLeaveCriticalSection 87 | #ntdll.dll!RtlReleaseSRWLockExclusive 88 | #ntdll.dll!RtlReleaseSRWLockShared 89 | #ntdll.dll!RtlSetLastWin32Error 90 | #ntdll.dll!RtlTryEnterCriticalSection 91 | #ntdll.dll!wcscpy_s 92 | #ntdll.dll!wcsrchr 93 | #ntdll.dll!ZwCallbackReturn 94 | #KERNEL32.dll!FlsGetValue 95 | #KERNEL32.dll!FlsSetValue 96 | #KERNEL32.dll!GetLastError 97 | #KERNEL32.dll!LocalAlloc 98 | #KERNEL32.dll!LocalFree 99 | #KERNEL32.dll!SetLastError 100 | #KERNEL32.dll!TlsGetValue 101 | #KERNEL32.dll!TlsSetValue 102 | #KERNELBASE.dll!FlsGetValue 103 | #KERNELBASE.dll!FlsSetValue 104 | #KERNELBASE.dll!GetLastError 105 | #KERNELBASE.dll!LocalAlloc 106 | #KERNELBASE.dll!LocalFree 107 | #KERNELBASE.dll!SetLastError 108 | #KERNELBASE.dll!TlsGetValue 109 | #KERNELBASE.dll!TlsSetValue 110 | #msvcrt.dll!_lock 111 | #msvcrt.dll!_unlock 112 | #msvcrt.dll!free 113 | #msvcrt.dll!malloc 114 | #msvcrt.dll!memcmp 115 | #msvcrt.dll!memcpy 116 | #msvcrt.dll!memset 117 | -------------------------------------------------------------------------------- /drltrace_src/drltrace_utils.cpp: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | #ifdef WINDOWS 34 | #include 35 | #else 36 | #include 37 | #endif 38 | 39 | #include "drltrace_utils.h" 40 | 41 | static thread_id_t primary_thread = INVALID_THREAD_ID; 42 | 43 | bool op_print_stderr = true; 44 | uint op_verbose_level; 45 | uint op_prefix_style; 46 | file_t f_global = INVALID_FILE; 47 | int reported_disk_error; 48 | uint op_ignore_asserts = false; 49 | 50 | 51 | void 52 | print_prefix_to_buffer(char *buf, size_t bufsz, size_t *sofar) 53 | { 54 | void *drcontext = dr_get_current_drcontext(); 55 | ssize_t len; 56 | if (op_prefix_style == PREFIX_STYLE_NONE) { 57 | BUFPRINT_NO_ASSERT(buf, bufsz, *sofar, len, ""); 58 | return; 59 | } else if (op_prefix_style == PREFIX_STYLE_BLANK) { 60 | BUFPRINT_NO_ASSERT(buf, bufsz, *sofar, len, "%s", PREFIX_DRLTRACE); 61 | return; 62 | } else if (drcontext != NULL) { 63 | thread_id_t tid = dr_get_thread_id(drcontext); 64 | if (primary_thread != INVALID_THREAD_ID/*initialized?*/ && 65 | tid != primary_thread) { 66 | /* no-assert since used for errors, etc. in fragile contexts */ 67 | BUFPRINT_NO_ASSERT(buf, bufsz, *sofar, len, "~~%d~~ ", tid); 68 | return; 69 | } 70 | } 71 | /* no-assert since used for errors, etc. in fragile contexts */ 72 | BUFPRINT_NO_ASSERT(buf, bufsz, *sofar, len, "%s", PREFIX_DEFAULT_MAIN_THREAD); 73 | } 74 | 75 | void 76 | print_prefix_to_console(void) 77 | { 78 | char buf[16]; 79 | size_t sofar = 0; 80 | print_prefix_to_buffer(buf, BUFFER_SIZE_ELEMENTS(buf), &sofar); 81 | PRINT_CONSOLE("%s", buf); 82 | } 83 | 84 | /* Not available in ntdll CRT so we supply our own. 85 | * It is available on Mac and Android, and we want to avoid it for libs that do not 86 | * want a libc dependence. 87 | */ 88 | #if !defined(MACOS) && !defined(ANDROID) && !defined(NOLINK_STRCASESTR) 89 | const char * 90 | strcasestr(const char *text, const char *pattern) 91 | { 92 | const char *cur_text, *cur_pattern, *root; 93 | cur_text = text; 94 | root = text; 95 | cur_pattern = pattern; 96 | while (true) { 97 | if (*cur_pattern == '\0') 98 | return root; 99 | if (*cur_text == '\0') 100 | return NULL; 101 | /* XXX DRi#943: toupper is better, for int18n, and we need to call 102 | * islower() first to be safe for all tolower() implementations. 103 | * Even better would be switching to our own locale-independent case 104 | * folding. 105 | */ 106 | if ((char)tolower(*cur_text) == (char)tolower(*cur_pattern)) { 107 | cur_text++; 108 | cur_pattern++; 109 | } 110 | else { 111 | root++; 112 | cur_text = root; 113 | cur_pattern = pattern; 114 | } 115 | } 116 | } 117 | #endif 118 | -------------------------------------------------------------------------------- /drltrace_src/drltrace_options.cpp: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | #include "droption.h" 34 | #include "drltrace_options.h" 35 | 36 | /* Frontend scope is defined here because if logdir is a forbidden path we have to change 37 | * it and provide for our client manually. 38 | */ 39 | droption_t op_logdir 40 | (DROPTION_SCOPE_ALL, "logdir", ".", "Log directory to print library call data", 41 | "Specify log directory where library call data will be written, in a separate file per " 42 | "process. The default value is \".\" (current dir). If set to \"-\", data for all " 43 | "processes are printed to stderr (warning: this can be slow)."); 44 | 45 | droption_t op_only_from_app 46 | (DROPTION_SCOPE_CLIENT, "only_from_app", false, "Reports only library calls from the app", 47 | "Only reports library calls from the application itself, as opposed to all calls even " 48 | "from other libraries or within the same library."); 49 | 50 | droption_t op_follow_children 51 | (DROPTION_SCOPE_FRONTEND, "follow_children", true, "Trace child processes", 52 | "Trace child processes created by a target application. Specify -no_follow_children " 53 | "to disable."); 54 | 55 | droption_t op_print_ret_addr 56 | (DROPTION_SCOPE_CLIENT, "print_ret_addr", false, "Print library call's return address", 57 | "Print return addresses of library calls."); 58 | 59 | droption_t op_unknown_args 60 | (DROPTION_SCOPE_CLIENT, "num_unknown_args", 2, "Number of unknown libcall args to print", 61 | "Number of arguments to print for unknown library calls. Specify 0 to disable " 62 | "unknown args printing."); 63 | 64 | droption_t op_max_args 65 | (DROPTION_SCOPE_CLIENT, "num_max_args", 6, "Maximum number of arguments to print", 66 | "Maximum number of arguments to print. This option allows to limit the number of " 67 | "arguments to be printed. Specify 0 to disable args printing (including unknown)."); 68 | 69 | droption_t op_config_file_default 70 | (DROPTION_SCOPE_FRONTEND, "default_config", true, "Use default config file.", 71 | "Use config file that comes with drltrace and located in the same path. Specify " 72 | "no_use_config and provide a path to custom config file using -config option."); 73 | 74 | droption_t op_config_file 75 | (DROPTION_SCOPE_ALL, "config", "", "The path to custom config file.", 76 | "Specify a custom path where config is located. The config file describes the prototype" 77 | " of library functions for printing library call arguments. See drltrace documentation" 78 | " for more details."); 79 | 80 | droption_t op_filter_file 81 | (DROPTION_SCOPE_CLIENT, "filter", "filter.config", "The path of the whitelist/blacklist file.", 82 | "Specify a path to a file containing the list of functions to whitelist or blacklist."); 83 | 84 | droption_t op_ignore_underscore 85 | (DROPTION_SCOPE_CLIENT, "ignore_underscore", false, "Ignores library routine names " 86 | "starting with \"_\".", "Ignores library routine names starting with \"_\"."); 87 | 88 | droption_t op_help 89 | (DROPTION_SCOPE_FRONTEND, "help", false, "Print this message.", "Print this message"); 90 | 91 | droption_t op_version 92 | (DROPTION_SCOPE_FRONTEND, "version", 0, "Print version number.", "Print version number."); 93 | 94 | droption_t op_verbose 95 | (DROPTION_SCOPE_ALL, "verbose", 1, "Change verbosity.", "Change verbosity."); 96 | 97 | droption_t op_use_config 98 | (DROPTION_SCOPE_CLIENT, "use_config", true, "Use config file", 99 | "Use config file for library call arguments printing. Specify no_use_config to disable."); 100 | 101 | droption_t op_ltracelib_ops 102 | (DROPTION_SCOPE_CLIENT, "ltracelib_ops", 103 | DROPTION_FLAG_SWEEP | DROPTION_FLAG_ACCUMULATE | DROPTION_FLAG_INTERNAL, 104 | "", "(For internal use: sweeps up drltracelib options)", 105 | "This is an internal option that sweeps up other options to pass to the drltracelib."); 106 | 107 | droption_t op_grepable 108 | (DROPTION_SCOPE_CLIENT, "grepable", false, "Grepable output", 109 | "Outputs function names and arguments entirely on one line to enable easy log grepping."); 110 | -------------------------------------------------------------------------------- /drltrace_src/scripts/test_drltrace_out.py: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # Copyright (c) 2017 Google, Inc. All rights reserved. 3 | # *************************************************************************** 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # * Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # * Redistributions in binary form must reproduce the above copyright notice, 12 | # this list of conditions and the following disclaimer in the documentation 13 | # and/or other materials provided with the distribution. 14 | # 15 | # * Neither the name of Google, Inc. nor the names of its contributors may be 16 | # used to 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 "AS IS" 20 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | # ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 23 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 29 | # DAMAGE. 30 | # 31 | # ****************************************************************************** 32 | # The script is used to search for expected API calls and their arguments in the 33 | # output of drltrace. The script uses an external file which contains a list of 34 | # strings for checking. Each string should have only constant elements that do 35 | # not change from run to run or depend on machine architecture. 36 | # For example: 37 | # ~~6048~~ ADVAPI32.dll!RegQueryValueExW => ~~~~ ADVAPI32.dll!RegQueryValueExW 38 | # arg 5: 0x001df390 => 0x400 (type=DWORD*, size=0x4) => arg 5: => 0x400 type=DWORD* 39 | 40 | import os 41 | import sys 42 | import subprocess 43 | 44 | def parse_drltrace_log(log_content, allow_api_duplicates): 45 | ''' 46 | The function is used to parse drltrace output format and represents it as 47 | a dictionary in the following way: {API name: [list of arguments]}. 48 | 49 | @in log_content - drltrace output (or expected results) to parse. 50 | @in allow_api_duplicates - if True, the function will add unique id for 51 | each duplicate and save them in a dictionary. 52 | if False, the function will print error and exit 53 | in case when duplicates exist. 54 | @out - dictionary, where a key is API call name and value is a list of its 55 | arguments. 56 | ''' 57 | 58 | log_dict = dict() 59 | api_call_to_add = "" 60 | for i, line_to_check in enumerate(log_content): 61 | # search for API calls 62 | if line_to_check.startswith("~~") and "!" in line_to_check: 63 | line_to_check = line_to_check.replace("~","") 64 | api_call_to_add = line_to_check 65 | api_call_exist_name = log_dict.get(api_call_to_add, None) 66 | if api_call_exist_name != None: 67 | if allow_api_duplicates == True: 68 | # append unique id (line number) 69 | api_call_to_add = api_call_to_add + "_" + str(i) 70 | else: 71 | print("Found duplicates while parsing the log file, exit.") 72 | sys.exit(-1) 73 | 74 | log_dict[api_call_to_add] = list() # List will store arguments. 75 | continue 76 | if api_call_to_add != '': 77 | # It means that we are parsing arguments of API call found above. 78 | log_dict[api_call_to_add].append(line_to_check) 79 | return log_dict 80 | 81 | def check_args(args_expected, args_log): 82 | ''' 83 | The function is used to compare expected arguments with arguments printed 84 | by drltrace. 85 | @in args_expected - expected arguments. 86 | @in args_log - arguments printed by drltrace. 87 | @out - True, if arguments are the same. 88 | False, otherwise. 89 | ''' 90 | 91 | for i, arg_expected in enumerate(args_expected): 92 | arg_expected = arg_expected[:-1] 93 | # First, take "arg x" string as a separate element. 94 | arg_expected = arg_expected.split(":") 95 | arg_id = arg_expected[0] 96 | arg_expected = arg_expected[1] # the rest 97 | arg_element = arg_expected.split(" ") 98 | arg_element.append(arg_id) 99 | for element in arg_element: 100 | if element == "": 101 | continue 102 | if element not in args_log[i]: 103 | print "Failed to find %s" % element 104 | # It doesn't mean that the whole test is failed, probably, we 105 | # pick an API call with different set arguments. 106 | return False 107 | return True 108 | 109 | def check_log_file(log_output, expected_log_path): 110 | ''' 111 | The main function for expected results checking. 112 | 113 | @in log_output - content of the drltrace log. 114 | @in expected_log_path - a path where expected log file located. 115 | @out -1, if the function failed to find at least one string from expected log. 116 | 0, otherwise. 117 | ''' 118 | api_calls_found_not_found = dict() 119 | has_errors = 0 120 | 121 | # read and parse expected log file and drltrace output 122 | expected_log_content = open(expected_log_path, 'r').readlines() 123 | expected_log_dict = parse_drltrace_log(expected_log_content, False) 124 | log_dict = parse_drltrace_log(log_output.split("\n"), True) 125 | 126 | print("Comparing log with expected results") 127 | for key_expected, args_expected in expected_log_dict.iteritems(): 128 | key_expected = key_expected[:-1] # remove "\n" 129 | for key_log, args_log in log_dict.iteritems(): 130 | if key_expected in key_log: 131 | # We managed to find API call, let's check its arguments. 132 | if check_args(args_expected, args_log) == True: 133 | api_calls_found_not_found[key_expected] = 1 134 | break 135 | if api_calls_found_not_found.get(key_expected, None) == None: 136 | api_calls_found_not_found[key_expected] = 0 137 | 138 | # print results and setup return value accordingly 139 | for key, value in api_calls_found_not_found.iteritems(): 140 | if value == 1: 141 | print("Found %s" % key) 142 | else: 143 | print("Not found %s" % key) 144 | has_errors = -1 145 | return has_errors 146 | 147 | def main(): 148 | expected_log_path = sys.argv[1] 149 | drltrace_bin_path = sys.argv[2] 150 | drltrace_test_app_path = sys.argv[3] 151 | command = "%s -logdir - -num_max_args 15 -- %s" %\ 152 | (drltrace_bin_path, drltrace_test_app_path) 153 | print("Running the following command %s" % command) 154 | p = subprocess.Popen(command, 155 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 156 | output, err = p.communicate() # Wait while the child process finished. 157 | if p.returncode != 0: 158 | print("drltrace finished with error %d" % p.returncode) 159 | sys.exit(p.returncode) 160 | # Drltrace prints output in STDERR when symbol '-' is specified. 161 | res = check_log_file(err, expected_log_path) 162 | sys.exit(res) 163 | if __name__ == "__main__": 164 | main() -------------------------------------------------------------------------------- /api_calls_viz/api_calls_vis.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import random 4 | import pylab 5 | import numpy 6 | import argparse 7 | from PIL import Image 8 | import math 9 | from colorsys import hsv_to_rgb 10 | 11 | 12 | # A user can add his/her own list of API calls here 13 | marked_calls = ["GetProcAddress"] 14 | 15 | all_dots = list() 16 | 17 | css_styles = "" 18 | divs = "" 19 | divs_image = "" 20 | 21 | def get_specific_color(api_name): 22 | if api_name not in marked_calls: 23 | return 0 24 | print "Picked hardcoded color for ", api_name 25 | return 0xFF0000 26 | 27 | def add_one_color_in_legend(color, libcall_name): 28 | global css_styles, divs 29 | str_color = str(hex(color)) 30 | str_color = str_color.replace("0x", "") 31 | css_color = ".name%s { background: #%x;}\n" % (str_color, color) 32 | div_color = "%s

\n" % (libcall_name, str_color) 33 | div_image_color = "
\n" % (str_color, libcall_name) 34 | css_styles += css_color 35 | divs += div_color 36 | 37 | def add_one_color_on_html_page(color, libcall_name): 38 | global divs_image 39 | str_color = str(hex(color)) 40 | str_color = str_color.replace("0x", "") 41 | div_image_color = "
\n" % (str_color, libcall_name) 42 | divs_image += div_image_color 43 | 44 | def choose_colors(unique_libcalls, grayscale): 45 | libcall_colors_dict = dict() 46 | 47 | print "Generating random color for each entry" 48 | # generate a random unique color for each call excluding whiteness (checkit) 49 | dot_nums = random.sample(xrange(0x0, 0x00EEFFFF), len(unique_libcalls)) 50 | print "Done" 51 | 52 | for i, api_name in enumerate(unique_libcalls): 53 | color_dot = get_specific_color(api_name) # we assign specific red color for marked calls 54 | if color_dot != 0 and not grayscale: # FIXIT: we disable marking in case of grayscale due to strange img results 55 | add_one_color_in_legend(color_dot, api_name) 56 | else: 57 | color_dot = dot_nums[i] 58 | add_one_color_in_legend(dot_nums[i], api_name) 59 | 60 | libcall_colors_dict[api_name] = color_dot 61 | return libcall_colors_dict 62 | 63 | def add_dots_on_image(libcalls_colors_dict, libcalls_seq, html_page_name, grayscale): 64 | libcalls_count = len(libcalls_seq) 65 | print "Adding %d colors on image/images" % libcalls_count 66 | for api_name in libcalls_seq: 67 | # take an RGB color associated with API call 68 | color_dot = libcalls_colors_dict.get(api_name, None) 69 | if color_dot == None: 70 | print "[CRITICAL] Failed to find color for %s, exiting" % api_name 71 | sys.exit(0) 72 | if html_page_name != None: 73 | add_one_color_on_html_page(color_dot, api_name) 74 | R = (color_dot & 0x00FF0000) >> 16 75 | G = (color_dot & 0x0000FF00) >> 8 76 | B = color_dot & 0x000000FF 77 | if grayscale == True: 78 | color_gray = (R + G + B)/3 79 | color_dot = (color_gray, color_gray, color_gray) 80 | else: 81 | color_dot = (R, G, B) 82 | all_dots.append(color_dot) 83 | return all_dots 84 | 85 | def create_image(all_dots, image_name): 86 | #calculate width and height 87 | total_size = math.sqrt(len(all_dots)) 88 | w = int(total_size) + 1 89 | h = int(total_size) + 1 90 | 91 | print "Generating picture %dx%d" % (h, w) 92 | 93 | data = numpy.zeros((h, w, 3), dtype=numpy.uint8) 94 | i = 0 95 | j = 0 96 | for dot in all_dots: 97 | data[i][j] = dot 98 | j += 1 99 | if j >= w: 100 | i += 1 101 | j = 0 102 | img = Image.fromarray(data, 'RGB') 103 | img.save(image_name) 104 | print "Done" 105 | 106 | def create_html_page(name): 107 | global css_styles, divs_image 108 | # page headers */ 109 | print "Generating HTML image" 110 | 111 | html_image = "%s\n\ 112 | \n\n" 124 | for div in divs_image: 125 | html_image += div 126 | html_image += "" 127 | 128 | fh = open(name, 'w') 129 | fh.write(html_image) 130 | fh.close() 131 | print "Done" 132 | 133 | def create_html_legend(name): 134 | global css_styles, divs 135 | # page headers */ 136 | html_name = name + ".html" 137 | print "Generating legend in HTML" 138 | html_legenda = "Legenda\n\ 139 | \n\n" 151 | for div in divs: 152 | html_legenda += div 153 | html_legenda += "" 154 | 155 | fh = open("legend_%s.html" % name, 'w') 156 | fh.write(html_legenda) 157 | fh.close() 158 | print "Done" 159 | 160 | def gen_image(trace_name, image_name, html_page_name, grayscale): 161 | unique_libcalls = list() 162 | libcalls_seq = list() 163 | 164 | random.seed(0) # to be able to generate the same image each run for single trace 165 | try: 166 | content = open(trace_name).readlines() 167 | except Exception, e: 168 | print 'Failed to open the file %s, error msg: %s' % (trace_name, str(e)) 169 | #TODO: what if I randomly selected red 0x255 0x0 0x0? 170 | for line in content: 171 | if " arg" in line or "module id" in line: # skip arguments provided by drltrace 172 | continue 173 | #extract API call name 174 | line = line[:line.find("(")] 175 | api_name = line[line.find("!")+1:] 176 | 177 | if api_name not in unique_libcalls: 178 | unique_libcalls.append(api_name) # save in unique calls 179 | libcalls_seq.append(api_name) # save in sequence for future proceedings 180 | 181 | entries_count = len(libcalls_seq) 182 | 183 | if entries_count <= 0: 184 | print "Failed to find any API calls matching dll_name!api_name pattern in the file specified" 185 | sys.exit(0) 186 | else: 187 | print "Found %d api calls in the file" % entries_count 188 | 189 | print "Starting image generation" 190 | 191 | #choose colors for each unique image 192 | libcall_colors_dict = choose_colors(unique_libcalls, grayscale) 193 | dots = add_dots_on_image(libcall_colors_dict, libcalls_seq, html_page_name, grayscale) 194 | create_image(dots, image_name) 195 | create_html_legend(image_name) 196 | if html_page_name != None: 197 | create_html_page(html_page_name) 198 | 199 | def main(): 200 | #bug: whites for many calls ? 201 | parser = argparse.ArgumentParser(prog = "api_calls_vis.py", 202 | description = 'A script for API calls trace visualization.', 203 | usage = '%(prog)s -t calc.exe.txt -i calc.jpeg -ht calc.html') 204 | parser.add_argument('-t', '--trace', required=True, help = "A trace file with API calls in the following format \"library_name!api_call_name\"") 205 | parser.add_argument('-i', '--image', default="tmp.jpeg", help = "A name of image file") # generate jpeg image 206 | parser.add_argument('-ht', '--html', help = "A name of html page (heavy)") # generate html page with image 207 | parser.add_argument('-gr', '--grayscale', dest='grayscale', action='store_true', help = "Generate an image in grayscale") 208 | args = parser.parse_args() 209 | gen_image(args.trace, args.image, args.html, args.grayscale) 210 | 211 | if __name__ == "__main__": 212 | main() 213 | -------------------------------------------------------------------------------- /drltrace_src/test.cmake: -------------------------------------------------------------------------------- 1 | # ********************************************************** 2 | # Copyright (c) 2010-2017 Google, Inc. All rights reserved. 3 | # Copyright (c) 2009-2010 VMware, Inc. All rights reserved. 4 | # ********************************************************** 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # * Neither the name of VMware, Inc. nor the names of its contributors may be 17 | # used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. ENTER NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE 24 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ENTER CONTRACT, STRICT 28 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING ENTER ANY WAY 29 | # EXIT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | # DAMAGE. 31 | 32 | cmake_minimum_required(VERSION 2.6) 33 | 34 | # To match Makefiles and have just one build type per configured build 35 | # dir, we collapse VS generator configs to a single choice. 36 | # This must be done prior to the project() command and the var 37 | # must be set in the cache. 38 | if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 39 | if (DEBUG OR "${CMAKE_BUILD_TYPE}" MATCHES "Debug") 40 | set(CMAKE_CONFIGURATION_TYPES "Debug" CACHE STRING "" FORCE) 41 | else () 42 | # Go w/ debug info (i#1392) 43 | set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" CACHE STRING "" FORCE) 44 | endif () 45 | # we want to use the _LOCATION_ property 46 | string(TOUPPER "${CMAKE_CONFIGURATION_TYPES}" upper) 47 | set(location_suffix "_${upper}") 48 | else ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 49 | set(location_suffix "") 50 | endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 51 | 52 | project(mytest) 53 | 54 | if ("${CMAKE_VERSION}" VERSION_EQUAL "3.0" OR 55 | "${CMAKE_VERSION}" VERSION_GREATER "3.0") 56 | # XXX i#1557: update our code to satisfy the changes in 3.x 57 | cmake_policy(SET CMP0026 OLD) 58 | # XXX i#1375: if we make 2.8.12 the minimum we can remove the @rpath 59 | # Mac stuff and this policy, right? 60 | cmake_policy(SET CMP0042 OLD) 61 | endif () 62 | 63 | set(output_dir "${PROJECT_BINARY_DIR}/bin") 64 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${output_dir}") 65 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") 66 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${output_dir}") 67 | if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 68 | # we don't support the Debug and Release subdirs 69 | foreach (config ${CMAKE_CONFIGURATION_TYPES}) 70 | string(TOUPPER "${config}" config_upper) 71 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper} 72 | "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 73 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config_upper} 74 | "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}") 75 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config_upper} 76 | "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") 77 | endforeach () 78 | endif () 79 | 80 | if (BUILD_TESTS) 81 | # For automated sanity tests we can't have msgboxes or output 82 | set(SHOW_RESULTS_DEFAULT OFF) 83 | else (BUILD_TESTS) 84 | set(SHOW_RESULTS_DEFAULT ON) 85 | endif (BUILD_TESTS) 86 | 87 | option(SHOW_RESULTS 88 | "Display client results in pop-up (Windows) or console message (Linux)" 89 | ${SHOW_RESULTS_DEFAULT}) 90 | if (SHOW_RESULTS) 91 | add_definitions(-DSHOW_RESULTS) 92 | endif (SHOW_RESULTS) 93 | 94 | option(SHOW_SYMBOLS "Use symbol lookup in clients that support it" ON) 95 | if (SHOW_SYMBOLS) 96 | add_definitions(-DSHOW_SYMBOLS) 97 | endif (SHOW_SYMBOLS) 98 | if (NOT DEFINED GENERATE_PDBS) 99 | # support running tests over ssh where pdb building is problematic 100 | set(GENERATE_PDBS ON) 101 | endif (NOT DEFINED GENERATE_PDBS) 102 | 103 | # i#379: We usually want to build the samples with optimizations to improve the 104 | # chances of inlining, but it's nice to be able to turn that off easily. A 105 | # release build should already have optimizations, so this should only really 106 | # affect debug builds. 107 | option(OPTIMIZE_SAMPLES 108 | "Build samples with optimizations to increase the chances of clean call inlining (overrides debug flags)" 109 | ON) 110 | if (WIN32) 111 | set(OPT_CFLAGS "/O2") 112 | else (WIN32) 113 | set(OPT_CFLAGS "-O2") 114 | endif (WIN32) 115 | 116 | if (DEBUG) 117 | set(OPT_CFLAGS "-DDEBUG") 118 | endif (DEBUG) 119 | 120 | # For C clients that only rely on the DR API and not on any 3rd party 121 | # library routines, we could shrink the size of the client binary 122 | # by disabling libc via "set(DynamoRIO_USE_LIBC OFF)". 123 | 124 | if (NOT DEFINED DynamoRIO_DIR) 125 | set(DynamoRIO_DIR "${PROJECT_SOURCE_DIR}/../cmake" CACHE PATH 126 | "DynamoRIO installation's cmake directory") 127 | endif (NOT DEFINED DynamoRIO_DIR) 128 | 129 | find_package(DynamoRIO 7.0) 130 | if (NOT DynamoRIO_FOUND) 131 | message(FATAL_ERROR "DynamoRIO package required to build") 132 | endif(NOT DynamoRIO_FOUND) 133 | 134 | if (WIN32) 135 | # disable stack protection: "unresolved external symbol ___security_cookie" 136 | # disable the warning "unreferenced formal parameter" #4100 137 | # disable the warning "conditional expression is constant" #4127 138 | # disable the warning "cast from function pointer to data pointer" #4054 139 | set(CL_CFLAGS "/GS- /wd4100 /wd4127 /wd4054") 140 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CL_CFLAGS}") 141 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CL_CFLAGS}") 142 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 143 | endif (WIN32) 144 | 145 | if (OPTIMIZE_SAMPLES) 146 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPT_CFLAGS}") 147 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPT_CFLAGS}") 148 | endif () 149 | 150 | function (add_sample_client name source_file_list extension_list) 151 | add_library(${name} SHARED ${source_file_list}) 152 | configure_DynamoRIO_client(${name}) 153 | foreach (ext ${extension_list}) 154 | use_DynamoRIO_extension(${name} ${ext}) 155 | endforeach (ext) 156 | 157 | # Provide a hint for how to use the client 158 | if (NOT DynamoRIO_INTERNAL OR NOT "${CMAKE_GENERATOR}" MATCHES "Ninja") 159 | get_target_property(path ${name} LOCATION${location_suffix}) 160 | add_custom_command(TARGET ${name} 161 | POST_BUILD 162 | COMMAND ${CMAKE_COMMAND} 163 | ARGS -E echo "Usage: pass to drconfig or drrun: -c ${path}" 164 | VERBATIM) 165 | endif () 166 | 167 | endfunction (add_sample_client) 168 | 169 | function (add_sample_standalone name source_file_list) 170 | add_executable(${name} ${source_file_list}) 171 | 172 | configure_DynamoRIO_standalone(${name}) 173 | 174 | # Provide a hint for running 175 | if (NOT DynamoRIO_INTERNAL OR NOT "${CMAKE_GENERATOR}" MATCHES "Ninja") 176 | if (UNIX) 177 | set(FIND_MSG "(set LD_LIBRARY_PATH)") 178 | else (UNIX) 179 | set(FIND_MSG "(set PATH or copy to same directory)") 180 | endif (UNIX) 181 | add_custom_command(TARGET ${name} 182 | POST_BUILD 183 | COMMAND ${CMAKE_COMMAND} 184 | ARGS -E echo "Make sure the loader finds the dynamorio library ${FIND_MSG}" 185 | VERBATIM) 186 | endif () 187 | endfunction (add_sample_standalone) 188 | 189 | ########################################################################### 190 | 191 | # As we'll be calling configure_DynamoRIO_{client,standalone} from within 192 | # a function scope, we must set the global vars ahead of time: 193 | configure_DynamoRIO_global(OFF ON) 194 | 195 | # Use ;-separated lists for source files and extensions. 196 | 197 | file( 198 | GLOB_RECURSE 199 | TEST_SOURCE_FILES 200 | ${CMAKE_SOURCE_DIR}/src/*.hpp 201 | ${CMAKE_SOURCE_DIR}/src/*.cpp 202 | ) 203 | 204 | if(UNIX) 205 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 206 | endif() 207 | 208 | 209 | add_sample_client(Test "${TEST_SOURCE_FILES}" "drmgr_static") 210 | 211 | if (WIN32) 212 | # XXX i#893: drwrap's asm isn't SEH compliant. 213 | _DR_append_property_string(TARGET Test LINK_FLAGS "/safeseh:no") 214 | endif () -------------------------------------------------------------------------------- /drltrace_src/drltrace.h: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | #include "dr_api.h" 34 | #include "drltrace_options.h" 35 | #include "drmgr.h" 36 | #include "drwrap.h" 37 | #include "drx.h" 38 | #include "drcovlib.h" 39 | #include "drltrace_utils.h" 40 | #include 41 | #include 42 | 43 | typedef enum { 44 | DRSYS_PARAM_IN = 0x01, /**< Input parameter. */ 45 | DRSYS_PARAM_OUT = 0x02, /**< Output parameter. */ 46 | /** 47 | * May be IN or OUT. Used only in pre-syscall to indicate the 48 | * size of an entire data structure, when only some fields are 49 | * actually read or writen. Those fields will be presented as 50 | * separate IN or OUT arguments which will of course overlap this 51 | * one. 52 | */ 53 | DRSYS_PARAM_BOUNDS = 0x04, 54 | /** 55 | * Not used for memory iteration, only for type iteration, where 56 | * the type of the return value is indicated if it is other than a 57 | * status or error code. 58 | */ 59 | DRSYS_PARAM_RETVAL = 0x08, 60 | /** 61 | * If this flag is not set, the parameter is passed as a pointer to 62 | * the specified type. If this flag is set, the parameter's value 63 | * is passed in. 64 | */ 65 | DRSYS_PARAM_INLINED = 0x10, 66 | } drsys_param_mode_t; 67 | 68 | /* Keep this in synch with param_type_names[] */ 69 | /** 70 | * Indicates the data type of a parameter. 71 | * For the non-memarg iterators, a pointer type is implied whenever the 72 | * mode is DRSYS_PARAM_OUT. Thus, a system call parameter of type DRSYS_TYPE_INT 73 | * and mode DRSYS_PARAM_OUT can be assumed to be a pointer to an int. 74 | */ 75 | typedef enum { 76 | DRSYS_TYPE_INVALID, /**< This type field is not used for this iteration type. */ 77 | DRSYS_TYPE_UNKNOWN, /**< Unknown type. */ 78 | 79 | /* Inlined */ 80 | DRSYS_TYPE_VOID, /**< Void type. */ 81 | DRSYS_TYPE_BOOL, /**< Boolean type. */ 82 | DRSYS_TYPE_INT, /**< Integer type of unspecified signedness. */ 83 | DRSYS_TYPE_SIGNED_INT, /**< Signed integer type. */ 84 | DRSYS_TYPE_UNSIGNED_INT,/**< Unsigned integer type. */ 85 | DRSYS_TYPE_SIZE_T, /**< Size_t type */ 86 | DRSYS_TYPE_HANDLE, /**< Windows-only: kernel/GDI/user handle type. */ 87 | DRSYS_TYPE_NTSTATUS, /**< Windows-only: NTSTATUS Native API/RTL type. */ 88 | DRSYS_TYPE_ATOM, /**< Windows-only: ATOM type. */ 89 | DRSYS_TYPE_LCID, /**< Windows-only: LCID type. */ 90 | DRSYS_TYPE_LPARAM, /**< Windows-only: LPARAM type. */ 91 | DRSYS_TYPE_HMODULE, /**< Windows-only: HMODULE type. */ 92 | DRSYS_TYPE_HFILE, /**< Windows-only: HFILE type. */ 93 | DRSYS_TYPE_POINTER, /**< Pointer to an unspecified type. */ 94 | 95 | /* Structs */ 96 | DRSYS_TYPE_STRUCT, /**< Unspecified structure type. */ 97 | DRSYS_TYPE_CSTRING, /**< Null-terminated string of characters (C string). */ 98 | DRSYS_TYPE_CWSTRING, /**< Null-terminated string of wide characters. */ 99 | DRSYS_TYPE_CARRAY, /**< Non-null-terminated string of characters. */ 100 | DRSYS_TYPE_CWARRAY, /**< Non-null-terminated string of wide characters. */ 101 | DRSYS_TYPE_CSTRARRAY, /**< Null-terminated array of C strings. */ 102 | DRSYS_TYPE_UNICODE_STRING, /**< UNICODE_STRING structure. */ 103 | DRSYS_TYPE_LARGE_STRING, /**< LARGE_STRING structure. */ 104 | DRSYS_TYPE_OBJECT_ATTRIBUTES, /**< OBJECT_ATTRIBUTES structure. */ 105 | DRSYS_TYPE_SECURITY_DESCRIPTOR, /**< SECURITY_DESCRIPTOR structure. */ 106 | DRSYS_TYPE_SECURITY_QOS, /**< SECURITY_QUALITY_OF_SERVICE structure */ 107 | DRSYS_TYPE_PORT_MESSAGE, /**< PORT_MESSAGE structure. */ 108 | DRSYS_TYPE_CONTEXT, /**< CONTEXT structure. */ 109 | DRSYS_TYPE_EXCEPTION_RECORD, /**< EXCEPTION_RECORD structure. */ 110 | DRSYS_TYPE_DEVMODEW, /**< DEVMODEW structure. */ 111 | DRSYS_TYPE_WNDCLASSEXW, /**< WNDCLASSEXW structure. */ 112 | DRSYS_TYPE_CLSMENUNAME, /**< CLSMENUNAME structure. */ 113 | DRSYS_TYPE_MENUITEMINFOW, /**< MENUITEMINFOW structure. */ 114 | DRSYS_TYPE_ALPC_PORT_ATTRIBUTES,/**< ALPC_PORT_ATTRIBUTES structure. */ 115 | DRSYS_TYPE_ALPC_SECURITY_ATTRIBUTES,/**< ALPC_SECURITY_ATTRIBUTES structure. */ 116 | DRSYS_TYPE_LOGFONTW, /**< LOGFONTW structure. */ 117 | DRSYS_TYPE_NONCLIENTMETRICSW, /**< NONCLIENTMETRICSW structure. */ 118 | DRSYS_TYPE_ICONMETRICSW, /**< ICONMETRICSW structure. */ 119 | DRSYS_TYPE_SERIALKEYSW, /**< SERIALKEYSW structure. */ 120 | DRSYS_TYPE_SOCKADDR, /**< struct sockaddr. */ 121 | DRSYS_TYPE_MSGHDR, /**< struct msghdr. */ 122 | DRSYS_TYPE_MSGBUF, /**< struct msgbuf. */ 123 | DRSYS_TYPE_LARGE_INTEGER, /**< LARGE_INTEGER structure. */ 124 | DRSYS_TYPE_ULARGE_INTEGER, /**< ULARGE_INTEGER structure. */ 125 | DRSYS_TYPE_IO_STATUS_BLOCK, /**< IO_STATUS_BLOCK structure. */ 126 | DRSYS_TYPE_FUNCTION, /**< Function of unspecified signature. */ 127 | DRSYS_TYPE_BITMAPINFO, /**< BITMAPINFO structure. */ 128 | DRSYS_TYPE_ALPC_CONTEXT_ATTRIBUTES,/**< ALPC_CONTEXT_ATTRIBUTES structure. */ 129 | DRSYS_TYPE_ALPC_MESSAGE_ATTRIBUTES,/**< ALPC_MESSAGE_ATTRIBUTES structure. */ 130 | 131 | /* Additional types may be added in the future. */ 132 | DRSYS_TYPE_LAST = DRSYS_TYPE_ALPC_MESSAGE_ATTRIBUTES, 133 | } drsys_param_type_t; 134 | 135 | /* Describes a library call parameter (inherited from drys_arg_t). */ 136 | typedef struct _drltrace_arg_t { 137 | /** 138 | * Whether operating pre-system call (if true) or post-system call (if false). 139 | * Set for the dynamic iterators only (drsys_iterate_args() and 140 | * drsys_iterate_memargs()). 141 | */ 142 | bool pre; 143 | 144 | /* Library call argument information ****************************/ 145 | /** The ordinal of the parameter. Set to -1 for a return value. */ 146 | int ordinal; 147 | /** The mode (whether inlined, or read or written memory, etc.) of the parameter. */ 148 | drsys_param_mode_t mode; 149 | /** The type of the parameter. */ 150 | drsys_param_type_t type; 151 | /** A string further describing the type of the parameter. May be NULL. */ 152 | const char *type_name; 153 | /** A string describing the parameter. This may be NULL. */ 154 | const char *arg_name; 155 | /** 156 | * If not set to DR_REG_NULL, indicates which register the parameter's 157 | * value is stored in. 158 | */ 159 | reg_id_t reg; 160 | /** 161 | * For the arg iterator, holds the value of the parameter. 162 | * Unused for the memarg iterator. 163 | * 164 | * \deprecated For 32-bit applications, some platforms (namely 165 | * MacOS) support 64-bit arguments. For such cases, this field 166 | * will hold only the bottom 32 bits of the value. Use the \p 167 | * value64 field to retrieve the whole value. For cross-platform 168 | * code, we recommend using \p value64 rather than this field. 169 | */ 170 | ptr_uint_t value; 171 | /** 172 | * For the memarg iterator, specifies the size in bytes of the memory region. 173 | * For the arg iterator, specifies the size in bytes of the parameter. 174 | */ 175 | size_t size; 176 | /** 177 | * Identical to \p value, except it holds the full value of the 178 | * parameter for the arg iterator for 32-bit applications on MacOS 179 | * when the value is an 8-byte type. For cross-plaform code, we 180 | * recommend using this field rather than \p value. 181 | * 182 | * Unused for the memarg iterator. 183 | */ 184 | uint64 value64; 185 | } drltrace_arg_t; 186 | 187 | 188 | void parse_config(void); 189 | std::vector *libcalls_search(const char *name); 190 | void libcalls_hashtable_delete(); 191 | -------------------------------------------------------------------------------- /drltrace_src/drltrace_utils.h: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | #ifndef _DRLTRACE_UTILS_H 34 | #define _DRLTRACE_UTILS_H 35 | 36 | #include "dr_api.h" 37 | 38 | /* XXX: some data types were taken from drsyscall.h and utils.h (DrMemory) */ 39 | 40 | void print_prefix_to_console(void); 41 | 42 | /* A faster(?) version of strcmp(), since strcmp() does extra string 43 | * comparison we don't need (we just need an equality test). Returns 44 | * 0 when strings are equal, otherwise returns non-zero. */ 45 | inline int 46 | fast_strcmp(char *s1, size_t s1_len, char *s2, size_t s2_len) { 47 | if (s1_len != s2_len) 48 | return -1; 49 | 50 | #ifdef WINDOWS 51 | return memcmp(s1, s2, s1_len); /* VC2013 doesn't have bcmp(), sadly. */ 52 | #else 53 | return bcmp(s1, s2, s1_len); /* Fastest option. */ 54 | #endif 55 | } 56 | 57 | #define MIN(x,y) (((x) > (y)) ? (y) : (x)) 58 | 59 | #ifdef DEBUG 60 | # define IF_DEBUG(x) x 61 | # define IF_DEBUG_ELSE(x,y) x 62 | # define _IF_DEBUG(x) , x 63 | #else 64 | # define IF_DEBUG(x) 65 | # define IF_DEBUG_ELSE(x,y) y 66 | # define _IF_DEBUG(x) 67 | #endif 68 | 69 | #ifdef UNIX 70 | # define DIRSEP '/' 71 | # define ALT_DIRSEP '/' 72 | # define NL "\n" 73 | #else 74 | /* We can pick which we want for usability: the auto-launch of notepad 75 | * converts to backslash regardless (i#1123). Backslash works in all 76 | * Windows apps, while forward works in most and in cygwin (though 77 | * still not first-class there as it has a drive letter) but not in 78 | * File Open dialogs or older notepad Save As (or as executable path 79 | * when launching). We could consider making this a runtime option 80 | * or auto-picked if in cygwin but for now we're going with backslash. 81 | */ 82 | # define DIRSEP '\\' 83 | # define ALT_DIRSEP '/' 84 | # define NL "\r\n" 85 | #endif 86 | 87 | #define INVALID_THREAD_ID 0 88 | 89 | #define TEST(mask, var) (((mask) & (var)) != 0) 90 | 91 | #ifdef WINDOWS 92 | # define IF_WINDOWS(x) x 93 | # define IF_WINDOWS_(x) x, 94 | # define _IF_WINDOWS(x) , x 95 | # define IF_WINDOWS_ELSE(x,y) x 96 | # define IF_UNIX(x) 97 | # define IF_UNIX_ELSE(x,y) y 98 | # define IF_LINUX(x) 99 | # define IF_LINUX_ELSE(x,y) y 100 | # define IF_UNIX_(x) 101 | #else 102 | # define IF_WINDOWS(x) 103 | # define IF_WINDOWS_(x) 104 | # define _IF_WINDOWS(x) 105 | # define IF_WINDOWS_ELSE(x,y) y 106 | # define IF_UNIX(x) x 107 | # define IF_UNIX_ELSE(x,y) x 108 | # define IF_UNIX_(x) x, 109 | # ifdef LINUX 110 | # define IF_LINUX(x) x 111 | # define IF_LINUX_ELSE(x,y) x 112 | # define IF_LINUX_(x) x, 113 | # else 114 | # define IF_LINUX(x) 115 | # define IF_LINUX_ELSE(x,y) y 116 | # define IF_LINUX_(x) 117 | # endif 118 | #endif 119 | 120 | /* for notifying user 121 | * XXX: should add messagebox, controlled by option 122 | */ 123 | enum { 124 | PREFIX_STYLE_DEAULT, 125 | PREFIX_STYLE_NONE, 126 | PREFIX_STYLE_BLANK, 127 | }; 128 | 129 | /* globals that affect NOTIFY* and *LOG* macros */ 130 | extern bool op_print_stderr; 131 | extern uint op_verbose_level; 132 | extern file_t f_global; 133 | extern int reported_disk_error; 134 | extern uint op_prefix_style; 135 | extern uint op_ignore_asserts; 136 | 137 | #if defined(WIN32) && defined(USE_DRSYMS) 138 | # define IN_CMD (dr_using_console()) 139 | # define USE_MSGBOX (op_print_stderr && IN_CMD) 140 | #else 141 | /* For non-USE_DRSYMS Windows we just don't support cmd: unfortunately this 142 | * includes cygwin in cmd. With PR 561181 we'll get cygwin into USE_DRSYMS. 143 | */ 144 | # define USE_MSGBOX (false) 145 | #endif 146 | 147 | # define PREFIX_DEFAULT_MAIN_THREAD "~~drltrace~~ " 148 | #define PREFIX_DRLTRACE "~~drltrace~~ " 149 | 150 | /* For printing to a buffer. 151 | * Usage: have a size_t variable "sofar" that counts the chars used so far. 152 | * We take in "len" to avoid repeated locals, which some compilers won't 153 | * combine (grrr: xref some PR). 154 | * If we had i#168 dr_vsnprintf this wouldn't have to be a macro. 155 | */ 156 | #define BUFPRINT_NO_ASSERT(buf, bufsz, sofar, len, ...) do { \ 157 | len = dr_snprintf((buf)+(sofar), (bufsz)-(sofar), __VA_ARGS__); \ 158 | sofar += (len == -1 ? ((bufsz)-(sofar)) : (len < 0 ? 0 : len)); \ 159 | /* be paranoid: though usually many calls in a row and could delay until end */ \ 160 | (buf)[(bufsz)-1] = '\0'; \ 161 | } while (0) 162 | 163 | /* dr_fprintf() now prints to the console after dr_enable_console_printing() */ 164 | #define PRINT_CONSOLE(...) dr_fprintf(STDERR, __VA_ARGS__) 165 | 166 | #define REPORT_DISK_ERROR() do { \ 167 | /* this implements a DO_ONCE with multiple instantiations */ \ 168 | int report_count = dr_atomic_add32_return_sum(&reported_disk_error, 1); \ 169 | if (report_count == 1) {\ 170 | if (op_print_stderr) {\ 171 | print_prefix_to_console(); \ 172 | PRINT_CONSOLE("WARNING: Unable to write to the disk. "\ 173 | "Ensure that you have enough space and permissions.\n"); \ 174 | } \ 175 | if (USE_MSGBOX) {\ 176 | IF_WINDOWS(dr_messagebox("Unable to write to the disk. "\ 177 | "Ensure that you have enough space and permissions.\n")); \ 178 | } \ 179 | } \ 180 | } while (0) 181 | 182 | /* we require a ,fmt arg but C99 requires one+ argument which we just strip */ 183 | #define ELOGF(level, f, ...) do { \ 184 | if (op_verbose_level >= (level) && (f) != INVALID_FILE) {\ 185 | if (dr_fprintf(f, __VA_ARGS__) < 0) \ 186 | REPORT_DISK_ERROR(); \ 187 | } \ 188 | } while (0) 189 | 190 | #define LOGFILE(pt) ((pt) == NULL ? f_global : (pt)->f) 191 | 192 | #define ELOGPT(level, pt, ...) \ 193 | ELOGF(level, LOGFILE(pt), __VA_ARGS__) 194 | 195 | #define ELOG(level, ...) do { \ 196 | if (op_verbose_level >= (level)) { /* avoid unnec PT_GET */ \ 197 | ELOGPT(level, PT_LOOKUP(), __VA_ARGS__); \ 198 | } \ 199 | } while (0) 200 | 201 | #define NOTIFY_COND(cond, f, ...) do { \ 202 | ELOGF(0, f, __VA_ARGS__); \ 203 | if ((cond) && op_print_stderr) {\ 204 | print_prefix_to_console(); \ 205 | PRINT_CONSOLE(__VA_ARGS__); \ 206 | }\ 207 | } while (0) 208 | 209 | /* XXX: VNOTIFY prints in console only for debug build, however we have 210 | * some important user notifications that would be good to keep visible 211 | * in release build as well. 212 | */ 213 | #define VNOTIFY(level, ...) do { \ 214 | NOTIFY_COND(op_verbose.get_value() >= level, f_global, __VA_ARGS__); \ 215 | } while (0) 216 | 217 | #define OPTION_MAX_LENGTH MAXIMUM_PATH 218 | 219 | #define BUFFER_SIZE_BYTES(buf) sizeof(buf) 220 | #define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof((buf)[0])) 221 | #define BUFFER_LAST_ELEMENT(buf) (buf)[BUFFER_SIZE_ELEMENTS(buf) - 1] 222 | #define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0 223 | 224 | #if !defined(MACOS) && !defined(ANDROID) && !defined(NOLINK_STRCASESTR) 225 | const char * 226 | strcasestr(const char *text, const char *pattern); 227 | #endif 228 | 229 | 230 | #define NOTIFY_ERROR(...) do { \ 231 | IF_WINDOWS({ if (USE_MSGBOX) dr_messagebox(__VA_ARGS__); }) \ 232 | } while (0) 233 | 234 | #ifdef DEBUG 235 | # define ASSERT(x, msg) do { \ 236 | if (!(x)) {\ 237 | NOTIFY_ERROR("ASSERT FAILURE (thread " TIDFMT "): %s:%d: %s (%s)" NL, \ 238 | (dr_get_current_drcontext() == NULL ? 0 : \ 239 | dr_get_thread_id(dr_get_current_drcontext())), \ 240 | __FILE__, __LINE__, #x, msg); \ 241 | if (!op_ignore_asserts) dr_abort(); \ 242 | } \ 243 | } while (0) 244 | #else 245 | # define ASSERT(x, msg) /* nothing */ 246 | # define ASSERT_NOT_TESTED(msg) /* nothing */ 247 | #endif 248 | 249 | #endif /* _DRLTRACE_UTILS_H */ 250 | -------------------------------------------------------------------------------- /api_calls_viz/api_calls_vis_python3.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import random 4 | import pylab 5 | import numpy 6 | import argparse 7 | from PIL import Image 8 | import math 9 | from colorsys import hsv_to_rgb 10 | 11 | 12 | # A user can add his/her own list of API calls here 13 | marked_calls = ["GetProcAddress"] 14 | 15 | css_styles = "" 16 | divs = "" 17 | divs_image = "" 18 | 19 | 20 | def get_specific_color(api_name): 21 | if api_name not in marked_calls: 22 | return 0 23 | print("Picked hardcoded color for ", api_name) 24 | return 0xFF0000 25 | 26 | 27 | def add_one_color_in_legend(color, libcall_name): 28 | global css_styles, divs 29 | str_color = str(hex(color)) 30 | str_color = str_color.replace("0x", "") 31 | css_color = ".name%s { background: #%x;}\n" % (str_color, color) 32 | div_color = "%s

\n" % ( 33 | libcall_name, str_color) 34 | css_styles += css_color 35 | divs += div_color 36 | 37 | 38 | def add_one_color_on_html_page(color, libcall_name): 39 | global divs_image 40 | str_color = str(hex(color)) 41 | str_color = str_color.replace("0x", "") 42 | div_image_color = "
\n" % ( 43 | str_color, libcall_name) 44 | divs_image += div_image_color 45 | 46 | 47 | def choose_colors(unique_libcalls, grayscale): 48 | libcall_colors_dict = dict() 49 | 50 | print("Generating random color for each entry") 51 | # generate a random unique color for each 52 | # call excluding whiteness (checkit) 53 | dot_nums = random.sample(range(0x0, 0x00EEFFFF), len(unique_libcalls)) 54 | print("Done") 55 | 56 | for i, api_name in enumerate(unique_libcalls): 57 | # we assign specific red color for marked calls 58 | color_dot = get_specific_color(api_name) 59 | # FIXIT: we disable marking in case of grayscale 60 | # due to strange img results 61 | if color_dot != 0 and not grayscale: 62 | add_one_color_in_legend(color_dot, api_name) 63 | else: 64 | color_dot = dot_nums[i] 65 | add_one_color_in_legend(dot_nums[i], api_name) 66 | 67 | libcall_colors_dict[api_name] = color_dot 68 | return libcall_colors_dict 69 | 70 | 71 | def add_dots_on_image( 72 | libcalls_colors_dict, libcalls_seq, html_page_name, grayscale): 73 | all_dots = [] 74 | libcalls_count = len(libcalls_seq) 75 | print("Adding %d colors on image/images" % libcalls_count) 76 | for api_name in libcalls_seq: 77 | # take an RGB color associated with API call 78 | color_dot = libcalls_colors_dict.get(api_name, None) 79 | if color_dot is None: 80 | print("[CRITICAL] Failed to find color for %s, exiting" % api_name) 81 | sys.exit(0) 82 | if html_page_name is not None: 83 | add_one_color_on_html_page(color_dot, api_name) 84 | R = (color_dot & 0x00FF0000) >> 16 85 | G = (color_dot & 0x0000FF00) >> 8 86 | B = color_dot & 0x000000FF 87 | if grayscale is True: 88 | color_gray = (R + G + B)/3 89 | color_dot = (color_gray, color_gray, color_gray) 90 | else: 91 | color_dot = (R, G, B) 92 | all_dots.append(color_dot) 93 | return all_dots 94 | 95 | 96 | def create_image(all_dots, image_name, output_folder): 97 | # calculate width and height 98 | total_size = math.sqrt(len(all_dots)) 99 | w = int(total_size) + 1 100 | h = int(total_size) + 1 101 | 102 | print("Generating picture %dx%d" % (h, w)) 103 | 104 | data = numpy.zeros((h, w, 3), dtype=numpy.uint8) 105 | i = 0 106 | j = 0 107 | for dot in all_dots: 108 | data[i][j] = dot 109 | j += 1 110 | if j >= w: 111 | i += 1 112 | j = 0 113 | img = Image.fromarray(data, 'RGB') 114 | img.save(os.path.join(output_folder, image_name)) 115 | print("Done") 116 | 117 | 118 | def create_html_page(name, output_folder): 119 | global css_styles, divs_image 120 | # page headers */ 121 | print("Generating HTML image") 122 | 123 | html_image = "%s\n\ 124 | \n\n" 136 | for div in divs_image: 137 | html_image += div 138 | html_image += "" 139 | with open(os.path.join(output_folder, name), 'w') as file: 140 | file.write(html_image) 141 | print("Done") 142 | 143 | 144 | def create_html_legend(name, output_folder): 145 | global css_styles, divs 146 | # page headers */ 147 | print("Generating legend in HTML") 148 | html_legenda = "Legenda\n\ 149 | \n\n" 161 | for div in divs: 162 | html_legenda += div 163 | html_legenda += "" 164 | 165 | with open(os.path.join(output_folder, "legend_%s.html" % name), 166 | 'w') as file: 167 | file.write(html_legenda) 168 | print("Done") 169 | 170 | 171 | def gen_image( 172 | trace_name, image_name, html_page_name, grayscale, output_folder): 173 | unique_libcalls = [] 174 | libcalls_seq = [] 175 | 176 | global css_styles, divs, divs_image 177 | css_styles = "" 178 | divs = "" 179 | divs_image = "" 180 | 181 | # to be able to generate the same image each run for single trace 182 | random.seed(0) 183 | 184 | with open(file=trace_name, mode="r", encoding="utf8") as file: 185 | content = file.readlines() 186 | # TODO: what if I randomly selected red 0x255 0x0 0x0? 187 | for line in content: 188 | # skip arguments provided by drltrace 189 | if " arg" in line or "module id" in line: 190 | continue 191 | # extract API call name 192 | line = line[:line.find("(")] 193 | api_name = line[line.find("!")+1:] 194 | 195 | if api_name not in unique_libcalls: 196 | unique_libcalls.append(api_name) # save in unique calls 197 | # save in sequence for future proceedings 198 | libcalls_seq.append(api_name) 199 | 200 | entries_count = len(libcalls_seq) 201 | 202 | if entries_count <= 0: 203 | print("Failed to find any API calls matching dll_name!" 204 | + "api_name pattern in the file specified") 205 | sys.exit(0) 206 | else: 207 | print("Found %d api calls in the file" % entries_count) 208 | 209 | print("Starting image generation") 210 | 211 | # choose colors for each unique image 212 | libcall_colors_dict = choose_colors(unique_libcalls, grayscale) 213 | dots = add_dots_on_image( 214 | libcall_colors_dict, libcalls_seq, html_page_name, grayscale) 215 | create_image(dots, image_name, output_folder) 216 | create_html_legend(image_name, output_folder) 217 | if html_page_name is not None: 218 | create_html_page(html_page_name) 219 | 220 | 221 | def main(): 222 | # bug: whites for many calls ? 223 | parser = argparse.ArgumentParser(prog="api_calls_vis.py", 224 | description='A script for API calls trace' 225 | + 'visualization.', 226 | usage='% (prog)s - t calc.exe.txt - i' 227 | + 'calc.jpeg - ht calc.html') 228 | parser.add_argument('-t', '--trace', 229 | help="A trace file with API calls in the following" 230 | + "format \"library_name!api_call_name\"") 231 | parser.add_argument('-if', '--input_folder', default="input", 232 | help="An input folder containing" 233 | + "files with API calls in the following" 234 | + "format \"library_name!api_call_name\"") 235 | parser.add_argument('-of', '--output_folder', default=".", 236 | help="An output folder that will contain the images" 237 | + "format \"library_name!api_call_name\"") 238 | # generate jpeg image 239 | parser.add_argument('-i', '--image', default="tmp.jpeg", 240 | help="A name of image file") 241 | # generate html page with image 242 | parser.add_argument('-ht', '--html', help="A name of html page (heavy)") 243 | parser.add_argument('-gr', '--grayscale', dest='grayscale', 244 | action='store_true', 245 | help="Generate an image in grayscale") 246 | args = parser.parse_args() 247 | if not os.path.exists(args.output_folder): 248 | os.mkdir(args.output_folder) 249 | 250 | # If we chose to generate an image from one log 251 | if args.trace: 252 | gen_image(args.trace, args.image, args.html, 253 | args.grayscale, args.output_folder) 254 | # If we chose to generate images for an entire folder of logs 255 | elif args.input_folder: 256 | for root, dirs, files in os.walk(args.input_folder): 257 | for filename in files: 258 | gen_image(os.path.join(root, filename), 259 | filename + ".jpeg", args.html, 260 | args.grayscale, args.output_folder) 261 | else: 262 | print("No input file nor folder provided") 263 | 264 | 265 | if __name__ == "__main__": 266 | main() 267 | -------------------------------------------------------------------------------- /drltrace_src/scripts/headers_parser.py: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # Copyright (c) 2017 Google, Inc. All rights reserved. 3 | # *************************************************************************** 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # * Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # * Redistributions in binary form must reproduce the above copyright notice, 12 | # this list of conditions and the following disclaimer in the documentation 13 | # and/or other materials provided with the distribution. 14 | # 15 | # * Neither the name of Google, Inc. nor the names of its contributors may be 16 | # used to 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 "AS IS" 20 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | # ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 23 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 29 | # DAMAGE. 30 | # 31 | # ******************************************************************************** 32 | # The script is used to search for WinAPI function prototypes in the SDK's headers based 33 | # on the list/lists of exported functions and to save them in a separate set of files for 34 | # the further postprocessing using gen_drltrace_config.py. The list of exported functions 35 | # should have one exported function name per line. For example, the list can be obtained 36 | # using dumpbin tool on Windows. 37 | # 38 | # The script saves possible function prototypes using the following pattern: 39 | # [exported function name] -> [possible return types] [function name found]([args]) 40 | # where [exported function name] is a name of function we are searching for. 41 | # 42 | # In some cases, we can find several function prototypes that match a certain exported 43 | # function. Such cases should be resolved manually and we add a special label [DUPLICATE] 44 | # to make the search easier for the user. 45 | 46 | import os 47 | import sys 48 | from os import listdir 49 | from os.path import isfile, join 50 | import argparse 51 | 52 | # the list of ignored exported functions 53 | common_dll_names = {"DllCanUnloadNow", "DllGetClassObject", "DllRegisterServer", 54 | "DllUnregisterServer", "DllEntryPoint", "DllInstall"} 55 | 56 | # the maximum number of elements in a function prototype 57 | prototype_length = 70 58 | 59 | def merge_entries(entry, duplicates, function_name): 60 | ''' 61 | The function converts a list of entries into the string to save in the log. 62 | @in entry - a list of entries. 63 | @in duplicates_found - prepend an output string with [DUPLICATE] token. 64 | @out - a string to save in the log. 65 | ''' 66 | final_str = function_name + " -> " 67 | if duplicates == 1: 68 | final_str += "[DUPLICATE] " 69 | for element in entry: 70 | element = element.replace("\n", "") 71 | element = ' '.join(element.split()) 72 | element = element.replace(" *", "*") 73 | element = element.replace("*", "* ") 74 | final_str = final_str + " " + element 75 | return final_str 76 | 77 | def parse_headers(headers_path): 78 | ''' 79 | @in - the routine parse SDKs headers located in the headers_path. 80 | @out - a list of lines from each header. 81 | ''' 82 | 83 | headers_content = list() 84 | headers = [f for f in listdir(headers_path) if isfile(join(headers_path, f))] 85 | for header in headers: 86 | if not header.endswith(".h"): 87 | continue 88 | header_content = open(headers_path + header, 'r').readlines() 89 | headers_content.append(header_content) 90 | return headers_content 91 | 92 | def find_in_headers(entry_name, headers_content): 93 | ''' 94 | The function looks for the entry_name in the headers_count and returns a list of 95 | potentially similar functions (that looks like WinAPI function prototype). 96 | @in entry_name - a name of WinAPI function to search for. 97 | @in headers_content - headers content to search in. 98 | @out - a list of WinAPI function prototypes. 99 | ''' 100 | 101 | list_of_results = list() 102 | for header_content in headers_content: 103 | for idx, line in enumerate(header_content): 104 | if entry_name in line: 105 | # parse line, look for separate names 106 | if "(" in line and not "#define" in line and not "return " in line\ 107 | and not "//" in line and not "/*" in line and not "*/" in line\ 108 | and not "=" in line and not "STDMETHOD" in line\ 109 | and not "EXTERN_GUID" in line: 110 | 111 | final_line = tuple() 112 | tmp_idx = idx 113 | # take return type (before function or at the same line) 114 | while tmp_idx > 1: 115 | tmp_idx = tmp_idx - 1 # looking back 116 | if header_content[tmp_idx] == '\n' or ';' in header_content[tmp_idx]\ 117 | or "#" in header_content[tmp_idx]: 118 | break 119 | if header_content[tmp_idx].startswith("\\"): # ignore comments 120 | continue 121 | final_line = final_line + (header_content[tmp_idx],) 122 | 123 | tmp_idx = idx 124 | # take arguments if exist 125 | while tmp_idx < len(header_content): 126 | if not header_content[tmp_idx].startswith("//"): 127 | comment_idx = header_content[tmp_idx].find("//") 128 | new_line = header_content[tmp_idx] 129 | if comment_idx: # remove comment after an argument 130 | new_line = header_content[tmp_idx][:comment_idx] 131 | final_line = final_line + (new_line,) 132 | if ";" in header_content[tmp_idx]: 133 | break 134 | tmp_idx = tmp_idx + 1 135 | # We need some limitation for the prototype length to filter out really 136 | # long incorrect results. For example, we found that the threshold of 137 | # 70 elements is the best for kernel32.dll. However, it might be 138 | # different for other dlls. 139 | # The special check for "Boolean status" is required to handle specific 140 | # case when a header has the comment like "Boolean status. Error code 141 | # available via GetLastError()". 142 | if len(final_line) > prototype_length or "{" in final_line\ 143 | or "Boolean status" in final_line or "&&" in final_line: 144 | continue 145 | list_of_results.append(final_line) 146 | results = "" 147 | duplicates = 0 148 | for entry in list_of_results: 149 | results += merge_entries(entry, duplicates, entry_name) + "\n" 150 | duplicates = 1 151 | return results 152 | 153 | if __name__ == "__main__": 154 | parser = argparse.ArgumentParser(description = "The script is used to search for WinAPI" 155 | " function prototypes in the SDK headers.") 156 | parser.add_argument('-exports_path', help = "The path where a list or lists of WinAPI" 157 | " functions to search for are located.", default = "exports\\") 158 | parser.add_argument('-headers_path', help = "The path where Windows headers are located" 159 | " (install Microsoft SDK to have them).", 160 | default = "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Include\\") 161 | parser.add_argument('-results_path', help = "The path where the script saves results.", 162 | default = "results\\") 163 | parser.add_argument('-parse_one_export_file', help = "The option is used to specify" 164 | " for parsing a single file located in the exports_path.") 165 | 166 | args = parser.parse_args() 167 | 168 | print "Using %s as the exports dir, %s as the headers dir, %s as the results dir " %\ 169 | (args.exports_path, args.headers_path, args.results_path) 170 | 171 | export_files = [f for f in listdir(args.exports_path) if isfile(join(args.exports_path, f))] 172 | # parse headers 173 | headers_content = parse_headers(args.headers_path) 174 | 175 | for idx, file in enumerate(export_files): 176 | print "Done %d out of %d" % (idx, len(export_files)) 177 | 178 | if args.parse_one_export_file != None and args.parse_one_export_file not in file: 179 | continue 180 | 181 | if file.startswith("api-ms-win"): # just special wrappers for the system dlls 182 | continue 183 | 184 | exp_file_content = open(args.exports_path + file, 'r').readlines() 185 | config = open(args.results_path + file +".headers_out", 'w') 186 | 187 | for export_entry in exp_file_content: 188 | export_entry = export_entry[:-1] # remove \n 189 | if export_entry in common_dll_names: # ignore common exports 190 | continue 191 | 192 | config.write("Looking for entry " + export_entry + "\n") 193 | 194 | entry = find_in_headers(export_entry, headers_content) 195 | if entry == "": 196 | config.write("NONE\n") 197 | else: 198 | config.write(entry) 199 | config.close() 200 | print "All done. Please use gen_drltrace_config.py for postprocessing." 201 | -------------------------------------------------------------------------------- /drltrace_src/drltrace_libcalls.cpp: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | #include "drltrace.h" 34 | #include "hashtable.h" 35 | #include 36 | 37 | /**************************************************************************** 38 | * Routines to support libcalls hashtable 39 | */ 40 | 41 | #define LIBCALLS_TABLE_HASH_BITS 6 42 | /* We init the following hashtable to be able to get library call arguments when 43 | * it is required for printing. 44 | */ 45 | static hashtable_t libcalls_table; 46 | 47 | static void 48 | free_args_list(void *p) 49 | { 50 | std::vector *args_list = (std::vector *) p; 51 | std::vector::iterator it; 52 | for (it = args_list->begin(); it != args_list->end(); ++it) 53 | dr_global_free(*it, sizeof(drltrace_arg_t)); 54 | 55 | delete args_list; 56 | } 57 | 58 | static void 59 | init_libcalls_hashtable() 60 | { 61 | hashtable_init_ex(&libcalls_table, LIBCALLS_TABLE_HASH_BITS, HASH_STRING_NOCASE, 62 | false/*!strdup*/, false, free_args_list, NULL, NULL); 63 | } 64 | 65 | void 66 | libcalls_hashtable_delete() 67 | { 68 | hashtable_delete(&libcalls_table); 69 | } 70 | 71 | std::vector * 72 | libcalls_search(const char *name) 73 | { 74 | return (std::vector *)hashtable_lookup(&libcalls_table, (void *)name); 75 | } 76 | 77 | static bool 78 | libcalls_hashtable_insert(const char *name, std::vector *args_list) 79 | { 80 | return hashtable_add(&libcalls_table, (void *)name, (void *)args_list); 81 | } 82 | 83 | /**************************************************************************** 84 | * Config file parsing routines 85 | */ 86 | 87 | static std::string 88 | erase_token(std::string src_string, std::string pattern) 89 | { 90 | std::string::size_type i = src_string.find(pattern); 91 | while (i != std::string::npos) { 92 | src_string.erase(i, pattern.length()); 93 | i = src_string.find(pattern, i); 94 | } 95 | return src_string; 96 | } 97 | 98 | /* The function returns a new drltrace_arg_t object allocated on the global heap 99 | * that the caller should free. 100 | */ 101 | static drltrace_arg_t * 102 | config_parse_type(std::string type_name, uint index) 103 | { 104 | 105 | ASSERT(!type_name.empty(), "an empty type name was provided"); 106 | 107 | drltrace_arg_t *arg = (drltrace_arg_t *)dr_global_alloc(sizeof(drltrace_arg_t)); 108 | 109 | /* init arg */ 110 | arg->type = DRSYS_TYPE_UNKNOWN; 111 | arg->ordinal = index; 112 | arg->size = 0; 113 | arg->arg_name = NULL; 114 | arg->type_name = NULL; 115 | arg->pre = true; 116 | 117 | if (type_name.find("__inout") != std::string::npos) 118 | arg->mode = (drsys_param_mode_t)(DRSYS_PARAM_IN|DRSYS_PARAM_OUT); 119 | else if (type_name.find("__out") != std::string::npos) 120 | arg->mode = DRSYS_PARAM_OUT; 121 | else if (type_name.find("*") != std::string::npos) 122 | arg->mode = DRSYS_PARAM_IN; 123 | else 124 | arg->mode = DRSYS_PARAM_INLINED; 125 | 126 | /* we don't need special symbols __inout, __out or * further */ 127 | type_name = erase_token(type_name, "*"); 128 | type_name = erase_token(type_name, "__inout"); 129 | type_name = erase_token(type_name, "__out"); 130 | 131 | /* sanitize input type, we assume ASCII chars here */ 132 | std::transform(type_name.begin(), type_name.end(), type_name.begin(), ::toupper); 133 | type_name.erase(std::remove(type_name.begin(), type_name.end(), ' '), 134 | type_name.end()); 135 | type_name.erase(std::remove(type_name.begin(), type_name.end(), '\r'), 136 | type_name.end()); 137 | type_name.erase(std::remove(type_name.begin(), type_name.end(), '\n'), 138 | type_name.end()); 139 | 140 | /* FIXME Drmemory i#1948: Currently, we have only few cross-platform libcalls 141 | * in the config file which is possible to use both for Windows and Linux. 142 | * However, we need to separate them into two configs and fix CMAKE accordingly. 143 | * Moreover, we have to provide two different interfaces for type parsing 144 | * in Windows and Linux. 145 | */ 146 | 147 | /* XXX DrMemory i#1948: We have to extend a list of supported types here. */ 148 | if (type_name.compare("VOID") == 0) { 149 | arg->type_name = "void"; 150 | arg->type = DRSYS_TYPE_VOID; 151 | } else if (type_name.compare("INT") == 0) { 152 | arg->type_name = "int"; 153 | arg->size = sizeof(int); 154 | arg->type = DRSYS_TYPE_SIGNED_INT; 155 | } else if (type_name.compare("LONG") == 0) { 156 | arg->type_name = "long"; 157 | arg->size = sizeof(long); 158 | arg->type = DRSYS_TYPE_SIGNED_INT; 159 | } else if (type_name.compare("SIZE_T") == 0) { 160 | arg->type_name = "size_t"; 161 | arg->size = sizeof(size_t); 162 | arg->type = DRSYS_TYPE_SIZE_T; 163 | } 164 | #ifdef WINDOWS 165 | else if (type_name.compare("HANDLE") == 0) { 166 | arg->type_name = "HANDLE"; 167 | arg->size = sizeof(HANDLE); 168 | arg->type = DRSYS_TYPE_HANDLE; 169 | } else if (type_name.compare("HFILE") == 0) { 170 | arg->type_name = "HFILE"; 171 | arg->size = sizeof(HFILE); 172 | arg->type = DRSYS_TYPE_HFILE; 173 | } else if (type_name.compare("HMODULE") == 0) { 174 | arg->type_name = "HMODULE"; 175 | arg->size = sizeof(HFILE); 176 | arg->type = DRSYS_TYPE_HMODULE; 177 | } else if (type_name.compare("UINT") == 0) { 178 | arg->type_name = "uint"; 179 | arg->size = sizeof(UINT); 180 | arg->type = DRSYS_TYPE_UNSIGNED_INT; 181 | } else if (type_name.compare("DWORD") == 0) { 182 | arg->type_name = "DWORD"; 183 | arg->size = sizeof(DWORD); 184 | arg->type = DRSYS_TYPE_UNSIGNED_INT; 185 | } else if (type_name.compare("WORD") == 0) { 186 | arg->type_name = "WORD"; 187 | arg->size = sizeof(WORD); 188 | arg->type = DRSYS_TYPE_UNSIGNED_INT; 189 | } else if (type_name.compare("BYTE") == 0) { 190 | arg->type_name = "BYTE"; 191 | arg->size = sizeof(BYTE); 192 | arg->type = DRSYS_TYPE_UNSIGNED_INT; 193 | } else if (type_name.compare("BOOL") == 0) { 194 | arg->type_name = "BOOL"; 195 | arg->size = sizeof(BOOL); 196 | arg->type = DRSYS_TYPE_BOOL; 197 | } else if (type_name.compare("LCID") == 0) { 198 | arg->type_name = "LCID"; 199 | arg->size = sizeof(LCID); 200 | arg->type = DRSYS_TYPE_LCID; 201 | } else if (type_name.compare("LPARAM") == 0) { 202 | arg->type_name = "LPARAM"; 203 | arg->size = sizeof(LPARAM); 204 | arg->type = DRSYS_TYPE_LPARAM; 205 | } 206 | #endif 207 | else if (type_name.compare("CHAR") == 0) { 208 | arg->type_name = "char"; 209 | arg->type = DRSYS_TYPE_CSTRING; 210 | } else if (type_name.compare("WCHAR") == 0) { 211 | arg->type_name = "wchar_t"; 212 | arg->type = DRSYS_TYPE_CWSTRING; 213 | } else { 214 | arg->type_name = ""; 215 | VNOTIFY(0, "found unknown type %s in the config file" NL, type_name.c_str()); 216 | } 217 | 218 | return arg; 219 | } 220 | 221 | static int 222 | split(const char *buf, const char delim, std::vector *tokens_list) 223 | { 224 | int count = 0; 225 | std::stringstream ss; 226 | std::string item; 227 | 228 | ss.str(buf); 229 | while (std::getline(ss, item, delim)) { 230 | tokens_list->push_back(item); 231 | count++; 232 | } 233 | return count; 234 | } 235 | 236 | static bool 237 | parse_line(const char *line, int line_num) 238 | { 239 | std::vector tokens; 240 | drltrace_arg_t *tmp_arg; 241 | const char *func_name = NULL; 242 | int elem_index = 0, tokens_count = 0; 243 | 244 | if (line == NULL) 245 | return false; 246 | 247 | if (line[0] == '#') /* just a comment */ 248 | return true; 249 | 250 | if (strlen(line) <= 0 || line[0] == '\n' || line[0] == '\r') 251 | return true; /* just an empty line */ 252 | 253 | tokens_count = split(line, '|', &tokens); 254 | 255 | if (tokens_count <= 0) { 256 | VNOTIFY(0, "unable to parse config file at line %d: %s" NL, line_num, line); 257 | return false; 258 | } 259 | 260 | std::vector *args_vector = new std::vector(); 261 | std::vector::iterator it; 262 | for (it = tokens.begin(); it != tokens.end(); ++it) { 263 | /* FIXME DrMemory i#1948: Currently, we don't support ret value printing and 264 | * skipping it here. 265 | */ 266 | if (elem_index >= 2) { 267 | tmp_arg = config_parse_type(*it, elem_index - 2); 268 | args_vector->push_back(tmp_arg); 269 | } else if (elem_index == 1) 270 | func_name = it->c_str(); 271 | 272 | elem_index++; 273 | } 274 | 275 | if (func_name == NULL || args_vector->size() <= 0) { 276 | VNOTIFY(0, "unable to parse config file at line %d: %s" NL, line_num, line); 277 | return false; 278 | } 279 | 280 | VNOTIFY(2, "adding %s from config file with %d arguments in the hashtable" NL, 281 | func_name, args_vector->size()); 282 | IF_DEBUG(bool ok =) 283 | libcalls_hashtable_insert(strdup(func_name), args_vector); 284 | ASSERT(ok, "failed to add libcall in the hashtable"); 285 | 286 | return true; 287 | } 288 | 289 | void 290 | parse_config(void) 291 | { 292 | void *map = NULL; 293 | uint64 size_to_read = 0; 294 | size_t actual_size = 0; 295 | file_t file_desc = INVALID_FILE; 296 | int lines_count = 0, line_num = 1; 297 | bool res = false; 298 | std::vector lines_list; 299 | 300 | if (!op_use_config.get_value()) 301 | return; 302 | 303 | /* open and map config file */ 304 | file_desc = dr_open_file(op_config_file.get_value().c_str(), DR_FILE_READ); 305 | if (file_desc != INVALID_FILE) { 306 | res = dr_file_size(file_desc, &size_to_read); 307 | if (res) { 308 | actual_size = (size_t)size_to_read; 309 | map = dr_map_file(file_desc, &actual_size, 0, 310 | NULL, DR_MEMPROT_READ, 0); 311 | } 312 | } 313 | 314 | if (!res || map == NULL || actual_size < size_to_read) { 315 | if (map != NULL) 316 | dr_unmap_file(map, actual_size); 317 | if (file_desc != INVALID_FILE) 318 | dr_close_file(file_desc); 319 | VNOTIFY(0, "unable to open config file at %s, config is not used" NL, 320 | op_config_file.get_value().c_str()); 321 | op_use_config.set_value(false); 322 | return; 323 | } 324 | 325 | lines_count = split((const char *)map, '\n', &lines_list); /* split buffer by lines */ 326 | 327 | dr_unmap_file(map, actual_size); 328 | dr_close_file(file_desc); 329 | 330 | if (lines_count <= 0) { 331 | VNOTIFY(0, "An empty config file was specified, config is not used" NL); 332 | op_use_config.set_value(false); 333 | return; 334 | } 335 | 336 | init_libcalls_hashtable(); 337 | 338 | std::vector::iterator it; 339 | for (it = lines_list.begin(); it != lines_list.end(); it++) { 340 | /* XXX: we have to describe a format of the config file in the drltrace's 341 | * documentation as well as list supported types. 342 | */ 343 | if (!parse_line(it->c_str(), line_num)) { 344 | VNOTIFY(0, "incorrect format for the line %d: %s in config file" NL, 345 | line_num, it->c_str()); 346 | op_use_config.set_value(false); 347 | libcalls_hashtable_delete(); 348 | break; 349 | } 350 | line_num++; 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drltrace 2 | Windows test build ![build status](https://ci.appveyor.com/api/projects/status/github/mxmssh/drltrace?branch=master&svg=true) 3 | 4 | Linux test build [![Build Status](https://travis-ci.com/mxmssh/drltrace.svg?branch=master)](https://travis-ci.com/mxmssh/drltrace) 5 | 6 | Drltrace is a dynamic API calls tracer for Windows and Linux applications. Drltrace is built on top of [DynamoRIO](http://www.dynamorio.org/) dynamic binary instrumentation framework. Drltrace was initialy implemented by Derek Bruening and distributed with DynamoRIO and DrMemory frameworks. This repository contains a standalone version of drltrace with additional scripts and materials on how to use it for malware analysis. The release build can be downloaded [here](https://github.com/mxmssh/drltrace/releases). 7 | 8 | # Usage 9 | The usage of drltrace is very simple. A user needs to specify a log directory and a name of a target process in the following way: 10 | ``` 11 | drltrace -logdir . -- calc.exe 12 | ``` 13 | That’s all, the tool will inject required DLLs in the target process, starts instrumentation and in parallel will log information about all library calls which are executed in the target process: 14 | 15 | ``` 16 | ~~43600~~ msvcrt.dll!__wgetmainargs 17 | arg 0: 0x010d2364 18 | arg 1: 0x010d2368 19 | and return to module id:0, offset:0x193a 20 | ~~43600~~ ntdll.dll!EtwEventRegister 21 | arg 0: 0x002ff994 22 | arg 1: 0x010d1490 23 | and return to module id:0, offset:0x157e 24 | ~~43600~~ ntdll.dll!EtwEventSetInformation 25 | arg 0: 0x007b4b40 26 | arg 1: 0x00000033 27 | and return to module id:0, offset:0x15a1 28 | ~~43600~~ SHELL32.dll!ShellExecuteW 29 | arg 0: (type=, size=0x0) 30 | arg 1: (type=wchar_t*, size=0x0) 31 | arg 2: calculator:// (type=wchar_t*, size=0x0) 32 | arg 3: (type=wchar_t*, size=0x0) 33 | arg 4: (type=wchar_t*, size=0x0) 34 | arg 5: 0x1 (type=int, size=0x4) 35 | and return to module id:0, offset:0x167d 36 | ``` 37 | The format of the output is simple and can be easily parsed by an external script: 38 | ``` 39 | ~~[thread id]~~ [dll name]![api call name] 40 | arg [arg #]: [value] (type=[Windows type name], size=[size of arg]) 41 | and return to module id:[module unique id], offset:[offset in memory] 42 | ``` 43 | Parsing with `grep` can be done when the `-grepable` argument is used; this prints the function names and arguments all on one line: 44 | ``` 45 | ~~4824~~ KERNELBASE.dll!CreateFileW {0: C:\Windows\Fonts\staticcache.dat (type=wchar_t*, size=0x0)} {1: 0x80000000 (type=DWORD, size=0x4)} {2: 0x3 (type=DWORD, size=0x4)} {3: 0x005cde8c (type=*, size=0x0)} {4: 0x3 (type=DWORD, size=0x4)} {5: 0x80 (type=DWORD, size=0x4)} 46 | ``` 47 | The module unique identifiers table is printed at the end of the log file: 48 | ``` 49 | Module Table: version 3, count 70 50 | Columns: id, containing_id, start, end, entry, checksum, timestamp, path 51 | 0, 0, 0x010d0000, 0x010da000, 0x010d1b80, 0x0000f752, 0xb5fe3575, C:\Windows\SysWOW64\calc.exe 52 | 1, 1, 0x6d4c0000, 0x6d621000, 0x6d563940, 0x00136d65, 0x59ce1b0b, C:\Users\Max\Downloads\drltrace\drltrace\dynamorio\lib32\release\dynamorio.dll 53 | 2, 2, 0x73800000, 0x73975000, 0x7380dbf7, 0x00000000, 0x59ce1b0f, C:\Users\Max\Downloads\drltrace\drltrace\bin\release/drltracelib.dll 54 | 3, 3, 0x742f0000, 0x742fa000, 0x742f2a00, 0x0000c877, 0x0adc52c1, C:\Windows\System32\CRYPTBASE.dll 55 | 4, 4, 0x74300000, 0x74320000, 0x7430c9b0, 0x0002c617, 0x245970b4, C:\Windows\System32\SspiCli.dll 56 | 5, 5, 0x74410000, 0x74431000, 0x74416900, 0x0002a940, 0x88a53c1d, C:\Windows\System32\GDI32.dll 57 | 6, 6, 0x74440000, 0x74500000, 0x7446fb20, 0x000cc410, 0xd343d532, C:\Windows\System32\RPCRT4.dll 58 | 7, 7, 0x74500000, 0x74525000, 0x745047d0, 0x00026737, 0xa39c8991, C:\Windows\System32\IMM32.DLL 59 | 8, 8, 0x74550000, 0x745c7000, 0x7456e8a0, 0x00081857, 0x73b971e1, C:\Windows\System32\advapi32.dll 60 | 9, 9, 0x748f0000, 0x74929000, 0x748febd0, 0x00045303, 0xa58be652, C:\Windows\System32\cfgmgr32.dll 61 | 10, 10, 0x74930000, 0x75c78000, 0x74aa09d0, 0x01377aa6, 0x4b39926b, C:\Windows\System32\SHELL32.dll 62 | ``` 63 | 64 | Drltrace can easily filter out interlibrary calls and print only API calls performed from the main module (or from a heap) of a target application by specifying ```-only_from_app``` option which is very useful in case of applications that generate huge logs. For more granular control, the `-filter` option allows the user to specify a filter configuration file in order to filter in specific whitelisted functions, or ignore blacklisted functions (see the *filter.config* file for examples). Drltrace also has several useful external scripts to filter API calls for certain library, print only potentially interesting API calls and strings. 65 | 66 | # License 67 | 68 | Drltrace main modules are distributed under BSD. 69 | 70 | Some files required for drltrace are distributed under LGPL. See source files for more details. 71 | 72 | # Motivation 73 | Malware analysis is not an easy task. Sophisticated software packers like Themida and Armadillo and of course dozens of unnamed packers written by malware authors plus code & data encryption significantly facilitate (in some cases making it completely impossible) static reverse engineering of such samples making life of malware analysts complicated. In such case, API calls tracing can significantly reduce amount of time required to understand an actual malicious intent and reveal a lot of technical details about protected malicious code. 74 | 75 | While traditional technique of API-hooking was successfully implemented in several solutions, the approach is well studied by malware authors and can be easily detected and/or bypassed. Moreover, these tools are distributed as standalone heavy-weight GUI applications (as proprietary products) which are not often easy to integrate within existent malware analysis workflow. 76 | 77 | If we look on Linux world, there is a wonderful tool called [ltrace](https://linux.die.net/man/1/ltrace). Using a single bash command, we can easily get the full trace of API calls of a certain executable. 78 | 79 | **Why don’t we have such tool (like ltrace in Linux) for Windows which is also transparent against anti-research tricks used by modern malware?** 80 | 81 | It turns that there is a technique that can help us to have such tool for Windows and trace API calls transparently towards executed program. This technique is called dynamic binary instrumentation aka DBI. DBI is a technique of analyzing the behavior of a binary application at runtime through the injection of instrumentation code. 82 | 83 | However, application of DBI for malware analysis is undeservedly limited by unpacking automatization and several proofs of concepts for instructions, basic blocks and function calls tracing. As far as we know, drltrace is a first tool for API calls tracing based on DBI which can be used in practice for malware analysis. We provided several malware analysis examples in our [wiki](https://github.com/mxmssh/drltrace/wiki/Malware-Analysis-Examples) where we described how drltrace allowed to revel in several minutes a lot of internal technical details about sophisticated malicious samples without even starting IDA or debugger. 84 | 85 | # Why Drltrace Rock ? 86 | - Fast enough to perform analysis of malicious samples without being detected by time-based anti-research techniques. 87 | - Supports both x86 and x64 (ARM in future). 88 | - Supports both Windows and Linux (macOS in future). 89 | - Supports self-modifying code. 90 | - Supports all types of library linkage (static and dynamic). 91 | - Not-detectable by standard anti-research approaches (anti-hooking, anti-debugging and anti-emulation). 92 | - User can easily add a new function prototype to tell drltrace how to print more details about previously unknown API calls (even about non-system DLLs). External configuration file is used. 93 | - Easy-to-use and modify for your own purposes (no additional package requirements, no heavy-weight GUI interface). 94 | - Open-source, code is clear and well-documented. You can freely build & use your own advanced solution on top of drltrace. 95 | 96 | # Command line options 97 | ``` 98 | -logdir [ .] Log directory to print library call data 99 | -only_from_app [ false] Reports only library calls from the app 100 | -follow_children [ true] Trace child processes 101 | -print_ret_addr [ false] Print library call's return address 102 | -num_unknown_args [ 2] Number of unknown libcall args to print 103 | -num_max_args [ 6] Maximum number of arguments to print 104 | -default_config [ true] Use default config file. 105 | -config [ ""] The path to custom config file. 106 | -filter [filter.config] The path of the whitelist/blacklist file. 107 | -ignore_underscore [ false] Ignores library routine names starting with "_". 108 | -help [ false] Print this message. 109 | -version [ false] Print version number. 110 | -verbose [ 1] Change verbosity. 111 | -use_config [ true] Use config file 112 | -grepable [ false] Grepable output 113 | ``` 114 | # Configuration file syntax 115 | Drltrace supports external configuration files where a user can describe how drltrace should print arguments for certain API calls. 116 | ``` 117 | HANDLE|CreateRemoteThread|HANDLE|SECURITY_ATTRIBUTES*|size_t|THREAD_START_ROUTINE*|VOID*|DWORD|__out DWORD* 118 | ``` 119 | Each function argument should be separated by ```|```. The first argument is return type, the second argument is a function name itself and the rest are the function arguments. A token ```__out``` is used to mark output arguments and ```___inout``` is used to mark input+output arguments. 120 | 121 | # Malware Analysis Examples 122 | 123 | You can find examples of how to use drltrace for analysis of complex malware at our [Wiki page](https://github.com/mxmssh/drltrace/wiki/Malware-Analysis-Examples). 124 | 125 | # Log Visualization 126 | 127 | To make the work with log files easier, we have implemented a script called ```api_calls_viz.py``` which can be used to generate RGB images where each pixel color represents unique API call. For example, the picture below represents log file of WannaCry malware. 128 | 129 | ![API calls picture](pictures/api_calls_vis.png) 130 | 131 | The large green areas on the picture represent API calls (```wcscmp/wcsicmp```) which are used to select files with interesting extensions (e.g. docx, xls, py) to encrypt them. The purple areas represent API calls (```FindFirstFile/FindNextFile/CryptEncrypt```) which are used to enumerate and encrypt files and folders on the disk. 132 | 133 | The script can also generate an HTML representation of generated RGB image where each element can be selected to show a name of API call. 134 | 135 | ![API calls picture](pictures/wannacry_truncated_log_html.png) 136 | 137 | [Raw HTML File](pictures/wannacry_truncated_log.html). 138 | 139 | See the ```api_calls_viz``` [directory](https://github.com/mxmssh/drltrace/tree/master/api_calls_viz) for more details. 140 | 141 | # How to Build 142 | You can find a detailed manual at this [Wiki page](https://github.com/mxmssh/drltrace/wiki/How-To-Build). 143 | 144 | # OS Support 145 | Windows, Linux (macOS in future). 146 | 147 | # CPU Architectures Support 148 | x86, x64 (ARM on the list). 149 | 150 | # Languages 151 | C and C++ standard library (and logs handling scripts written in Python). 152 | 153 | # Technical Details 154 | 155 | We decided to implement our API calls tracer on top of dynamic binary instrumentation framework [DynamoRIO](http://www.dynamorio.org/). Drltrace asks DynamoRIO to perform instrumentation of LoadLibrary call to be able to handle new libraries being loaded by the target process. When the process tries to load a new library, DynamoRIO redirects control flow to ```drltracelib.dll```. In turn, drltrace enumerates exported functions in the newly loaded DLL and registers a special callback for each of them. Thus, if some exported function would be called by malware, drltrace’s callback will be executed before this function and the tool will be able to log all required information such as a function name and arguments. Another callback might be registered after the function to save results of execution. 156 | 157 | Why not Intel Pin ? We decided to use DynamoRIO motivated by the following reasons: 158 | 159 | 1. The source code of DynamoRIO is available on github.com and distributed under BSD license while Intel Pin is a proprietary software. 160 | 2. One of the basic requirements for DynamoRIO at the time of development was transparency towards the instrumented executable. 161 | 3. DynamoRIO uses different technology of instrumentation based on code transformation while Intel PIN uses special trampolines which is not transparent towards analyzed executable and might be detected by malware. 162 | 163 | # Future Work 164 | 1. While drltrace is not detectable by standard anti-research tricks, DBI-engine itself can be detected as shown in these works [1](https://www.youtube.com/watch?v=VGmvx2B5qdo), [2](https://recon.cx/2012/schedule/events/216.en.html). Making DynamoRIO resistant against these tricks is important path for future work. 165 | 2. Currently, drltrace prints a raw log and provides several scripts to print important strings and library calls. In future, we plan to add heuristics (probably by applying YARA rules) to be able to select indicative behavior from malware automatically. 166 | 2. Currently, DynamoRIO has beta support of ARM architecture, testing and porting drltrace on ARM is required. 167 | 3. Drltrace doesn’t support situation when malware injects code in a remote process. In such cases, it is possible to tell DynamoRIO inject drltrace in all newly created processes (```-syswide_on``` option of ```drrun.exe```). However, in future, it is necessary to implement a special support in drltrace for such situations. 168 | 169 | Our issue tracker contains more details about future of drltrace. 170 | 171 | # Acknowledgments 172 | Maksim Shudrak https://github.com/mxmssh 173 | 174 | Derek Bruening https://github.com/derekbruening 175 | 176 | Joe Testa https://github.com/jtesta 177 | -------------------------------------------------------------------------------- /drltrace_src/drltrace_frontend.cpp: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | /* front end for drltrace tool */ 34 | 35 | #ifdef WINDOWS 36 | # define UNICODE 37 | # define _UNICODE 38 | #endif 39 | 40 | #include "dr_api.h" 41 | #include "dr_inject.h" 42 | #include "dr_config.h" 43 | #include "dr_frontend.h" 44 | #include "drltrace_options.h" 45 | #include "drltrace_utils.h" 46 | #include 47 | 48 | #define MAX_DR_CMDLINE (MAXIMUM_PATH*6) 49 | 50 | #define DRLTRACE_ERROR(msg, ...) do { \ 51 | fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); \ 52 | fflush(stderr); \ 53 | exit(1); \ 54 | } while (0) 55 | 56 | #define DRLTRACE_WARN(msg, ...) do { \ 57 | fprintf(stderr, "WARNING: " msg "\n", ##__VA_ARGS__); \ 58 | fflush(stderr); \ 59 | } while (0) 60 | 61 | #define DRLTRACE_INFO(level, msg, ...) do { \ 62 | if (op_verbose.get_value() >= level) {\ 63 | fprintf(stderr, "INFO: " msg "\n", ##__VA_ARGS__); \ 64 | fflush(stderr); \ 65 | }\ 66 | } while (0) 67 | 68 | #undef BUFPRINT 69 | #define BUFPRINT(buf, bufsz, sofar, len, ...) do { \ 70 | drfront_status_t sc = drfront_bufprint(buf, bufsz, &(sofar), &(len), ##__VA_ARGS__); \ 71 | if (sc != DRFRONT_SUCCESS) \ 72 | DRLTRACE_ERROR("drfront_bufprint failed: %d\n", sc); \ 73 | NULL_TERMINATE_BUFFER(buf); \ 74 | } while (0) 75 | 76 | /* check that drltracelib.dll, dynamorio.dll and target executable exist */ 77 | static void 78 | check_input_files(const char *target_app_full_name, char *dr_root, char *drltrace_path) { 79 | bool result = false; 80 | 81 | /* check that the target application exists */ 82 | if (target_app_full_name[0] == '\0') 83 | DRLTRACE_ERROR("target application is not specified"); 84 | 85 | if (drfront_access(target_app_full_name, DRFRONT_READ, &result) != DRFRONT_SUCCESS) 86 | DRLTRACE_ERROR("cannot find target application at %s", target_app_full_name); 87 | if (!result) { 88 | DRLTRACE_ERROR("cannot open target application for read at %s", 89 | target_app_full_name); 90 | } 91 | 92 | /* check that dynamorio's root dir exists and is accessible */ 93 | if (drfront_access(dr_root, DRFRONT_READ, &result) != DRFRONT_SUCCESS) 94 | DRLTRACE_ERROR("cannot find DynamoRIO's root dir at %s", dr_root); 95 | if (!result) 96 | DRLTRACE_ERROR("cannot open DynamoRIO's root dir for read at %s", dr_root); 97 | 98 | /* check that drfrontendlib exist */ 99 | if (drfront_access(drltrace_path, DRFRONT_READ, &result) != DRFRONT_SUCCESS) 100 | DRLTRACE_ERROR("cannot find drltracelib at %s", drltrace_path); 101 | if (!result) 102 | DRLTRACE_ERROR("cannot open drltracelib for read at %s", drltrace_path); 103 | } 104 | 105 | #define TOOLNAME "drltrace" 106 | static void 107 | print_version() { 108 | #if defined(BUILD_NUMBER) && defined(VERSION_NUMBER) 109 | printf("drltrace version %s -- build %d\n", STRINGIFY(VERSION_NUMBER), BUILD_NUMBER); 110 | #elif defined(BUILD_NUMBER) 111 | printf(TOOLNAME" custom build %d -- %s\n", BUILD_NUMBER, __DATE__); 112 | #else 113 | printf(TOOLNAME" custom build -- %s, %s\n", __DATE__, __TIME__); 114 | #endif 115 | exit(0); 116 | } 117 | 118 | static void 119 | configure_application(char *app_name, char **app_argv, void **inject_data, 120 | const char *dr_root, const char *lib_path, const char *log_dir, 121 | const char *config_dir) 122 | { 123 | bool is_debug = false; 124 | #ifdef DEBUG 125 | is_debug = true; 126 | #endif 127 | int errcode; 128 | ssize_t len; 129 | size_t sofar = 0; 130 | char *process; 131 | process_id_t pid; 132 | char dr_option[MAX_DR_CMDLINE]; 133 | char drltrace_option[MAX_DR_CMDLINE]; 134 | dr_option[0] = '\0'; 135 | 136 | if (!op_follow_children.get_value()) 137 | dr_snprintf(dr_option, BUFFER_SIZE_ELEMENTS(dr_option), "-no_follow_children"); 138 | NULL_TERMINATE_BUFFER(dr_option); 139 | 140 | BUFPRINT(drltrace_option, BUFFER_SIZE_ELEMENTS(drltrace_option), sofar, len, "%s ", 141 | op_ltracelib_ops.get_value().c_str()); 142 | 143 | if (log_dir[0] != '\0') { 144 | BUFPRINT(drltrace_option, BUFFER_SIZE_ELEMENTS(drltrace_option), sofar, len, 145 | "-logdir `%s` ", log_dir); 146 | } 147 | if (config_dir[0] != '\0') { 148 | BUFPRINT(drltrace_option, BUFFER_SIZE_ELEMENTS(drltrace_option), sofar, len, 149 | "-config `%s` ", config_dir); 150 | } 151 | 152 | #ifdef UNIX 153 | errcode = dr_inject_prepare_to_exec(app_name, (const char **)app_argv, inject_data); 154 | #else 155 | errcode = dr_inject_process_create(app_name, (const char **)app_argv, inject_data); 156 | #endif 157 | if (errcode != 0 && errcode != WARN_IMAGE_MACHINE_TYPE_MISMATCH_EXE) { 158 | std::string msg = 159 | std::string("failed to create process for \"") + app_name + "\""; 160 | #ifdef WINDOWS 161 | char buf[MAXIMUM_PATH]; 162 | int sofar = dr_snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", msg.c_str()); 163 | NULL_TERMINATE_BUFFER(buf); 164 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 165 | NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 166 | (LPTSTR) buf + sofar, 167 | BUFFER_SIZE_ELEMENTS(buf) - sofar*sizeof(char), NULL); 168 | #endif 169 | DRLTRACE_ERROR("%s", msg.c_str()); 170 | } 171 | 172 | pid = dr_inject_get_process_id(*inject_data); 173 | 174 | process = dr_inject_get_image_name(*inject_data); 175 | if (dr_register_process(process, pid, 176 | false, dr_root, 177 | DR_MODE_CODE_MANIPULATION, 178 | is_debug, DR_PLATFORM_DEFAULT, 179 | dr_option) != DR_SUCCESS) { 180 | DRLTRACE_ERROR("failed to register DynamoRIO configuration"); 181 | } 182 | 183 | if (dr_register_client(process, pid, false, DR_PLATFORM_DEFAULT, 0, 0, lib_path, 184 | drltrace_option) != DR_SUCCESS) { 185 | DRLTRACE_ERROR("failed to register DynamoRIO client configuration"); 186 | } 187 | } 188 | 189 | static void 190 | check_logdir_path(char *logdir, size_t logdir_len) { 191 | drfront_status_t sc; 192 | char absolute_logdir_path[MAXIMUM_PATH]; 193 | char alter_logdir_path[MAXIMUM_PATH]; 194 | bool use_root; 195 | 196 | if (strcmp(logdir, "-") == 0) 197 | return; /* logdir is stderr */ 198 | 199 | sc = drfront_get_absolute_path(logdir, absolute_logdir_path, 200 | BUFFER_SIZE_ELEMENTS(absolute_logdir_path)); 201 | if (sc != DRFRONT_SUCCESS) 202 | DRLTRACE_ERROR("drfront_get_absolute_path failed, error code = %d\n", sc); 203 | 204 | if (!dr_directory_exists(absolute_logdir_path)) 205 | DRLTRACE_ERROR("specified logdir doesn't exist"); 206 | 207 | sc = drfront_appdata_logdir(absolute_logdir_path, "Dr. LTrace", &use_root, 208 | alter_logdir_path, 209 | BUFFER_SIZE_ELEMENTS(alter_logdir_path)); 210 | if (sc != DRFRONT_SUCCESS) 211 | DRLTRACE_ERROR("drfront_appdata_logdir failed, error code = %d\n", sc); 212 | if (!use_root) { 213 | DRLTRACE_WARN("cannot write log file into %s, writing log into %s instead", 214 | absolute_logdir_path, alter_logdir_path); 215 | dr_snprintf(logdir, logdir_len, "%s", alter_logdir_path); 216 | /* if folder doesn't exist, create it */ 217 | if (!dr_directory_exists(alter_logdir_path) && !dr_create_dir(alter_logdir_path)) 218 | DRLTRACE_ERROR("failed to create a folder at %s", alter_logdir_path); 219 | } 220 | else { 221 | dr_snprintf(logdir, logdir_len, "%s", absolute_logdir_path); 222 | } 223 | logdir[logdir_len - 1] = '\0'; 224 | } 225 | 226 | int 227 | _tmain(int argc, const TCHAR *targv[]) 228 | { 229 | char drlibpath[MAXIMUM_PATH]; 230 | #ifdef WINDOWS 231 | static const char *libname = "drltracelib.dll"; 232 | #elif MACOS 233 | static const char *libname = "libdrltracelib.dylib"; 234 | #elif LINUX 235 | static const char *libname = "libdrltracelib.so"; 236 | #endif 237 | dr_snprintf(drlibpath, BUFFER_SIZE_ELEMENTS(drlibpath), "%s", libname); 238 | NULL_TERMINATE_BUFFER(drlibpath); 239 | 240 | void *inject_data; 241 | int exitcode; 242 | char **argv; 243 | char *tmp; 244 | const char *target_app_name; 245 | 246 | char full_target_app_path[MAXIMUM_PATH]; 247 | char tmp_path[MAXIMUM_PATH]; 248 | char full_frontend_path[MAXIMUM_PATH]; 249 | char full_dr_root_path[MAXIMUM_PATH]; 250 | char full_drlibtrace_path[MAXIMUM_PATH]; 251 | char logdir[MAXIMUM_PATH]; 252 | char config_dir[MAXIMUM_PATH]; 253 | 254 | int last_index; 255 | std::string parse_err; 256 | drfront_status_t sc; 257 | 258 | #if defined(WINDOWS) && !defined(_UNICODE) 259 | # error _UNICODE must be defined 260 | #else 261 | /* Convert to UTF-8 if necessary */ 262 | sc = drfront_convert_args((const TCHAR **)targv, &argv, argc); 263 | if (sc != DRFRONT_SUCCESS) 264 | DRLTRACE_ERROR("failed to process args, error code = %d\n", sc); 265 | #endif 266 | 267 | if (!droption_parser_t::parse_argv(DROPTION_SCOPE_FRONTEND, argc, (const char **)argv, 268 | &parse_err, &last_index) || argc < 2) { 269 | DRLTRACE_ERROR("Usage error: %s\n Usage:\n%s\n", parse_err.c_str(), 270 | droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str()); 271 | } 272 | 273 | if (op_version.get_value()) { 274 | print_version(); 275 | return 0; 276 | } 277 | if (op_help.get_value()) { 278 | printf("Usage:\n%s", droption_parser_t::usage_long(DROPTION_SCOPE_ALL).c_str()); 279 | return 0; 280 | } 281 | 282 | target_app_name = argv[last_index]; 283 | if (target_app_name == NULL) { 284 | DRLTRACE_ERROR("Usage error, target application is not specified.\n Usage:\n%s\n", 285 | droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str()); 286 | } 287 | sc = drfront_get_app_full_path(target_app_name, full_target_app_path, 288 | BUFFER_SIZE_ELEMENTS(full_target_app_path)); 289 | if (sc != DRFRONT_SUCCESS) { 290 | DRLTRACE_ERROR("drfront_get_app_full_path failed on %s, error code = %d\n", 291 | target_app_name, sc); 292 | } 293 | 294 | /* get DR's root directory and drltrace.exe full path */ 295 | sc = drfront_get_app_full_path(argv[0], full_frontend_path, 296 | BUFFER_SIZE_ELEMENTS(full_frontend_path)); 297 | if (sc != DRFRONT_SUCCESS) { 298 | DRLTRACE_ERROR("drfront_get_app_full_path failed on %s, error code = %d\n", 299 | argv[0], sc); 300 | } 301 | 302 | tmp = full_frontend_path + strlen(full_frontend_path) - 1; 303 | 304 | /* we assume that default root for our executable is /bin/drltrace.exe */ 305 | while (*tmp != DIRSEP && *tmp != ALT_DIRSEP && tmp > full_frontend_path) 306 | tmp--; 307 | *(tmp+1) = '\0'; 308 | 309 | /* in case of default config option, we use drltrace's frontend path */ 310 | if (op_config_file_default.get_value()) { 311 | dr_snprintf(tmp_path, BUFFER_SIZE_ELEMENTS(tmp_path), "%s/drltrace.config", 312 | full_frontend_path /* binary path */); 313 | NULL_TERMINATE_BUFFER(tmp_path); 314 | sc = drfront_get_absolute_path(tmp_path, config_dir, 315 | BUFFER_SIZE_ELEMENTS(config_dir)); 316 | if (sc != DRFRONT_SUCCESS) 317 | DRLTRACE_ERROR("drfront_get_absolute_path failed, error code = %d\n", sc); 318 | } else { 319 | dr_snprintf(config_dir, BUFFER_SIZE_ELEMENTS(config_dir), "%s", 320 | op_config_file.get_value().c_str()); 321 | } 322 | NULL_TERMINATE_BUFFER(config_dir); 323 | 324 | dr_snprintf(tmp_path, BUFFER_SIZE_ELEMENTS(tmp_path), "%s../dynamorio", 325 | full_frontend_path); 326 | NULL_TERMINATE_BUFFER(tmp_path); 327 | 328 | sc = drfront_get_absolute_path(tmp_path, full_dr_root_path, 329 | BUFFER_SIZE_ELEMENTS(full_dr_root_path)); 330 | NULL_TERMINATE_BUFFER(full_dr_root_path); 331 | 332 | if (sc != DRFRONT_SUCCESS) 333 | DRLTRACE_ERROR("drfront_get_absolute_path failed, error code = %d\n", sc); 334 | 335 | dr_snprintf(full_drlibtrace_path, BUFFER_SIZE_ELEMENTS(full_drlibtrace_path), 336 | "%s%s", full_frontend_path, drlibpath); 337 | NULL_TERMINATE_BUFFER(full_drlibtrace_path); 338 | 339 | check_input_files(full_target_app_path, full_dr_root_path, full_drlibtrace_path); 340 | 341 | if (op_logdir.get_value().c_str() != NULL) { 342 | dr_snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s", 343 | op_logdir.get_value().c_str()); 344 | NULL_TERMINATE_BUFFER(logdir); 345 | /* check logdir access rights, convert in absolute path and replace if it is 346 | * required. 347 | */ 348 | check_logdir_path(logdir, BUFFER_SIZE_ELEMENTS(logdir)); 349 | NULL_TERMINATE_BUFFER(logdir); /* logdir has been replaced */ 350 | } else { 351 | logdir[0] = '\0'; 352 | } 353 | 354 | dr_standalone_init(); 355 | 356 | configure_application(full_target_app_path, &argv[last_index], 357 | &inject_data, full_dr_root_path, full_drlibtrace_path, logdir, 358 | config_dir); 359 | 360 | if (!dr_inject_process_inject(inject_data, false/*!force*/, NULL)) 361 | DRLTRACE_ERROR("unable to inject"); 362 | 363 | if (!dr_inject_process_run(inject_data)) 364 | DRLTRACE_ERROR("unable to execute target application"); 365 | 366 | DRLTRACE_INFO(1, "%s sucessfully started, waiting app for exit", full_target_app_path); 367 | 368 | dr_inject_wait_for_child(inject_data, 0/*wait forever*/); 369 | 370 | exitcode = dr_inject_process_exit(inject_data, false); 371 | 372 | sc = drfront_cleanup_args(argv, argc); 373 | if (sc != DRFRONT_SUCCESS) 374 | DRLTRACE_ERROR("drfront_cleanup_args error, error code = %d", sc); 375 | 376 | return exitcode; 377 | } 378 | -------------------------------------------------------------------------------- /drltrace_src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ********************************************************** 2 | # Copyright (c) 2018 Maksim Shudrak. All rights reserved. 3 | # Copyright (c) 2010-2017 Google, Inc. All rights reserved. 4 | # Copyright (c) 2009-2010 VMware, Inc. All rights reserved. 5 | # ********************************************************** 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright notice, 11 | # this list of conditions and the following disclaimer. 12 | # 13 | # * Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation 15 | # and/or other materials provided with the distribution. 16 | # 17 | # * Neither the name of VMware, Inc. nor the names of its contributors may be 18 | # used to endorse or promote products derived from this software without 19 | # specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | # ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 31 | # DAMAGE. 32 | 33 | cmake_minimum_required(VERSION 3.2) 34 | 35 | function (append_property_string type target name value) 36 | # XXX: if we require cmake 2.8.6 we can simply use APPEND_STRING 37 | get_property(cur ${type} ${target} PROPERTY ${name}) 38 | if (cur) 39 | set(value "${cur} ${value}") 40 | endif (cur) 41 | set_property(${type} ${target} PROPERTY ${name} "${value}") 42 | endfunction (append_property_string) 43 | 44 | function (set_output_dirs dir) 45 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${dir}" PARENT_SCOPE) 46 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${dir}" PARENT_SCOPE) 47 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}" PARENT_SCOPE) 48 | if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 49 | # we don't support the Debug and Release subdirs 50 | foreach (config ${CMAKE_CONFIGURATION_TYPES}) 51 | string(TOUPPER "${config}" config_upper) 52 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper} 53 | "${dir}" PARENT_SCOPE) 54 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config_upper} 55 | "${dir}" PARENT_SCOPE) 56 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config_upper} 57 | "${dir}" PARENT_SCOPE) 58 | endforeach () 59 | endif () 60 | endfunction (set_output_dirs) 61 | 62 | if (UNIX) 63 | set(DEFINES ${DEFINES} -DUNIX) 64 | set(LINUX 1) 65 | endif (UNIX) 66 | 67 | # To match Makefiles and have just one build type per configured build 68 | # dir, we collapse VS generator configs to a single choice. 69 | # This must be done prior to the project() command and the var 70 | # must be set in the cache. 71 | if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 72 | if (DEBUG OR "${CMAKE_BUILD_TYPE}" MATCHES "Debug") 73 | set(CMAKE_CONFIGURATION_TYPES "Debug" CACHE STRING "" FORCE) 74 | else () 75 | # Go w/ debug info (DRMemory i#1392) 76 | set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" CACHE STRING "" FORCE) 77 | endif () 78 | # we want to use the _LOCATION_ property 79 | string(TOUPPER "${CMAKE_CONFIGURATION_TYPES}" upper) 80 | set(location_suffix "_${upper}") 81 | else ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 82 | set(location_suffix "") 83 | endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 84 | 85 | message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}") 86 | 87 | project(drltrace) 88 | 89 | if ("${CMAKE_VERSION}" VERSION_EQUAL "3.0" OR 90 | "${CMAKE_VERSION}" VERSION_GREATER "3.0") 91 | # XXX DrMemory i#1557: update our code to satisfy the changes in 3.x 92 | cmake_policy(SET CMP0026 OLD) 93 | # XXX DrMemory i#1375: if we make 2.8.12 the minimum we can remove the @rpath 94 | # Mac stuff and this policy, right? 95 | cmake_policy(SET CMP0042 OLD) 96 | endif () 97 | 98 | # setup everything to be able to run drltrace 99 | 100 | if (CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) 101 | set(X64 ON) 102 | set(LIB_ARCH "lib64") 103 | set(BIN_ARCH "bin64") 104 | set(DEFINES ${DEFINES} -DX64) 105 | else() 106 | set(X64 OFF) 107 | set(LIB_ARCH "lib32") 108 | set(BIN_ARCH "bin") 109 | endif () 110 | 111 | if ("${CMAKE_BUILD_TYPE}" MATCHES "Debug") 112 | set(build_type "debug") 113 | else() 114 | set(build_type "release") 115 | endif() 116 | 117 | set(output_dir "${PROJECT_BINARY_DIR}/${BIN_ARCH}") 118 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${output_dir}") 119 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") 120 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${output_dir}") 121 | if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 122 | # we don't support the Debug and Release subdirs 123 | foreach (config ${CMAKE_CONFIGURATION_TYPES}) 124 | string(TOUPPER "${config}" config_upper) 125 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper} 126 | "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 127 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config_upper} 128 | "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}") 129 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config_upper} 130 | "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") 131 | endforeach () 132 | endif () 133 | 134 | option(SHOW_RESULTS 135 | "Display client results in pop-up (Windows) or console message (Linux)" 136 | ${SHOW_RESULTS_DEFAULT}) 137 | if (SHOW_RESULTS) 138 | add_definitions(-DSHOW_RESULTS) 139 | endif (SHOW_RESULTS) 140 | 141 | option(SHOW_SYMBOLS "Use symbol lookup in clients that support it" ON) 142 | if (SHOW_SYMBOLS) 143 | add_definitions(-DSHOW_SYMBOLS) 144 | endif (SHOW_SYMBOLS) 145 | if (NOT DEFINED GENERATE_PDBS) 146 | # support running tests over ssh where pdb building is problematic 147 | set(GENERATE_PDBS ON) 148 | endif (NOT DEFINED GENERATE_PDBS) 149 | 150 | # DrMemory i#379: We usually want to build the samples with optimizations to improve the 151 | # chances of inlining, but it's nice to be able to turn that off easily. A 152 | # release build should already have optimizations, so this should only really 153 | # affect debug builds. 154 | option(OPTIMIZE_SAMPLES 155 | "Build samples with optimizations to increase the chances of clean call inlining (overrides debug flags)" 156 | ON) 157 | if (WIN32) 158 | set(OPT_CFLAGS "/O2") 159 | else (WIN32) 160 | set(OPT_CFLAGS "-O2") 161 | if (DEBUG) 162 | set(OPT_CFLAGS "-DDEBUG") 163 | endif (DEBUG) 164 | endif (WIN32) 165 | 166 | if (DEFINED DynamoRIO_DIR) 167 | message(STATUS "Attempting to use pre-built DynamoRIO: ${DynamoRIO_DIR}") 168 | set(USER_SPECIFIED_DynamoRIO_DIR ON) 169 | set(user_DynamoRIO_DIR "dynamorio/cmake" CACHE PATH "DynamoRIO installation's cmake directory") 170 | get_filename_component(DynamoRIO_DIR "${PROJECT_BINARY_DIR}/${user_DynamoRIO_DIR}" ABSOLUTE) 171 | find_package(DynamoRIO) 172 | if (NOT DynamoRIO_FOUND) 173 | message(FATAL_ERROR "DynamoRIO package required to build") 174 | endif(NOT DynamoRIO_FOUND) 175 | else () 176 | set(USER_SPECIFIED_DynamoRIO_DIR OFF) 177 | set(DynamoRIO_DIR "${PROJECT_BINARY_DIR}/dynamorio/cmake" CACHE PATH "Path to DynamoRIO.") 178 | message(STATUS "Building DynamoRIO from local sources ${DynamoRIO_DIR}") 179 | if ("${CMAKE_BUILD_TYPE}" MATCHES "Debug") 180 | set(DEBUG ON CACHE BOOL "DynamoRIO option: debug build") 181 | set(INTERNAL ON CACHE BOOL "DynamoRIO option: internal build") 182 | endif ("${CMAKE_BUILD_TYPE}" MATCHES "Debug") 183 | # We do not want DR install, except for the targets we need for our 184 | # multi-export-set install of the DRMF. We need those targets 185 | # even for BUILDING_SUB_PACKAGE. 186 | set(DO_DR_INSTALL OFF) 187 | set(DO_DR_INSTALL_TARGETS ON) 188 | # Stick the binaries somewhere outside of the install dir. 189 | # However, NSIS won't allow an absolute path (DrMemory i#1099). 190 | # So for an automated package.cmake build via cpack, we use .. which 191 | # is fine in the package dir structure. We don't want .. for a 192 | # user-specified destination of course. We simply don't 193 | # support creating a package manually outside of package.cmake. 194 | if (BUILDING_PACKAGE) 195 | set(DR_INSTALL_TARGETS_DEST ../ignored-installs) 196 | else () 197 | set(DR_INSTALL_TARGETS_DEST ${PROJECT_BINARY_DIR}/dynamorio/installs) 198 | endif () 199 | # DrMemory i#1449: we need cmake to remove the absolute path from the LC_LOAD_DYLIB 200 | # entries, which only happens on install. We can't easily do a two-step 201 | # install b/c subdirs go after the main dir -- so we have DR cooperation. 202 | set(DR_INSTALL_DEPLOY_BIN_DEST ${DR_install_dir}/${BIN_ARCH}) 203 | 204 | add_subdirectory(dynamorio) 205 | 206 | # don't show DR options in drmem cmake list 207 | # to really hide we should mark as INTERNAL but not worth it since would 208 | # have to do for all of DR's many options. 209 | # see comment above about DR prefixing its options. 210 | mark_as_advanced(BUILD_CORE BUILD_DOCS BUILD_SAMPLES BUILD_EXT BUILD_TESTS 211 | BUILD_TOOLS DEBUG INTERNAL) 212 | 213 | # do not import dynamorio lib target: we'd end up w/ duplicate 214 | # dynamorio targets 215 | set(DynamoRIO_INTERNAL ON) 216 | # our included DynamoRIO project will set DynamoRIO_SOURCE_DIR in cache 217 | # for us so we'll get proper include dirs for extensions. 218 | 219 | find_package(DynamoRIO ${DynamoRIO_VERSION_REQUIRED}) 220 | if (NOT DynamoRIO_FOUND OR 221 | # make sure it didn't go find some other pre-built version after 222 | # seeing that the local one is somehow not suitable 223 | NOT "${DynamoRIO_CONFIG}" STREQUAL "${DynamoRIO_DIR}/DynamoRIOConfig.cmake") 224 | message(FATAL_ERROR "Local DynamoRIO mis-configured") 225 | endif () 226 | 227 | # Restore global flags 228 | foreach (config "" ${CMAKE_BUILD_TYPE} ${CMAKE_CONFIGURATION_TYPES}) 229 | if ("${config}" STREQUAL "") 230 | set(config_upper "") 231 | else ("${config}" STREQUAL "") 232 | string(TOUPPER "_${config}" config_upper) 233 | endif ("${config}" STREQUAL "") 234 | foreach (var CMAKE_C_FLAGS${config_upper};CMAKE_CXX_FLAGS${config_upper}) 235 | set(${var} "${SAVE_${var}}") 236 | endforeach (var) 237 | endforeach (config) 238 | 239 | endif () 240 | 241 | if (WIN32) 242 | # disable stack protection: "unresolved external symbol ___security_cookie" 243 | # disable the warning "unreferenced formal parameter" #4100 244 | # disable the warning "conditional expression is constant" #4127 245 | # disable the warning "cast from function pointer to data pointer" #4054 246 | set(CL_CFLAGS "/GS- /wd4100 /wd4127 /wd4054") 247 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CL_CFLAGS}") 248 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CL_CFLAGS}") 249 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 250 | if (DEBUG) 251 | #set(OPT_CFLAGS "/DEBUG") 252 | add_definitions(-DDEBUG) 253 | endif (DEBUG) 254 | endif (WIN32) 255 | 256 | #if (OPTIMIZE_SAMPLES) 257 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPT_CFLAGS}") 258 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPT_CFLAGS}") 259 | #endif () 260 | 261 | if (UNIX) 262 | set(EXTRA_CXXFLAGS "-std=c++11") 263 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS}") 264 | endif() 265 | 266 | #include(${PROJECT_SOURCE_DIR}/make/policies.cmake NO_POLICY_SCOPE) 267 | 268 | ################################################## 269 | # drltrace frontend 270 | 271 | set(front_srcs 272 | drltrace_frontend.cpp 273 | drltrace_options.cpp) 274 | #if (WIN32) 275 | # set(front_srcs ${front_srcs} ${PROJECT_SOURCE_DIR}/make/resources.rc) 276 | #endif () 277 | 278 | macro(fix_win32_flags srcfile) 279 | if (WIN32) 280 | if (DEBUG) 281 | get_property(cur SOURCE ${srcfile} PROPERTY COMPILE_FLAGS) 282 | string(REPLACE "/MTd " "" cur "${cur}") # Avoid override warning. 283 | set_source_files_properties(${srcfile} PROPERTIES COMPILE_FLAGS "${cur} /EHsc /MTd") 284 | append_property_string(SOURCE ${srcfile} LINK_FLAGS "/nodefaultlib:libcmt") 285 | else () 286 | append_property_string(SOURCE ${srcfile} COMPILE_FLAGS "/EHsc /MT") 287 | endif () 288 | endif () 289 | endmacro () 290 | 291 | fix_win32_flags(drltrace_frontend.cpp) 292 | 293 | add_executable(drltrace ${front_srcs}) 294 | 295 | #set_library_version(drltrace ${DRMF_VERSION}) 296 | 297 | if (WIN32) 298 | set_property(TARGET drltrace PROPERTY COMPILE_DEFINITIONS 299 | ${DEFINES_NO_D} RC_IS_DRLTRACE) 300 | endif () 301 | 302 | configure_DynamoRIO_standalone(drltrace) 303 | 304 | target_link_libraries(drltrace drinjectlib drconfiglib drfrontendlib) 305 | 306 | ################################################## 307 | # drltracelib 308 | 309 | set(srcs 310 | drltrace.cpp 311 | drltrace_libcalls.cpp 312 | drltrace_options.cpp 313 | drltrace_utils.cpp) 314 | 315 | set(DynamoRIO_USE_LIBC OFF) 316 | 317 | set(TOOLNAME "drltrace") 318 | 319 | add_library(drltracelib SHARED ${srcs}) 320 | 321 | # We share the framework version # for now 322 | #set_library_version(drltracelib ${DRMF_VERSION_MAJOR_MINOR}) 323 | 324 | if (WIN32) 325 | set_property(TARGET drltracelib PROPERTY COMPILE_DEFINITIONS 326 | "${DEFINES_NO_D};RC_IS_DRLTRACELIB") 327 | else () 328 | set_property(TARGET drltracelib PROPERTY COMPILE_DEFINITIONS ${DEFINES_NO_D}) 329 | endif () 330 | 331 | set(DynamoRIO_RPATH ON) 332 | set_target_properties(drltracelib PROPERTIES 333 | RUNTIME_OUTPUT_DIRECTORY${location_suffix} "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") 334 | 335 | 336 | configure_DynamoRIO_client(drltracelib) 337 | 338 | use_DynamoRIO_extension(drltracelib drmgr_static) 339 | use_DynamoRIO_extension(drltracelib drwrap_static) 340 | use_DynamoRIO_extension(drltracelib drx_static) 341 | use_DynamoRIO_extension(drltracelib drcovlib_static) 342 | use_DynamoRIO_extension(drltracelib droption) 343 | 344 | # XXX DrMemory i#2429: DR's droption.h includes both and 345 | # we should remove and replace the strcmp comparison with 346 | # a std::string. Then we can remove special if here and below. 347 | if (UNIX) 348 | # to avoid conflicts with new declaration with "C" linkage in utils.h 349 | append_property_string(TARGET drltracelib COMPILE_FLAGS "-DNOLINK_STRCASESTR") 350 | endif() 351 | if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") 352 | # DrMemory i#1805: see comment in drstrace/CMakeLists.txt 353 | append_property_string(TARGET drltracelib LINK_FLAGS "/force:multiple") 354 | endif () 355 | 356 | if (WIN32) 357 | if (USER_SPECIFIED_DynamoRIO_DIR) 358 | # already built so we can copy at config time 359 | configure_file("${DynamoRIO_DIR}/../${LIB_ARCH}/drconfiglib.dll" 360 | "${output_dir}/drconfiglib.dll" COPYONLY) 361 | configure_file("${DynamoRIO_DIR}/../${LIB_ARCH}/drinjectlib.dll" 362 | "${output_dir}/drinjectlib.dll" COPYONLY) 363 | configure_file("${DynamoRIO_DIR}/../${LIB_ARCH}/${build_type}/dynamorio.dll" 364 | "${output_dir}/dynamorio.dll" COPYONLY) 365 | else (USER_SPECIFIED_DynamoRIO_DIR) 366 | # XXX: I can't get "TARGET drconfiglib POST_BUILD" to work, maybe b/c 367 | # the target is in a subdir? 368 | set(drconfiglib_copy "${output_dir}/drconfiglib.dll") 369 | add_custom_target(drconfiglib_copy_tgt ALL DEPENDS "${drconfiglib_copy}") 370 | add_custom_command(OUTPUT "${drconfiglib_copy}" DEPENDS drconfiglib 371 | COMMAND ${CMAKE_COMMAND} 372 | ARGS -E copy "${DynamoRIO_DIR}/../${LIB_ARCH}/drconfiglib.dll" 373 | "${drconfiglib_copy}" VERBATIM) 374 | set(drinjectlib_copy "${output_dir}/drinjectlib.dll") 375 | add_custom_target(drinjectlib_copy_tgt ALL DEPENDS "${drinjectlib_copy}") 376 | add_custom_command(OUTPUT "${drinjectlib_copy}" DEPENDS drinjectlib 377 | COMMAND ${CMAKE_COMMAND} 378 | ARGS -E copy "${DynamoRIO_DIR}/../${LIB_ARCH}/drinjectlib.dll" 379 | "${drinjectlib_copy}" VERBATIM) 380 | set(drdll_copy "${output_dir}/dynamorio.dll") 381 | add_custom_target(drdll_copy_tgt ALL DEPENDS "${drdll_copy}") 382 | add_custom_command(OUTPUT "${drdll_copy}" DEPENDS dynamorio 383 | COMMAND ${CMAKE_COMMAND} 384 | ARGS -E copy "${DynamoRIO_DIR}/../${LIB_ARCH}/${build_type}/dynamorio.dll" 385 | "${drdll_copy}" VERBATIM) 386 | endif (USER_SPECIFIED_DynamoRIO_DIR) 387 | endif (WIN32) 388 | 389 | ################################################## 390 | # drltrace config 391 | 392 | set(conf_out ${PROJECT_BINARY_DIR}/${BIN_ARCH}/drltrace.config) 393 | if (WIN32) 394 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/drltrace_win.config ${conf_out} COPYONLY) 395 | else () 396 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/drltrace_linux.config ${conf_out} COPYONLY) 397 | endif () 398 | 399 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/filter.config ${PROJECT_BINARY_DIR}/${BIN_ARCH}/filter.config COPYONLY) 400 | 401 | ################################################## 402 | # drltrace tests 403 | 404 | function(tobuild name source) 405 | set(srcs ${source}) 406 | add_executable(${name} ${srcs}) 407 | if (UNIX) 408 | set(targ_property UNIX) 409 | else() 410 | set(targ_property WINDOWS) 411 | endif(UNIX) 412 | set_target_properties(${name} PROPERTIES COMPILE_DEFINITIONS ${targ_property}) 413 | endfunction(tobuild) 414 | 415 | function (prefix_cmd_if_necessary cmd_out use_ats cmd_in) 416 | DynamoRIO_prefix_cmd_if_necessary(local ${use_ats} ${cmd_in} ${ARGN}) 417 | set(${cmd_out} ${local} PARENT_SCOPE) 418 | endfunction (prefix_cmd_if_necessary) 419 | 420 | # XXX DrMemory i#1960: add more tests for drltrace that check the output contains 421 | # expected library calls. 422 | # 423 | if (BUILD_TOOL_TESTS) 424 | message(STATUS "building tests") 425 | enable_testing() 426 | 427 | tobuild(drltrace_app drsyscall_app.c) 428 | 429 | if (X64) 430 | set(drltrace_path "./bin64") 431 | set(app_path "./bin64") 432 | else() 433 | set(drltrace_path "./bin") 434 | set(app_path "./bin") 435 | endif(X64) 436 | 437 | if (WIN32) 438 | set(drltrace_path "${drltrace_path}/drltrace.exe") 439 | set(app_path "${app_path}/drltrace_app.exe") 440 | else() 441 | set(drltrace_path "${drltrace_path}/drltrace") 442 | set(app_path "${app_path}/drltrace_app") 443 | endif(WIN32) 444 | 445 | prefix_cmd_if_necessary(drltrace_path OFF ${drltrace_path}) 446 | 447 | add_test(drltrace ${drltrace_path} -- ${app_path}) 448 | add_test(drltrace_libcalls ${drltrace_path} -logdir - -print_ret_addr -- ${app_path}) 449 | add_test(drltrace_symargs ${drltrace_path} -logdir - -num_max_args 4 -- ${app_path}) 450 | 451 | add_test(drltrace_libargs ${drltrace_path} -logdir - -only_from_app -- ${app_path}) 452 | 453 | #regex strings for libcalls and arguments printing test 454 | set(libcall_ret " and return to module id:([0-9]+), offset:0x([0-9a-f]+)\n") 455 | 456 | if (WIN32) 457 | set(libcall_args1_01 " arg 0: 0x([0-9a-f]+) => 0x([0-9a-f]+) \\(type=void\\*, size=0x([0-9a-f]+)\\)\n") 458 | set(libcall_args1_02 " arg 2: 0x([0-9a-f]+) \\(type=size_t, size=0x([0-9a-f]+)\\)\n") 459 | 460 | set(libcall_args2_0 " arg 0: 0x([0-9a-f]+) \\(type=HANDLE, size=0x([0-9a-f]+)\\)\n") 461 | set(libcall_args2_1 " arg 1: 0x([0-9a-f]+) \\(type=int, size=0x([0-9a-f]+)\\)\n") 462 | 463 | set(libcall_name1 "~~([0-9a-f]+)~~ KERNELBASE.dll!VirtualQuery\n") 464 | set(libcall_name2 "~~([0-9a-f]+)~~ ntdll.dll!ZwQueryInformationProcess\n") 465 | set(libcall_printf "arg 0: done\n \\(type=char\\*, size=0x([0-9a-f]+)\\)\n") 466 | # some machines use WriteFile instead of printf, trying to handle both 467 | set(libcall_WriteFile "arg 2: 0x([0-9a-f]+) \\(type=DWORD, size=0x([0-9a-f]+)\\)\n") 468 | set(libcall_both_variants "${libcall_printf}\;${libcall_WriteFile}") 469 | else () 470 | set(libcall_both_variants " arg 0: done \\(type=char\\*, size=0x([0-9a-f]+)\\)\n") 471 | set(libcall_args1_01 ${libcall_both_variants}) 472 | 473 | set(libcall_args2_0 " arg 0: /dev/null \\(type=char\\*, size=0x([0-9a-f]+)\\)\n") #arg 0: /dev/null (type=char*, size=0x0) 474 | set(libcall_args2_1 " arg 1: 0x([0-9a-f]+) \\(type=int, size=0x([0-9a-f]+)\\)\n") #arg 1: 0x1 (type=int, size=0x4) 475 | set(libcall_name1 "~~([0-9a-f]+)~~ libc.so.6!puts\n") 476 | set(libcall_name2 "~~([0-9a-f]+)~~ libc.so.6!open\n") 477 | endif(WIN32) 478 | set_tests_properties(drltrace_libcalls PROPERTIES PASS_REGULAR_EXPRESSION 479 | ${libcall_name1}${libcall_args1_01}${libcall_args1_02}${libcall_ret}) 480 | set_tests_properties(drltrace_symargs PROPERTIES PASS_REGULAR_EXPRESSION 481 | ${libcall_name2}${libcall_args2_0}${libcall_args2_1}) 482 | set_tests_properties(drltrace_libargs PROPERTIES PASS_REGULAR_EXPRESSION 483 | ${libcall_both_variants}) 484 | endif (BUILD_TOOL_TESTS) 485 | 486 | 487 | ##### some manual fixes ########## 488 | 489 | if (UNIX AND NOT X64) 490 | append_property_string(TARGET drltrace COMPILE_FLAGS "-m32") 491 | append_property_string(TARGET drltrace LINK_FLAGS "-m32") 492 | append_property_string(TARGET drltracelib COMPILE_FLAGS "-m32") 493 | append_property_string(TARGET drltracelib LINK_FLAGS "-m32") 494 | if(BUILD_TOOL_TESTS) 495 | append_property_string(TARGET drltrace_app COMPILE_FLAGS "-m32") 496 | append_property_string(TARGET drltrace_app LINK_FLAGS "-m32") 497 | endif(BUILD_TOOL_TESTS) 498 | endif() 499 | 500 | -------------------------------------------------------------------------------- /drltrace_src/drltrace.cpp: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2013-2017 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | /* Library Tracing Tool: drltrace 34 | * 35 | * Records calls to exported library routines. 36 | * 37 | * The runtime options for this client are specified in drltrace_options.h, 38 | * see DROPTION_SCOPE_CLIENT options. 39 | */ 40 | #include 41 | #include 42 | #include "drltrace.h" 43 | #include "drltrace_utils.h" 44 | 45 | /* Where to write the trace */ 46 | static file_t outf; 47 | 48 | /* Avoid exe exports, as on Linux many apps have a ton of global symbols. */ 49 | static app_pc exe_start; 50 | 51 | static inline generic_func_t 52 | cast_to_func(void *p) 53 | { 54 | return (generic_func_t) p; 55 | } 56 | 57 | struct _wblist { 58 | char *func_name; 59 | size_t func_name_len; 60 | unsigned int is_wildcard; /* Set to 1 when this is a wildcard, otherwise 0. */ 61 | }; 62 | typedef struct _wblist wb_list; /* Stands for white/black list. */ 63 | 64 | /* Arrays to hold functions in the whitelist/blacklist. Used instead 65 | * of vectors due to speed requirements. */ 66 | static wb_list *filter_function_whitelist = NULL; 67 | static unsigned int filter_function_whitelist_len = 0; 68 | 69 | static wb_list *filter_function_blacklist = NULL; 70 | static unsigned int filter_function_blacklist_len = 0; 71 | 72 | /* Vectors to hold modules in the whitelist/blacklist. */ 73 | static std::vector filter_module_whitelist; 74 | static std::vector filter_module_blacklist; 75 | 76 | 77 | 78 | /**************************************************************************** 79 | * Arguments printing 80 | */ 81 | 82 | static void 83 | print_simple_value(drltrace_arg_t *arg, bool leading_zeroes) 84 | { 85 | bool pointer = !TEST(DRSYS_PARAM_INLINED, arg->mode); 86 | dr_fprintf(outf, pointer ? PFX : (leading_zeroes ? PFX : PIFX), arg->value); 87 | if (pointer && ((arg->pre && TEST(DRSYS_PARAM_IN, arg->mode)) || 88 | (!arg->pre && TEST(DRSYS_PARAM_OUT, arg->mode)))) { 89 | ptr_uint_t deref = 0; 90 | ASSERT(arg->size <= sizeof(deref), "too-big simple type"); 91 | /* We assume little-endian */ 92 | if (dr_safe_read((void *)arg->value, arg->size, &deref, NULL)) 93 | dr_fprintf(outf, (leading_zeroes ? " => " PFX : " => " PIFX), deref); 94 | } 95 | } 96 | 97 | static void 98 | print_string(void *drcontext, void *pointer_str, bool is_wide) 99 | { 100 | if (pointer_str == NULL) 101 | dr_fprintf(outf, ""); 102 | else { 103 | DR_TRY_EXCEPT(drcontext, { 104 | dr_fprintf(outf, is_wide ? "%S" : "%s", pointer_str); 105 | }, { 106 | dr_fprintf(outf, ""); 107 | }); 108 | } 109 | } 110 | 111 | static void 112 | print_arg(void *drcontext, drltrace_arg_t *arg) 113 | { 114 | if (arg->pre && (TEST(DRSYS_PARAM_OUT, arg->mode) && !TEST(DRSYS_PARAM_IN, arg->mode))) 115 | return; 116 | dr_fprintf(outf, "%s%d: ", (op_grepable.get_value() ? " {" : "\n arg "), arg->ordinal); 117 | switch (arg->type) { 118 | case DRSYS_TYPE_VOID: print_simple_value(arg, true); break; 119 | case DRSYS_TYPE_POINTER: print_simple_value(arg, true); break; 120 | case DRSYS_TYPE_BOOL: print_simple_value(arg, false); break; 121 | case DRSYS_TYPE_INT: print_simple_value(arg, false); break; 122 | case DRSYS_TYPE_SIGNED_INT: print_simple_value(arg, false); break; 123 | case DRSYS_TYPE_UNSIGNED_INT: print_simple_value(arg, false); break; 124 | case DRSYS_TYPE_HANDLE: print_simple_value(arg, false); break; 125 | case DRSYS_TYPE_NTSTATUS: print_simple_value(arg, false); break; 126 | case DRSYS_TYPE_ATOM: print_simple_value(arg, false); break; 127 | #ifdef WINDOWS 128 | case DRSYS_TYPE_LCID: print_simple_value(arg, false); break; 129 | case DRSYS_TYPE_LPARAM: print_simple_value(arg, false); break; 130 | case DRSYS_TYPE_SIZE_T: print_simple_value(arg, false); break; 131 | case DRSYS_TYPE_HMODULE: print_simple_value(arg, false); break; 132 | #endif 133 | case DRSYS_TYPE_CSTRING: 134 | print_string(drcontext, (void *)arg->value, false); 135 | break; 136 | case DRSYS_TYPE_CWSTRING: 137 | print_string(drcontext, (void *)arg->value, true); 138 | break; 139 | default: { 140 | if (arg->value == 0) 141 | dr_fprintf(outf, ""); 142 | else 143 | dr_fprintf(outf, PFX, arg->value); 144 | } 145 | } 146 | 147 | dr_fprintf(outf, " (%s%s%stype=%s%s, size=" PIFX ")", 148 | (arg->arg_name == NULL) ? "" : "name=", 149 | (arg->arg_name == NULL) ? "" : arg->arg_name, 150 | (arg->arg_name == NULL) ? "" : ", ", 151 | (arg->type_name == NULL) ? "\"\"" : arg->type_name, 152 | (arg->type_name == NULL || 153 | TESTANY(DRSYS_PARAM_INLINED|DRSYS_PARAM_RETVAL, arg->mode)) ? "" : "*", 154 | arg->size); 155 | 156 | if (op_grepable.get_value()) 157 | dr_fprintf(outf, "}"); 158 | } 159 | 160 | static bool 161 | drlib_iter_arg_cb(drltrace_arg_t *arg, void *wrapcxt) 162 | { 163 | if (arg->ordinal == -1) 164 | return true; 165 | if (arg->ordinal >= op_max_args.get_value()) 166 | return false; /* limit number of arguments to be printed */ 167 | 168 | arg->value = (ptr_uint_t)drwrap_get_arg(wrapcxt, arg->ordinal); 169 | 170 | print_arg(drwrap_get_drcontext(wrapcxt), arg); 171 | return true; /* keep going */ 172 | } 173 | 174 | static void 175 | print_args_unknown_call(app_pc func, void *wrapcxt) 176 | { 177 | uint i; 178 | void *drcontext = drwrap_get_drcontext(wrapcxt); 179 | char *prefix = "\n arg "; 180 | char *suffix = ""; 181 | if (op_grepable.get_value()) { 182 | prefix = " {"; 183 | suffix = "}"; 184 | } 185 | DR_TRY_EXCEPT(drcontext, { 186 | for (i = 0; i < op_unknown_args.get_value(); i++) { 187 | dr_fprintf(outf, "%s%d: " PFX, prefix, i, 188 | drwrap_get_arg(wrapcxt, i)); 189 | if (*suffix != '\0') 190 | dr_fprintf(outf, suffix); 191 | } 192 | }, { 193 | dr_fprintf(outf, ""); 194 | /* Just keep going */ 195 | }); 196 | /* all args have been sucessfully printed */ 197 | dr_fprintf(outf, op_print_ret_addr.get_value() ? "\n ": ""); 198 | } 199 | 200 | static bool 201 | print_libcall_args(std::vector *args_vec, void *wrapcxt) 202 | { 203 | if (args_vec == NULL || args_vec->size() <= 0) 204 | return false; 205 | 206 | std::vector::iterator it; 207 | for (it = args_vec->begin(); it != args_vec->end(); ++it) { 208 | if (!drlib_iter_arg_cb(*it, wrapcxt)) 209 | break; 210 | } 211 | return true; 212 | } 213 | 214 | static void 215 | print_symbolic_args(const char *name, void *wrapcxt, app_pc func) 216 | { 217 | std::vector *args_vec; 218 | 219 | if (op_max_args.get_value() == 0) 220 | return; 221 | 222 | if (op_use_config.get_value()) { 223 | /* looking for libcall in libcalls hashtable */ 224 | args_vec = libcalls_search(name); 225 | if (print_libcall_args(args_vec, wrapcxt)) { 226 | dr_fprintf(outf, op_print_ret_addr.get_value() ? "\n " : ""); 227 | return; /* we found libcall and sucessfully printed all arguments */ 228 | } 229 | } 230 | /* use standard type-blind scheme */ 231 | if (op_unknown_args.get_value() > 0) 232 | print_args_unknown_call(func, wrapcxt); 233 | } 234 | 235 | /**************************************************************************** 236 | * Library entry wrapping 237 | */ 238 | 239 | static void 240 | lib_entry(void *wrapcxt, INOUT void **user_data) 241 | { 242 | const char *name = (const char *) *user_data; 243 | const char *modname = NULL; 244 | app_pc func = drwrap_get_func(wrapcxt); 245 | module_data_t *mod; 246 | thread_id_t tid; 247 | uint mod_id; 248 | app_pc mod_start, ret_addr; 249 | drcovlib_status_t res; 250 | 251 | void *drcontext = drwrap_get_drcontext(wrapcxt); 252 | 253 | if (op_only_from_app.get_value()) { 254 | /* For just this option, the modxfer approach might be better */ 255 | app_pc retaddr = NULL; 256 | DR_TRY_EXCEPT(drcontext, { 257 | retaddr = drwrap_get_retaddr(wrapcxt); 258 | }, { /* EXCEPT */ 259 | retaddr = NULL; 260 | }); 261 | if (retaddr != NULL) { 262 | mod = dr_lookup_module(retaddr); 263 | if (mod != NULL) { 264 | bool from_exe = (mod->start == exe_start); 265 | dr_free_module_data(mod); 266 | if (!from_exe) 267 | return; 268 | } 269 | } else { 270 | /* Nearly all of these cases should be things like KiUserCallbackDispatcher 271 | * or other abnormal transitions. 272 | * If the user really wants to see everything they can not pass 273 | * -only_from_app. 274 | */ 275 | return; 276 | } 277 | } 278 | /* XXX: it may be better to heap-allocate the "module!func" string and 279 | * pass in, to avoid this lookup. 280 | */ 281 | mod = dr_lookup_module(func); 282 | if (mod != NULL) 283 | modname = dr_module_preferred_name(mod); 284 | 285 | /* Build the module & function string, then compare to the white/black 286 | * list. */ 287 | char module_name[256]; 288 | memset(module_name, 0, sizeof(module_name)); 289 | 290 | /* Temporary workaround for VC2013, which doesn't have snprintf(). 291 | * apparently, this was added in later releases... */ 292 | #ifdef WINDOWS 293 | #define snprintf _snprintf 294 | #endif 295 | unsigned int module_name_len = (unsigned int)snprintf(module_name, \ 296 | sizeof(module_name) - 1, "%s%s%s", modname == NULL ? "" : modname, \ 297 | modname == NULL ? "" : "!", name); 298 | 299 | /* Check if this module & function is in the whitelist. */ 300 | bool allowed = false; 301 | bool tested = false; /* True only if any white/blacklist testing below is done. */ 302 | for (unsigned int i = 0; (allowed == false) && (i < filter_function_whitelist_len); i++) { 303 | tested = true; 304 | 305 | /* If the whitelist entry contains a wildcard, then compare only the shortest 306 | * part of either string. */ 307 | unsigned int module_name_len_compare; 308 | if (filter_function_whitelist[i].is_wildcard) 309 | module_name_len_compare = MIN(module_name_len, \ 310 | filter_function_whitelist[i].func_name_len); 311 | else 312 | module_name_len_compare = module_name_len; 313 | 314 | if (fast_strcmp(module_name, module_name_len_compare, \ 315 | filter_function_whitelist[i].func_name, \ 316 | filter_function_whitelist[i].func_name_len) == 0) { 317 | allowed = true; 318 | } 319 | } 320 | 321 | /* Check the blacklist if it was specified instead of a whitelist. */ 322 | if (!allowed && filter_function_blacklist_len > 0) { 323 | allowed = true; 324 | for (unsigned int i = 0; allowed && (i < filter_function_blacklist_len); i++) { 325 | tested = true; 326 | 327 | /* If the blacklist entry contains a wildcard, then compare only the shortest 328 | * part of either string. */ 329 | unsigned int module_name_len_compare; 330 | if (filter_function_blacklist[i].is_wildcard) 331 | module_name_len_compare = MIN(module_name_len, \ 332 | filter_function_blacklist[i].func_name_len); 333 | else 334 | module_name_len_compare = module_name_len; 335 | 336 | if (fast_strcmp(module_name, module_name_len_compare, \ 337 | filter_function_blacklist[i].func_name, \ 338 | filter_function_blacklist[i].func_name_len) == 0) { 339 | allowed = false; 340 | } 341 | } 342 | } 343 | 344 | /* If whitelist/blacklist testing was performed, and it was determined 345 | * this function is not to be logged... */ 346 | if (tested && !allowed) 347 | return; 348 | 349 | tid = dr_get_thread_id(drcontext); 350 | if (tid != INVALID_THREAD_ID) 351 | dr_fprintf(outf, "~~%d~~ ", tid); 352 | else 353 | dr_fprintf(outf, "~~Dr.L~~ "); 354 | dr_fprintf(outf, module_name); 355 | 356 | /* XXX: We employ two schemes of arguments printing. We are looking for prototypes 357 | * in config file specified by user to get symbolic representation of arguments 358 | * for known library calls. For the rest of library calls. If there is no info 359 | * we employ type-blindprinting and use -num_unknown_args to get a count of arguments 360 | * to print. 361 | */ 362 | print_symbolic_args(name, wrapcxt, func); 363 | 364 | if (op_print_ret_addr.get_value()) { 365 | ret_addr = drwrap_get_retaddr(wrapcxt); 366 | res = drmodtrack_lookup(drcontext, ret_addr, &mod_id, &mod_start); 367 | if (res == DRCOVLIB_SUCCESS) { 368 | dr_fprintf(outf, 369 | op_print_ret_addr.get_value() ? 370 | " and return to module id:%d, offset:" PIFX : "", 371 | mod_id, ret_addr - mod_start); 372 | } 373 | } 374 | dr_fprintf(outf, "\n"); 375 | if (mod != NULL) 376 | dr_free_module_data(mod); 377 | } 378 | 379 | static void 380 | iterate_exports(const module_data_t *info, bool add) 381 | { 382 | dr_symbol_export_iterator_t *exp_iter = 383 | dr_symbol_export_iterator_start(info->handle); 384 | while (dr_symbol_export_iterator_hasnext(exp_iter)) { 385 | dr_symbol_export_t *sym = dr_symbol_export_iterator_next(exp_iter); 386 | app_pc func = NULL; 387 | if (sym->is_code) 388 | func = sym->addr; 389 | #ifdef LINUX 390 | else if (sym->is_indirect_code) { 391 | /* Invoke the export to get the real entry: */ 392 | app_pc (*indir)(void) = (app_pc (*)(void)) cast_to_func(sym->addr); 393 | void *drcontext = dr_get_current_drcontext(); 394 | DR_TRY_EXCEPT(drcontext, { 395 | func = (*indir)(); 396 | }, { /* EXCEPT */ 397 | func = NULL; 398 | }); 399 | VNOTIFY(2, "export %s indirected from " PFX " to " PFX NL, 400 | sym->name, sym->addr, func); 401 | } 402 | #endif 403 | if (op_ignore_underscore.get_value() && strstr(sym->name, "_") == sym->name) 404 | func = NULL; 405 | if (func != NULL) { 406 | if (add) { 407 | IF_DEBUG(bool ok =) 408 | drwrap_wrap_ex(func, lib_entry, NULL, (void *) sym->name, 0); 409 | ASSERT(ok, "wrap request failed"); 410 | VNOTIFY(2, "wrapping export %s!%s @" PFX NL, 411 | dr_module_preferred_name(info), sym->name, func); 412 | } else { 413 | IF_DEBUG(bool ok =) 414 | drwrap_unwrap(func, lib_entry, NULL); 415 | ASSERT(ok, "unwrap request failed"); 416 | } 417 | } 418 | } 419 | dr_symbol_export_iterator_stop(exp_iter); 420 | } 421 | 422 | static bool 423 | library_matches_filter(const module_data_t *info) 424 | { 425 | if (!filter_module_whitelist.empty()) { 426 | const char *libname = dr_module_preferred_name(info); 427 | if (libname == NULL) 428 | return false; 429 | 430 | for (std::vector::const_iterator iter = \ 431 | filter_module_whitelist.begin(); iter != filter_module_whitelist.end(); \ 432 | iter++) { 433 | 434 | if (strcmp(libname, iter->c_str()) == 0) 435 | return true; 436 | } 437 | 438 | return false; 439 | } else if (!filter_module_blacklist.empty()) { 440 | const char *libname = dr_module_preferred_name(info); 441 | if (libname == NULL) 442 | return true; 443 | 444 | for (std::vector::const_iterator iter = \ 445 | filter_module_blacklist.begin(); iter != filter_module_blacklist.end(); \ 446 | iter++) { 447 | if (strcmp(libname, iter->c_str()) == 0) 448 | return false; 449 | } 450 | 451 | return true; 452 | } else 453 | return true; 454 | } 455 | 456 | static void 457 | event_module_load(void *drcontext, const module_data_t *info, bool loaded) 458 | { 459 | if (info->start != exe_start && library_matches_filter(info)) 460 | iterate_exports(info, true/*add*/); 461 | } 462 | 463 | static void 464 | event_module_unload(void *drcontext, const module_data_t *info) 465 | { 466 | if (info->start != exe_start && library_matches_filter(info)) 467 | iterate_exports(info, false/*remove*/); 468 | } 469 | 470 | /**************************************************************************** 471 | * Init and exit 472 | */ 473 | 474 | static void 475 | open_log_file(void) 476 | { 477 | char buf[MAXIMUM_PATH]; 478 | if (op_logdir.get_value().compare("-") == 0) 479 | outf = STDERR; 480 | else { 481 | outf = drx_open_unique_appid_file(op_logdir.get_value().c_str(), 482 | dr_get_process_id(), 483 | "drltrace", "log", 484 | #ifndef WINDOWS 485 | DR_FILE_CLOSE_ON_FORK | 486 | #endif 487 | DR_FILE_ALLOW_LARGE, 488 | buf, BUFFER_SIZE_ELEMENTS(buf)); 489 | ASSERT(outf != INVALID_FILE, "failed to open log file"); 490 | VNOTIFY(0, "drltrace log file is %s" NL, buf); 491 | 492 | } 493 | } 494 | 495 | /* Frees a wblist array. */ 496 | static void 497 | free_wblist_array(wb_list **wbl, unsigned int wb_list_len) { 498 | if ((wbl == NULL) || (*wbl == NULL) || (wb_list_len == 0)) 499 | return; 500 | 501 | /* Loop through all entries and free the function name, since it was 502 | * created with strdup(). Set it (and the length) to zero for good 503 | * measure. */ 504 | for (unsigned int i = 0; i < wb_list_len; i++) { 505 | wb_list *l = *wbl; 506 | free(l[i].func_name); l[i].func_name = NULL; 507 | l[i].func_name_len = 0; 508 | } 509 | 510 | /* Now free the array itself. */ 511 | free(*wbl); *wbl = NULL; 512 | } 513 | 514 | /* Adds a module name to the module filter. */ 515 | void 516 | add_module_filter(std::vector &module_wbl, const char *module_name) { 517 | 518 | /* If the module name is already in the filter, ignore. */ 519 | for (std::vector::const_iterator iter = module_wbl.begin(); iter != module_wbl.end(); iter++) { 520 | if (strcmp(module_name, iter->c_str()) == 0) 521 | return; 522 | } 523 | 524 | module_wbl.push_back(module_name); 525 | } 526 | 527 | /* Convert a vector to an array of wb_list structs. This may be faster 528 | * to process than a vector when handling function call-backs. */ 529 | static void 530 | parse_filter(std::vector &v_in, bool is_whitelist, std::vector &module_wbl, wb_list **func_wbl, unsigned int *func_wbl_len) { 531 | 532 | /* First look for entries that don't have a '!'; these are module names that 533 | * need to be filtered at the module-level. */ 534 | for (std::vector::const_iterator iter = v_in.begin(); \ 535 | iter != v_in.end(); iter++) { 536 | if (iter->find('!') == std::string::npos) 537 | add_module_filter(module_wbl, iter->c_str()); 538 | } 539 | 540 | /* Allocate the array of wb_list structs. */ 541 | unsigned int v_in_len = v_in.size(); 542 | *func_wbl = (wb_list *)calloc(v_in_len, sizeof(wb_list)); 543 | if (*func_wbl == NULL) { 544 | fprintf(stderr, "Failed to allocate whitelist/blacklist array.\n"); 545 | exit(-1); 546 | } 547 | 548 | /* For every entry in the vector, strdup() the function name and add 549 | * it to the array. */ 550 | unsigned j = 0; 551 | for (std::vector::const_iterator iter = v_in.begin(); \ 552 | (iter != v_in.end()) && (j < v_in_len); iter++, j++) { 553 | 554 | unsigned int is_wildcard = 0; 555 | char *s = strdup(iter->c_str()); 556 | if (s == NULL) { 557 | fprintf(stderr, "Failed to allocate whitelist/blacklist array.\n"); 558 | exit(-1); 559 | } 560 | 561 | /* If the function name ends with a '*', then it is a wildcard prefix. Set the 562 | * wildcard flag and cut off the trailing '*'. */ 563 | if (s[strlen(s) - 1] == '*') { 564 | s[strlen(s) - 1] = '\0'; 565 | is_wildcard = 1; 566 | 567 | /* If a module name was provided without a corresponding function, then this is a 568 | * wildcard module. These, too, must be added to the function-level filter in 569 | * order for whitelisted functions to be allowed through. */ 570 | } else if (strchr(s, '!') == NULL) 571 | is_wildcard = 1; 572 | 573 | /* If we're parsing the whitelist filter, ensure that the module for this function 574 | * is in the module whitelist as well, otherwise the function-level filtering will 575 | * never trigger. */ 576 | if (is_whitelist) { 577 | char *module_name = strdup(s); 578 | char *bang_pos = strchr(module_name, '!'); 579 | if (bang_pos != NULL) { 580 | *bang_pos = '\0'; 581 | add_module_filter(module_wbl, module_name); 582 | } 583 | free(module_name); module_name = NULL; 584 | } 585 | 586 | wb_list *l = *func_wbl; 587 | l[j].func_name = s; 588 | l[j].is_wildcard = is_wildcard; 589 | 590 | /* We store the function name length too, so we don't repeatedly 591 | * call strlen() on an unchanging string in the critical region. */ 592 | l[j].func_name_len = strlen(s); 593 | } 594 | 595 | *func_wbl_len = v_in_len; 596 | } 597 | 598 | /* Parse the whitelist/blacklist entries in the filter file. */ 599 | static void 600 | parse_filter_file(void) 601 | { 602 | if (op_filter_file.get_value().empty()) 603 | return; 604 | 605 | std::vector temp_whitelist; 606 | std::vector temp_blacklist; 607 | 608 | std::ifstream filter_file(op_filter_file.get_value().c_str()); 609 | 610 | /* Loop through every line in the filter file. */ 611 | std::string line; 612 | bool mode_whitelist = false, mode_blacklist = false; 613 | while (std::getline(filter_file, line)) { 614 | 615 | /* Skip empty lines and comments. */ 616 | if (line.empty() || (line.find("#") == 0)) 617 | continue; 618 | 619 | /* When we find a whitelist header, add subsequent lines to the whitelist. 620 | /* Otherwise, when we find a blacklist header, add subsequent lines to the 621 | /* blacklist. */ 622 | if (line == std::string("[whitelist]")) { 623 | mode_whitelist = true; 624 | mode_blacklist = false; 625 | continue; 626 | } else if (line == std::string("[blacklist]")) { 627 | mode_whitelist = false; 628 | mode_blacklist = true; 629 | continue; 630 | } 631 | 632 | if (mode_whitelist) 633 | temp_whitelist.push_back(line); 634 | else if (mode_blacklist) 635 | temp_blacklist.push_back(line); 636 | } 637 | 638 | /* If both a whitelist and a blacklist was specified, the blacklist is 639 | * ignored. */ 640 | if (!temp_whitelist.empty() && !temp_blacklist.empty()) 641 | temp_blacklist.clear(); 642 | 643 | /* Convert the vectors to an array of wb_list structs. This is 644 | * possibly faster to process in the critical region later. */ 645 | if (!temp_whitelist.empty()) 646 | parse_filter(temp_whitelist, true, filter_module_whitelist, &filter_function_whitelist, &filter_function_whitelist_len); 647 | else if (!temp_blacklist.empty()) 648 | parse_filter(temp_blacklist, false, filter_module_blacklist, &filter_function_blacklist, &filter_function_blacklist_len); 649 | 650 | filter_file.close(); 651 | } 652 | 653 | #ifndef WINDOWS 654 | static void 655 | event_fork(void *drcontext) 656 | { 657 | /* The old file was closed by DR b/c we passed DR_FILE_CLOSE_ON_FORK */ 658 | open_log_file(); 659 | } 660 | #endif 661 | 662 | static void 663 | event_exit(void) 664 | { 665 | if (op_use_config.get_value()) 666 | libcalls_hashtable_delete(); 667 | 668 | if (outf != STDERR) { 669 | if (op_print_ret_addr.get_value()) 670 | drmodtrack_dump(outf); 671 | dr_close_file(outf); 672 | } 673 | 674 | free_wblist_array(&filter_function_whitelist, filter_function_whitelist_len); 675 | free_wblist_array(&filter_function_blacklist, filter_function_blacklist_len); 676 | 677 | drx_exit(); 678 | drwrap_exit(); 679 | drmgr_exit(); 680 | if (op_print_ret_addr.get_value()) 681 | drmodtrack_exit(); 682 | } 683 | 684 | DR_EXPORT void 685 | dr_client_main(client_id_t id, int argc, const char *argv[]) 686 | { 687 | module_data_t *exe; 688 | IF_DEBUG(bool ok;) 689 | 690 | dr_set_client_name("Dr. LTrace", "???"); 691 | 692 | if (!droption_parser_t::parse_argv(DROPTION_SCOPE_CLIENT, argc, argv, 693 | NULL, NULL)) 694 | ASSERT(false, "unable to parse options specified for drltracelib"); 695 | 696 | IF_DEBUG(ok = ) 697 | drmgr_init(); 698 | ASSERT(ok, "drmgr failed to initialize"); 699 | IF_DEBUG(ok = ) 700 | drwrap_init(); 701 | ASSERT(ok, "drwrap failed to initialize"); 702 | IF_DEBUG(ok = ) 703 | drx_init(); 704 | ASSERT(ok, "drx failed to initialize"); 705 | if (op_print_ret_addr.get_value()) { 706 | IF_DEBUG(ok = ) 707 | drmodtrack_init(); 708 | ASSERT(ok == DRCOVLIB_SUCCESS, "drmodtrack failed to initialize"); 709 | } 710 | 711 | exe = dr_get_main_module(); 712 | if (exe != NULL) 713 | exe_start = exe->start; 714 | dr_free_module_data(exe); 715 | 716 | /* No-frills is safe b/c we're the only module doing wrapping, and 717 | * we're only wrapping at module load and unwrapping at unload. 718 | * Fast cleancalls is safe b/c we're only wrapping func entry and 719 | * we don't care about the app context. 720 | */ 721 | drwrap_set_global_flags((drwrap_global_flags_t) 722 | (DRWRAP_NO_FRILLS | DRWRAP_FAST_CLEANCALLS)); 723 | 724 | dr_register_exit_event(event_exit); 725 | #ifdef UNIX 726 | dr_register_fork_init_event(event_fork); 727 | #endif 728 | drmgr_register_module_load_event(event_module_load); 729 | drmgr_register_module_unload_event(event_module_unload); 730 | 731 | #ifdef WINDOWS 732 | dr_enable_console_printing(); 733 | #endif 734 | if (op_max_args.get_value() > 0) 735 | parse_config(); 736 | 737 | open_log_file(); 738 | parse_filter_file(); 739 | } 740 | --------------------------------------------------------------------------------