├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── dump_symbols.py ├── symbolize.py ├── targets └── fuzzer_ipv6_input.cc ├── volatility_patch ├── LICENSE └── vmware_volatility_wtf.patch └── wtf_vmware.patch /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # How to Contribute 3 | 4 | Thanks for your interest in contributing to WTF support for macOS! Here are a few general guidelines on contributing and 5 | reporting bugs that we ask you to review. Following these guidelines helps to communicate that you respect the time of 6 | the contributors managing and developing this open source project. In return, they should reciprocate that respect in 7 | addressing your issue, assessing changes, and helping you finalize your pull requests. In that spirit of mutual respect, 8 | we endeavor to review incoming issues and pull requests within 10 days, and will close any lingering issues or pull 9 | requests after 60 days of inactivity. 10 | 11 | ## Reporting Issues 12 | 13 | Before reporting a new issue, please ensure that the issue was not already reported or fixed by searching through our issues list. 14 | 15 | When creating a new issue, please be sure to include a **title and clear description**, as much relevant information as 16 | possible, and, if possible, a test case. 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Cisco Systems, Inc. and its affiliates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WTF Snapshot fuzzing of macOS targets 2 | 3 | Support tooling , patches and sample fuzzers. 4 | For a detailed usage guide, see: https://blog.talosintelligence.com/macos-snapshot-fuzzing 5 | 6 | Contains: 7 | - `dump_symbols.py` - an LLDB scripts to make a JSON symbol store to use for symbolicating traces 8 | - `symbolize.py` - turn raw memory addresses into symbolized traces (module+offset) for easier debugging 9 | - `vmware_volatility_wtf.patch` - patch for Volatility framework to dump CPU state in format that WTF expects 10 | - `wtf_vmware.patch` - patch that adds VMWare memory snapshot loading to WTF. 11 | - `targets` - sample fuzzing harness(es) 12 | 13 | # Usage 14 | 15 | After applying the patches and acquiring the snapshot, you can test run the fuzzer with IPv6 target like so: 16 | 17 | ``` 18 | c:\work\codes\wtf\targets\ipv6_input>..\..\src\build\wtf.exe run --backend=bochscpu --name IPv6_Input --state state --input inputs\ipv6 --trace-type 1 --trace-path . 19 | The debugger instance is loaded with 0 items 20 | load raw mem dump1 21 | Done 22 | Setting debug register status to zero. 23 | Setting debug register status to zero. 24 | Segment with selector 0 has invalid attributes. 25 | Segment with selector 0 has invalid attributes. 26 | Segment with selector 8 has invalid attributes. 27 | Segment with selector 0 has invalid attributes. 28 | Segment with selector 10 has invalid attributes. 29 | Segment with selector 0 has invalid attributes. 30 | Trace file .\ipv6.trace 31 | Running inputs\ipv6 32 | -------------------------------------------------- 33 | Run stats: 34 | Instructions executed: 13001 (4961 unique) 35 | Dirty pages: 229376 bytes (0 MB) 36 | Memory accesses: 46135 bytes (0 MB) 37 | #1 cov: 4961 exec/s: infm lastcov: 0.0s crash: 0 timeout: 0 cr3: 0 uptime: 0.0s 38 | 39 | c:\work\codes\wtf\targets\ipv6_input> 40 | ``` 41 | 42 | 43 | 44 | # License 45 | 46 | WTF patches and support utilities are MIT licensed as is WTF itself. Volatility patches are GPL licensed to match Volatility's license. 47 | 48 | # Acquire and patch volatility 49 | 50 | Needs to be applied to specific commit. 51 | ``` 52 | git clone https://github.com/volatilityfoundation/volatility 53 | git checkout a438e768194a9e05eb4d9ee9338b881c0fa25937 54 | git apply vmware_volatility_wtf.patch 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /dump_symbols.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # 3 | # Copyright 2024 Cisco Systems, Inc. and its affiliates 4 | # 5 | # Use of this source code is governed by an MIT-style 6 | # license that can be found in the LICENSE file or at 7 | # https://opensource.org/licenses/MIT. 8 | 9 | import lldb 10 | import json 11 | symbols = {} 12 | 13 | """ 14 | (lldb) image lookup -a 0xffffff801172277f 15 | Address: kernel[0xffffff800031277f] (kernel.__TEXT.__text + 989055) 16 | Summary: kernel`vm_fault_deactivate_behind + 15 at vm_fault.c:501 17 | """ 18 | def parseLookup(lookup): 19 | summary = lookup.split("\n")[1].strip() 20 | print(summary) 21 | name = summary.split(" ")[1].split("`")[1].split("(")[0] 22 | print(name) 23 | return name 24 | 25 | def parseDump(dump,debugger): 26 | result1 = lldb.SBCommandReturnObject() 27 | ci = debugger.GetCommandInterpreter() 28 | for line in dump.split("\n"): 29 | if line.startswith("["): 30 | parts = line.split() 31 | if parts[0] == '[': 32 | parts.pop(0) 33 | if parts[3] == "Invalid": 34 | continue 35 | if len(parts) < 9: 36 | continue 37 | file_addr = parts[4] 38 | load_addr = parts[5] 39 | size = parts[6] 40 | print(parts) 41 | name = parts[8].split("(")[0] 42 | if "___lldb_unnamed_symbol" in name: 43 | ci.HandleCommand("image lookup -a " + load_addr,result1) 44 | name = None 45 | if result1.Succeeded(): 46 | name = parseLookup(result1.GetOutput()) 47 | if name == None: 48 | continue 49 | print("%s @ %s"%(name,load_addr)) 50 | symbols[load_addr] = name 51 | 52 | 53 | def dumpSymbols(debugger, command, result, dict): 54 | result1 = lldb.SBCommandReturnObject() 55 | ci = debugger.GetCommandInterpreter() 56 | ci.HandleCommand(command,result1) 57 | if result1.Succeeded(): 58 | parseDump(result1.GetOutput(),debugger) 59 | f = open("symbol-store.json","w") 60 | json.dump(symbols,f) 61 | f.close() 62 | 63 | def __lldb_init_module (debugger, dict): 64 | debugger.HandleCommand('command script add -f dump_symbols.dumpSymbols dumpSymbols') 65 | -------------------------------------------------------------------------------- /symbolize.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # 3 | # Copyright 2024 Cisco Systems, Inc. and its affiliates 4 | # 5 | # Use of this source code is governed by an MIT-style 6 | # license that can be found in the LICENSE file or at 7 | # https://opensource.org/licenses/MIT. 8 | 9 | import json 10 | import sys 11 | import numpy as np 12 | 13 | f = open("symbol-store.json","r") 14 | symbols = json.load(f) 15 | f.close() 16 | l = [int(x,16) for x in symbols.keys()] 17 | nl = np.array(l, dtype=np.uint64) 18 | addresses = np.sort(nl) 19 | last_addr = 0 20 | last_found = 0 21 | 22 | def findSymbol(addr): 23 | global last_addr 24 | global last_found 25 | #high chance that next instruction belongs to the same function 26 | # this "prediction" can fail in certain situations 27 | # but it's inconsequential and greatly speeds things up 28 | if abs(addr-last_addr) < 16: #my approx. at a longest instruction 29 | found = last_found 30 | else: 31 | try: 32 | found = addresses[addresses<=addr][-1] 33 | except: 34 | return None, None 35 | last_found = found 36 | last_addr = addr 37 | return found,symbols["0x%.16x"%found] 38 | #input() 39 | 40 | def symbolize(address): 41 | addr , name = findSymbol(address) 42 | if not addr: 43 | return hex(address) 44 | offset = (address - addr) 45 | if offset > 0: 46 | name = "%s+0x%x"%(name,offset) 47 | return name 48 | 49 | if __name__ == '__main__': 50 | trace = open(sys.argv[1],"r") 51 | of = open(sys.argv[2],"w") 52 | for l in trace.readlines(): 53 | of.write(symbolize(int(l.strip(),16))+"\n") 54 | of.close() 55 | -------------------------------------------------------------------------------- /targets/fuzzer_ipv6_input.cc: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | 3 | Copyright 2024 Cisco Systems, Inc. and its affiliates 4 | 5 | Use of this source code is governed by an MIT-style 6 | license that can be found in the LICENSE file or at 7 | https://opensource.org/licenses/MIT. 8 | */ 9 | 10 | #include "backend.h" 11 | #include "targets.h" 12 | #include 13 | //ping6 fe80::817:b01e:899b:45b2%en0 -c 1 -p 41 -s 1016 -b 1064 14 | namespace fs = std::filesystem; 15 | 16 | namespace IPv6_Input { 17 | 18 | constexpr bool LoggingOn = true; 19 | 20 | template 21 | void DebugPrint(const char *Format, const Args_t &...args) { 22 | if constexpr (LoggingOn) { 23 | fmt::print("IPv6_Input: "); 24 | fmt::print(Format, args...); 25 | } 26 | } 27 | 28 | 29 | bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { 30 | 31 | if (BufferSize < 40) return true; // mutated data too short 32 | 33 | Gva_t ipv6_header = Gva_t(0xffffff80644206d8); 34 | if(!g_Backend->VirtWriteDirty(ipv6_header,Buffer,40)){ 35 | DebugPrint("VirtWriteDirtys failed\n"); 36 | } 37 | 38 | Gva_t icmp6_data = Gva_t(0xffffff8064401000); 39 | if(!g_Backend->VirtWriteDirty(icmp6_data,Buffer+40,BufferSize-40)){ 40 | DebugPrint("VirtWriteDirtys failed\n"); 41 | } 42 | 43 | return true; 44 | } 45 | 46 | bool Init(const Options_t &Opts, const CpuState_t &) { 47 | 48 | 49 | const Gva_t Rip = Gva_t(g_Backend->Rip()); 50 | 51 | 52 | Gva_t exception_triage = Gva_t(0xffffff8011d63acd); 53 | if (!g_Backend->SetBreakpoint(exception_triage, [](Backend_t *Backend) { 54 | 55 | const Gva_t rdi = Gva_t(g_Backend->Rdi()); 56 | const Gva_t rsi = Gva_t(g_Backend->Rdi()); 57 | const Gva_t rsp = Gva_t(g_Backend->Rdi()); 58 | const Gva_t saved_rip = rsp+Gva_t(0x20); 59 | const uint64_t rip = g_Backend->VirtRead8(saved_rip); 60 | const uint64_t fault_address = g_Backend->VirtRead8(rsi+Gva_t(8)); 61 | const std::string Filename = fmt::format( 62 | "crash-{:#x}-{:#x}-{:#x}", rdi, rip, fault_address); 63 | DebugPrint("Crash: {}\n", Filename); 64 | Backend->Stop(Crash_t(Filename)); 65 | 66 | })) { 67 | return false; 68 | } 69 | 70 | Gva_t retq = Gva_t(0xffffff801278fcb9); 71 | if (!g_Backend->SetBreakpoint(retq, [](Backend_t *Backend) { 72 | Backend->Stop(Ok_t()); 73 | })) { 74 | return false; 75 | } 76 | 77 | // patch icmp6 checksum check 78 | 79 | retq = Gva_t(0xffffff801275acbe); //397 80 | if (!g_Backend->SetBreakpoint(retq, [](Backend_t *Backend) { 81 | g_Backend->Rax(0); 82 | })) { 83 | return false; 84 | } 85 | 86 | //patch tcp_checksum check 87 | retq = Gva_t(0xffffff80125fbe57); // 88 | if (!g_Backend->SetBreakpoint(retq, [](Backend_t *Backend) { 89 | 90 | g_Backend->Rax(0); 91 | })) { 92 | return false; 93 | } 94 | 95 | return true; 96 | } 97 | 98 | bool Restore() { return true; } 99 | 100 | // 101 | // Register the target. 102 | // 103 | 104 | Target_t IPv6_Input("IPv6_Input", Init, InsertTestcase, Restore); 105 | 106 | } // namespace Hevd 107 | -------------------------------------------------------------------------------- /volatility_patch/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | -------------------------------------------------------------------------------- /volatility_patch/vmware_volatility_wtf.patch: -------------------------------------------------------------------------------- 1 | diff --git a/volatility/plugins/addrspaces/vmware.py b/volatility/plugins/addrspaces/vmware.py 2 | index be0411d0..4d32fe10 100644 3 | --- a/volatility/plugins/addrspaces/vmware.py 4 | +++ b/volatility/plugins/addrspaces/vmware.py 5 | @@ -174,7 +174,6 @@ class VMWareAddressSpace(addrspace.AbstractRunBasedMemory): 6 | 7 | ## A VMware header is found at offset zero of the file 8 | self.header = obj.Object("_VMWARE_HEADER", offset = 0, vm = base) 9 | - 10 | self.as_assert(self.header.Magic in [0xbed2bed0, 0xbad1bad1, 0xbed2bed2, 0xbed3bed3], 11 | "Invalid VMware signature: {0:#x}".format(self.header.Magic)) 12 | 13 | @@ -182,43 +181,44 @@ class VMWareAddressSpace(addrspace.AbstractRunBasedMemory): 14 | region_count = self.get_tag(self.header, grp_name = "memory", 15 | tag_name = "regionsCount", 16 | data_type = "unsigned int") 17 | - 18 | if not region_count.is_valid() or region_count == 0: 19 | ## Create a single run from the main memory region 20 | memory_tag = self.get_tag(self.header, grp_name = "memory", 21 | tag_name = "Memory") 22 | - 23 | - self.as_assert(memory_tag != None, 24 | - "Cannot find the single-region Memory tag") 25 | - 26 | - self.runs.append((0, memory_tag.RealDataOffset, memory_tag.DataDiskSize)) 27 | + memory_offset = self.get_tag(self.header, grp_name = "memory", 28 | + tag_name = "regionPPN", 29 | + indices = [i], 30 | + data_type = "unsigned int") * self.PAGE_SIZE 31 | + #self.as_assert(memory_tag != None, 32 | + # "Cannot find the single-region Memory tag") 33 | + 34 | + self.runs.append((0, 0, 0)) 35 | else: 36 | ## Create multiple runs - one for each region in the header 37 | - for i in range(region_count): 38 | - memory_tag = self.get_tag(self.header, grp_name = "memory", 39 | - tag_name = "Memory", 40 | - indices = [0, 0]) 41 | - 42 | - self.as_assert(memory_tag != None, 43 | - "Cannot find the Memory tag") 44 | - 45 | - memory_offset = self.get_tag(self.header, grp_name = "memory", 46 | - tag_name = "regionPPN", 47 | - indices = [i], 48 | - data_type = "unsigned int") * self.PAGE_SIZE 49 | - 50 | - file_offset = self.get_tag(self.header, grp_name = "memory", 51 | - tag_name = "regionPageNum", indices = [i], 52 | - data_type = "unsigned int") * \ 53 | - self.PAGE_SIZE + memory_tag.RealDataOffset 54 | - 55 | - length = self.get_tag(self.header, grp_name = "memory", 56 | - tag_name = "regionSize", 57 | - indices = [i], 58 | - data_type = "unsigned int") * self.PAGE_SIZE 59 | - 60 | - self.runs.append((memory_offset, file_offset, length)) 61 | - 62 | + try: 63 | + for i in range(region_count): 64 | + memory_tag = self.get_tag(self.header, grp_name = "memory", 65 | + tag_name = "Memory", 66 | + indices = [0, 0]) 67 | + #self.as_assert(memory_tag != None, 68 | + # "Cannot find the Memory tag") 69 | + memory_offset = self.get_tag(self.header, grp_name = "memory", 70 | + tag_name = "regionPPN", 71 | + indices = [i], 72 | + data_type = "unsigned int") * self.PAGE_SIZE 73 | + 74 | + file_offset = self.get_tag(self.header, grp_name = "memory", 75 | + tag_name = "regionPageNum", indices = [i], 76 | + data_type = "unsigned int") * \ 77 | + self.PAGE_SIZE + memory_tag.RealDataOffset 78 | + 79 | + length = self.get_tag(self.header, grp_name = "memory", 80 | + tag_name = "regionSize", 81 | + indices = [i], 82 | + data_type = "unsigned int") * self.PAGE_SIZE 83 | + self.runs.append((memory_offset, file_offset, length)) 84 | + except Exception as e: 85 | + print(e) 86 | ## Make sure we found at least one memory run 87 | self.as_assert(len(self.runs) > 0, 88 | "Cannot find any memory run information") 89 | diff --git a/volatility/plugins/vmwareinfo.py b/volatility/plugins/vmwareinfo.py 90 | index 05aaa462..a5436f0c 100644 91 | --- a/volatility/plugins/vmwareinfo.py 92 | +++ b/volatility/plugins/vmwareinfo.py 93 | @@ -24,7 +24,7 @@ import volatility.utils as utils 94 | class VMwareInfo(crashinfo.CrashInfo): 95 | """Dump VMware VMSS/VMSN information""" 96 | 97 | - target_as = ['VMWareAddressSpace', 'VMWareMetaAddressSpace'] 98 | + target_as = ['VMWareAddressSpace'] 99 | 100 | def __init__(self, config, *args, **kwargs): 101 | crashinfo.CrashInfo.__init__(self, config, *args, **kwargs) 102 | @@ -58,7 +58,10 @@ class VMwareInfo(crashinfo.CrashInfo): 103 | ("DataSize", "#018x"), 104 | ("Name", "50"), 105 | ("Value", "")]) 106 | - 107 | + segments = [] 108 | + segment = {} 109 | + from collections import OrderedDict 110 | + registers = OrderedDict() 111 | for group in header.Groups: 112 | for tag in group.Tags: 113 | 114 | @@ -86,7 +89,49 @@ class VMwareInfo(crashinfo.CrashInfo): 115 | tag.DataMemSize, 116 | "{0}/{1}{2}".format(group.Name, tag.Name, indices), 117 | value) 118 | - 119 | + if(str(group.Name) == 'SVGA'): 120 | + break 121 | + if(str(group.Name) == "cpu" and str(tag.Name).startswith('S')): 122 | + tn = str(tag.Name) 123 | + if(len(tn) == 1 or tn[1] == '.'): #segment 124 | + seg_idx = tag.TagIndices[1] 125 | + if tn == "S": 126 | + segment["sel"] = value 127 | + elif tn == "S.limit": 128 | + segment["lim"] = value 129 | + elif tn == "S.base": 130 | + segment["base"] = value 131 | + elif tn == "S.base64": 132 | + segment["base64"] = value 133 | + elif tn == "S.rights": 134 | + segment["rights"] = "0x%x"%(int(value,16)>>8) 135 | + print(segment) 136 | + segments.append(segment.copy()) 137 | + if str(group.Name) == "cpu": 138 | + if str(tag.Name) == "tscOffset": 139 | + registers['tsc'] = value 140 | + if str(tag.Name) == "tscAux": 141 | + registers['tsc_aux'] = value 142 | + if str(tag.Name) == "sysEnterCS": 143 | + registers['sysenter_cs'] = value 144 | + if str(tag.Name) == "sysEnterESP": 145 | + registers['sysenter_esp'] = value 146 | + if str(tag.Name) == "sysEnterEIP": 147 | + registers['sysenter_eip'] = value 148 | + if str(tag.Name) == "PAT": 149 | + registers['pat'] = value 150 | + if str(tag.Name) == "EFER": 151 | + registers['efer'] = value 152 | + if str(tag.Name) == "KERNELGSBASE": 153 | + registers['kernel_gs_base'] = value 154 | + if str(tag.Name) == "XCR0": 155 | + registers['xcr0'] = value 156 | + if str(tag.Name) == "EFLAGS": 157 | + registers['rflags'] = value 158 | + if str(tag.Name) == "rip": 159 | + registers['rip'] = value 160 | + 161 | + 162 | ## In verbose mode, when we're *not* dealing with memory segments, 163 | ## print a hexdump of the data 164 | if (self._config.VERBOSE and tag.DataMemSize > 0 165 | @@ -102,6 +147,48 @@ class VMwareInfo(crashinfo.CrashInfo): 166 | 167 | ## If an output directory was supplied, extract the 168 | ## snapshot thumbnail image using the code below. 169 | + 170 | + #parse gp regs 171 | + import struct 172 | + if(str(group.Name) == "cpu" and str(tag.Name) == "gpregs"): 173 | + rax,rcx,rdx,rbx,rsp,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15 = struct.unpack("Q"*16, data) 174 | + registers['rax'] = "0x%x"%(rax) 175 | + registers['rbx'] = "0x%x"%(rbx) 176 | + registers['rcx'] = "0x%x"%(rcx) 177 | + registers['rdx'] = "0x%x"%(rdx) 178 | + registers['rsi'] = "0x%x"%(rsi) 179 | + registers['rdi'] = "0x%x"%(rdi) 180 | + registers['rsp'] = "0x%x"%(rsp) 181 | + registers['rbp'] = "0x%x"%(rbp) 182 | + registers['r8'] ="0x%x"%( r8) 183 | + registers['r9'] ="0x%x"%( r9) 184 | + registers['r10'] = "0x%x"%(r10) 185 | + registers['r11'] = "0x%x"%(r11) 186 | + registers['r12'] = "0x%x"%(r12) 187 | + registers['r13'] = "0x%x"%(r13) 188 | + registers['r14'] = "0x%x"%(r14) 189 | + registers['r15'] = "0x%x"%(r15) 190 | + if(str(group.Name) == "cpu" and str(tag.Name) == "CR64"): 191 | + cr0,cr1,cr2,cr3,cr4,cr5,cr6,cr7,cr8 = struct.unpack("Q"*9,data) 192 | + registers['cr0'] = "0x%x"%(cr0) 193 | + registers['cr2'] = "0x%x"%(cr2) 194 | + registers['cr3'] = "0x%x"%(cr3) 195 | + registers['cr4'] = "0x%x"%(cr4) 196 | + registers['cr8'] = "0x%x"%(cr8) 197 | + if(str(group.Name) == "cpu" and str(tag.Name) == "DR64"): 198 | + dr0,dr1,dr2,dr3,dr4,dr5,dr6,dr7 = struct.unpack("Q"*8,data) 199 | + registers['dr0'] = "0x%x"%(dr0) 200 | + registers['dr1'] = "0x%x"%(dr1) 201 | + registers['dr2'] = "0x%x"%(dr2) 202 | + registers['dr3'] = "0x%x"%(dr3) 203 | + registers['dr6'] = "0x%x"%(dr6) 204 | + registers['dr7'] = "0x%x"%(dr7) 205 | + if(str(group.Name) == "cpu" and str(tag.Name) == "GDTR"): 206 | + gdtr_limit,gdtr_base = struct.unpack("=HQ",data) 207 | + registers['gdtr'] = {"base": "0x%x"%gdtr_base,"limit": "0x%x"%gdtr_limit} 208 | + if(str(group.Name) == "cpu" and str(tag.Name) == "IDTR"): 209 | + idtr_limit,idtr_base = struct.unpack("=HQ",data) 210 | + registers['idtr'] = {"base": "0x%x"%idtr_base,"limit": "0x%x"%idtr_limit} 211 | if (self._config.DUMP_DIR and 212 | str(group.Name) == "MKSVMX" and 213 | str(tag.Name) == "imageData"): 214 | @@ -109,5 +196,29 @@ class VMwareInfo(crashinfo.CrashInfo): 215 | with open(full_path, "wb") as fh: 216 | fh.write(data) 217 | outfd.write("Wrote screenshot to: {0}\n".format(full_path)) 218 | + for s,segment_name in zip(segments, ["es","cs","ss","ds","fs","gs","ldtr","tr"]): 219 | + registers[segment_name] = {"present": True ,"selector": s['sel'],"base": s['base64'], "limit" : s['lim'], 'attr' : s['rights']} 220 | + #just add the ones we don't care or have 221 | + registers["star"] = "0x001b000800000000" 222 | + registers["lstar"]="0xfffff68600002720" 223 | + registers["cstar"]="0x0000000000000000" 224 | + registers["sfmask"]="0x0000000000004700" 225 | + registers["fpcw"]="0x27f" 226 | + registers["fpsw"]="0x0" 227 | + registers["fptw"]= "0x0" 228 | + registers["fpst"] = ["0x-Infinity", 229 | + "0x-Infinity", 230 | + "0x-Infinity", 231 | + "0x-Infinity", 232 | + "0x-Infinity", 233 | + "0x-Infinity", 234 | + "0x-Infinity", 235 | + "0x-Infinity"] 236 | + registers["mxcsr"] = "0x00001f80" 237 | + registers["mxcsr_mask"]="0xffbf" 238 | + registers["fpop"]="0x0" 239 | + registers["apic_base"] = "0x0" 240 | + import json 241 | + print(json.dumps(registers,indent=4)) 242 | 243 | 244 | -------------------------------------------------------------------------------- /wtf_vmware.patch: -------------------------------------------------------------------------------- 1 | From 2871a7c89d4d7c631f661b81b326b7c1f93de7e1 Mon Sep 17 00:00:00 2001 2 | From: aleks 3 | Date: Tue, 12 Dec 2023 15:51:21 -0600 4 | Subject: [PATCH 1/2] add alternative raw memory loading to kdmp parser 5 | 6 | --- 7 | src/libs/kdmp-parser/src/lib/kdmp-parser.h | 53 +++++++++++++++------- 8 | 1 file changed, 37 insertions(+), 16 deletions(-) 9 | 10 | diff --git a/src/libs/kdmp-parser/src/lib/kdmp-parser.h b/src/libs/kdmp-parser/src/lib/kdmp-parser.h 11 | index 20cb239..e0c44cc 100644 12 | --- a/src/libs/kdmp-parser/src/lib/kdmp-parser.h 13 | +++ b/src/libs/kdmp-parser/src/lib/kdmp-parser.h 14 | @@ -70,25 +70,29 @@ public: 15 | // 16 | 17 | if (!ParseDmpHeader()) { 18 | - printf("ParseDmpHeader failed.\n"); 19 | - return false; 20 | - } 21 | - 22 | - // 23 | - // Retrieve the physical memory according to the type of dump we have. 24 | - // 25 | - 26 | - if (DmpHdr_->DumpType == DumpType_t::FullDump) { 27 | - if (!BuildPhysmemFullDump()) { 28 | - printf("BuildPhysmemFullDump failed.\n"); 29 | + printf("ParseDmpHeader failed. Not a .dmp file? Trying to load as VMWare raw dump.\n"); 30 | + //try to load it as a vmware snapshot 31 | + if(!BuildPhysmemRawDump()){ 32 | + printf("BuildPhysmemRawDump failed. Not VMWare snapshot either?\n"); 33 | return false; 34 | } 35 | - } else if (DmpHdr_->DumpType == DumpType_t::BMPDump) { 36 | - if (!BuildPhysmemBMPDump()) { 37 | - printf("BuildPhysmemBMPDump failed.\n"); 38 | - return false; 39 | + }else{ 40 | + // 41 | + // Retrieve the physical memory according to the type of dump we have. 42 | + // 43 | + 44 | + if (DmpHdr_->DumpType == DumpType_t::FullDump) { 45 | + if (!BuildPhysmemFullDump()) { 46 | + printf("BuildPhysmemFullDump failed.\n"); 47 | + return false; 48 | + } 49 | + } else if (DmpHdr_->DumpType == DumpType_t::BMPDump) { 50 | + if (!BuildPhysmemBMPDump()) { 51 | + printf("BuildPhysmemBMPDump failed.\n"); 52 | + return false; 53 | + } 54 | } 55 | - } 56 | + } 57 | 58 | return true; 59 | } 60 | @@ -528,6 +532,23 @@ private: 61 | return true; 62 | } 63 | 64 | + 65 | +bool BuildPhysmemRawDump(){ 66 | + //vmware snapshot is just a raw linear dump of physical memory, with some gaps 67 | + //just fill up a structure for all the pages with appropriate physmem file offsets 68 | + //assuming physmem dump file is from a vm with 4gb of ram 69 | + uint8_t *base = (uint8_t *)FileMap_.ViewBase(); 70 | + for(uint64_t i = 0;i < 786432; i++ ){ //that many pages, first 3gb 71 | + uint64_t offset = i*4096; 72 | + Physmem_.try_emplace(offset, (uint8_t *)base+offset); 73 | + } 74 | + //there's a gap in VMWare's memory dump from 3 to 4gb, last 1gb is mapped above 4gb 75 | + for(uint64_t i = 0;i < 262144; i++ ){ 76 | + uint64_t offset = (i+786432)*4096; 77 | + Physmem_.try_emplace(i*4096+4294967296, (uint8_t *)base+offset); 78 | + } 79 | + return true; 80 | +} 81 | // 82 | // Parse the DMP_HEADER. 83 | // 84 | -- 85 | 2.33.0.windows.1 86 | 87 | 88 | From 198cec9464676bb485d8cf4f58ca586c76aa370d Mon Sep 17 00:00:00 2001 89 | From: aleks 90 | Date: Tue, 12 Dec 2023 15:52:46 -0600 91 | Subject: [PATCH 2/2] Added build flag to relax certain errors 92 | 93 | A debugger won't work for vmware dumps, so just warn in that case and 94 | continue on. 95 | 96 | Additionally, there's some sort of discrepancy with cpu state between 97 | dumps from hyper-v and from vmware. Ignoring these didn't result in 98 | anything horrible, so warn on it but continue. 99 | --- 100 | src/CMakeLists.txt | 5 +++++ 101 | src/build/build-release-vmware-support.bat | 2 ++ 102 | src/wtf/utils.cc | 5 +++++ 103 | src/wtf/wtf.cc | 5 +++++ 104 | 4 files changed, 17 insertions(+) 105 | create mode 100644 src/build/build-release-vmware-support.bat 106 | 107 | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt 108 | index 7c13cf3..d9ef5f9 100644 109 | --- a/src/CMakeLists.txt 110 | +++ b/src/CMakeLists.txt 111 | @@ -16,6 +16,11 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/libs/CLI11/include) 112 | include_directories(${CMAKE_CURRENT_LIST_DIR}/libs/fmt/include) 113 | include_directories(${CMAKE_CURRENT_LIST_DIR}/libs/yas/include) 114 | 115 | +option(ERROR_WARN "Relax error handling , for loading vmware snapshots" OFF) 116 | +if (ERROR_WARN) 117 | + add_definitions(-DERROR_WARN) 118 | +endif() 119 | + 120 | file( 121 | GLOB_RECURSE 122 | wtf_srcfiles 123 | diff --git a/src/build/build-release-vmware-support.bat b/src/build/build-release-vmware-support.bat 124 | new file mode 100644 125 | index 0000000..9b7728a 126 | --- /dev/null 127 | +++ b/src/build/build-release-vmware-support.bat 128 | @@ -0,0 +1,2 @@ 129 | +cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DERROR_WARN=ON 130 | +cmake --build . 131 | diff --git a/src/wtf/utils.cc b/src/wtf/utils.cc 132 | index 25960db..c88f7d2 100644 133 | --- a/src/wtf/utils.cc 134 | +++ b/src/wtf/utils.cc 135 | @@ -238,7 +238,12 @@ bool SanitizeCpuState(CpuState_t &CpuState) { 136 | if (Seg->Reserved != ((Seg->Limit >> 16) & 0xF)) { 137 | fmt::print("Segment with selector {:x} has invalid attributes.\n", 138 | Seg->Selector); 139 | + #if defined(ERROR_WARN) 140 | + fmt::print("Above error could be fatal, but continuing anyway."); 141 | + #else 142 | return false; 143 | + #endif 144 | + 145 | } 146 | } 147 | 148 | diff --git a/src/wtf/wtf.cc b/src/wtf/wtf.cc 149 | index 2ac1bc4..c863ae2 100644 150 | --- a/src/wtf/wtf.cc 151 | +++ b/src/wtf/wtf.cc 152 | @@ -419,7 +419,12 @@ int main(int argc, const char *argv[]) { 153 | // 154 | 155 | if (!g_Dbg.Init(Opts.DumpPath, Opts.SymbolFilePath)) { 156 | + fmt::print("WARNING: Debugger init failed.\n"); 157 | + #if defined(ERROR_WARN) 158 | + fmt::print("Above error could be fatal, but continuing anyway."); 159 | + #else 160 | return EXIT_FAILURE; 161 | + #endif 162 | } 163 | 164 | // 165 | -- 166 | 2.33.0.windows.1 --------------------------------------------------------------------------------