├── .gitattributes ├── .gitignore ├── .gitmodules ├── ApfsDump ├── Apfs.cpp ├── Dumper.cpp └── Dumper.h ├── ApfsDumpQuick └── ApfsDumpQuick.cpp ├── ApfsLib ├── ApfsContainer.cpp ├── ApfsContainer.h ├── ApfsDir.cpp ├── ApfsDir.h ├── ApfsNodeMapper.cpp ├── ApfsNodeMapper.h ├── ApfsNodeMapperBTree.cpp ├── ApfsNodeMapperBTree.h ├── ApfsTypes.h ├── ApfsVolume.cpp ├── ApfsVolume.h ├── BTree.cpp ├── BTree.h ├── BlockDumper.cpp ├── BlockDumper.h ├── CheckPointMap.cpp ├── CheckPointMap.h ├── Crc32.cpp ├── Crc32.h ├── Decmpfs.cpp ├── Decmpfs.h ├── Device.cpp ├── Device.h ├── DeviceDMG.cpp ├── DeviceDMG.h ├── DeviceLinux.cpp ├── DeviceLinux.h ├── DeviceMac.cpp ├── DeviceMac.h ├── DeviceSparseImage.cpp ├── DeviceSparseImage.h ├── DeviceVDI.cpp ├── DeviceVDI.h ├── DeviceWinFile.cpp ├── DeviceWinFile.h ├── DeviceWinPhys.cpp ├── DeviceWinPhys.h ├── DiskImageFile.cpp ├── DiskImageFile.h ├── DiskStruct.h ├── Endian.h ├── Global.h ├── GptPartitionMap.cpp ├── GptPartitionMap.h ├── KeyMgmt.cpp ├── KeyMgmt.h ├── PList.cpp ├── PList.h ├── Unicode.cpp ├── Unicode.h ├── UnicodeTables_v10.h ├── Util.cpp └── Util.h ├── ApfsUtil └── ApfsUtil.cpp ├── CMakeLists.txt ├── Crypto ├── Aes.cpp ├── Aes.h ├── AesXts.cpp ├── AesXts.h ├── Asn1Der.cpp ├── Asn1Der.h ├── Crypto.cpp ├── Crypto.h ├── Des.cpp ├── Des.h ├── Sha1.cpp ├── Sha1.h ├── Sha256.cpp ├── Sha256.h ├── TripleDes.cpp └── TripleDes.h ├── LICENSE ├── README.md └── apfsfuse └── ApfsFuse.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | # Own Files 264 | Data/ 265 | 266 | # Linux 267 | build/ 268 | .kdev4/ 269 | data/ 270 | doc/ 271 | 272 | # Mac 273 | .DS_Store 274 | xcode/ 275 | 276 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/lzfse"] 2 | path = 3rdparty/lzfse 3 | url = https://github.com/lzfse/lzfse.git 4 | -------------------------------------------------------------------------------- /ApfsDump/Apfs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "Dumper.h" 33 | 34 | #ifdef __linux__ 35 | #include 36 | #endif 37 | 38 | #undef RAW_VERBOSE 39 | 40 | 41 | constexpr size_t BLOCKSIZE = 0x1000; 42 | 43 | volatile bool g_abort = 0; 44 | 45 | void DumpBlockTrunc(std::ostream &os, const uint8_t *data) 46 | { 47 | unsigned int sz = BLOCKSIZE - 1; 48 | 49 | while (sz > 0 && data[sz] == 0) 50 | sz--; 51 | 52 | sz = (sz + 0x10) & 0xFFFFFFF0; 53 | 54 | DumpHex(os, data, sz); 55 | } 56 | 57 | #if 0 58 | 59 | void MapBlocks(std::ostream &os, Device &dev, uint64_t bid_start, uint64_t bcnt) 60 | { 61 | using namespace std; 62 | 63 | uint64_t bid; 64 | uint8_t block[BLOCKSIZE]; 65 | const APFS_ObjHeader * const blk = reinterpret_cast(block); 66 | const APFS_TableHeader * const tbl = reinterpret_cast(block + sizeof(APFS_ObjHeader)); 67 | bool last_was_used = false; 68 | 69 | os << hex << uppercase << setfill('0'); 70 | 71 | os << "[Block] | oid | xid | type | subtype | Page | Levl | Entries | Description" << endl; 72 | os << "---------+----------+----------+----------+----------+------+------+----------+---------------------------------" << endl; 73 | 74 | for (bid = 0; bid < bcnt && !g_abort; bid++) 75 | { 76 | if ((bid & 0xFFF) == 0) 77 | { 78 | std::cout << '.'; 79 | std::cout.flush(); 80 | } 81 | 82 | dev.Read(block, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE); 83 | 84 | if (IsEmptyBlock(block, BLOCKSIZE)) 85 | { 86 | if (last_was_used) 87 | os << "---------+----------+----------+----------+----------+------+------+----------+ Empty" << endl; 88 | last_was_used = false; 89 | continue; 90 | } 91 | 92 | if (VerifyBlock(block, BLOCKSIZE)) 93 | { 94 | os << setw(8) << bid << " | "; 95 | os << setw(8) << blk->oid << " | "; 96 | os << setw(8) << blk->xid << " | "; 97 | os << setw(8) << blk->type << " | "; 98 | os << setw(8) << blk->subtype << " | "; 99 | os << setw(4) << tbl->page << " | "; 100 | os << setw(4) << tbl->level << " | "; 101 | os << setw(8) << tbl->entries_cnt << " | "; 102 | os << BlockDumper::GetNodeType(blk->type, blk->subtype); 103 | if (APFS_OBJ_TYPE(blk->type) == 2) 104 | os << " [Root]"; 105 | os << endl; 106 | last_was_used = true; 107 | } 108 | else 109 | { 110 | os << setw(8) << bid; 111 | os << " | | | | | | | | Data" << endl; 112 | last_was_used = true; 113 | } 114 | } 115 | 116 | os << endl; 117 | } 118 | 119 | void ScanBlocks(std::ostream &os, Device &dev, uint64_t bid_start, uint64_t bcnt) 120 | { 121 | BlockDumper bd(os, BLOCKSIZE); 122 | uint64_t bid; 123 | uint8_t block[BLOCKSIZE]; 124 | 125 | bd.SetTextFlags(0x00); 126 | 127 | for (bid = 0; bid < bcnt && !g_abort; bid++) 128 | { 129 | if ((bid & 0xFFF) == 0) 130 | { 131 | std::cout << '.'; 132 | std::cout.flush(); 133 | } 134 | 135 | dev.Read(block, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE); 136 | 137 | if (IsEmptyBlock(block, BLOCKSIZE)) 138 | continue; 139 | 140 | if (VerifyBlock(block, BLOCKSIZE)) 141 | bd.DumpNode(block, bid); 142 | else 143 | { 144 | #if 0 145 | os << std::hex << std::setw(16) << blk_nr << std::endl; 146 | DumpBlockTrunc(os, block); 147 | os << std::endl; 148 | os << "===========================================================================================================================" << std::endl; 149 | os << std::endl; 150 | #endif 151 | } 152 | } 153 | } 154 | 155 | void DumpSpaceman(std::ostream &os, Device &dev, uint64_t bid_start, uint64_t bcnt) 156 | { 157 | uint8_t sb[BLOCKSIZE]; 158 | uint8_t data[BLOCKSIZE]; 159 | BlockDumper bd(os, BLOCKSIZE); 160 | const APFS_ObjHeader *bhdr; 161 | const APFS_Spaceman *sm; 162 | const APFS_NX_Superblock *nxsb; 163 | uint64_t bid; 164 | uint64_t cnt; 165 | uint64_t smbmp_bid = 0; 166 | uint64_t smbmp_cnt = 0; 167 | uint64_t volbmp_bid = 0; 168 | uint64_t volbmp_cnt = 0; 169 | 170 | (void)bcnt; 171 | 172 | if (!dev.Read(sb, bid_start * BLOCKSIZE, BLOCKSIZE)) 173 | return; 174 | 175 | if (!VerifyBlock(sb, BLOCKSIZE)) 176 | return; 177 | 178 | nxsb = reinterpret_cast(sb); 179 | if (nxsb->hdr.type != 0x80000001) 180 | return; 181 | 182 | if (nxsb->nx_magic != 0x4253584E) 183 | return; 184 | 185 | bd.DumpNode(sb, 0); 186 | 187 | os << std::endl; 188 | os << "Dumping Superblock Area" << std::endl; 189 | os << std::endl; 190 | 191 | bid = nxsb->nx_xp_desc_base; 192 | cnt = nxsb->nx_xp_desc_blocks; 193 | 194 | while (cnt > 0) 195 | { 196 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE); 197 | bd.DumpNode(data, bid); 198 | cnt--; 199 | bid++; 200 | } 201 | 202 | os << std::endl; 203 | os << "Dumping Spaceman Area" << std::endl; 204 | os << std::endl; 205 | 206 | bid = nxsb->nx_xp_data_base; 207 | cnt = nxsb->nx_xp_data_blocks; 208 | 209 | while (cnt > 0) 210 | { 211 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE); 212 | bd.DumpNode(data, bid); 213 | cnt--; 214 | bid++; 215 | 216 | bhdr = reinterpret_cast(data); 217 | if (bhdr->type == 0x80000005) 218 | { 219 | sm = reinterpret_cast(data); 220 | smbmp_bid = sm->ip_bm_base_address; 221 | smbmp_cnt = sm->ip_bitmap_block_count; 222 | volbmp_bid = sm->ip_base_address; 223 | volbmp_cnt = sm->ip_block_count; 224 | } 225 | } 226 | 227 | bid = smbmp_bid; 228 | cnt = smbmp_cnt; 229 | 230 | while (cnt > 0) 231 | { 232 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE); 233 | bd.DumpNode(data, bid); 234 | cnt--; 235 | bid++; 236 | } 237 | 238 | bid = volbmp_bid; 239 | cnt = volbmp_cnt; 240 | 241 | while (cnt > 0) 242 | { 243 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE); 244 | bd.DumpNode(data, bid); 245 | cnt--; 246 | bid++; 247 | } 248 | } 249 | 250 | #endif 251 | 252 | static void ctrl_c_handler(int sig) 253 | { 254 | (void)sig; 255 | g_abort = true; 256 | } 257 | 258 | #if 0 259 | 260 | int main(int argc, const char *argv[]) 261 | { 262 | if (argc < 3) 263 | { 264 | std::cerr << "Syntax: apfs-dump file.img output.txt [map.txt]" << std::endl; 265 | return 1; 266 | } 267 | 268 | Device *dev; 269 | std::ofstream os; 270 | uint64_t bid_start = 0; 271 | uint64_t bcnt = 0; 272 | 273 | g_debug = 255; 274 | 275 | #if defined(__linux__) || defined(__APPLE__) 276 | signal(SIGINT, ctrl_c_handler); 277 | #endif 278 | 279 | dev = Device::OpenDevice(argv[1]); 280 | 281 | if (!dev) 282 | { 283 | std::cerr << "Device " << argv[1] << " not found." << std::endl; 284 | return 2; 285 | } 286 | 287 | { 288 | GptPartitionMap pmap; 289 | if (pmap.LoadAndVerify(*dev)) 290 | { 291 | int partid = pmap.FindFirstAPFSPartition(); 292 | if (partid >= 0) 293 | { 294 | std::cout << "Dumping EFI partition" << std::endl; 295 | pmap.GetPartitionOffsetAndSize(partid, bid_start, bcnt); 296 | bid_start /= BLOCKSIZE; 297 | bcnt /= BLOCKSIZE; 298 | } 299 | } 300 | } 301 | 302 | if (bcnt == 0) 303 | bcnt = dev->GetSize() / BLOCKSIZE; 304 | 305 | if (argc > 3) 306 | { 307 | os.open(argv[3]); 308 | if (!os.is_open()) 309 | { 310 | std::cerr << "Could not open output file " << argv[3] << std::endl; 311 | dev->Close(); 312 | delete dev; 313 | return 3; 314 | } 315 | 316 | MapBlocks(os, *dev, bid_start, bcnt); 317 | os.close(); 318 | } 319 | 320 | os.open(argv[2]); 321 | if (!os.is_open()) 322 | { 323 | std::cerr << "Could not open output file " << argv[2] << std::endl; 324 | dev->Close(); 325 | delete dev; 326 | return 3; 327 | } 328 | 329 | DumpSpaceman(os, *dev, bid_start, bcnt); 330 | ScanBlocks(os, *dev, bid_start, bcnt); 331 | 332 | dev->Close(); 333 | os.close(); 334 | 335 | delete dev; 336 | 337 | return 0; 338 | } 339 | 340 | #else 341 | 342 | void usage() 343 | { 344 | std::cerr << "Syntax:" << std::endl; 345 | std::cerr << "apfs-dump [-map mapfile.txt] file.img output.txt" << std::endl; 346 | std::cerr << "apfs-dump [-map mapfile.txt] -fusion main.img tier2.img output.txt" << std::endl; 347 | } 348 | 349 | int main(int argc, const char *argv[]) 350 | { 351 | const char *name_dev_main = 0; 352 | const char *name_dev_tier2 = 0; 353 | const char *name_map = 0; 354 | const char *name_output = 0; 355 | bool use_fusion = false; 356 | 357 | int n; 358 | int idx = 0; 359 | 360 | for (n = 1; n < argc; n++) 361 | { 362 | if (!strcmp(argv[n], "-map")) 363 | { 364 | n++; 365 | if (n >= argc) 366 | { 367 | usage(); 368 | return 1; 369 | } 370 | name_map = argv[n]; 371 | } 372 | 373 | else if (!strcmp(argv[n], "-fusion")) 374 | use_fusion = true; 375 | 376 | else 377 | { 378 | switch (idx) 379 | { 380 | case 0: 381 | name_dev_main = argv[n]; 382 | printf("main: %s\n", name_dev_main); 383 | if (use_fusion) 384 | idx++; 385 | else 386 | idx += 2; 387 | break; 388 | case 1: 389 | name_dev_tier2 = argv[n]; 390 | printf("tier2: %s\n", name_dev_tier2); 391 | idx++; 392 | break; 393 | case 2: 394 | name_output = argv[n]; 395 | printf("out: %s\n", name_output); 396 | idx++; 397 | break; 398 | default: 399 | usage(); 400 | return 1; 401 | break; 402 | } 403 | } 404 | } 405 | 406 | if ((name_output == 0) || (use_fusion && name_dev_tier2 == 0)) 407 | { 408 | usage(); 409 | return 1; 410 | } 411 | 412 | std::unique_ptr dev_main; 413 | std::unique_ptr dev_tier2; 414 | std::ofstream os; 415 | 416 | g_debug = 255; 417 | 418 | #if defined(__linux__) || defined(__APPLE__) 419 | signal(SIGINT, ctrl_c_handler); 420 | #endif 421 | 422 | dev_main.reset(Device::OpenDevice(name_dev_main)); 423 | if (use_fusion) 424 | dev_tier2.reset(Device::OpenDevice(name_dev_tier2)); 425 | 426 | if (!dev_main) 427 | { 428 | std::cerr << "Device " << name_dev_main << " not found." << std::endl; 429 | return 2; 430 | } 431 | 432 | if (use_fusion && !dev_tier2) 433 | { 434 | std::cerr << "Device " << name_dev_tier2 << " not found." << std::endl; 435 | return 2; 436 | } 437 | 438 | { 439 | Dumper dmp(dev_main.get(), dev_tier2.get()); 440 | 441 | if (!dmp.Initialize()) 442 | return -1; 443 | 444 | #if 1 445 | if (name_map) 446 | { 447 | os.open(name_map); 448 | if (!os.is_open()) 449 | { 450 | std::cerr << "Could not open map file " << name_map << std::endl; 451 | dev_main->Close(); 452 | if (dev_tier2) 453 | dev_tier2->Close(); 454 | return 3; 455 | } 456 | 457 | dmp.DumpBlockList(os); 458 | os.close(); 459 | } 460 | #endif 461 | 462 | os.open(name_output); 463 | if (!os.is_open()) 464 | { 465 | std::cerr << "Could not open output file " << name_output << std::endl; 466 | dev_main->Close(); 467 | if (dev_tier2) 468 | dev_tier2->Close(); 469 | return 3; 470 | } 471 | 472 | dmp.DumpContainer(os); 473 | } 474 | 475 | dev_main->Close(); 476 | if (dev_tier2) 477 | dev_tier2->Close(); 478 | os.close(); 479 | 480 | return 0; 481 | } 482 | 483 | #endif 484 | -------------------------------------------------------------------------------- /ApfsDump/Dumper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | extern volatile bool g_abort; // In Apfs.cpp 9 | 10 | class Device; 11 | 12 | class Dumper 13 | { 14 | public: 15 | Dumper(Device *dev_main, Device *dev_tier2); 16 | ~Dumper(); 17 | 18 | bool Initialize(); 19 | bool DumpContainer(std::ostream &os); 20 | bool DumpBlockList(std::ostream &os); 21 | 22 | private: 23 | bool Read(void *data, uint64_t paddr, uint64_t cnt); 24 | bool Read(std::vector &data, uint64_t paddr, uint64_t cnt); 25 | 26 | void Decrypt(uint8_t *data, size_t size, uint64_t paddr); 27 | 28 | const checkpoint_mapping_t* cpm_lookup(const checkpoint_map_phys_t *cpm, uint64_t oid); 29 | 30 | Device *m_dev_main; 31 | Device *m_dev_tier2; 32 | uint64_t m_base_main; 33 | uint64_t m_size_main; 34 | uint64_t m_base_tier2; 35 | uint64_t m_size_tier2; 36 | uint32_t m_blocksize; 37 | 38 | AesXts m_aes; 39 | bool m_is_encrypted; 40 | }; 41 | -------------------------------------------------------------------------------- /ApfsDumpQuick/ApfsDumpQuick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | int main(int argc, char *argv[]) 33 | { 34 | bool rc; 35 | int volume_id; 36 | int n; 37 | std::unique_ptr main_disk; 38 | std::unique_ptr tier2_disk; 39 | uint64_t main_offset; 40 | uint64_t tier2_offset; 41 | uint64_t main_size; 42 | uint64_t tier2_size; 43 | 44 | const char *main_name; 45 | const char *tier2_name; 46 | const char *output_name; 47 | 48 | if (argc < 3) 49 | { 50 | std::cerr << "Syntax: apfs-dump-quick [-f fusion-device] " << std::endl; 51 | return -1; 52 | } 53 | 54 | main_name = argv[1]; 55 | if (!strcmp(argv[2], "-f")) 56 | { 57 | if (argc < 5) 58 | std::cerr << "Syntax: apfs-dump-quick [-f fusion-secondary-device] " << std::endl; 59 | 60 | tier2_name = argv[3]; 61 | output_name = argv[4]; 62 | } 63 | else 64 | { 65 | tier2_name = nullptr; 66 | output_name = argv[2]; 67 | } 68 | 69 | g_debug = 255; 70 | 71 | main_disk.reset(Device::OpenDevice(main_name)); 72 | if (tier2_name) 73 | tier2_disk.reset(Device::OpenDevice(tier2_name)); 74 | 75 | if (!main_disk) 76 | { 77 | std::cerr << "Unable to open device " << main_name << std::endl; 78 | return -1; 79 | } 80 | 81 | if (tier2_name && !tier2_disk) 82 | { 83 | std::cerr << "Unable to open secondary device " << tier2_name << std::endl; 84 | return -1; 85 | } 86 | 87 | std::ofstream st; 88 | st.open(output_name); 89 | 90 | if (!st.is_open()) 91 | { 92 | std::cerr << "Unable to open output file " << output_name << std::endl; 93 | main_disk->Close(); 94 | return -1; 95 | } 96 | 97 | main_offset = 0; 98 | main_size = main_disk->GetSize(); 99 | 100 | tier2_offset = 0; 101 | if (tier2_disk) 102 | tier2_size = tier2_disk->GetSize(); 103 | else 104 | tier2_size = 0; 105 | 106 | GptPartitionMap gpt; 107 | if (gpt.LoadAndVerify(*main_disk.get())) 108 | { 109 | std::cout << "Info: Found valid GPT partition table on main device. Dumping first APFS partition." << std::endl; 110 | 111 | n = gpt.FindFirstAPFSPartition(); 112 | if (n != -1) 113 | gpt.GetPartitionOffsetAndSize(n, main_offset, main_size); 114 | } 115 | 116 | if (tier2_disk && gpt.LoadAndVerify(*tier2_disk.get())) 117 | { 118 | std::cout << "Info: Found valid GPT partition table on tier2 device. Dumping first APFS partition." << std::endl; 119 | 120 | n = gpt.FindFirstAPFSPartition(); 121 | if (n != -1) 122 | gpt.GetPartitionOffsetAndSize(n, tier2_offset, tier2_size); 123 | } 124 | 125 | std::unique_ptr container(new ApfsContainer(main_disk.get(), main_offset, main_size, tier2_disk.get(), tier2_offset, tier2_size)); 126 | 127 | rc = container->Init(); 128 | 129 | if (!rc) 130 | { 131 | std::cerr << "Unable to init container." << std::endl; 132 | container.reset(); 133 | main_disk->Close(); 134 | return -1; 135 | } 136 | 137 | #if 1 138 | BlockDumper bd(st, container->GetBlocksize()); 139 | 140 | container->dump(bd); 141 | 142 | #if 1 143 | for (volume_id = 0; volume_id < NX_MAX_FILE_SYSTEMS; volume_id++) 144 | { 145 | ApfsVolume *vol; 146 | 147 | vol = container->GetVolume(volume_id); 148 | 149 | if (vol) 150 | { 151 | std::cout << "Volume " << volume_id << ": " << vol->name() << std::endl; 152 | 153 | vol->dump(bd); 154 | 155 | delete vol; 156 | } 157 | } 158 | #endif 159 | #endif 160 | 161 | #if 0 162 | ApfsVolume *vol; 163 | 164 | vol = container->GetVolume(0); 165 | 166 | if (vol) 167 | { 168 | ApfsDir dir(*vol); 169 | ApfsDir::Inode ino; 170 | std::vector root_list; 171 | 172 | #if 0 173 | dir.GetInode(ino, 0x15); 174 | 175 | std::cout << "Directory is called " << ino.name << std::endl; 176 | 177 | dir.ListDirectory(root_list, 0x15); 178 | 179 | for (auto it = root_list.cbegin(); it != root_list.cend(); ++it) 180 | { 181 | std::cout << it->name << " : " << it->inode_id << std::endl; 182 | } 183 | #endif 184 | 185 | #if 1 186 | std::vector buf; 187 | buf.resize(0x29000); 188 | 189 | dir.ReadFile(buf.data(), 0x39, 0, 0x29000); 190 | #endif 191 | } 192 | 193 | #endif 194 | 195 | container.reset(); 196 | 197 | st.close(); 198 | 199 | main_disk->Close(); 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /ApfsLib/ApfsContainer.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "Global.h" 23 | #include "BTree.h" 24 | #include "DiskStruct.h" 25 | #include "Device.h" 26 | #include "CheckPointMap.h" 27 | #include "ApfsNodeMapperBTree.h" 28 | #include "KeyMgmt.h" 29 | 30 | #include 31 | #include 32 | 33 | class ApfsVolume; 34 | class BlockDumper; 35 | 36 | class ApfsContainer 37 | { 38 | public: 39 | ApfsContainer(Device *disk_main, uint64_t main_start, uint64_t main_len, Device *disk_tier2 = 0, uint64_t tier2_start = 0, uint64_t tier2_len = 0); 40 | ~ApfsContainer(); 41 | 42 | bool Init(xid_t req_xid = 0); 43 | 44 | ApfsVolume *GetVolume(unsigned int fsid, const std::string &passphrase = std::string(), xid_t snap_xid = 0); 45 | bool GetVolumeInfo(unsigned int fsid, apfs_superblock_t &apsb); 46 | 47 | bool ReadBlocks(uint8_t *data, paddr_t paddr, uint64_t blkcnt = 1) const; 48 | bool ReadAndVerifyHeaderBlock(uint8_t *data, paddr_t paddr) const; 49 | 50 | uint32_t GetBlocksize() const { return m_nx.nx_block_size; } 51 | uint64_t GetBlockCount() const { return m_nx.nx_block_count; } 52 | uint64_t GetFreeBlocks() const { return m_sm->sm_dev[SD_MAIN].sm_free_count + m_sm->sm_dev[SD_TIER2].sm_free_count; } 53 | 54 | bool GetVolumeKey(uint8_t *key, const apfs_uuid_t &vol_uuid, const char *password = nullptr); 55 | bool GetPasswordHint(std::string &hint, const apfs_uuid_t &vol_uuid); 56 | bool IsUnencrypted() const { return m_keymgr.IsUnencrypted(); } 57 | 58 | void dump(BlockDumper& bd); 59 | 60 | private: 61 | Device *m_main_disk; 62 | const uint64_t m_main_part_start; 63 | const uint64_t m_main_part_len; 64 | 65 | Device *m_tier2_disk; 66 | const uint64_t m_tier2_part_start; 67 | const uint64_t m_tier2_part_len; 68 | 69 | std::string m_passphrase; 70 | 71 | nx_superblock_t m_nx; 72 | 73 | CheckPointMap m_cpm; 74 | ApfsNodeMapperBTree m_omap; 75 | 76 | std::vector m_sm_data; 77 | const spaceman_phys_t *m_sm; 78 | // Block_8_11 -> omap 79 | 80 | // BTree m_omap_tree; // see ApfsNodeMapperBTree 81 | BTree m_fq_tree_mgr; 82 | BTree m_fq_tree_vol; 83 | 84 | KeyManager m_keymgr; 85 | }; 86 | -------------------------------------------------------------------------------- /ApfsLib/ApfsDir.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #include "DiskStruct.h" 26 | 27 | class BTree; 28 | class ApfsVolume; 29 | 30 | class ApfsDir 31 | { 32 | public: 33 | struct Inode 34 | { 35 | Inode(); 36 | Inode(const Inode &other); 37 | 38 | /* TODO */ 39 | uint64_t obj_id; 40 | 41 | uint64_t parent_id; 42 | uint64_t private_id; 43 | 44 | uint64_t create_time; 45 | uint64_t mod_time; 46 | uint64_t change_time; 47 | uint64_t access_time; 48 | 49 | uint64_t internal_flags; 50 | 51 | uint64_t nchildren_nlink; 52 | 53 | cp_key_class_t default_protection_class; 54 | uint32_t write_generation_counter; 55 | uint32_t bsd_flags; 56 | uint32_t owner; 57 | uint32_t group; 58 | uint16_t mode; 59 | 60 | uint64_t uncompressed_size; 61 | 62 | uint64_t snap_xid; 63 | uint64_t delta_tree_oid; 64 | uint64_t prev_fsize; 65 | // FinderInfo 66 | uint64_t ds_size; 67 | uint64_t ds_alloced_size; 68 | uint64_t ds_default_crypto_id; 69 | uint64_t ds_total_bytes_written; 70 | uint64_t ds_total_bytes_read; 71 | // j_dir_stats_val_t dir_stats; 72 | apfs_uuid_t fs_uuid; 73 | uint64_t sparse_bytes; 74 | uint32_t document_id; 75 | uint32_t rdev; 76 | std::string name; 77 | 78 | uint32_t optional_present_flags; 79 | 80 | enum PresentFlags { 81 | INO_HAS_SNAP_XID = 1, 82 | INO_HAS_DELTA_TREE_OID = 2, 83 | INO_HAS_DOCUMENT_ID = 4, 84 | INO_HAS_NAME = 8, 85 | INO_HAS_PREV_FSIZE = 16, 86 | INO_HAS_FINDER_INFO = 64, 87 | INO_HAS_DSTREAM = 128, 88 | INO_HAS_DIR_STATS = 512, 89 | INO_HAS_FS_UUID = 1024, 90 | INO_HAS_SPARSE_BYTES = 4096, 91 | INO_HAS_RDEV = 8192 92 | }; 93 | }; 94 | 95 | struct DirRec 96 | { 97 | DirRec(); 98 | DirRec(const DirRec &other); 99 | 100 | uint64_t parent_id; 101 | uint32_t hash; 102 | std::string name; 103 | 104 | uint64_t file_id; 105 | uint64_t date_added; 106 | 107 | uint64_t sibling_id; 108 | uint16_t flags; 109 | bool has_sibling_id; 110 | }; 111 | 112 | struct XAttr 113 | { 114 | XAttr(); 115 | XAttr(const XAttr &other); 116 | 117 | uint16_t flags; 118 | uint16_t xdata_len; 119 | j_xattr_dstream_t xstrm; 120 | }; 121 | 122 | 123 | ApfsDir(ApfsVolume &vol); 124 | ~ApfsDir(); 125 | 126 | bool GetInode(Inode &res, uint64_t inode); 127 | 128 | bool ListDirectory(std::vector &dir, uint64_t inode); 129 | bool LookupName(DirRec &res, uint64_t parent_id, const char *name); 130 | bool ReadFile(void *data, uint64_t inode, uint64_t offs, size_t size); 131 | bool ListAttributes(std::vector &names, uint64_t inode); 132 | bool GetAttribute(std::vector &data, uint64_t inode, const char *name); 133 | bool GetAttributeInfo(XAttr &attr, uint64_t inode, const char *name); 134 | 135 | private: 136 | static int CompareStdDirKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context); 137 | static int CompareFextKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context); 138 | 139 | ApfsVolume &m_vol; 140 | BTree &m_fs_tree; 141 | uint32_t m_txt_fmt; 142 | uint32_t m_blksize; 143 | uint64_t m_blksize_mask_hi; 144 | uint64_t m_blksize_mask_lo; 145 | int m_blksize_sh; 146 | std::vector m_tmp_blk; 147 | }; 148 | -------------------------------------------------------------------------------- /ApfsLib/ApfsNodeMapper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include "ApfsNodeMapper.h" 21 | 22 | ApfsNodeMapper::ApfsNodeMapper() 23 | { 24 | } 25 | 26 | 27 | ApfsNodeMapper::~ApfsNodeMapper() 28 | { 29 | } 30 | -------------------------------------------------------------------------------- /ApfsLib/ApfsNodeMapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "ApfsTypes.h" 23 | 24 | struct omap_res_t 25 | { 26 | oid_t oid; 27 | xid_t xid; 28 | uint32_t flags; 29 | uint32_t size; 30 | paddr_t paddr; 31 | }; 32 | 33 | class ApfsNodeMapper 34 | { 35 | public: 36 | ApfsNodeMapper(); 37 | virtual ~ApfsNodeMapper(); 38 | 39 | virtual bool Lookup(omap_res_t &res, oid_t oid, xid_t xid) = 0; 40 | }; 41 | -------------------------------------------------------------------------------- /ApfsLib/ApfsNodeMapperBTree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "ApfsNodeMapperBTree.h" 25 | #include "ApfsContainer.h" 26 | 27 | static int CompareOMapKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context) 28 | { 29 | (void)context; 30 | (void)skey_len; 31 | (void)ekey_len; 32 | 33 | assert(skey_len == sizeof(omap_key_t)); 34 | assert(ekey_len == sizeof(omap_val_t)); 35 | 36 | const omap_key_t *skey_map = reinterpret_cast(skey); 37 | const omap_key_t *ekey_map = reinterpret_cast(ekey); 38 | 39 | if (ekey_map->ok_oid < skey_map->ok_oid) 40 | return -1; 41 | if (ekey_map->ok_oid > skey_map->ok_oid) 42 | return 1; 43 | if (ekey_map->ok_xid < skey_map->ok_xid) 44 | return -1; 45 | if (ekey_map->ok_xid > skey_map->ok_xid) 46 | return 1; 47 | return 0; 48 | } 49 | 50 | ApfsNodeMapperBTree::ApfsNodeMapperBTree(ApfsContainer &container) : 51 | m_tree(container), 52 | m_container(container) 53 | { 54 | } 55 | 56 | ApfsNodeMapperBTree::~ApfsNodeMapperBTree() 57 | { 58 | } 59 | 60 | bool ApfsNodeMapperBTree::Init(oid_t omap_oid, xid_t xid) 61 | { 62 | std::vector blk; 63 | 64 | blk.resize(m_container.GetBlocksize()); 65 | 66 | if (!m_container.ReadAndVerifyHeaderBlock(blk.data(), omap_oid)) 67 | { 68 | std::cerr << "ERROR: Invalid omap block @ oid 0x" << std::hex << omap_oid << std::endl; 69 | return false; 70 | } 71 | 72 | memcpy(&m_omap, blk.data(), sizeof(omap_phys_t)); 73 | 74 | if ((m_omap.om_o.o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_BTREE) 75 | { 76 | std::cerr << "ERROR: Wrong omap type 0x" << std::hex << m_omap.om_o.o_type << std::endl; 77 | return false; 78 | } 79 | 80 | return m_tree.Init(m_omap.om_tree_oid, xid); 81 | } 82 | 83 | bool ApfsNodeMapperBTree::Lookup(omap_res_t &omr, oid_t oid, xid_t xid) 84 | { 85 | omap_key_t key; 86 | 87 | const omap_key_t *res_key = nullptr; 88 | const omap_val_t *res_val = nullptr; 89 | 90 | BTreeEntry res; 91 | 92 | key.ok_oid = oid; 93 | key.ok_xid = xid; 94 | 95 | // std::cout << std::hex << "Omap Lookup: oid = " << oid << ", xid = " << xid << " => "; 96 | 97 | if (!m_tree.Lookup(res, &key, sizeof(key), CompareOMapKey, this, false)) 98 | { 99 | // std::cout << "NOT FOUND" << std::endl; 100 | std::cerr << std::hex << "oid " << oid << " xid " << xid << " NOT FOUND!!!" << std::endl; 101 | return false; 102 | } 103 | 104 | assert(res.val_len == sizeof(omap_val_t)); 105 | 106 | res_key = reinterpret_cast(res.key); 107 | res_val = reinterpret_cast(res.val); 108 | 109 | #if 0 110 | if (g_debug & Dbg_Info) { 111 | std::cout << std::hex << "Omap Lookup: oid=" << oid << " xid=" << xid << ": "; 112 | std::cout << "oid=" << res_key->ok_oid << " xid=" << res_key->ok_xid << " => flags=" << res_val->ov_flags << " size=" << res_val->ov_size << " paddr=" << res_val->ov_paddr << std::endl; 113 | } 114 | #endif 115 | 116 | if (key.ok_oid != res_key->ok_oid) 117 | { 118 | // std::cout << "NOT FOUND" << std::endl; 119 | std::cerr << std::hex << "oid " << oid << " xid " << xid << " NOT FOUND!!!" << std::endl; 120 | omr.oid = oid; 121 | omr.xid = xid; 122 | omr.flags = 0; 123 | omr.size = 0; 124 | omr.paddr = 0; 125 | return false; 126 | } 127 | 128 | // std::cout << val->blockid << std::endl; 129 | 130 | omr.oid = res_key->ok_oid; 131 | omr.xid = res_key->ok_xid; 132 | omr.flags = res_val->ov_flags; 133 | omr.size = res_val->ov_size; 134 | omr.paddr = res_val->ov_paddr; 135 | 136 | return true; 137 | } 138 | -------------------------------------------------------------------------------- /ApfsLib/ApfsNodeMapperBTree.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "DiskStruct.h" 23 | 24 | #include "ApfsNodeMapper.h" 25 | #include "BTree.h" 26 | 27 | class BlockDumper; 28 | 29 | class ApfsNodeMapperBTree : public ApfsNodeMapper 30 | { 31 | public: 32 | ApfsNodeMapperBTree(ApfsContainer &container); 33 | virtual ~ApfsNodeMapperBTree(); 34 | 35 | bool Init(oid_t omap_oid, xid_t xid); 36 | bool Lookup(omap_res_t & omr, oid_t oid, xid_t xid) override; 37 | 38 | void dump(BlockDumper &bd) { m_tree.dump(bd); } 39 | 40 | private: 41 | omap_phys_t m_omap; 42 | BTree m_tree; 43 | 44 | ApfsContainer &m_container; 45 | }; 46 | -------------------------------------------------------------------------------- /ApfsLib/ApfsTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | // Commonly used FS types 25 | typedef unsigned char apfs_uuid_t[16]; 26 | typedef uint64_t paddr_t; // Apple: int64_t 27 | typedef uint64_t oid_t; 28 | typedef uint64_t xid_t; 29 | 30 | -------------------------------------------------------------------------------- /ApfsLib/ApfsVolume.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "Global.h" 25 | 26 | #include "ApfsContainer.h" 27 | #include "ApfsVolume.h" 28 | #include "BlockDumper.h" 29 | #include "Util.h" 30 | 31 | ApfsVolume::ApfsVolume(ApfsContainer &container) : 32 | m_container(container), 33 | m_omap(container), 34 | m_fs_tree(container, this), 35 | m_extentref_tree(container, this), 36 | m_snap_meta_tree(container, this), 37 | m_fext_tree(container, this) 38 | { 39 | m_apsb_paddr = 0; 40 | m_is_encrypted = false; 41 | } 42 | 43 | ApfsVolume::~ApfsVolume() 44 | { 45 | } 46 | 47 | bool ApfsVolume::Init(paddr_t apsb_paddr) 48 | { 49 | std::vector blk; 50 | 51 | m_apsb_paddr = apsb_paddr; 52 | 53 | blk.resize(m_container.GetBlocksize()); 54 | 55 | if (!ReadBlocks(blk.data(), apsb_paddr, 1, 0)) 56 | return false; 57 | 58 | if (!VerifyBlock(blk.data(), blk.size())) 59 | return false; 60 | 61 | memcpy(&m_sb, blk.data(), sizeof(m_sb)); 62 | 63 | if (m_sb.apfs_magic != APFS_MAGIC) 64 | return false; 65 | 66 | if (!m_omap.Init(m_sb.apfs_omap_oid, m_sb.apfs_o.o_xid)) { 67 | std::cerr << "WARNING: Volume omap tree init failed." << std::endl; 68 | return false; 69 | } 70 | 71 | if ((m_sb.apfs_fs_flags & 3) != APFS_FS_UNENCRYPTED && !m_container.IsUnencrypted()) 72 | { 73 | uint8_t vek[0x20]; 74 | std::string str; 75 | 76 | std::cout << "Volume " << m_sb.apfs_volname << " is encrypted." << std::endl; 77 | 78 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid)) 79 | { 80 | if (m_container.GetPasswordHint(str, m_sb.apfs_vol_uuid)) 81 | std::cout << "Hint: " << str << std::endl; 82 | 83 | std::cout << "Enter Password: "; 84 | GetPassword(str); 85 | 86 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid, str.c_str())) 87 | { 88 | std::cout << "Wrong password!" << std::endl; 89 | return false; 90 | } 91 | } 92 | 93 | m_aes.SetKey(vek, vek + 0x10); 94 | m_is_encrypted = true; 95 | } 96 | 97 | if (!m_fs_tree.Init(m_sb.apfs_root_tree_oid, m_sb.apfs_o.o_xid, &m_omap)) 98 | std::cerr << "ERROR: root tree init failed" << std::endl; 99 | 100 | if (!m_extentref_tree.Init(m_sb.apfs_extentref_tree_oid, m_sb.apfs_o.o_xid)) 101 | std::cerr << "WARNING: extentref tree init failed" << std::endl; 102 | 103 | if (!m_snap_meta_tree.Init(m_sb.apfs_snap_meta_tree_oid, m_sb.apfs_o.o_xid)) 104 | std::cerr << "WARNING: snap meta tree init failed" << std::endl; 105 | 106 | if (m_sb.apfs_incompatible_features & APFS_INCOMPAT_SEALED_VOLUME) 107 | { 108 | if (!m_fext_tree.Init(m_sb.apfs_fext_tree_oid, m_sb.apfs_o.o_xid)) 109 | std::cerr << "ERROR: fext tree init failed" << std::endl; 110 | } 111 | 112 | return true; 113 | } 114 | 115 | bool ApfsVolume::MountSnapshot(paddr_t apsb_paddr, xid_t snap_xid) 116 | { 117 | BTree snap_btree(m_container); 118 | BTreeEntry snap_entry; 119 | j_snap_metadata_key_t snap_key; 120 | const j_snap_metadata_val_t *snap_val = nullptr; 121 | std::vector blk; 122 | 123 | m_apsb_paddr = apsb_paddr; 124 | 125 | blk.resize(m_container.GetBlocksize()); 126 | 127 | if (!ReadBlocks(blk.data(), apsb_paddr, 1, 0)) 128 | return false; 129 | 130 | if (!VerifyBlock(blk.data(), blk.size())) 131 | return false; 132 | 133 | memcpy(&m_sb, blk.data(), sizeof(m_sb)); 134 | 135 | if (m_sb.apfs_magic != APFS_MAGIC) 136 | return false; 137 | 138 | if (m_sb.apfs_snap_meta_tree_oid == 0) 139 | return false; 140 | 141 | if (!snap_btree.Init(m_sb.apfs_snap_meta_tree_oid, m_sb.apfs_o.o_xid)) { 142 | std::cerr << "snap meta tree init failed" << std::endl; 143 | return false; 144 | } 145 | 146 | snap_key.hdr.obj_id_and_type = APFS_TYPE_ID(APFS_TYPE_SNAP_METADATA, snap_xid); 147 | if (!snap_btree.Lookup(snap_entry, &snap_key, sizeof(snap_key), CompareSnapMetaKey, nullptr, true)) { 148 | std::cerr << "snap xid not found" << std::endl; 149 | return false; 150 | } 151 | 152 | snap_val = reinterpret_cast(snap_entry.val); 153 | 154 | if (!m_omap.Init(m_sb.apfs_omap_oid, m_sb.apfs_o.o_xid)) { 155 | std::cerr << "WARNING: Volume omap tree init failed." << std::endl; 156 | return false; 157 | } 158 | 159 | if (!ReadBlocks(blk.data(), snap_val->sblock_oid, 1, 0)) { 160 | std::cerr << "failed to read snapshot superblock" << std::endl; 161 | return false; 162 | } 163 | if (!VerifyBlock(blk.data(), blk.size())) { 164 | std::cerr << "snap superblock checksum error" << std::endl; 165 | return false; 166 | } 167 | 168 | memcpy(&m_sb, blk.data(), sizeof(m_sb)); 169 | 170 | if (m_sb.apfs_magic != APFS_MAGIC) 171 | return false; 172 | 173 | if ((m_sb.apfs_fs_flags & 3) != APFS_FS_UNENCRYPTED) 174 | { 175 | uint8_t vek[0x20]; 176 | std::string str; 177 | 178 | std::cout << "Volume " << m_sb.apfs_volname << " is encrypted." << std::endl; 179 | 180 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid)) 181 | { 182 | if (m_container.GetPasswordHint(str, m_sb.apfs_vol_uuid)) 183 | std::cout << "Hint: " << str << std::endl; 184 | 185 | std::cout << "Enter Password: "; 186 | GetPassword(str); 187 | 188 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid, str.c_str())) 189 | { 190 | std::cout << "Wrong password!" << std::endl; 191 | return false; 192 | } 193 | } 194 | 195 | m_aes.SetKey(vek, vek + 0x10); 196 | m_is_encrypted = true; 197 | } 198 | 199 | if (!m_fs_tree.Init(m_sb.apfs_root_tree_oid, m_sb.apfs_o.o_xid, &m_omap)) 200 | std::cerr << "WARNING: root tree init failed" << std::endl; 201 | 202 | if (!m_extentref_tree.Init(m_sb.apfs_extentref_tree_oid, m_sb.apfs_o.o_xid)) 203 | std::cerr << "WARNING: extentref tree init failed" << std::endl; 204 | 205 | if (!m_snap_meta_tree.Init(m_sb.apfs_snap_meta_tree_oid, m_sb.apfs_o.o_xid)) 206 | std::cerr << "WARNING: snap meta tree init failed" << std::endl; 207 | 208 | if (m_sb.apfs_incompatible_features & APFS_INCOMPAT_SEALED_VOLUME) 209 | { 210 | if (!m_fext_tree.Init(m_sb.apfs_fext_tree_oid, m_sb.apfs_o.o_xid)) 211 | std::cerr << "ERROR: fext tree init failed" << std::endl; 212 | } 213 | 214 | return true; 215 | } 216 | 217 | void ApfsVolume::dump(BlockDumper& bd) 218 | { 219 | std::vector blk; 220 | omap_res_t om; 221 | oid_t omap_snapshot_tree_oid = 0; 222 | 223 | blk.resize(m_container.GetBlocksize()); 224 | 225 | if (!ReadBlocks(blk.data(), m_apsb_paddr, 1, 0)) 226 | return; 227 | 228 | if (!VerifyBlock(blk.data(), blk.size())) 229 | return; 230 | 231 | bd.SetTextFlags(m_sb.apfs_incompatible_features & 0xFF); 232 | 233 | bd.DumpNode(blk.data(), m_apsb_paddr); 234 | 235 | ReadBlocks(blk.data(), m_sb.apfs_omap_oid, 1, 0); 236 | bd.DumpNode(blk.data(), m_sb.apfs_omap_oid); 237 | 238 | { 239 | const omap_phys_t *om = reinterpret_cast(blk.data()); 240 | omap_snapshot_tree_oid = om->om_snapshot_tree_oid; 241 | 242 | ReadBlocks(blk.data(), omap_snapshot_tree_oid, 1, 0); 243 | bd.DumpNode(blk.data(), omap_snapshot_tree_oid); 244 | } 245 | 246 | if (m_sb.apfs_er_state_oid) { 247 | ReadBlocks(blk.data(), m_sb.apfs_er_state_oid, 1, 0); 248 | bd.DumpNode(blk.data(), m_sb.apfs_er_state_oid); 249 | } 250 | 251 | m_omap.dump(bd); 252 | m_fs_tree.dump(bd); 253 | // m_extentref_tree.dump(bd); 254 | m_snap_meta_tree.dump(bd); 255 | 256 | if (m_sb.apfs_integrity_meta_oid != 0) { 257 | if (m_omap.Lookup(om, m_sb.apfs_integrity_meta_oid, m_sb.apfs_o.o_xid)) 258 | { 259 | ReadBlocks(blk.data(), om.paddr, 1, 0); 260 | bd.DumpNode(blk.data(), om.paddr); 261 | } 262 | } 263 | 264 | if (m_sb.apfs_snap_meta_ext_oid != 0) { 265 | if (m_omap.Lookup(om, m_sb.apfs_snap_meta_ext_oid, m_sb.apfs_o.o_xid)) 266 | { 267 | ReadBlocks(blk.data(), om.paddr, 1, 0); 268 | bd.DumpNode(blk.data(), om.paddr); 269 | } 270 | } 271 | 272 | #if 1 273 | if (m_sb.apfs_fext_tree_oid != 0) { 274 | BTree fxtree(m_container, this); 275 | fxtree.Init(m_sb.apfs_fext_tree_oid, m_sb.apfs_o.o_xid); 276 | fxtree.dump(bd); 277 | } 278 | #endif 279 | 280 | BTreeEntry bte; 281 | #if 0 282 | BTreeIterator it; 283 | const j_snap_metadata_key_t *sm_key; 284 | const j_snap_metadata_val_t *sm_val; 285 | 286 | if (m_snap_meta_tree.GetIteratorBegin(it)) { 287 | for (;;) { 288 | if (!it.GetEntry(bte)) break; 289 | 290 | sm_key = reinterpret_cast(bte.key); 291 | sm_val = reinterpret_cast(bte.val); 292 | 293 | if ((sm_key->hdr.obj_id_and_type >> OBJ_TYPE_SHIFT) != APFS_TYPE_SNAP_METADATA) break; 294 | 295 | ReadBlocks(blk.data(), sm_val->sblock_oid, 1, 0); 296 | bd.DumpNode(blk.data(), sm_val->sblock_oid); 297 | 298 | apfs_superblock_t apsb; 299 | memcpy(&apsb, blk.data(), sizeof(apfs_superblock_t)); 300 | 301 | if (apsb.apfs_omap_oid) { 302 | ReadBlocks(blk.data(), apsb.apfs_omap_oid, 1, 0); 303 | bd.DumpNode(blk.data(), apsb.apfs_omap_oid); 304 | } 305 | 306 | if (!it.next()) break; 307 | } 308 | } 309 | 310 | bte.clear(); 311 | #endif 312 | 313 | #if 0 314 | { 315 | BTree omap_tree(m_container, this); 316 | BTreeIterator oit; 317 | omap_phys_t om; 318 | 319 | ReadBlocks(blk.data(), m_sb.apfs_omap_oid, 1, 0); 320 | memcpy(&om, blk.data(), sizeof(om)); 321 | 322 | omap_tree.Init(om.om_tree_oid, om.om_o.o_xid); 323 | if (omap_tree.GetIteratorBegin(oit)) { 324 | const omap_key_t *ok; 325 | const omap_val_t *ov; 326 | for (;;) { 327 | if (!oit.GetEntry(bte)) break; 328 | ok = reinterpret_cast(bte.key); 329 | ov = reinterpret_cast(bte.val); 330 | 331 | bd.st() << "omap: " << ok->ok_oid << " " << ok->ok_xid << " => " << ov->ov_flags << " " << ov->ov_size << " " << ov->ov_paddr << std::endl; 332 | if (ov->ov_flags & OMAP_VAL_NOHEADER) { 333 | ReadBlocks(blk.data(), ov->ov_paddr, 1, 0); 334 | bd.DumpNode(blk.data(), ov->ov_paddr); 335 | } 336 | 337 | if (!oit.next()) break; 338 | } 339 | } else { 340 | bd.st() << "Failed getting omap iterator" << std::endl; 341 | } 342 | } 343 | #endif 344 | } 345 | 346 | bool ApfsVolume::ReadBlocks(uint8_t * data, paddr_t paddr, uint64_t blkcnt, uint64_t xts_tweak) 347 | { 348 | constexpr int encryption_block_size = 0x200; 349 | 350 | if (!m_container.ReadBlocks(data, paddr, blkcnt)) 351 | return false; 352 | 353 | if (!m_is_encrypted || (xts_tweak == 0)) 354 | return true; 355 | 356 | uint64_t cs_factor = m_container.GetBlocksize() / encryption_block_size; 357 | uint64_t uno = xts_tweak * cs_factor; 358 | size_t size = blkcnt * m_container.GetBlocksize(); 359 | size_t k; 360 | 361 | for (k = 0; k < size; k += encryption_block_size) 362 | { 363 | m_aes.Decrypt(data + k, data + k, encryption_block_size, uno); 364 | uno++; 365 | } 366 | 367 | return true; 368 | } 369 | 370 | int ApfsVolume::CompareSnapMetaKey(const void* skey, size_t skey_len, const void* ekey, size_t ekey_len, void* context) 371 | { 372 | const j_key_t *ks = reinterpret_cast(skey); 373 | const j_key_t *ke = reinterpret_cast(ekey); 374 | const j_snap_name_key_t *sks; 375 | const j_snap_name_key_t *ske; 376 | 377 | if (ke->obj_id_and_type < ks->obj_id_and_type) 378 | return -1; 379 | if (ke->obj_id_and_type > ks->obj_id_and_type) 380 | return 1; 381 | 382 | switch (ks->obj_id_and_type >> OBJ_TYPE_SHIFT) 383 | { 384 | case APFS_TYPE_SNAP_METADATA: 385 | break; 386 | case APFS_TYPE_SNAP_NAME: 387 | sks = reinterpret_cast(skey); 388 | ske = reinterpret_cast(ekey); 389 | return apfs_strncmp(ske->name, ske->name_len, sks->name, sks->name_len); 390 | break; 391 | } 392 | 393 | return 0; 394 | } 395 | -------------------------------------------------------------------------------- /ApfsLib/ApfsVolume.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | #include "DiskStruct.h" 25 | #include "ApfsNodeMapperBTree.h" 26 | #include "BTree.h" 27 | #include 28 | 29 | class ApfsContainer; 30 | class BlockDumper; 31 | 32 | class ApfsVolume 33 | { 34 | public: 35 | ApfsVolume(ApfsContainer &container); 36 | ~ApfsVolume(); 37 | 38 | bool Init(paddr_t apsb_paddr); 39 | bool MountSnapshot(paddr_t apsb_paddr, xid_t snap_xid); 40 | 41 | const char *name() const { return reinterpret_cast(m_sb.apfs_volname); } 42 | 43 | void dump(BlockDumper &bd); 44 | 45 | BTree &fstree() { return m_fs_tree; } 46 | BTree &fexttree() { return m_fext_tree; } 47 | uint32_t getTextFormat() const { return m_sb.apfs_incompatible_features & 0x9; } 48 | 49 | ApfsContainer &getContainer() const { return m_container; } 50 | 51 | bool ReadBlocks(uint8_t *data, paddr_t paddr, uint64_t blkcnt, uint64_t xts_tweak); 52 | bool isSealed() const { return (m_sb.apfs_incompatible_features & APFS_INCOMPAT_SEALED_VOLUME) != 0; } 53 | 54 | private: 55 | static int CompareSnapMetaKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context); 56 | 57 | ApfsContainer &m_container; 58 | 59 | apfs_superblock_t m_sb; 60 | 61 | ApfsNodeMapperBTree m_omap; 62 | BTree m_fs_tree; 63 | BTree m_extentref_tree; 64 | BTree m_snap_meta_tree; 65 | BTree m_fext_tree; 66 | 67 | paddr_t m_apsb_paddr; 68 | 69 | bool m_is_encrypted; 70 | AesXts m_aes; 71 | }; 72 | -------------------------------------------------------------------------------- /ApfsLib/BTree.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "Global.h" 28 | #include "DiskStruct.h" 29 | 30 | #include "ApfsNodeMapper.h" 31 | 32 | class BTree; 33 | class BTreeNode; 34 | class BTreeIterator; 35 | class BlockDumper; 36 | 37 | class ApfsContainer; 38 | class ApfsVolume; 39 | 40 | // This enables a rudimentary disk cache ... 41 | #define BTREE_USE_MAP 42 | // TODO: Think about a better solution. 43 | // 8192 will take max. 32 MB of RAM. Higher may be faster, but use more RAM. 44 | #define BTREE_MAP_MAX_NODES 8192 45 | 46 | // ekey < skey: -1, ekey > skey: 1, ekey == skey: 0 47 | typedef int(*BTCompareFunc)(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context); 48 | 49 | int CompareStdKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context); 50 | 51 | class BTreeEntry 52 | { 53 | friend class BTree; 54 | public: 55 | BTreeEntry(); 56 | ~BTreeEntry(); 57 | 58 | BTreeEntry(const BTreeEntry &o) = delete; 59 | BTreeEntry &operator=(const BTreeEntry &o) = delete; 60 | 61 | void clear(); 62 | 63 | const void *key; 64 | const void *val; 65 | size_t key_len; 66 | size_t val_len; 67 | 68 | private: 69 | std::shared_ptr m_node; 70 | }; 71 | 72 | class BTreeNode 73 | { 74 | protected: 75 | BTreeNode(BTree &tree, const uint8_t *block, size_t blocksize, paddr_t paddr, const std::shared_ptr &parent, uint32_t parent_index); 76 | 77 | public: 78 | static std::shared_ptr CreateNode(BTree &tree, const uint8_t *block, size_t blocksize, paddr_t paddr, const std::shared_ptr &parent, uint32_t parent_index); 79 | 80 | virtual ~BTreeNode(); 81 | 82 | uint64_t nodeid() const { return m_btn->btn_o.o_oid; } 83 | uint32_t entries_cnt() const { return m_btn->btn_nkeys; } 84 | uint16_t level() const { return m_btn->btn_level; } 85 | uint16_t flags() const { return m_btn->btn_flags; } 86 | paddr_t paddr() const { return m_paddr; } 87 | 88 | const std::shared_ptr &parent() const { return m_parent; } 89 | uint16_t parent_index() const { return m_parent_index; } 90 | 91 | virtual bool GetEntry(BTreeEntry &result, uint32_t index) const = 0; 92 | // virtual uint32_t Find(const void *key, size_t key_size, BTCompareFunc func) const = 0; 93 | 94 | const std::vector &block() const { return m_block; } 95 | 96 | protected: 97 | std::vector m_block; 98 | BTree &m_tree; 99 | 100 | uint16_t m_keys_start; // Up 101 | uint16_t m_vals_start; // Dn 102 | 103 | const uint32_t m_parent_index; 104 | const std::shared_ptr m_parent; 105 | 106 | const paddr_t m_paddr; 107 | 108 | const btree_node_phys_t *m_btn; 109 | }; 110 | 111 | class BTreeNodeFix : public BTreeNode 112 | { 113 | public: 114 | BTreeNodeFix(BTree &tree, const uint8_t *block, size_t blocksize, paddr_t paddr, const std::shared_ptr &parent, uint32_t parent_index); 115 | 116 | bool GetEntry(BTreeEntry &result, uint32_t index) const override; 117 | // uint32_t Find(const void *key, size_t key_size, BTCompareFunc func) const override; 118 | 119 | private: 120 | const kvoff_t *m_entries; 121 | }; 122 | 123 | class BTreeNodeVar : public BTreeNode 124 | { 125 | public: 126 | BTreeNodeVar(BTree &tree, const uint8_t *block, size_t blocksize, paddr_t paddr, const std::shared_ptr &parent, uint32_t parent_index); 127 | 128 | bool GetEntry(BTreeEntry &result, uint32_t index) const override; 129 | // uint32_t Find(const void *key, size_t key_size, BTCompareFunc func) const override; 130 | 131 | private: 132 | const kvloc_t *m_entries; 133 | }; 134 | 135 | class BTree 136 | { 137 | enum class FindMode 138 | { 139 | EQ, 140 | LE, 141 | LT, 142 | GE, 143 | GT 144 | }; 145 | 146 | friend class BTreeIterator; 147 | public: 148 | BTree(ApfsContainer &container, ApfsVolume *vol = nullptr); 149 | ~BTree(); 150 | 151 | bool Init(oid_t oid_root, xid_t xid, ApfsNodeMapper *omap = nullptr); 152 | 153 | bool Lookup(BTreeEntry &result, const void *key, size_t key_size, BTCompareFunc func, void *context, bool exact); 154 | bool GetIterator(BTreeIterator &it, const void *key, size_t key_size, BTCompareFunc func, void *context); 155 | bool GetIteratorBegin(BTreeIterator &it); 156 | 157 | uint16_t GetKeyLen() const { return m_treeinfo.bt_fixed.bt_key_size; } 158 | uint16_t GetValLen() const { return m_treeinfo.bt_fixed.bt_val_size; } 159 | 160 | void dump(BlockDumper &out); 161 | 162 | void EnableDebugOutput() { m_debug = true; } 163 | 164 | private: 165 | void DumpTreeInternal(BlockDumper &out, const std::shared_ptr &node); 166 | uint32_t Find(const std::shared_ptr &node, const void *key, size_t key_size, BTCompareFunc func, void *context); 167 | int FindBin(const std::shared_ptr &node, const void *key, size_t key_size, BTCompareFunc func, void *context, FindMode mode); 168 | 169 | std::shared_ptr GetNode(oid_t oid, const std::shared_ptr &parent, uint32_t parent_index); 170 | 171 | ApfsContainer &m_container; 172 | ApfsVolume *m_volume; 173 | 174 | std::shared_ptr m_root_node; 175 | ApfsNodeMapper *m_omap; 176 | 177 | btree_info_t m_treeinfo; 178 | 179 | oid_t m_oid; 180 | xid_t m_xid; 181 | bool m_debug; 182 | 183 | #ifdef BTREE_USE_MAP 184 | std::map> m_nodes; 185 | std::mutex m_mutex; 186 | #endif 187 | }; 188 | 189 | class BTreeIterator 190 | { 191 | public: 192 | BTreeIterator(); 193 | BTreeIterator(BTree *tree, const std::shared_ptr &node, uint32_t index); 194 | ~BTreeIterator(); 195 | 196 | bool next(); 197 | void reset(); 198 | 199 | bool GetEntry(BTreeEntry &res) const; 200 | 201 | void Setup(BTree *tree, const std::shared_ptr &node, uint32_t index); 202 | 203 | private: 204 | BTree *m_tree; 205 | std::shared_ptr m_node; 206 | uint32_t m_index; 207 | 208 | std::shared_ptr next_node(); 209 | }; 210 | -------------------------------------------------------------------------------- /ApfsLib/BlockDumper.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "Global.h" 27 | #include "DiskStruct.h" 28 | 29 | struct FlagDesc 30 | { 31 | uint64_t flag; 32 | const char *desc; 33 | }; 34 | 35 | class BlockDumper 36 | { 37 | public: 38 | BlockDumper(std::ostream &os, size_t blocksize); 39 | ~BlockDumper(); 40 | 41 | void SetTextFlags(uint32_t flags) { m_text_flags = flags; } 42 | size_t GetBlockSize() const { return m_blocksize; } 43 | void SetBlockSize(size_t size) { m_blocksize = size; } 44 | 45 | void DumpNode(const uint8_t *block, uint64_t blk_nr); 46 | 47 | std::ostream &st() { return m_os; } 48 | 49 | private: 50 | typedef void(BlockDumper::*DumpFunc)(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 51 | 52 | void DumpNodeHeader(const obj_phys_t *blk, uint64_t blk_nr); 53 | void DumpBTNode(DumpFunc func, uint16_t key_size = 0, uint16_t value_size = 0); 54 | void DumpBTHeader(bool dump_offsets = false); 55 | void DumpBTreeInfo(); 56 | // void DumpTableHeader(const APFS_TableHeader &tbl); 57 | 58 | void DumpBlk_APSB(); 59 | void DumpBlk_CAB(); 60 | void DumpBlk_CIB(); 61 | void DumpBlk_OM(); 62 | void DumpBlk_CPM(); 63 | void DumpBlk_NXSB(); 64 | void DumpBlk_SM(); 65 | void DumpBlk_NR(); 66 | void DumpBlk_NRL(); 67 | void DumpBlk_JSDR(); 68 | void DumpBlk_ER(); 69 | 70 | void DumpBlk_WBC(); 71 | void DumpBlk_WBCL(); 72 | 73 | void DumpBlk_SnapMetaExt(); 74 | void DumpBlk_IntegrityMeta(); 75 | 76 | void DumpBTNode_0(); 77 | 78 | void DumpBTEntry_APFS_Root(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 79 | void DumpBTEntry_OMap(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 80 | void DumpBTEntry_APFS_ExtentRef(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 81 | void DumpBTEntry_APFS_SnapMeta(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 82 | void DumpBTEntry_OMap_Snapshot(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 83 | void DumpBTEntry_FreeList(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 84 | void DumpBTEntry_GBitmap(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 85 | void DumpBTEntry_FusionMT(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 86 | void DumpBTEntry_FExtTree(const void* key_ptr, size_t key_len, const void* val_ptr, size_t val_len, bool index); 87 | 88 | void DumpBTEntry_Unk(const void *key_ptr, size_t key_len, const void *val_ptr, size_t val_len, bool index); 89 | 90 | void DumpBTIndex(const void* val_ptr, uint16_t val_len); 91 | 92 | void Dump_XF(const uint8_t *xf_data, size_t xf_size, bool drec); 93 | 94 | void DumpBlockHex(); 95 | void DumpHex(const uint8_t *data, size_t size, size_t line_size = 16); 96 | 97 | static std::string flagstr(uint64_t flag, const FlagDesc *desc); 98 | static std::string enumstr(uint64_t flag, const FlagDesc *desc); 99 | 100 | public: 101 | static const char * GetNodeType(uint32_t type, uint32_t subtype); 102 | 103 | private: 104 | static std::string tstamp(uint64_t apfs_time); 105 | 106 | void dumpm(const char *name, const void *base, const uint8_t &v, bool lf = true); 107 | void dumpm(const char *name, const void *base, const le_uint16_t &v, bool lf = true); 108 | void dumpm(const char *name, const void *base, const le_uint32_t &v, bool lf = true); 109 | void dumpm(const char *name, const void *base, const le_uint64_t &v, bool lf = true); 110 | void dumpm(const char *name, const void *base, const apfs_uuid_t &uuid); 111 | 112 | uint32_t m_text_flags; // 00 - Alt, 01 - insensitive, 08 - sensitive 113 | 114 | std::ostream &m_os; 115 | const uint8_t *m_block; 116 | size_t m_blocksize; 117 | }; 118 | -------------------------------------------------------------------------------- /ApfsLib/CheckPointMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ApfsContainer.h" 4 | #include "DiskStruct.h" 5 | #include "BlockDumper.h" 6 | #include "CheckPointMap.h" 7 | 8 | CheckPointMap::CheckPointMap(ApfsContainer& container) : m_container(container) 9 | { 10 | m_cpm_oid = 0; 11 | m_blksize = 0; 12 | } 13 | 14 | CheckPointMap::~CheckPointMap() 15 | { 16 | } 17 | 18 | bool CheckPointMap::Init(oid_t root_oid, uint32_t blk_count) 19 | { 20 | uint32_t n; 21 | const checkpoint_map_phys_t *cpm; 22 | 23 | m_blksize = m_container.GetBlocksize(); 24 | m_cpm_data.resize(m_blksize * blk_count); 25 | 26 | for (n = 0; n < blk_count; n++) 27 | { 28 | if (!m_container.ReadAndVerifyHeaderBlock(m_cpm_data.data() + n * m_blksize, root_oid + n)) 29 | { 30 | m_cpm_data.clear(); 31 | return false; 32 | } 33 | 34 | cpm = reinterpret_cast(m_cpm_data.data() + m_blksize * n); 35 | 36 | assert((cpm->cpm_o.o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_CHECKPOINT_MAP); 37 | 38 | if ((cpm->cpm_o.o_type & OBJECT_TYPE_MASK) != OBJECT_TYPE_CHECKPOINT_MAP) 39 | { 40 | m_cpm_data.clear(); 41 | return false; 42 | } 43 | } 44 | 45 | m_cpm_oid = root_oid; 46 | 47 | return true; 48 | } 49 | 50 | bool CheckPointMap::Lookup(omap_res_t & res, oid_t oid, xid_t xid) 51 | { 52 | uint32_t blk_offs; 53 | uint32_t k; 54 | uint32_t cnt; 55 | const checkpoint_map_phys_t * cpm; 56 | 57 | (void)xid; 58 | 59 | for (blk_offs = 0; blk_offs < m_cpm_data.size(); blk_offs += m_blksize) 60 | { 61 | cpm = reinterpret_cast(m_cpm_data.data() + blk_offs); 62 | 63 | cnt = cpm->cpm_count; 64 | 65 | for (k = 0; k < cnt; k++) 66 | { 67 | if (oid == cpm->cpm_map[k].cpm_oid) 68 | { 69 | res.oid = cpm->cpm_map[k].cpm_oid; 70 | res.xid = cpm->cpm_o.o_xid; 71 | res.flags = 0; 72 | res.size = cpm->cpm_map[k].cpm_size; 73 | res.paddr = cpm->cpm_map[k].cpm_paddr; 74 | 75 | return true; 76 | } 77 | } 78 | } 79 | 80 | return false; 81 | } 82 | 83 | void CheckPointMap::dump(BlockDumper& bd) 84 | { 85 | bd.DumpNode(m_cpm_data.data(), m_cpm_oid); 86 | } 87 | -------------------------------------------------------------------------------- /ApfsLib/CheckPointMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "DiskStruct.h" 6 | #include "ApfsNodeMapper.h" 7 | 8 | class ApfsContainer; 9 | class BlockDumper; 10 | 11 | class CheckPointMap : public ApfsNodeMapper 12 | { 13 | public: 14 | CheckPointMap(ApfsContainer &container); 15 | virtual ~CheckPointMap(); 16 | 17 | bool Init(oid_t root_oid, uint32_t blk_count); 18 | bool Lookup(omap_res_t & res, oid_t oid, xid_t xid) override; 19 | 20 | void dump(BlockDumper &bd); 21 | 22 | private: 23 | ApfsContainer &m_container; 24 | std::vector m_cpm_data; 25 | oid_t m_cpm_oid; 26 | uint32_t m_blksize; 27 | }; 28 | -------------------------------------------------------------------------------- /ApfsLib/Crc32.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include "Crc32.h" 21 | 22 | Crc32::Crc32(bool reflect, uint32_t poly) 23 | { 24 | unsigned int i; 25 | uint32_t r; 26 | unsigned int b; 27 | 28 | m_reflect = reflect; 29 | m_crc = 0; 30 | 31 | if (reflect) { 32 | poly = ((poly << 16) & 0xFFFF0000) | ((poly >> 16) & 0x0000FFFF); 33 | poly = ((poly << 8) & 0xFF00FF00) | ((poly >> 8) & 0x00FF00FF); 34 | poly = ((poly << 4) & 0xF0F0F0F0) | ((poly >> 4) & 0x0F0F0F0F); 35 | poly = ((poly << 2) & 0xCCCCCCCC) | ((poly >> 2) & 0x33333333); 36 | poly = ((poly << 1) & 0xAAAAAAAA) | ((poly >> 1) & 0x55555555); 37 | 38 | for (i = 0; i < 256; i++) { 39 | r = i; 40 | for (b = 0; b < 8; b++) { 41 | if (r & 1) 42 | r = (r >> 1) ^ poly; 43 | else 44 | r = (r >> 1); 45 | } 46 | m_table[i] = r; 47 | } 48 | } 49 | else { 50 | for (i = 0; i < 256; i++) { 51 | r = (i << 24); 52 | for (b = 0; b < 8; b++) { 53 | if (r & 0x80000000) 54 | r = (r << 1) ^ poly; 55 | else 56 | r = (r << 1); 57 | } 58 | m_table[i] = r; 59 | } 60 | } 61 | } 62 | 63 | Crc32::~Crc32() 64 | { 65 | 66 | } 67 | 68 | void Crc32::Calc(const uint8_t *data, size_t size) 69 | { 70 | size_t i; 71 | 72 | for (i = 0; i < size; i++) { 73 | if (m_reflect) { 74 | CalcLE(data[i]); 75 | } 76 | else { 77 | CalcBE(data[i]); 78 | } 79 | } 80 | } 81 | 82 | void Crc32::CalcLE(uint8_t b) 83 | { 84 | m_crc = m_table[b ^ (m_crc & 0xFF)] ^ (m_crc >> 8); 85 | } 86 | 87 | void Crc32::CalcBE(uint8_t b) 88 | { 89 | m_crc = m_table[b ^ ((m_crc >> 24) & 0xFF)] ^ (m_crc << 8); 90 | } 91 | 92 | uint32_t Crc32::GetDataCRC(const uint8_t *data, size_t size, uint32_t initialXor, uint32_t finalXor) 93 | { 94 | m_crc = initialXor; 95 | Calc(data, size); 96 | return m_crc ^ finalXor; 97 | } 98 | -------------------------------------------------------------------------------- /ApfsLib/Crc32.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include "Global.h" 25 | 26 | class Crc32 27 | { 28 | public: 29 | Crc32(bool reflect, uint32_t poly = 0x04C11DB7); 30 | ~Crc32(); 31 | 32 | void SetCRC(uint32_t crc) { m_crc = crc; } 33 | uint32_t GetCRC() const { return m_crc; } 34 | void Calc(const uint8_t *data, size_t size); 35 | 36 | uint32_t GetDataCRC(const uint8_t *data, size_t size, uint32_t initialXor, uint32_t finalXor); 37 | 38 | private: 39 | void CalcLE(uint8_t b); 40 | void CalcBE(uint8_t b); 41 | 42 | uint32_t m_table[256]; 43 | uint32_t m_crc; 44 | 45 | bool m_reflect; 46 | }; 47 | 48 | -------------------------------------------------------------------------------- /ApfsLib/Decmpfs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "Decmpfs.h" 26 | #include "Endian.h" 27 | 28 | #include "Global.h" 29 | #include "Util.h" 30 | 31 | 32 | struct RsrcForkHeader 33 | { 34 | be_uint32_t data_offset; 35 | be_uint32_t mgmt_offset; 36 | be_uint32_t data_size; 37 | be_uint32_t mgmt_size; 38 | }; 39 | 40 | struct CmpfRsrcEntry 41 | { 42 | // 1 64K-Block 43 | le_uint32_t off; 44 | le_uint32_t size; 45 | }; 46 | 47 | struct CmpfRsrc 48 | { 49 | le_uint32_t entries; 50 | CmpfRsrcEntry entry[32]; 51 | }; 52 | 53 | bool IsDecompAlgoSupported(uint16_t algo) 54 | { 55 | switch (algo) 56 | { 57 | case 3: 58 | case 4: 59 | case 7: 60 | case 8: 61 | case 9: 62 | case 10: 63 | case 11: 64 | case 12: 65 | case 13: 66 | case 14: 67 | return true; 68 | default: 69 | return false; 70 | } 71 | } 72 | 73 | bool IsDecompAlgoInRsrc(uint16_t algo) 74 | { 75 | switch (algo) 76 | { 77 | case 4: 78 | case 8: 79 | case 10: 80 | case 12: 81 | case 14: 82 | return true; 83 | default: 84 | return false; 85 | } 86 | } 87 | 88 | bool DecompressFile(ApfsDir &dir, uint64_t ino, std::vector &decompressed, const std::vector &compressed) 89 | { 90 | if (compressed.size() < sizeof(CompressionHeader)) 91 | return false; 92 | 93 | const CompressionHeader *hdr = reinterpret_cast(compressed.data()); 94 | const uint8_t *cdata = compressed.data() + sizeof(CompressionHeader); 95 | size_t csize = compressed.size() - sizeof(CompressionHeader); 96 | size_t decoded_bytes = 0; 97 | 98 | #if 1 // Disable to get compressed data 99 | if (g_debug & Dbg_Cmpfs) 100 | { 101 | std::cout << "DecompressFile " << compressed.size() << " => " << hdr->size << ", algo = " << hdr->algo; 102 | 103 | switch (hdr->algo) 104 | { 105 | case 3: std::cout << " (Zlib, Attr)"; break; 106 | case 4: std::cout << " (Zlib, Rsrc)"; break; 107 | case 7: std::cout << " (LZVN, Attr)"; break; 108 | case 8: std::cout << " (LZVN, Rsrc)"; break; 109 | case 9: std::cout << " (Uncompressed, Attr)"; break; 110 | case 10: std::cout << " (Uncompressed, Rsrc)"; break; 111 | case 11: std::cout << " (LZFSE, Attr)"; break; 112 | case 12: std::cout << " (LZFSE, Rsrc)"; break; 113 | case 13: std::cout << " (LZBITMAP, Attr)"; break; 114 | case 14: std::cout << " (LZBITMAP, Rsrc)"; break; 115 | default: std::cout << " (Unknown)"; break; 116 | } 117 | 118 | std::cout << std::endl; 119 | } 120 | 121 | if (!IsDecompAlgoSupported(hdr->algo)) 122 | { 123 | if (g_debug & Dbg_Errors) { 124 | std::cout << "Unsupported decompression algorithm." << std::endl; 125 | DumpHex(std::cout, compressed.data(), compressed.size()); 126 | } 127 | return false; 128 | } 129 | 130 | if (IsDecompAlgoInRsrc(hdr->algo)) 131 | { 132 | std::vector rsrc; 133 | size_t k; 134 | 135 | bool rc = dir.GetAttribute(rsrc, ino, "com.apple.ResourceFork"); 136 | 137 | if (!rc) 138 | { 139 | if (g_debug & Dbg_Errors) 140 | std::cout << "Decmpfs: Could not find resource fork " << ino << std::endl; 141 | decompressed.clear(); 142 | return false; 143 | } 144 | 145 | if (hdr->algo == 4) // Zlib, rsrc 146 | { 147 | RsrcForkHeader rsrc_hdr; 148 | 149 | memcpy(&rsrc_hdr, rsrc.data(), sizeof(rsrc_hdr)); 150 | 151 | if (rsrc_hdr.data_offset > rsrc.size()) 152 | { 153 | if (g_debug & Dbg_Errors) 154 | std::cout << "Decmpfs: Invalid data offset in rsrc header." << std::endl; 155 | return false; 156 | } 157 | 158 | const uint8_t *cmpf_rsrc_base = rsrc.data() + rsrc_hdr.data_offset + sizeof(uint32_t); 159 | const CmpfRsrc *cmpf_rsrc = reinterpret_cast(cmpf_rsrc_base); 160 | 161 | decompressed.resize((hdr->size + 0xFFFF) & 0xFFFF0000); 162 | 163 | for (k = 0; k < cmpf_rsrc->entries; k++) 164 | { 165 | size_t src_offset = cmpf_rsrc->entry[k].off; 166 | const uint8_t *src = cmpf_rsrc_base + src_offset; 167 | size_t src_len = cmpf_rsrc->entry[k].size; 168 | uint8_t *dst = decompressed.data() + 0x10000 * k; 169 | size_t expected_len = hdr->size - (0x10000 * k); 170 | if (expected_len > 0x10000) 171 | expected_len = 0x10000; 172 | 173 | if (src_len > 0x10001) 174 | { 175 | if (g_debug & Dbg_Errors) 176 | std::cout << "Decmpfs: In rsrc, src_len too big (" << src_len << ")" << std::endl; 177 | return false; 178 | } 179 | 180 | if (src[0] == 0x78) 181 | { 182 | decoded_bytes = DecompressZLib(dst, 0x10000, src, src_len); 183 | } 184 | else if ((src[0] & 0x0F) == 0x0F) 185 | { 186 | memcpy(dst, src + 1, src_len - 1); 187 | decoded_bytes = src_len - 1; 188 | } 189 | else 190 | { 191 | if (g_debug & Dbg_Errors) 192 | std::cout << "Decmpfs: Something wrong with zlib data." << std::endl; 193 | decompressed.clear(); 194 | return false; 195 | } 196 | 197 | if (expected_len != decoded_bytes) 198 | { 199 | if (g_debug & Dbg_Errors) 200 | std::cout << "Decmpfs: Expected len != decompressed len: " << expected_len << " != " << decoded_bytes << std::endl; 201 | return false; 202 | } 203 | } 204 | } 205 | else 206 | { 207 | const uint32_t *off_list = reinterpret_cast(rsrc.data()); 208 | 209 | decompressed.resize((hdr->size + 0xFFFF) & 0xFFFF0000); 210 | 211 | for (k = 0; (k << 16) < decompressed.size(); k++) { 212 | size_t expected_len = hdr->size - (0x10000 * k); 213 | if (expected_len > 0x10000) 214 | expected_len = 0x10000; 215 | const uint8_t *src = rsrc.data() + off_list[k]; 216 | size_t src_len = off_list[k + 1] - off_list[k]; 217 | 218 | if (src_len > 0x10001) { 219 | if (g_debug & Dbg_Errors) 220 | std::cout << "Decmpfs: In rsrc, src_len too big (" << src_len << ")" << std::endl; 221 | return false; 222 | } 223 | 224 | switch (hdr->algo) { 225 | case 8: 226 | if (src[0] == 0x06) { 227 | memcpy(decompressed.data() + (k << 16), src + 1, src_len - 1); 228 | decoded_bytes = src_len - 1; 229 | } 230 | else 231 | decoded_bytes = DecompressLZVN(decompressed.data() + (k << 16), expected_len, src, src_len); 232 | break; 233 | case 10: 234 | // Assuming ... 235 | memcpy(decompressed.data() + (k << 16), src + 1, src_len - 1); 236 | decoded_bytes = src_len - 1; 237 | break; 238 | case 12: 239 | // Assuming ... 240 | decoded_bytes = DecompressLZFSE(decompressed.data() + (k << 16), expected_len, src, src_len); 241 | // TODO is there also an uncompressed variant? 242 | break; 243 | case 14: 244 | if (src[0] == 0xFF) { 245 | memcpy(decompressed.data() + (k << 16), src + 1, src_len - 1); 246 | decoded_bytes = src_len - 1; 247 | } 248 | else 249 | decoded_bytes = DecompressLZBITMAP(decompressed.data() + (k << 16), expected_len, src, src_len); 250 | break; 251 | default: 252 | decoded_bytes = 0; 253 | break; 254 | } 255 | 256 | if (decoded_bytes != expected_len) { 257 | if (g_debug & Dbg_Errors) 258 | std::cout << "Decmpfs: Expected length != decompressed length: " << expected_len << " != " << decoded_bytes << " [k = " << k << "]" << std::endl; 259 | 260 | return false; 261 | } 262 | } 263 | } 264 | 265 | decompressed.resize(hdr->size); 266 | } 267 | else 268 | { 269 | decompressed.resize(hdr->size); 270 | 271 | switch (hdr->algo) { 272 | case 3: 273 | if (cdata[0] == 0x78) 274 | decoded_bytes = DecompressZLib(decompressed.data(), decompressed.size(), cdata, csize); 275 | else if (cdata[0] == 0xFF) { 276 | assert(hdr->size == csize - 1); 277 | decompressed.assign(cdata + 1, cdata + csize); 278 | decoded_bytes = decompressed.size(); 279 | } 280 | else 281 | return false; 282 | break; 283 | 284 | case 7: 285 | if (cdata[0] == 0x06) { 286 | assert(hdr->size == csize - 1); 287 | decompressed.assign(cdata + 1, cdata + csize); 288 | decoded_bytes = decompressed.size(); 289 | } 290 | else 291 | decoded_bytes = DecompressLZVN(decompressed.data(), decompressed.size(), cdata, csize); 292 | break; 293 | 294 | case 9: 295 | assert(cdata[0] == 0xCC); 296 | assert(hdr->size == csize - 1); 297 | decompressed.assign(cdata + 1, cdata + csize); 298 | decoded_bytes = decompressed.size(); 299 | break; 300 | 301 | case 11: 302 | // TODO uncompressed variant? 303 | decoded_bytes = DecompressLZFSE(decompressed.data(), decompressed.size(), cdata, csize); 304 | break; 305 | 306 | case 13: 307 | if (cdata[0] == 0xFF) { 308 | assert(hdr->size == csize - 1); 309 | decompressed.assign(cdata + 1, cdata + csize); 310 | decoded_bytes = decompressed.size(); 311 | } 312 | else 313 | decoded_bytes = DecompressLZBITMAP(decompressed.data(), decompressed.size(), cdata, csize); 314 | break; 315 | 316 | default: 317 | decoded_bytes = 0; 318 | break; 319 | } 320 | 321 | if (decoded_bytes != hdr->size) 322 | { 323 | if (g_debug & Dbg_Errors) 324 | std::cout << "Decmpfs: In attr, expected len != decoded len: " << hdr->size << " != " << decoded_bytes << std::endl; 325 | return false; 326 | } 327 | } 328 | 329 | #else 330 | if (IsDecompAlgoInRsrc(hdr->algo)) 331 | { 332 | dir.GetAttribute(decompressed, ino, "com.apple.ResourceFork"); 333 | } 334 | else 335 | { 336 | decompressed = compressed; 337 | } 338 | #endif 339 | 340 | return true; 341 | } 342 | -------------------------------------------------------------------------------- /ApfsLib/Decmpfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | #include "ApfsDir.h" 25 | 26 | struct CompressionHeader 27 | { 28 | le_uint32_t signature; 29 | le_uint32_t algo; 30 | le_uint64_t size; 31 | }; 32 | 33 | bool IsDecompAlgoSupported(uint16_t algo); 34 | bool IsDecompAlgoInRsrc(uint16_t algo); 35 | 36 | bool DecompressFile(ApfsDir &dir, uint64_t ino, std::vector &decompressed, const std::vector &compressed); 37 | -------------------------------------------------------------------------------- /ApfsLib/Device.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #include "Device.h" 23 | 24 | #include "DeviceWinFile.h" 25 | #include "DeviceWinPhys.h" 26 | #include "DeviceLinux.h" 27 | #include "DeviceMac.h" 28 | #include "DeviceDMG.h" 29 | #include "DeviceSparseImage.h" 30 | #include "DeviceVDI.h" 31 | 32 | Device::Device() 33 | { 34 | m_sector_size = 0x200; 35 | } 36 | 37 | Device::~Device() 38 | { 39 | } 40 | 41 | Device * Device::OpenDevice(const char * name) 42 | { 43 | Device *dev = nullptr; 44 | bool rc; 45 | const char *ext; 46 | 47 | #ifdef _WIN32 48 | if (!strncmp(name, "\\\\.\\PhysicalDrive", 17)) 49 | { 50 | dev = new DeviceWinPhys(); 51 | rc = dev->Open(name); 52 | if (rc) 53 | return dev; 54 | else 55 | { 56 | dev->Close(); 57 | delete dev; 58 | dev = nullptr; 59 | } 60 | } 61 | #endif 62 | 63 | ext = strrchr(name, '.'); 64 | if (ext) 65 | { 66 | if (!strcmp(ext, ".dmg")) 67 | { 68 | DeviceDMG *dmg; 69 | dmg = new DeviceDMG(); 70 | rc = dmg->Open(name); 71 | if (rc) 72 | return dmg; 73 | dmg->Close(); 74 | delete dmg; 75 | } 76 | 77 | if (!strcmp(ext, ".sparseimage")) 78 | { 79 | DeviceSparseImage *sprs; 80 | sprs = new DeviceSparseImage(); 81 | rc = sprs->Open(name); 82 | if (rc) 83 | return sprs; 84 | sprs->Close(); 85 | delete sprs; 86 | } 87 | } 88 | 89 | if (!dev) 90 | { 91 | #ifdef _WIN32 92 | dev = new DeviceWinFile(); 93 | #endif 94 | #ifdef __linux__ 95 | dev = new DeviceLinux(); 96 | #endif 97 | #ifdef __APPLE__ 98 | dev = new DeviceMac(); 99 | #endif 100 | rc = dev->Open(name); 101 | 102 | if (!rc) 103 | { 104 | dev->Close(); 105 | delete dev; 106 | dev = nullptr; 107 | } 108 | } 109 | 110 | return dev; 111 | } 112 | -------------------------------------------------------------------------------- /ApfsLib/Device.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | class Device 25 | { 26 | protected: 27 | Device(); 28 | 29 | public: 30 | virtual ~Device(); 31 | 32 | virtual bool Open(const char *name) = 0; 33 | virtual void Close() = 0; 34 | 35 | virtual bool Read(void *data, uint64_t offs, uint64_t len) = 0; 36 | virtual uint64_t GetSize() const = 0; 37 | 38 | unsigned int GetSectorSize() const { return m_sector_size; } 39 | void SetSectorSize(unsigned int size) { m_sector_size = size; } 40 | 41 | static Device *OpenDevice(const char *name); 42 | 43 | private: 44 | unsigned int m_sector_size; 45 | }; 46 | -------------------------------------------------------------------------------- /ApfsLib/DeviceDMG.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "Device.h" 28 | #include "DiskImageFile.h" 29 | 30 | #include "Crc32.h" 31 | 32 | #undef DMG_DEBUG 33 | #define DMG_CACHE 34 | 35 | class DeviceDMG : public Device 36 | { 37 | struct DmgSection 38 | { 39 | DmgSection(); 40 | ~DmgSection(); 41 | 42 | uint32_t method; 43 | uint32_t comment; 44 | uint64_t disk_offset; 45 | uint64_t disk_length; 46 | uint64_t dmg_offset; 47 | uint64_t dmg_length; 48 | uint8_t *cache; 49 | }; 50 | 51 | public: 52 | DeviceDMG(); 53 | ~DeviceDMG(); 54 | 55 | bool Open(const char *name) override; 56 | void Close() override; 57 | 58 | bool Read(void *data, uint64_t offs, uint64_t len) override; 59 | uint64_t GetSize() const override; 60 | 61 | private: 62 | bool ProcessHeaderXML(uint64_t off, uint64_t size); 63 | bool ProcessHeaderRsrc(uint64_t off, uint64_t size); 64 | 65 | void ProcessMish(const uint8_t *data, size_t size); 66 | 67 | DiskImageFile m_img; 68 | uint64_t m_size; 69 | uint64_t m_offset; 70 | 71 | bool m_is_raw; 72 | 73 | Crc32 m_crc; 74 | 75 | std::vector m_sections; 76 | 77 | #ifdef DMG_DEBUG 78 | std::ofstream m_dbg; 79 | #endif 80 | #ifdef DMG_CACHE 81 | uint64_t m_cache_base; 82 | uint64_t m_cache_size; 83 | uint8_t *m_cache_data; 84 | #endif 85 | }; 86 | -------------------------------------------------------------------------------- /ApfsLib/DeviceLinux.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #ifdef __linux__ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "DeviceLinux.h" 33 | #include "Global.h" 34 | 35 | DeviceLinux::DeviceLinux() 36 | { 37 | m_device = -1; 38 | m_size = 0; 39 | } 40 | 41 | DeviceLinux::~DeviceLinux() 42 | { 43 | Close(); 44 | } 45 | 46 | bool DeviceLinux::Open(const char* name) 47 | { 48 | m_device = open(name, O_RDONLY | O_LARGEFILE); 49 | 50 | if (m_device == -1) 51 | { 52 | std::cout << "Opening device " << name << " failed with error " << strerror(errno) << std::endl; 53 | return false; 54 | } 55 | 56 | struct stat64 st; 57 | 58 | fstat64(m_device, &st); 59 | 60 | if (S_ISREG(st.st_mode)) 61 | { 62 | m_size = st.st_size; 63 | } 64 | else if (S_ISBLK(st.st_mode)) 65 | { 66 | // Hmmm ... 67 | ioctl(m_device, BLKGETSIZE64, &m_size); 68 | } 69 | 70 | if (g_debug & Dbg_Info) 71 | std::cout << "Device " << name << " opened. Size is " << m_size << std::endl; 72 | 73 | return m_device != -1; 74 | } 75 | 76 | void DeviceLinux::Close() 77 | { 78 | if (m_device != -1) 79 | close(m_device); 80 | m_device = -1; 81 | m_size = 0; 82 | } 83 | 84 | bool DeviceLinux::Read(void* data, uint64_t offs, uint64_t len) 85 | { 86 | size_t nread; 87 | 88 | nread = pread64(m_device, data, len, offs); 89 | 90 | // TODO: Better error handling ... 91 | return nread == len; 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /ApfsLib/DeviceLinux.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #ifdef __linux__ 23 | 24 | #include 25 | #include "Device.h" 26 | 27 | class DeviceLinux : public Device 28 | { 29 | public: 30 | DeviceLinux(); 31 | ~DeviceLinux(); 32 | 33 | bool Open(const char *name) override; 34 | void Close() override; 35 | 36 | bool Read(void *data, uint64_t offs, uint64_t len) override; 37 | 38 | uint64_t GetSize() const override { return m_size; } 39 | 40 | private: 41 | int m_device; 42 | uint64_t m_size; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /ApfsLib/DeviceMac.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #ifdef __APPLE__ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "DeviceMac.h" 34 | #include "Global.h" 35 | 36 | DeviceMac::DeviceMac() 37 | { 38 | m_device = -1; 39 | m_size = 0; 40 | } 41 | 42 | DeviceMac::~DeviceMac() 43 | { 44 | Close(); 45 | } 46 | 47 | bool DeviceMac::Open(const char* name) 48 | { 49 | m_device = open(name, O_RDONLY); 50 | 51 | if (m_device == -1) 52 | { 53 | std::cout << "Opening device " << name << " failed with error " << strerror(errno) << std::endl; 54 | return false; 55 | } 56 | 57 | struct stat st; 58 | 59 | fstat(m_device, &st); 60 | 61 | std::cout << "st_mode = " << st.st_mode << std::endl; 62 | 63 | if (S_ISREG(st.st_mode)) 64 | { 65 | m_size = st.st_size; 66 | } 67 | else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) 68 | { 69 | uint64_t sector_count = 0; 70 | uint32_t sector_size = 0; 71 | 72 | ioctl(m_device, DKIOCGETBLOCKCOUNT, §or_count); 73 | ioctl(m_device, DKIOCGETBLOCKSIZE, §or_size); 74 | 75 | m_size = sector_size * sector_count; 76 | 77 | std::cout << "Sector count = " << sector_count << std::endl; 78 | std::cout << "Sector size = " << sector_size << std::endl; 79 | } 80 | else 81 | { 82 | std::cout << "File mode unknown!" << std::endl; 83 | } 84 | 85 | if (g_debug & Dbg_Info) 86 | std::cout << "Device " << name << " opened. Size is " << m_size << std::endl; 87 | 88 | return m_device != -1; 89 | } 90 | 91 | void DeviceMac::Close() 92 | { 93 | if (m_device != -1) 94 | close(m_device); 95 | m_device = -1; 96 | m_size = 0; 97 | } 98 | 99 | bool DeviceMac::Read(void* data, uint64_t offs, uint64_t len) 100 | { 101 | size_t nread; 102 | 103 | nread = pread(m_device, data, len, offs); 104 | 105 | // TODO: Better error handling ... 106 | return nread == len; 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /ApfsLib/DeviceMac.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #ifdef __APPLE__ 23 | 24 | #include "Device.h" 25 | 26 | class DeviceMac : public Device 27 | { 28 | public: 29 | DeviceMac(); 30 | ~DeviceMac(); 31 | 32 | bool Open(const char *name) override; 33 | void Close() override; 34 | 35 | bool Read(void *data, uint64_t offs, uint64_t len) override; 36 | 37 | uint64_t GetSize() const override { return m_size; } 38 | 39 | private: 40 | int m_device; 41 | uint64_t m_size; 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /ApfsLib/DeviceSparseImage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #include "Endian.h" 23 | 24 | #include "DeviceSparseImage.h" 25 | 26 | #ifdef _MSC_VER 27 | #pragma pack(push, 4) 28 | #define __attribute__(x) 29 | #endif 30 | 31 | struct HeaderNode 32 | { 33 | be_uint32_t signature; 34 | be_uint32_t version; 35 | be_uint32_t sectors_per_band; 36 | be_uint32_t flags; 37 | be_uint32_t total_sectors_low; 38 | be_uint64_t next_node_offset; 39 | be_uint64_t total_sectors; 40 | be_uint32_t padding[7]; 41 | be_uint32_t band_id[0x3F0]; 42 | } __attribute__((packed, aligned(4))); 43 | 44 | struct IndexNode 45 | { 46 | be_uint32_t magic; 47 | be_uint32_t index_node_number; 48 | be_uint32_t flags; 49 | be_uint64_t next_node_offset; 50 | be_uint32_t padding[9]; 51 | be_uint32_t band_id[0x3F2]; 52 | } __attribute__((packed, aligned(4))); 53 | 54 | #ifdef _MSC_VER 55 | #pragma pack(pop) 56 | #undef __attribute__ 57 | #endif 58 | 59 | constexpr int SECTOR_SIZE = 0x200; 60 | constexpr size_t NODE_SIZE = 0x1000; 61 | constexpr size_t BAND_SIZE = 0x100000; 62 | constexpr uint32_t SPRS_SIGNATURE = 0x73707273; 63 | 64 | DeviceSparseImage::DeviceSparseImage() 65 | { 66 | m_band_size = 0; 67 | m_size = 0; 68 | } 69 | 70 | DeviceSparseImage::~DeviceSparseImage() 71 | { 72 | m_img.Close(); 73 | m_img.Reset(); 74 | } 75 | 76 | bool DeviceSparseImage::Open(const char * name) 77 | { 78 | if (!m_img.Open(name)) 79 | return false; 80 | 81 | if (!m_img.CheckSetupEncryption()) 82 | { 83 | m_img.Close(); 84 | m_img.Reset(); 85 | return false; 86 | } 87 | 88 | HeaderNode hdr; 89 | IndexNode idx; 90 | uint32_t off; 91 | uint64_t base; 92 | uint64_t next; 93 | size_t k; 94 | 95 | m_img.Read(0, &hdr, sizeof(hdr)); 96 | 97 | if (hdr.signature != SPRS_SIGNATURE) 98 | { 99 | m_img.Close(); 100 | m_img.Reset(); 101 | return false; 102 | } 103 | 104 | m_size = hdr.total_sectors * SECTOR_SIZE; 105 | m_band_size = hdr.sectors_per_band * SECTOR_SIZE; 106 | 107 | m_band_offset.resize((m_size + m_band_size - 1) / m_band_size, 0); 108 | 109 | base = 0x1000; 110 | 111 | for (k = 0; k < 0x3F0; k++) 112 | { 113 | off = hdr.band_id[k]; 114 | if (off) 115 | m_band_offset[off - 1] = base + m_band_size * k; 116 | } 117 | 118 | next = hdr.next_node_offset; 119 | base = next + NODE_SIZE; 120 | 121 | while (next) 122 | { 123 | m_img.Read(next, &idx, sizeof(idx)); 124 | 125 | if (hdr.signature != SPRS_SIGNATURE) 126 | { 127 | m_img.Close(); 128 | m_img.Reset(); 129 | return false; 130 | } 131 | 132 | for (k = 0; k < 0x3F2; k++) 133 | { 134 | off = idx.band_id[k]; 135 | if (off) 136 | m_band_offset[off - 1] = base + m_band_size * k; 137 | } 138 | 139 | next = idx.next_node_offset; 140 | base = next + NODE_SIZE; 141 | } 142 | 143 | return true; 144 | } 145 | 146 | void DeviceSparseImage::Close() 147 | { 148 | m_img.Close(); 149 | m_img.Reset(); 150 | } 151 | 152 | bool DeviceSparseImage::Read(void * data, uint64_t offs, uint64_t len) 153 | { 154 | uint32_t chunk; 155 | uint32_t chunk_offs; 156 | uint64_t chunk_base; 157 | size_t read_size; 158 | uint8_t *bdata = reinterpret_cast(data); 159 | 160 | while (len > 0) 161 | { 162 | chunk = offs >> 20; // TODO 163 | chunk_offs = offs & (m_band_size - 1); 164 | 165 | read_size = len; 166 | 167 | if ((chunk_offs + read_size) > m_band_size) 168 | read_size = m_band_size - chunk_offs; 169 | 170 | chunk_base = m_band_offset[chunk]; 171 | 172 | if (chunk_base) 173 | m_img.Read(chunk_base + chunk_offs, bdata, read_size); 174 | else 175 | memset(bdata, 0, read_size); 176 | 177 | len -= read_size; 178 | offs += read_size; 179 | bdata += read_size; 180 | } 181 | 182 | return true; 183 | } 184 | 185 | uint64_t DeviceSparseImage::GetSize() const 186 | { 187 | return m_size; 188 | } 189 | -------------------------------------------------------------------------------- /ApfsLib/DeviceSparseImage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Device.h" 6 | #include "DiskImageFile.h" 7 | 8 | class DeviceSparseImage : public Device 9 | { 10 | public: 11 | DeviceSparseImage(); 12 | ~DeviceSparseImage(); 13 | 14 | bool Open(const char *name) override; 15 | void Close() override; 16 | 17 | bool Read(void *data, uint64_t offs, uint64_t len) override; 18 | uint64_t GetSize() const override; 19 | 20 | private: 21 | std::vector m_band_offset; 22 | uint64_t m_size; 23 | uint64_t m_band_size; 24 | 25 | DiskImageFile m_img; 26 | }; 27 | -------------------------------------------------------------------------------- /ApfsLib/DeviceVDI.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceVDI.h" 2 | 3 | #pragma pack(1) 4 | 5 | struct VdiDiskGeometry 6 | { 7 | le_uint32_t cylinders; 8 | le_uint32_t heads; 9 | le_uint32_t sectors; 10 | le_uint32_t sector_size; 11 | }; 12 | 13 | struct VdiPreHeader 14 | { 15 | char file_info[0x40]; 16 | le_uint32_t signature; // 0xBEDA107F 17 | le_uint32_t version; // 0x00010001 18 | }; 19 | 20 | struct VdiHeader1Plus 21 | { 22 | le_uint32_t struct_size; 23 | le_uint32_t image_type; 24 | le_uint32_t flags; 25 | char comment[0x100]; 26 | le_uint32_t off_blocks; 27 | le_uint32_t off_data; 28 | VdiDiskGeometry legacy_geometry; 29 | le_uint32_t dummy; 30 | le_uint64_t disk_size; 31 | le_uint32_t block_size; 32 | le_uint32_t block_extra; 33 | le_uint32_t blocks_total; 34 | le_uint32_t blocks_allocated; 35 | char uuid_create[0x10]; 36 | char uuid_modify[0x10]; 37 | char uuid_linkage[0x10]; 38 | char uuid_parent_modify[0x10]; 39 | VdiDiskGeometry lchs_geometry; 40 | }; 41 | 42 | #pragma pack() 43 | 44 | 45 | DeviceVDI::DeviceVDI() 46 | { 47 | m_disk_size = 0; 48 | m_block_size = 0; 49 | m_block_count = 0; 50 | m_data_offset = 0; 51 | 52 | m_vdi = nullptr; 53 | } 54 | 55 | DeviceVDI::~DeviceVDI() 56 | { 57 | } 58 | 59 | bool DeviceVDI::Open(const char * name) 60 | { 61 | VdiPreHeader phdr; 62 | VdiHeader1Plus hdr; 63 | 64 | Close(); 65 | 66 | fopen_s(&m_vdi, name, "rb"); 67 | if (m_vdi) 68 | { 69 | if (fread(&phdr, sizeof(phdr), 1, m_vdi) == 1) 70 | { 71 | if (fread(&hdr, sizeof(hdr), 1, m_vdi) == 1) 72 | { 73 | if (phdr.signature == 0xBEDA107F && phdr.version == 0x00010001) 74 | { 75 | m_disk_size = hdr.disk_size; 76 | m_block_size = hdr.block_size; 77 | m_block_count = hdr.blocks_total; 78 | m_data_offset = hdr.off_data; 79 | 80 | m_block_map.resize(m_block_count, 0xFF); 81 | 82 | fseek(m_vdi, hdr.off_blocks, SEEK_SET); 83 | 84 | fread(m_block_map.data(), sizeof(uint32_t), m_block_count, m_vdi); 85 | 86 | fseek(m_vdi, 0, SEEK_END); 87 | 88 | return true; 89 | } 90 | } 91 | } 92 | } 93 | 94 | Close(); 95 | 96 | return false; 97 | } 98 | 99 | void DeviceVDI::Close() 100 | { 101 | if (m_vdi) 102 | { 103 | fclose(m_vdi); 104 | m_vdi = nullptr; 105 | } 106 | } 107 | 108 | bool DeviceVDI::Read(void * data, uint64_t offs, uint64_t len) 109 | { 110 | uint64_t block_nr; 111 | uint64_t block_offs; 112 | uint64_t vdi_offs; 113 | uint32_t map_entry; 114 | 115 | uint64_t read_size; 116 | uint8_t *pdata = reinterpret_cast(data); 117 | 118 | if (!m_vdi) 119 | return false; 120 | 121 | while (len > 0) 122 | { 123 | block_nr = offs >> 20; 124 | block_offs = offs & 0xFFFFF; 125 | 126 | map_entry = m_block_map[block_nr]; 127 | 128 | read_size = m_block_size - block_offs; 129 | 130 | if (read_size > len) 131 | read_size = len; 132 | 133 | if (map_entry == 0xFFFFFFFF) 134 | { 135 | memset(pdata, 0, read_size); 136 | } 137 | else 138 | { 139 | fseek(m_vdi, map_entry * m_block_size + m_data_offset + block_offs, SEEK_SET); 140 | fread(pdata, 1, read_size, m_vdi); 141 | } 142 | 143 | pdata += read_size; 144 | offs += read_size; 145 | len -= read_size; 146 | } 147 | 148 | return true; 149 | } 150 | 151 | uint64_t DeviceVDI::GetSize() const 152 | { 153 | return m_disk_size; 154 | } 155 | -------------------------------------------------------------------------------- /ApfsLib/DeviceVDI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Endian.h" 6 | #include "Device.h" 7 | 8 | 9 | class DeviceVDI : public Device 10 | { 11 | public: 12 | DeviceVDI(); 13 | ~DeviceVDI(); 14 | 15 | bool Open(const char *name) override; 16 | void Close() override; 17 | 18 | bool Read(void *data, uint64_t offs, uint64_t len) override; 19 | uint64_t GetSize() const override; 20 | 21 | private: 22 | uint64_t m_disk_size; 23 | uint32_t m_block_size; 24 | uint32_t m_block_count; 25 | uint32_t m_data_offset; 26 | 27 | std::vector m_block_map; 28 | 29 | FILE *m_vdi; 30 | }; 31 | -------------------------------------------------------------------------------- /ApfsLib/DeviceWinFile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #ifdef _WIN32 21 | 22 | #include "DeviceWinFile.h" 23 | 24 | DeviceWinFile::DeviceWinFile() 25 | { 26 | m_size = 0; 27 | } 28 | 29 | DeviceWinFile::~DeviceWinFile() 30 | { 31 | Close(); 32 | } 33 | 34 | bool DeviceWinFile::Open(const char * name) 35 | { 36 | m_vol.open(name, std::ios::binary); 37 | 38 | if (!m_vol.is_open()) 39 | return false; 40 | 41 | m_vol.seekg(0, std::ios::end); 42 | m_size = m_vol.tellg(); 43 | m_vol.seekg(0); 44 | 45 | return m_vol.is_open(); 46 | } 47 | 48 | void DeviceWinFile::Close() 49 | { 50 | m_vol.close(); 51 | } 52 | 53 | bool DeviceWinFile::Read(void *data, uint64_t offs, uint64_t len) 54 | { 55 | m_vol.seekg(offs); 56 | m_vol.read(reinterpret_cast(data), len); 57 | 58 | // TODO: Fehlerbehandlung ... 59 | return true; 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /ApfsLib/DeviceWinFile.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #ifdef _WIN32 23 | 24 | #include 25 | 26 | #include "Device.h" 27 | 28 | class DeviceWinFile : public Device 29 | { 30 | public: 31 | DeviceWinFile(); 32 | ~DeviceWinFile(); 33 | 34 | bool Open(const char *name) override; 35 | void Close() override; 36 | 37 | bool Read(void *data, uint64_t offs, uint64_t len) override; 38 | 39 | uint64_t GetSize() const override { return m_size; } 40 | 41 | private: 42 | std::ifstream m_vol; 43 | uint64_t m_size; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /ApfsLib/DeviceWinPhys.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #ifdef _WIN32 21 | 22 | #include "DeviceWinPhys.h" 23 | 24 | DeviceWinPhys::DeviceWinPhys() 25 | { 26 | m_drive = INVALID_HANDLE_VALUE; 27 | m_size = 0; 28 | } 29 | 30 | DeviceWinPhys::~DeviceWinPhys() 31 | { 32 | Close(); 33 | } 34 | 35 | bool DeviceWinPhys::Open(const char *name) 36 | { 37 | TCHAR path[MAX_PATH]; 38 | uint8_t buf[0x1000]; 39 | DWORD bytes_ret = 0; 40 | 41 | // _stprintf_s(path, _T("\\\\.\\PhysicalDrive%d"), disk); 42 | 43 | MultiByteToWideChar(CP_UTF8, 0, name, -1, path, MAX_PATH); 44 | 45 | m_drive = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, nullptr); 46 | if (m_drive == INVALID_HANDLE_VALUE) 47 | return false; 48 | 49 | DeviceIoControl(m_drive, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, &buf, sizeof(buf), &bytes_ret, nullptr); 50 | 51 | if (bytes_ret == 0) 52 | return false; 53 | 54 | const DISK_GEOMETRY_EX *geom = reinterpret_cast(buf); 55 | 56 | m_size = geom->DiskSize.QuadPart; 57 | 58 | return true; 59 | } 60 | 61 | void DeviceWinPhys::Close() 62 | { 63 | if (m_drive != INVALID_HANDLE_VALUE) 64 | CloseHandle(m_drive); 65 | m_drive = INVALID_HANDLE_VALUE; 66 | m_size = 0; 67 | } 68 | 69 | bool DeviceWinPhys::Read(void * data, uint64_t offs, uint64_t len) 70 | { 71 | DWORD read_bytes = 0; 72 | LARGE_INTEGER off; 73 | BOOL rc; 74 | 75 | if (m_drive == INVALID_HANDLE_VALUE) 76 | return false; 77 | 78 | off.QuadPart = offs; 79 | 80 | SetFilePointerEx(m_drive, off, nullptr, FILE_BEGIN); 81 | rc = ReadFile(m_drive, data, len, &read_bytes, nullptr); 82 | 83 | return rc == TRUE; 84 | } 85 | 86 | uint64_t DeviceWinPhys::GetSize() const 87 | { 88 | return m_size; 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /ApfsLib/DeviceWinPhys.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #ifdef _WIN32 23 | 24 | #include "Device.h" 25 | 26 | // #define WIN32_LEAN_AND_MEAN 27 | #include 28 | #include 29 | 30 | class DeviceWinPhys : public Device 31 | { 32 | public: 33 | DeviceWinPhys(); 34 | ~DeviceWinPhys(); 35 | 36 | bool Open(const char *name) override; 37 | void Close() override; 38 | 39 | bool Read(void *data, uint64_t offs, uint64_t len) override; 40 | uint64_t GetSize() const override; 41 | 42 | private: 43 | HANDLE m_drive; 44 | uint64_t m_size; 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /ApfsLib/DiskImageFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include "Device.h" 8 | 9 | class DiskImageFile 10 | { 11 | public: 12 | DiskImageFile(); 13 | ~DiskImageFile(); 14 | 15 | bool Open(const char *name); 16 | void Close(); 17 | void Reset(); 18 | 19 | void Read(uint64_t off, void *data, size_t size); 20 | 21 | uint64_t GetContentSize() const { return m_crypt_size; } 22 | 23 | bool CheckSetupEncryption(); 24 | 25 | private: 26 | bool SetupEncryptionV1(); 27 | bool SetupEncryptionV2(); 28 | size_t PkcsUnpad(const uint8_t *data, size_t size); 29 | 30 | std::ifstream m_image; 31 | 32 | bool m_is_encrypted; 33 | uint64_t m_crypt_offset; 34 | uint64_t m_crypt_size; 35 | uint32_t m_crypt_blocksize; 36 | uint8_t m_hmac_key[0x14]; 37 | 38 | AES m_aes; 39 | }; 40 | -------------------------------------------------------------------------------- /ApfsLib/Endian.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | /* 20 | This file is for handling the different endiannesses used by Apple. 21 | Also helps making the driver run on big-endian architectures. 22 | */ 23 | #pragma once 24 | 25 | #include 26 | 27 | // This should later be done by some configuration ... 28 | #define APFS_LITTLE_ENDIAN 29 | #undef APFS_BIG_ENDIAN 30 | 31 | #ifdef _MSC_VER 32 | // Definitions for Visual Studio 33 | #include 34 | 35 | #define bswap_16(x) _byteswap_ushort(x) 36 | #define bswap_32(x) _byteswap_ulong(x) 37 | #define bswap_64(x) _byteswap_uint64(x) 38 | 39 | #define be16toh(x) _byteswap_ushort(x) 40 | #define be32toh(x) _byteswap_ulong(x) 41 | #define be64toh(x) _byteswap_uint64(x) 42 | #define htobe16(x) _byteswap_ushort(x) 43 | #define htobe32(x) _byteswap_ulong(x) 44 | #define htobe64(x) _byteswap_uint64(x) 45 | 46 | #define le16toh(x) (x) 47 | #define le32toh(x) (x) 48 | #define le64toh(x) (x) 49 | #define htole16(x) (x) 50 | #define htole32(x) (x) 51 | #define htole64(x) (x) 52 | 53 | #define __attribute__(x) 54 | 55 | #endif 56 | #ifdef __linux__ 57 | // Definitions for Linux 58 | #include 59 | #include 60 | #endif 61 | #ifdef __APPLE__ 62 | // Definitions for macOS 63 | #include 64 | #define bswap_16(x) _OSSwapInt16(x) 65 | #define bswap_32(x) _OSSwapInt32(x) 66 | #define bswap_64(x) _OSSwapInt64(x) 67 | #define be16toh(x) OSSwapBigToHostInt16(x) 68 | #define be32toh(x) OSSwapBigToHostInt32(x) 69 | #define be64toh(x) OSSwapBigToHostInt64(x) 70 | #define htobe16(x) OSSwapHostToBigInt16(x) 71 | #define htobe32(x) OSSwapHostToBigInt32(x) 72 | #define htobe64(x) OSSwapHostToBigInt64(x) 73 | #define le16toh(x) OSSwapLittleToHostInt16(x) 74 | #define le32toh(x) OSSwapLittleToHostInt32(x) 75 | #define le64toh(x) OSSwapLittleToHostInt64(x) 76 | #define htole16(x) OSSwapHostToLittleInt16(x) 77 | #define htole32(x) OSSwapHostToLittleInt32(x) 78 | #define htole64(x) OSSwapHostToLittleInt64(x) 79 | #endif 80 | 81 | #ifdef APFS_LITTLE_ENDIAN 82 | // Swap to/from little endian. 83 | inline int16_t bswap_le(const int16_t &x) { return x; } 84 | inline int32_t bswap_le(const int32_t &x) { return x; } 85 | inline int64_t bswap_le(const int64_t &x) { return x; } 86 | inline uint16_t bswap_le(const uint16_t &x) { return x; } 87 | inline uint32_t bswap_le(const uint32_t &x) { return x; } 88 | inline uint64_t bswap_le(const uint64_t &x) { return x; } 89 | // Swap to/from big endian. 90 | inline int16_t bswap_be(const int16_t& x) { return bswap_16(x); } 91 | inline int32_t bswap_be(const int32_t& x) { return bswap_32(x); } 92 | inline int64_t bswap_be(const int64_t& x) { return bswap_64(x); } 93 | inline uint16_t bswap_be(const uint16_t& x) { return bswap_16(x); } 94 | inline uint32_t bswap_be(const uint32_t& x) { return bswap_32(x); } 95 | inline uint64_t bswap_be(const uint64_t& x) { return bswap_64(x); } 96 | 97 | typedef uint8_t le_uint8_t; 98 | typedef uint16_t le_uint16_t; 99 | typedef uint32_t le_uint32_t; 100 | typedef uint64_t le_uint64_t; 101 | typedef int8_t le_int8_t; 102 | typedef int16_t le_int16_t; 103 | typedef int32_t le_int32_t; 104 | typedef int64_t le_int64_t; 105 | typedef uint8_t be_uint8_t; 106 | struct be_uint16_t { 107 | void operator=(uint16_t v) { val = bswap_16(v); } 108 | operator uint16_t() const { return bswap_16(val); } 109 | uint16_t get() const { return bswap_16(val); } 110 | private: 111 | uint16_t val; 112 | } __attribute__((packed)); 113 | struct be_uint32_t { 114 | void operator=(uint32_t v) { val = bswap_32(v); } 115 | operator uint32_t() const { return bswap_32(val); } 116 | uint32_t get() const { return bswap_32(val); } 117 | private: 118 | uint32_t val; 119 | } __attribute__((packed)); 120 | struct be_uint64_t { 121 | void operator=(uint64_t v) { val = bswap_64(v); } 122 | operator uint64_t() const { return bswap_64(val); } 123 | uint64_t get() const { return bswap_64(val); } 124 | private: 125 | uint64_t val; 126 | } __attribute__((packed)); 127 | 128 | #endif 129 | 130 | #ifdef APFS_BIG_ENDIAN 131 | // Swap to/from little endian. 132 | inline int16_t bswap_le(const int16_t &x) { return bswap_16(x); } 133 | inline int32_t bswap_le(const int32_t &x) { return bswap_32(x); } 134 | inline int64_t bswap_le(const int64_t &x) { return bswap_64(x); } 135 | inline uint16_t bswap_le(const uint16_t &x) { return bswap_16(x); } 136 | inline uint32_t bswap_le(const uint32_t &x) { return bswap_32(x); } 137 | inline uint64_t bswap_le(const uint64_t &x) { return bswap_64(x); } 138 | // Swap to/from big endian. 139 | inline int16_t bswap_be(const int16_t &x) { return x; } 140 | inline int32_t bswap_be(const int32_t &x) { return x; } 141 | inline int64_t bswap_be(const int64_t &x) { return x; } 142 | inline uint16_t bswap_be(const uint16_t &x) { return x; } 143 | inline uint32_t bswap_be(const uint32_t &x) { return x; } 144 | inline uint64_t bswap_be(const uint64_t &x) { return x; } 145 | 146 | typedef uint8_t le_uint8_t; 147 | struct le_uint16_t { 148 | void operator=(uint16_t v) { val = bswap_16(v); } 149 | operator uint16_t() const { return bswap_16(val); } 150 | uint16_t get() const { return bswap_16(val); } 151 | private: 152 | uint16_t val; 153 | } __attribute__((packed)); 154 | struct le_uint32_t { 155 | void operator=(uint32_t v) { val = bswap_32(v); } 156 | operator uint32_t() const { return bswap_32(val); } 157 | uint32_t get() const { return bswap_32(val); } 158 | private: 159 | uint32_t val; 160 | } __attribute__((packed)); 161 | struct le_uint64_t { 162 | void operator=(uint64_t v) { val = bswap_64(v); } 163 | operator uint64_t() const { return bswap_64(val); } 164 | uint64_t get() const { return bswap_64(val); } 165 | private: 166 | uint64_t val; 167 | } __attribute__((packed)); 168 | struct le_int64_t { 169 | void operator=(int64_t v) { val = bswap_64(v); } 170 | operator int64_t() const { return bswap_64(val); } 171 | int64_t get() const { return bswap_64(val); } 172 | private: 173 | int64_t val; 174 | } __attribute__((packed)); 175 | 176 | typedef uint8_t be_uint8_t; 177 | typedef uint16_t be_uint16_t; 178 | typedef uint32_t be_uint32_t; 179 | typedef uint64_t be_uint64_t; 180 | #endif 181 | 182 | #if 0 183 | 184 | inline int8_t bswap_le(const int8_t &x) { return x; } 185 | inline int8_t bswap_be(const int8_t &x) { return x; } 186 | inline uint8_t bswap_le(const uint8_t &x) { return x; } 187 | inline uint8_t bswap_be(const uint8_t &x) { return x; } 188 | 189 | // Big-endian template that does automatic swapping if necessary. 190 | template 191 | struct be 192 | { 193 | public: 194 | void operator=(T v) { val = bswap_be(v); } 195 | operator T() const { return bswap_be(val); } 196 | T get() const { return bswap_be(val); } 197 | private: 198 | T val; 199 | }; 200 | 201 | // Little-endian template that does automatic swapping if necessary. 202 | template 203 | struct le 204 | { 205 | public: 206 | void operator=(T v) { val = bswap_le(v); } 207 | operator T() const { return bswap_le(val); } 208 | T get() const { return bswap_le(val); } 209 | private: 210 | T val; 211 | }; 212 | 213 | #endif 214 | 215 | #ifdef _MSC_VER 216 | #undef __attribute__ 217 | #endif 218 | -------------------------------------------------------------------------------- /ApfsLib/Global.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | // Debug level - defined in ApfsContainer.cpp 25 | extern int g_debug; 26 | // Lax mode - defined in ApfsContainer.cpp 27 | extern bool g_lax; 28 | 29 | enum DbgFlags 30 | { 31 | Dbg_Errors = 1, 32 | Dbg_Info = 2, 33 | Dbg_Dir = 4, 34 | Dbg_Cmpfs = 8, 35 | Dbg_Crypto = 16 36 | }; 37 | -------------------------------------------------------------------------------- /ApfsLib/GptPartitionMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Device.h" 6 | #include "GptPartitionMap.h" 7 | 8 | typedef uint8_t PM_GUID[0x10]; 9 | 10 | #pragma pack(1) 11 | 12 | struct PMAP_GptHeader 13 | { 14 | le_uint64_t Signature; 15 | le_uint32_t Revision; 16 | le_uint32_t HeaderSize; 17 | le_uint32_t HeaderCRC32; 18 | le_uint32_t Reserved; 19 | le_uint64_t MyLBA; 20 | le_uint64_t AlternateLBA; 21 | le_uint64_t FirstUsableLBA; 22 | le_uint64_t LastUsableLBA; 23 | PM_GUID DiskGUID; 24 | le_uint64_t PartitionEntryLBA; 25 | le_uint32_t NumberOfPartitionEntries; 26 | le_uint32_t SizeOfPartitionEntry; 27 | le_uint32_t PartitionEntryArrayCRC32; 28 | }; 29 | 30 | struct PMAP_Entry 31 | { 32 | PM_GUID PartitionTypeGUID; 33 | PM_GUID UniquePartitionGUID; 34 | le_uint64_t StartingLBA; 35 | le_uint64_t EndingLBA; 36 | le_uint64_t Attributes; 37 | le_uint16_t PartitionName[36]; 38 | }; 39 | 40 | static_assert(sizeof(PMAP_GptHeader) == 92, "PMAP GPT-Header wrong size"); 41 | static_assert(sizeof(PMAP_Entry) == 128, "PMAP Entry wrong size"); 42 | 43 | #pragma pack() 44 | 45 | static const PM_GUID partitiontype_apfs = { 0xEF, 0x57, 0x34, 0x7C, 0x00, 0x00, 0xAA, 0x11, 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }; 46 | 47 | static void PrintGUID(const PM_GUID &guid) 48 | { 49 | printf("%02X%02X%02X%02X-", guid[3], guid[2], guid[1], guid[0]); 50 | printf("%02X%02X-", guid[5], guid[4]); 51 | printf("%02X%02X-", guid[7], guid[6]); 52 | printf("%02X%02X-", guid[9], guid[8]); 53 | printf("%02X%02X%02X%02X%02X%02X", guid[10], guid[11], guid[12], guid[13], guid[14], guid[15]); 54 | } 55 | 56 | GptPartitionMap::GptPartitionMap() : m_crc(true) 57 | { 58 | m_hdr = nullptr; 59 | m_map = nullptr; 60 | m_sector_size = 0x200; 61 | } 62 | 63 | bool GptPartitionMap::LoadAndVerify(Device & dev) 64 | { 65 | PMAP_GptHeader *hdr; 66 | 67 | m_hdr = nullptr; 68 | m_map = nullptr; 69 | m_entry_data.clear(); 70 | m_hdr_data.clear(); 71 | 72 | m_sector_size = dev.GetSectorSize(); 73 | 74 | m_hdr_data.resize(m_sector_size); 75 | 76 | dev.Read(m_hdr_data.data(), m_sector_size, m_sector_size); 77 | 78 | hdr = reinterpret_cast(m_hdr_data.data()); 79 | 80 | if (hdr->Signature != 0x5452415020494645) { 81 | m_hdr_data.resize(0x1000); 82 | dev.Read(m_hdr_data.data(), 0x1000, 0x1000); 83 | 84 | hdr = reinterpret_cast(m_hdr_data.data()); 85 | 86 | if (hdr->Signature != 0x5452415020494645) 87 | return false; 88 | 89 | m_sector_size = 0x1000; 90 | } 91 | 92 | if (hdr->Revision != 0x00010000) 93 | return false; 94 | 95 | if (hdr->HeaderSize > m_sector_size) 96 | return false; 97 | 98 | if (hdr->SizeOfPartitionEntry != 0x80) 99 | return false; 100 | 101 | uint32_t hdr_crc; 102 | uint32_t calc_crc; 103 | 104 | hdr_crc = hdr->HeaderCRC32; 105 | hdr->HeaderCRC32 = 0; 106 | 107 | m_crc.SetCRC(0xFFFFFFFF); 108 | m_crc.Calc(m_hdr_data.data(), hdr->HeaderSize); 109 | calc_crc = m_crc.GetCRC() ^ 0xFFFFFFFF; 110 | 111 | if (calc_crc != hdr_crc) { 112 | m_hdr_data.clear(); 113 | return false; 114 | } 115 | 116 | size_t mapsize; 117 | 118 | mapsize = hdr->NumberOfPartitionEntries * hdr->SizeOfPartitionEntry; 119 | mapsize = (mapsize + m_sector_size - 1) & ~(static_cast(m_sector_size) - 1); 120 | 121 | m_entry_data.resize(mapsize); 122 | dev.Read(m_entry_data.data(), m_sector_size * hdr->PartitionEntryLBA, m_entry_data.size()); 123 | 124 | m_crc.SetCRC(0xFFFFFFFF); 125 | m_crc.Calc(m_entry_data.data(), hdr->SizeOfPartitionEntry * hdr->NumberOfPartitionEntries); 126 | calc_crc = m_crc.GetCRC() ^ 0xFFFFFFFF; 127 | 128 | if (calc_crc != hdr->PartitionEntryArrayCRC32) { 129 | m_entry_data.clear(); 130 | m_hdr_data.clear(); 131 | return false; 132 | } 133 | 134 | m_hdr = reinterpret_cast(m_hdr_data.data()); 135 | m_map = reinterpret_cast(m_entry_data.data()); 136 | 137 | return true; 138 | } 139 | 140 | int GptPartitionMap::FindFirstAPFSPartition() 141 | { 142 | if (!m_hdr || !m_map) 143 | return -1; 144 | 145 | unsigned int k; 146 | int rc = -1; 147 | 148 | for (k = 0; k < m_hdr->NumberOfPartitionEntries; k++) 149 | { 150 | if (m_map[k].StartingLBA == 0 && m_map[k].EndingLBA == 0) 151 | break; 152 | 153 | if (!memcmp(m_map[k].PartitionTypeGUID, partitiontype_apfs, sizeof(PM_GUID))) 154 | { 155 | rc = k; 156 | break; 157 | } 158 | } 159 | 160 | return rc; 161 | } 162 | 163 | bool GptPartitionMap::GetPartitionOffsetAndSize(int partnum, uint64_t & offset, uint64_t & size) 164 | { 165 | if (!m_hdr || !m_map) 166 | return false; 167 | 168 | offset = m_map[partnum].StartingLBA * m_sector_size; 169 | size = (m_map[partnum].EndingLBA - m_map[partnum].StartingLBA + 1) * m_sector_size; 170 | 171 | return true; 172 | } 173 | 174 | void GptPartitionMap::ListEntries() 175 | { 176 | if (!m_hdr || !m_map) 177 | return; 178 | 179 | size_t k; 180 | size_t n; 181 | 182 | for (k = 0; k < m_hdr->NumberOfPartitionEntries; k++) 183 | { 184 | const PMAP_Entry &e = m_map[k]; 185 | 186 | if (e.StartingLBA == 0 && e.EndingLBA == 0) 187 | break; 188 | 189 | PrintGUID(e.PartitionTypeGUID); 190 | printf(" "); 191 | PrintGUID(e.UniquePartitionGUID); 192 | printf(" %016" PRIX64 " %016" PRIX64 " ", e.StartingLBA, e.EndingLBA); 193 | printf("%016" PRIX64 " ", e.Attributes); 194 | 195 | for (n = 0; (n < 36) && (e.PartitionName[n] != 0); n++) 196 | printf("%c", e.PartitionName[n]); 197 | 198 | printf("\n"); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /ApfsLib/GptPartitionMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Endian.h" 7 | #include "Crc32.h" 8 | 9 | class Device; 10 | 11 | struct PMAP_GptHeader; 12 | struct PMAP_Entry; 13 | 14 | class GptPartitionMap 15 | { 16 | public: 17 | GptPartitionMap(); 18 | 19 | bool LoadAndVerify(Device &dev); 20 | 21 | int FindFirstAPFSPartition(); 22 | bool GetPartitionOffsetAndSize(int partnum, uint64_t &offset, uint64_t &size); 23 | 24 | void ListEntries(); 25 | 26 | private: 27 | Crc32 m_crc; 28 | 29 | std::vector m_hdr_data; 30 | std::vector m_entry_data; 31 | 32 | const PMAP_GptHeader *m_hdr; 33 | const PMAP_Entry *m_map; 34 | unsigned int m_sector_size; 35 | }; 36 | -------------------------------------------------------------------------------- /ApfsLib/KeyMgmt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Global.h" 9 | 10 | class ApfsContainer; 11 | 12 | struct bagdata_t; 13 | 14 | struct key_header_t; 15 | struct kek_entry_t; 16 | struct vek_entry_t; 17 | 18 | class Keybag 19 | { 20 | public: 21 | Keybag(); 22 | ~Keybag(); 23 | 24 | bool Init(const media_keybag_t *mk, size_t size); 25 | 26 | size_t GetKeyCnt(); 27 | const keybag_entry_t * GetKey(size_t nr); 28 | const keybag_entry_t * FindKey(const apfs_uuid_t &uuid, uint16_t type); 29 | 30 | void dump(std::ostream &st, Keybag *cbag, const apfs_uuid_t &vuuid); 31 | 32 | private: 33 | std::vector m_data; 34 | kb_locker_t *m_kl; 35 | }; 36 | 37 | class KeyManager 38 | { 39 | friend class Keybag; 40 | public: 41 | KeyManager(ApfsContainer &container); 42 | ~KeyManager(); 43 | 44 | bool Init(uint64_t block, uint64_t blockcnt, const apfs_uuid_t &container_uuid); 45 | 46 | bool GetPasswordHint(std::string &hint, const apfs_uuid_t &volume_uuid); 47 | bool GetVolumeKey(uint8_t *vek, const apfs_uuid_t &volume_uuid, const char *password = nullptr); 48 | 49 | bool IsValid() const { return m_is_valid; } 50 | bool IsUnencrypted() const { return m_is_unencrypted; } 51 | 52 | void dump(std::ostream &st); 53 | 54 | private: 55 | bool LoadKeybag(Keybag &bag, uint32_t type, uint64_t block, uint64_t blockcnt, const apfs_uuid_t &uuid); 56 | void DecryptBlocks(uint8_t *data, uint64_t block, uint64_t cnt, const uint8_t *key); 57 | 58 | static const uint8_t* DecodeKeyHeader(key_header_t &hdr, const uint8_t*& end, const uint8_t* der, const uint8_t* der_end); 59 | static bool DecodeKEK(kek_entry_t &kek, const uint8_t* der, const uint8_t* der_end); 60 | static bool DecodeVEK(vek_entry_t &vek, const uint8_t* der, const uint8_t* der_end); 61 | 62 | ApfsContainer &m_container; 63 | Keybag m_container_bag; 64 | apfs_uuid_t m_container_uuid; 65 | 66 | bool m_is_valid; 67 | bool m_is_unencrypted; 68 | }; 69 | -------------------------------------------------------------------------------- /ApfsLib/PList.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "PList.h" 4 | 5 | PLObject::PLObject() 6 | { 7 | } 8 | 9 | PLObject::~PLObject() 10 | { 11 | } 12 | 13 | const PLInteger * PLObject::toInt() const 14 | { 15 | return dynamic_cast(this); 16 | } 17 | 18 | const PLString * PLObject::toString() const 19 | { 20 | return dynamic_cast(this); 21 | } 22 | 23 | const PLData * PLObject::toData() const 24 | { 25 | return dynamic_cast(this); 26 | } 27 | 28 | const PLArray * PLObject::toArray() const 29 | { 30 | return dynamic_cast(this); 31 | } 32 | 33 | const PLDict * PLObject::toDict() const 34 | { 35 | return dynamic_cast(this); 36 | } 37 | 38 | PLInteger::PLInteger() 39 | { 40 | } 41 | 42 | PLInteger::~PLInteger() 43 | { 44 | } 45 | 46 | PLType PLInteger::type() const 47 | { 48 | return PLType_Integer; 49 | } 50 | 51 | PLString::PLString() 52 | { 53 | } 54 | 55 | PLString::~PLString() 56 | { 57 | } 58 | 59 | PLType PLString::type() const 60 | { 61 | return PLType_String; 62 | } 63 | 64 | PLData::PLData() 65 | { 66 | } 67 | 68 | PLData::~PLData() 69 | { 70 | } 71 | 72 | PLType PLData::type() const 73 | { 74 | return PLType_Data; 75 | } 76 | 77 | PLArray::PLArray() 78 | { 79 | } 80 | 81 | PLArray::~PLArray() 82 | { 83 | for (std::vector::iterator it = m_array.begin(); it != m_array.end(); it++) 84 | delete *it; 85 | m_array.clear(); 86 | } 87 | 88 | PLType PLArray::type() const 89 | { 90 | return PLType_Array; 91 | } 92 | 93 | PLObject * PLArray::get(size_t idx) const 94 | { 95 | if (idx < m_array.size()) 96 | return m_array[idx]; 97 | else 98 | return nullptr; 99 | } 100 | 101 | PLDict::PLDict() 102 | { 103 | } 104 | 105 | PLDict::~PLDict() 106 | { 107 | for (std::map::iterator it = m_dict.begin(); it != m_dict.end(); it++) 108 | delete it->second; 109 | m_dict.clear(); 110 | } 111 | 112 | PLType PLDict::type() const 113 | { 114 | return PLType_Dict; 115 | } 116 | 117 | PLObject * PLDict::get(const char * name) const 118 | { 119 | std::map::const_iterator it = m_dict.find(name); 120 | 121 | if (it != m_dict.cend()) 122 | return it->second; 123 | else 124 | return nullptr; 125 | } 126 | 127 | PListXmlParser::PListXmlParser(const char * data, size_t size) 128 | : 129 | m_data(data), 130 | m_size(size) 131 | { 132 | m_idx = 0; 133 | } 134 | 135 | PListXmlParser::~PListXmlParser() 136 | { 137 | 138 | } 139 | 140 | PLObject * PListXmlParser::Parse() 141 | { 142 | std::string name; 143 | std::string content; 144 | TagType type; 145 | PLObject *root = nullptr; 146 | 147 | while (FindTag(name, type)) 148 | { 149 | if (type == TagType::Start && name == "plist") 150 | { 151 | try 152 | { 153 | root = ParseObject(); 154 | } 155 | catch (PLException &ex) 156 | { 157 | delete root; 158 | root = nullptr; 159 | 160 | fprintf(stderr, "XML PList parse error: %s\n", ex.what()); 161 | } 162 | 163 | break; 164 | } 165 | } 166 | 167 | return root; 168 | } 169 | 170 | PLArray * PListXmlParser::ParseArray() 171 | { 172 | PLArray *arr = new PLArray(); 173 | PLObject *obj; 174 | 175 | for (;;) 176 | { 177 | obj = ParseObject(); 178 | 179 | if (obj) 180 | arr->m_array.push_back(obj); 181 | else 182 | break; 183 | } 184 | 185 | return arr; 186 | } 187 | 188 | PLDict * PListXmlParser::ParseDict() 189 | { 190 | PLDict *dict = new PLDict(); 191 | PLObject *obj; 192 | 193 | TagType tagtype; 194 | std::string tagname; 195 | std::string key; 196 | 197 | for (;;) 198 | { 199 | FindTag(tagname, tagtype); 200 | 201 | if (tagname == "dict" && tagtype == TagType::End) 202 | break; 203 | 204 | if (tagname != "key" || tagtype != TagType::Start) 205 | throw PLException("Invalid tag in dict"); 206 | 207 | GetContent(key); 208 | 209 | if (key.empty()) 210 | throw PLException("Empty key in dict"); 211 | 212 | FindTag(tagname, tagtype); 213 | 214 | if (tagname != "key" || tagtype != TagType::End) 215 | throw PLException("Invalid tag type, expected "); 216 | 217 | obj = ParseObject(); 218 | 219 | if (obj) 220 | dict->m_dict[key] = obj; 221 | } 222 | 223 | return dict; 224 | } 225 | 226 | PLObject * PListXmlParser::ParseObject() 227 | { 228 | std::string name; 229 | TagType type; 230 | std::string content_str; 231 | const char *content_start; 232 | size_t content_size; 233 | PLObject *robj = nullptr; 234 | 235 | FindTag(name, type); 236 | 237 | if (type == TagType::Start) 238 | { 239 | if (name == "integer") 240 | { 241 | GetContent(content_str); 242 | FindTag(name, type); 243 | if (name != "integer" || type != TagType::End) 244 | throw PLException("Invalid end tag, expected ."); 245 | 246 | PLInteger *obj = new PLInteger(); 247 | obj->m_value = strtoll(content_str.c_str(), nullptr, 0); 248 | robj = obj; 249 | } 250 | else if (name == "string") 251 | { 252 | GetContent(content_str); 253 | FindTag(name, type); 254 | if (name != "string" || type != TagType::End) 255 | throw PLException("Invalid end tag, expected ."); 256 | 257 | PLString *obj = new PLString(); 258 | obj->m_string = content_str; 259 | robj = obj; 260 | } 261 | else if (name == "data") 262 | { 263 | content_start = m_data + m_idx; 264 | content_size = GetContentSize(); 265 | 266 | FindTag(name, type); 267 | if (name != "data" || type != TagType::End) 268 | throw PLException("Invalid end tag, expected ."); 269 | 270 | PLData *obj = new PLData(); 271 | Base64Decode(obj->m_data, content_start, content_size); 272 | robj = obj; 273 | } 274 | else if (name == "array") 275 | { 276 | PLArray *obj = ParseArray(); 277 | if (!obj) 278 | return nullptr; 279 | robj = obj; 280 | } 281 | else if (name == "dict") 282 | { 283 | PLDict *obj = ParseDict(); 284 | if (!obj) 285 | return nullptr; 286 | robj = obj; 287 | } 288 | else 289 | { 290 | throw PLException("Unexpected start tag."); 291 | } 292 | } 293 | else if (type == TagType::Empty) 294 | { 295 | if (name == "true") 296 | { 297 | PLInteger *obj = new PLInteger(); 298 | obj->m_value = 1; 299 | robj = obj; 300 | } 301 | else if (name == "false") 302 | { 303 | PLInteger *obj = new PLInteger(); 304 | obj->m_value = 0; 305 | robj = obj; 306 | } 307 | else 308 | { 309 | throw PLException("Unexpected empty tag."); 310 | } 311 | } 312 | else if (type == TagType::End) 313 | { 314 | return nullptr; 315 | } 316 | 317 | return robj; 318 | } 319 | 320 | void PListXmlParser::Base64Decode(std::vector& bin, const char * str, size_t size) 321 | { 322 | int chcnt; 323 | uint32_t buf; 324 | size_t ip; 325 | char ch; 326 | uint32_t dec; 327 | 328 | bin.clear(); 329 | bin.reserve(size * 4 / 3); 330 | 331 | chcnt = 0; 332 | buf = 0; 333 | 334 | for (ip = 0; ip < size; ip++) 335 | { 336 | ch = str[ip]; 337 | 338 | if (ch >= 'A' && ch <= 'Z') 339 | dec = ch - 'A'; 340 | else if (ch >= 'a' && ch <= 'z') 341 | dec = ch - 'a' + 0x1A; 342 | else if (ch >= '0' && ch <= '9') 343 | dec = ch - '0' + 0x34; 344 | else if (ch == '+') 345 | dec = 0x3E; 346 | else if (ch == '/') 347 | dec = 0x3F; 348 | else if (ch != '=') 349 | continue; 350 | else 351 | break; 352 | 353 | buf = (buf << 6) | dec; 354 | chcnt++; 355 | 356 | if (chcnt == 2) 357 | bin.push_back((buf >> 4) & 0xFF); 358 | else if (chcnt == 3) 359 | bin.push_back((buf >> 2) & 0xFF); 360 | else if (chcnt == 4) 361 | { 362 | bin.push_back((buf >> 0) & 0xFF); 363 | chcnt = 0; 364 | } 365 | } 366 | } 367 | 368 | bool PListXmlParser::FindTag(std::string & name, TagType & type) 369 | { 370 | char ch; 371 | bool in_name = false; 372 | 373 | name.clear(); 374 | type = TagType::None; 375 | 376 | do { 377 | ch = GetChar(); 378 | } while (ch != '<' && ch != 0); 379 | 380 | if (ch == 0) 381 | return false; 382 | 383 | ch = GetChar(); 384 | 385 | switch (ch) 386 | { 387 | case '?': 388 | type = TagType::ProcInstr; 389 | break; 390 | case '!': 391 | type = TagType::Doctype; 392 | break; 393 | case '/': 394 | type = TagType::End; 395 | in_name = true; 396 | break; 397 | default: 398 | type = TagType::Start; 399 | name.push_back(ch); 400 | in_name = true; 401 | break; 402 | } 403 | 404 | do 405 | { 406 | ch = GetChar(); 407 | 408 | switch (ch) 409 | { 410 | case 0x09: 411 | case 0x0A: 412 | case 0x0D: 413 | case ' ': 414 | case '>': 415 | in_name = false; 416 | break; 417 | case '/': 418 | if (m_data[m_idx] == '>') 419 | type = TagType::Empty; 420 | in_name = false; 421 | break; 422 | } 423 | 424 | if (in_name) 425 | name.push_back(ch); 426 | } while (ch != '>' && ch != 0); 427 | 428 | return true; 429 | } 430 | 431 | bool PListXmlParser::GetContent(std::string & content) 432 | { 433 | size_t start = m_idx; 434 | 435 | content.clear(); 436 | 437 | while (m_idx < m_size && m_data[m_idx] != '<') 438 | m_idx++; 439 | 440 | if (m_idx == m_size) 441 | return false; 442 | 443 | content.assign(m_data + start, m_idx - start); 444 | 445 | return true; 446 | } 447 | 448 | size_t PListXmlParser::GetContentSize() 449 | { 450 | size_t start = m_idx; 451 | 452 | while (m_idx < m_size && m_data[m_idx] != '<') 453 | m_idx++; 454 | 455 | if (m_idx == m_size) 456 | return m_idx - start; 457 | 458 | return m_idx - start; 459 | } 460 | 461 | PList::PList() 462 | { 463 | m_root = nullptr; 464 | } 465 | 466 | PList::~PList() 467 | { 468 | } 469 | 470 | bool PList::parseXML(const char * data, size_t size) 471 | { 472 | (void)data; 473 | (void)size; 474 | 475 | return false; 476 | } 477 | -------------------------------------------------------------------------------- /ApfsLib/PList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum PLType 8 | { 9 | PLType_Integer, 10 | PLType_String, 11 | PLType_Data, 12 | PLType_Array, 13 | PLType_Dict 14 | }; 15 | 16 | class PLInteger; 17 | class PLString; 18 | class PLData; 19 | class PLArray; 20 | class PLDict; 21 | 22 | class PLException : public std::exception 23 | { 24 | public: 25 | PLException(const char *reason) { m_reason = reason; } 26 | 27 | const char *what() const noexcept override { return m_reason; } 28 | 29 | private: 30 | const char *m_reason; 31 | }; 32 | 33 | class PLObject 34 | { 35 | protected: 36 | PLObject(); 37 | public: 38 | virtual ~PLObject(); 39 | 40 | virtual PLType type() const = 0; 41 | 42 | const PLInteger *toInt() const; 43 | const PLString *toString() const; 44 | const PLData *toData() const; 45 | const PLArray *toArray() const; 46 | const PLDict *toDict() const; 47 | }; 48 | 49 | class PLInteger : public PLObject 50 | { 51 | friend class PListXmlParser; 52 | public: 53 | PLInteger(); 54 | virtual ~PLInteger(); 55 | 56 | PLType type() const override; 57 | 58 | int64_t value() const { return m_value; } 59 | 60 | private: 61 | int64_t m_value; 62 | }; 63 | 64 | class PLString : public PLObject 65 | { 66 | friend class PListXmlParser; 67 | public: 68 | PLString(); 69 | virtual ~PLString(); 70 | 71 | PLType type() const override; 72 | 73 | const std::string &string() const { return m_string; } 74 | 75 | private: 76 | std::string m_string; 77 | }; 78 | 79 | class PLData : public PLObject 80 | { 81 | friend class PListXmlParser; 82 | public: 83 | PLData(); 84 | virtual ~PLData(); 85 | 86 | PLType type() const override; 87 | 88 | const uint8_t *data() const { return m_data.data(); } 89 | size_t size() const { return m_data.size(); } 90 | 91 | private: 92 | std::vector m_data; 93 | }; 94 | 95 | class PLArray : public PLObject 96 | { 97 | friend class PListXmlParser; 98 | public: 99 | PLArray(); 100 | virtual ~PLArray(); 101 | 102 | PLType type() const override; 103 | 104 | PLObject *get(size_t idx) const; 105 | size_t size() const { return m_array.size(); } 106 | 107 | const std::vector &array() const { return m_array; } 108 | 109 | private: 110 | std::vector m_array; 111 | }; 112 | 113 | class PLDict : public PLObject 114 | { 115 | friend class PListXmlParser; 116 | public: 117 | PLDict(); 118 | virtual ~PLDict(); 119 | 120 | PLType type() const override; 121 | 122 | PLObject *get(const char *name) const; 123 | 124 | const std::map &dict() const { return m_dict; } 125 | 126 | private: 127 | std::map m_dict; 128 | }; 129 | 130 | class PListXmlParser 131 | { 132 | enum class TagType 133 | { 134 | None, 135 | Empty, 136 | Start, 137 | End, 138 | ProcInstr, 139 | Doctype 140 | }; 141 | 142 | public: 143 | PListXmlParser(const char *data, size_t size); 144 | ~PListXmlParser(); 145 | 146 | PLObject *Parse(); 147 | 148 | private: 149 | PLArray * ParseArray(); 150 | PLDict * ParseDict(); 151 | PLObject * ParseObject(); 152 | void Base64Decode(std::vector &bin, const char *str, size_t size); 153 | 154 | bool FindTag(std::string &name, TagType &type); 155 | bool GetContent(std::string &content); 156 | size_t GetContentSize(); 157 | 158 | char GetChar() 159 | { 160 | if (m_idx < m_size) 161 | return m_data[m_idx++]; 162 | else 163 | return 0; 164 | } 165 | 166 | const char * const m_data; 167 | const size_t m_size; 168 | size_t m_idx; 169 | }; 170 | 171 | // Not used ... maybe later, if we need bplists ... 172 | class PList 173 | { 174 | public: 175 | PList(); 176 | ~PList(); 177 | 178 | bool parseXML(const char *data, size_t size); 179 | 180 | private: 181 | PLObject * m_root; 182 | }; 183 | -------------------------------------------------------------------------------- /ApfsLib/Unicode.cpp: -------------------------------------------------------------------------------- 1 | #include "Unicode.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "UnicodeTables_v10.h" 7 | 8 | int normalizeJimdo(char32_t ch, char32_t *nfd, uint8_t *ccc) 9 | { 10 | constexpr int SBase = 0xAC00; 11 | constexpr int LBase = 0x1100; 12 | constexpr int VBase = 0x1161; 13 | constexpr int TBase = 0x11A7; 14 | // constexpr int LCount = 19; 15 | constexpr int VCount = 21; 16 | constexpr int TCount = 28; 17 | constexpr int NCount = VCount * TCount; 18 | // constexpr int SCount = LCount * NCount; 19 | 20 | int SIndex = ch - SBase; 21 | int LIndex = SIndex / NCount; 22 | int VIndex = (SIndex % NCount) / TCount; 23 | int TIndex = SIndex % TCount; 24 | 25 | nfd[0] = LBase + LIndex; 26 | ccc[0] = 0; 27 | nfd[1] = VBase + VIndex; 28 | ccc[1] = 0; 29 | if (TIndex > 0) 30 | { 31 | nfd[2] = TBase + TIndex; 32 | ccc[2] = 0; 33 | return 3; 34 | } 35 | 36 | return 2; 37 | } 38 | 39 | int normalizeOptFoldU32Char(char32_t ch, bool case_fold, char32_t *nfd, uint8_t *ccc) 40 | { 41 | char32_t ch_idx; 42 | uint16_t hi_res; 43 | uint16_t mi_res; 44 | uint16_t lo_res; 45 | const uint16_t *seq_u16 = 0; 46 | const uint32_t *seq_u32 = 0; 47 | uint32_t seq_len = 0; 48 | uint32_t cnt; 49 | char32_t c; 50 | 51 | ccc[0] = 0; 52 | if (ch >= 0xF0000) 53 | { 54 | if ((ch & 0xFFFE) == 0xFFFE) 55 | return -1; 56 | else 57 | { 58 | nfd[0] = ch; 59 | return 1; 60 | } 61 | } 62 | 63 | if (ch < 0x2FB00) 64 | ch_idx = ch; 65 | else if ((ch & 0xFFFFFE00) == 0xE0000) 66 | ch_idx = ch - 0xB0500; 67 | else 68 | return -1; 69 | 70 | hi_res = nf_trie_hi[ch_idx >> 8]; 71 | 72 | if (hi_res == 0xFFFF) 73 | return -1; 74 | if (hi_res == 0 || ((hi_res & 0xFF00) == 0xAD00)) 75 | { 76 | nfd[0] = ch; 77 | ccc[0] = hi_res & 0xFF; 78 | return 1; 79 | } 80 | 81 | if (hi_res == 0xAC00) // Naja, fast ... sollte funktionieren 82 | return normalizeJimdo(ch, nfd, ccc); 83 | 84 | mi_res = nf_trie_mid[((hi_res & 0xFFF) << 4) | ((ch_idx >> 4) & 0xF)]; 85 | 86 | if (mi_res == 0xFFFF) 87 | return -1; 88 | 89 | if (mi_res == 0xAC00) 90 | return normalizeJimdo(ch, nfd, ccc); 91 | 92 | if (mi_res == 0 || (mi_res & 0xFF00) == 0xAD00) 93 | { 94 | ccc[0] = mi_res & 0xFF; 95 | if (case_fold && (ch < 0x500)) 96 | nfd[0] = nf_basic_cf[ch]; 97 | else 98 | nfd[0] = ch; 99 | return 1; 100 | } 101 | 102 | if ((mi_res & 0xFF00) == 0xAE00) 103 | { 104 | uint16_t mask = nf_u16_inv_masks[mi_res & 0xFF]; 105 | if ((mask >> (ch_idx & 0xF)) & 1) 106 | return -1; 107 | if (case_fold && (ch < 0x500)) 108 | nfd[0] = nf_basic_cf[ch]; 109 | else 110 | nfd[0] = ch; 111 | return 1; 112 | } 113 | 114 | lo_res = nf_trie_lo[((mi_res & 0xFFF) << 4) | (ch_idx & 0xF)]; 115 | 116 | if (lo_res == 0xFFFF) 117 | return -1; 118 | 119 | if (lo_res == 0xAC00) 120 | return normalizeJimdo(ch, nfd, ccc); 121 | 122 | if (lo_res < 0xB000 || lo_res >= 0xF900) 123 | { 124 | if (lo_res == 0 || ((lo_res & 0xFF00) == 0xAD00)) 125 | ccc[0] = lo_res & 0xFF; 126 | else 127 | ch = lo_res; 128 | 129 | if (case_fold && (ch < 0x500)) 130 | nfd[0] = nf_basic_cf[ch]; 131 | else 132 | nfd[0] = ch; 133 | return 1; 134 | } 135 | 136 | switch ((lo_res >> 12) & 0xF) 137 | { 138 | case 0xB: 139 | if ((lo_res & 0x800) && !case_fold) 140 | { 141 | nfd[0] = ch; 142 | return 1; 143 | } 144 | 145 | seq_u16 = nf_u16_seq_2 + 2 * (lo_res & 0x7FF); 146 | seq_len = 2; 147 | break; 148 | 149 | case 0xC: 150 | if ((lo_res & 0x800) && !case_fold) 151 | { 152 | nfd[0] = ch; 153 | return 1; 154 | } 155 | 156 | seq_u16 = nf_u16_seq_3 + 3 * (lo_res & 0x7FF); 157 | seq_len = 3; 158 | break; 159 | 160 | case 0xD: 161 | seq_u16 = nf_u16_seq_misc + (lo_res & 0x3FF) + 1; 162 | seq_len = nf_u16_seq_misc[lo_res & 0x3FF]; // Rest >> 4 in eax 163 | ccc[0] = seq_len >> 4; 164 | seq_len &= 0xF; 165 | if (seq_len > 4) 166 | return 0; 167 | break; 168 | 169 | case 0xE: 170 | if ((lo_res & 0x800) && !case_fold) 171 | { 172 | nfd[0] = ch; 173 | return 1; 174 | } 175 | 176 | seq_u32 = nf_u32_char + (lo_res & 0x7FF); 177 | seq_len = 1; 178 | break; 179 | 180 | case 0xF: 181 | seq_u32 = nf_u32_seq_misc + (lo_res & 0x3FF) + 1; 182 | seq_len = nf_u32_seq_misc[lo_res & 0x3FF]; 183 | ccc[0] = seq_len >> 4; 184 | seq_len &= 0xF; 185 | if (seq_len > 4) 186 | return 0; 187 | break; 188 | } 189 | 190 | for (cnt = 0; cnt < seq_len; cnt++) 191 | { 192 | if (seq_u16) 193 | c = seq_u16[cnt]; 194 | else 195 | c = seq_u32[cnt]; 196 | nfd[cnt] = c; 197 | 198 | if (cnt > 0) 199 | { 200 | if (c >= 0xF0000) 201 | { 202 | ccc[cnt] = 0; 203 | continue; 204 | } 205 | 206 | if (c == 0x3B9) 207 | { 208 | ccc[cnt] = 0xF0; 209 | continue; 210 | } 211 | 212 | ch_idx = (c > 0x2FB00) ? (c - 0xB0500) : c; 213 | 214 | hi_res = nf_trie_hi[ch_idx >> 8]; 215 | 216 | if (hi_res == 0 || ((hi_res & 0xFF00) == 0xAD00)) 217 | { 218 | ccc[cnt] = hi_res & 0xFF; 219 | continue; 220 | } 221 | 222 | mi_res = nf_trie_mid[((hi_res & 0xFFF) << 4) | ((ch_idx >> 4) & 0xF)]; 223 | 224 | if (mi_res == 0 || ((mi_res & 0xFF00) == 0xAE00)) 225 | { 226 | ccc[cnt] = 0; 227 | continue; 228 | } 229 | if ((mi_res & 0xFF00) == 0xAD00) 230 | { 231 | ccc[cnt] = mi_res & 0xFF; 232 | continue; 233 | } 234 | 235 | lo_res = nf_trie_lo[((mi_res & 0xFFF) << 4) | (ch_idx & 0xF)]; 236 | 237 | if ((lo_res & 0xFF00) == 0xAD00) 238 | ccc[cnt] = lo_res & 0xFF; 239 | else 240 | ccc[cnt] = 0; 241 | } 242 | } 243 | 244 | if (case_fold) 245 | { 246 | if (nfd[0] < 0x500) 247 | nfd[0] = nf_basic_cf[nfd[0]]; 248 | 249 | if (cnt >= 2) 250 | { 251 | if (nfd[cnt - 1] == 0x345) 252 | nfd[cnt - 1] = 0x3B9; 253 | } 254 | } 255 | 256 | return cnt; 257 | } 258 | 259 | void CanonicalReorder(char32_t *nfd, uint8_t *ccc, size_t len) 260 | { 261 | size_t i; 262 | size_t k; 263 | bool sorted = false; 264 | uint8_t sort_ccc; 265 | char32_t sort_ch; 266 | 267 | for (k = 0; k < (len - 1); k++) 268 | { 269 | if (ccc[k] == 0) 270 | continue; 271 | 272 | if (ccc[k + 1] == 0) 273 | continue; 274 | 275 | do 276 | { 277 | sorted = true; 278 | 279 | for (i = k; i < (len - 1) && ccc[i + 1] != 0; i++) 280 | { 281 | if (ccc[i] > ccc[i + 1]) 282 | { 283 | sorted = false; 284 | sort_ccc = ccc[i + 1]; 285 | ccc[i + 1] = ccc[i]; 286 | ccc[i] = sort_ccc; 287 | sort_ch = nfd[i + 1]; 288 | nfd[i + 1] = nfd[i]; 289 | nfd[i] = sort_ch; 290 | } 291 | } 292 | } while (!sorted); 293 | 294 | k = i + 1; 295 | } 296 | } 297 | 298 | bool NormalizeFoldString(std::vector &nfd, const std::vector &in, bool case_fold) 299 | { 300 | char32_t ch; 301 | size_t k; 302 | size_t out_size; 303 | int rc; 304 | 305 | std::vector ccc; 306 | 307 | nfd.clear(); 308 | nfd.resize(in.size() * 4); 309 | ccc.resize(nfd.size()); 310 | 311 | for (k = 0, out_size = 0; k < in.size(); k++) 312 | { 313 | ch = in[k]; 314 | rc = normalizeOptFoldU32Char(ch, case_fold, nfd.data() + out_size, ccc.data() + out_size); 315 | 316 | if (rc == -1) 317 | return false; 318 | 319 | out_size += rc; 320 | } 321 | 322 | nfd.resize(out_size); 323 | ccc.resize(out_size); 324 | 325 | CanonicalReorder(nfd.data(), ccc.data(), nfd.size()); 326 | 327 | return true; 328 | } 329 | -------------------------------------------------------------------------------- /ApfsLib/Unicode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int normalizeOptFoldU32Char(char32_t ch, bool case_insensitive, char32_t *sequence_out, unsigned char *unknown_out); 8 | void CanonicalReorder(char32_t *nfd, uint8_t *ccc, size_t len); 9 | 10 | bool NormalizeFoldString(std::vector &out, const std::vector &in, bool case_fold); 11 | -------------------------------------------------------------------------------- /ApfsLib/Util.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of apfs-fuse, a read-only implementation of APFS 3 | (Apple File System) for FUSE. 4 | Copyright (C) 2017 Simon Gander 5 | 6 | Apfs-fuse is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | Apfs-fuse is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with apfs-fuse. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "ApfsTypes.h" 29 | 30 | uint64_t Fletcher64(const uint32_t *data, size_t cnt, uint64_t init); 31 | bool VerifyBlock(const void *block, size_t size); 32 | bool IsZero(const uint8_t *data, size_t size); 33 | bool IsEmptyBlock(const void *data, size_t blksize); 34 | void DumpHex(std::ostream &os, const uint8_t *data, size_t size, size_t line_size = 16); 35 | void DumpBuffer(const uint8_t *data, size_t len, const char *label); 36 | 37 | std::string uuidstr(const apfs_uuid_t &uuid); 38 | std::string hexstr(const uint8_t *data, size_t size); 39 | void dump_utf8(std::ostream &st, const uint8_t *str); 40 | void dump_utf32(std::ostream &st, const char32_t *str, size_t size); 41 | 42 | uint32_t HashFilename(const uint8_t *utf8str, uint16_t name_len, bool case_fold); 43 | 44 | int apfs_strncmp(const uint8_t *s1, size_t s1_len, const uint8_t *s2, size_t s2_len); 45 | int StrCmpUtf8NormalizedFolded(const uint8_t *s1, const uint8_t *s2, bool case_fold); 46 | 47 | bool Utf8toUtf32(std::vector &str32, const uint8_t * str); 48 | 49 | size_t DecompressZLib(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size); 50 | size_t DecompressADC(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size); 51 | size_t DecompressLZVN(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size); 52 | size_t DecompressBZ2(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size); 53 | size_t DecompressLZFSE(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size); 54 | size_t DecompressLZBITMAP(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size); 55 | 56 | bool GetPassword(std::string &pw); 57 | 58 | int log2(uint32_t val); 59 | 60 | void log_debug(const char *msg, ...) __attribute__((format(printf, 1, 2))); 61 | void log_warn(const char *msg, ...) __attribute__((format(printf, 1, 2))); 62 | void log_error(const char *msg, ...) __attribute__((format(printf, 1, 2))); 63 | -------------------------------------------------------------------------------- /ApfsUtil/ApfsUtil.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | static void print_apfs_uuid(const apfs_uuid_t &uuid) 9 | { 10 | printf("%02X%02X%02X%02X-", uuid[0], uuid[1], uuid[2], uuid[3]); 11 | printf("%02X%02X-", uuid[4], uuid[5]); 12 | printf("%02X%02X-", uuid[6], uuid[7]); 13 | printf("%02X%02X-", uuid[8], uuid[9]); 14 | printf("%02X%02X%02X%02X%02X%02X", uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); 15 | } 16 | 17 | static void print_role(uint16_t role) 18 | { 19 | static const char *rolestr[] = { 20 | "System", "User", "Recovery", "VM", "Preboot", "Installer" 21 | }; 22 | static const char *rolestr_enum[] = { 23 | "", "Data", "Baseband", "Update", 24 | "Xart", "Hardware", "Backup", "Reserved-7", 25 | "Reserved-8", "Enterprise", "Reserved-10", "Prelogin" 26 | }; 27 | bool first = true; 28 | int k; 29 | 30 | if (role == 0) 31 | printf("No specific role"); 32 | else if (role <= 0x20) { 33 | for (k = 0; k < 6; k++) { 34 | if (role & (1 << k)) { 35 | if (!first) 36 | printf(", "); 37 | printf("%s", rolestr[k]); 38 | first = false; 39 | } 40 | } 41 | } else if (role <= APFS_VOL_ROLE_PRELOGIN) { 42 | printf("%s", rolestr_enum[role >> APFS_VOLUME_ENUM_SHIFT]); 43 | } 44 | // printf(" (%d)", role); 45 | } 46 | 47 | static void print_filevault(uint64_t flags) 48 | { 49 | if (flags == 1) 50 | printf("No"); 51 | else 52 | printf("Yes"); 53 | } 54 | 55 | int main(int argc, char *argv[]) 56 | { 57 | const char *devname = nullptr; 58 | Device *device = nullptr; 59 | ApfsContainer *container = nullptr; 60 | uint64_t offset; 61 | uint64_t size; 62 | apfs_superblock_t apsb; 63 | 64 | g_debug = 0; 65 | 66 | if (argc < 2) 67 | { 68 | printf("Syntax: %s [device]\n", argv[0]); 69 | return EINVAL; 70 | } 71 | 72 | devname = argv[1]; 73 | 74 | device = Device::OpenDevice(devname); 75 | 76 | if (device) { 77 | offset = 0; 78 | size = device->GetSize(); 79 | 80 | GptPartitionMap gpt; 81 | if (gpt.LoadAndVerify(*device)) { 82 | printf("Found partitions:\n"); 83 | gpt.ListEntries(); 84 | 85 | int partnum = gpt.FindFirstAPFSPartition(); 86 | if (partnum >= 0) { 87 | printf("First APFS partition is %d\n", partnum); 88 | gpt.GetPartitionOffsetAndSize(partnum, offset, size); 89 | } 90 | printf("\n"); 91 | } 92 | 93 | container = new ApfsContainer(device, offset, size); 94 | 95 | if (container->Init()) { 96 | // printf("Listing volumes:\n"); 97 | for (int k = 0; k < NX_MAX_FILE_SYSTEMS; k++) { 98 | if (!container->GetVolumeInfo(k, apsb)) 99 | continue; 100 | printf("Volume %d ", k); 101 | print_apfs_uuid(apsb.apfs_vol_uuid); 102 | printf("\n"); 103 | printf("---------------------------------------------\n"); 104 | 105 | printf("Role: "); 106 | print_role(apsb.apfs_role); 107 | printf("\n"); 108 | printf("Name: %s", apsb.apfs_volname); 109 | if (apsb.apfs_incompatible_features & APFS_INCOMPAT_CASE_INSENSITIVE) 110 | printf(" (Case-insensitive)\n"); 111 | else if (apsb.apfs_incompatible_features & APFS_INCOMPAT_NORMALIZATION_INSENSITIVE) 112 | printf(" (Case-sensitive)\n"); 113 | else 114 | printf("\n"); 115 | printf("Capacity Consumed: %" PRIu64 " Bytes\n", apsb.apfs_fs_alloc_count * container->GetBlocksize()); 116 | printf("FileVault: "); 117 | print_filevault(apsb.apfs_fs_flags & APFS_FS_CRYPTOFLAGS); 118 | printf("\n"); 119 | if (apsb.apfs_snap_meta_tree_oid) { 120 | printf("Snapshots:\n"); 121 | BTree snap_tree(*container); 122 | BTreeIterator it; 123 | BTreeEntry bte; 124 | const j_snap_metadata_key_t *sme_k; 125 | const j_snap_metadata_val_t *sme_v; 126 | 127 | snap_tree.Init(apsb.apfs_snap_meta_tree_oid, apsb.apfs_o.o_xid); 128 | if (snap_tree.GetIteratorBegin(it)) { 129 | for (;;) { 130 | if (!it.GetEntry(bte)) break; 131 | sme_k = reinterpret_cast(bte.key); 132 | sme_v = reinterpret_cast(bte.val); 133 | if ((sme_k->hdr.obj_id_and_type >> OBJ_TYPE_SHIFT) != APFS_TYPE_SNAP_METADATA) break; 134 | printf(" %" PRIu64 " : '%s'\n", sme_k->hdr.obj_id_and_type & OBJ_ID_MASK, sme_v->name); 135 | if (!it.next()) break; 136 | } 137 | } 138 | } 139 | printf("\n"); 140 | } 141 | } else { 142 | printf("Unable to open APFS container\n"); 143 | } 144 | 145 | delete container; 146 | 147 | device->Close(); 148 | delete device; 149 | } else { 150 | printf("Error opening device.\n"); 151 | return EIO; 152 | } 153 | 154 | 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | # set(CMAKE_C_COMPILER "clang") 3 | # set(CMAKE_CXX_COMPILER "clang++") 4 | 5 | project(Apfs) 6 | 7 | option(USE_FUSE3 "Use the FUSE 3 library (required on 32-bit systems)" ON) 8 | 9 | set(CMAKE_C_STANDARD 99) 10 | set(CMAKE_CXX_STANDARD 11) 11 | set(CMAKE_BUILD_TYPE Release) 12 | 13 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") 15 | 16 | # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -march=native -fsanitize=undefined") 17 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -march=native -fsanitize=undefined") 18 | # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") 19 | 20 | include_directories(. 3rdparty/lzfse/src) 21 | 22 | add_library(lzfse 23 | 3rdparty/lzfse/src/lzfse.h 24 | 3rdparty/lzfse/src/lzfse_decode.c 25 | 3rdparty/lzfse/src/lzfse_decode_base.c 26 | 3rdparty/lzfse/src/lzfse_encode.c 27 | 3rdparty/lzfse/src/lzfse_encode_base.c 28 | 3rdparty/lzfse/src/lzfse_encode_tables.h 29 | 3rdparty/lzfse/src/lzfse_fse.c 30 | 3rdparty/lzfse/src/lzfse_fse.h 31 | 3rdparty/lzfse/src/lzfse_internal.h 32 | 3rdparty/lzfse/src/lzfse_tunables.h 33 | 3rdparty/lzfse/src/lzvn_decode_base.c 34 | 3rdparty/lzfse/src/lzvn_decode_base.h 35 | 3rdparty/lzfse/src/lzvn_encode_base.c 36 | 3rdparty/lzfse/src/lzvn_encode_base.h 37 | ) 38 | 39 | add_library(crypto 40 | Crypto/Aes.cpp 41 | Crypto/Aes.h 42 | Crypto/AesXts.cpp 43 | Crypto/AesXts.h 44 | Crypto/Asn1Der.cpp 45 | Crypto/Asn1Der.h 46 | Crypto/Crypto.cpp 47 | Crypto/Crypto.h 48 | Crypto/Des.cpp 49 | Crypto/Des.h 50 | Crypto/Sha1.cpp 51 | Crypto/Sha1.h 52 | Crypto/Sha256.cpp 53 | Crypto/Sha256.h 54 | Crypto/TripleDes.cpp 55 | Crypto/TripleDes.h 56 | ) 57 | 58 | add_library(apfs 59 | ApfsLib/ApfsContainer.cpp 60 | ApfsLib/ApfsContainer.h 61 | ApfsLib/ApfsDir.cpp 62 | ApfsLib/ApfsDir.h 63 | ApfsLib/ApfsNodeMapper.cpp 64 | ApfsLib/ApfsNodeMapper.h 65 | ApfsLib/ApfsNodeMapperBTree.cpp 66 | ApfsLib/ApfsNodeMapperBTree.h 67 | ApfsLib/ApfsVolume.cpp 68 | ApfsLib/ApfsVolume.h 69 | ApfsLib/BlockDumper.cpp 70 | ApfsLib/BlockDumper.h 71 | ApfsLib/BTree.cpp 72 | ApfsLib/BTree.h 73 | ApfsLib/CheckPointMap.cpp 74 | ApfsLib/CheckPointMap.h 75 | ApfsLib/Crc32.cpp 76 | ApfsLib/Crc32.h 77 | ApfsLib/Decmpfs.cpp 78 | ApfsLib/Decmpfs.h 79 | ApfsLib/Device.cpp 80 | ApfsLib/Device.h 81 | ApfsLib/DeviceDMG.cpp 82 | ApfsLib/DeviceDMG.h 83 | ApfsLib/DeviceLinux.cpp 84 | ApfsLib/DeviceLinux.h 85 | ApfsLib/DeviceMac.cpp 86 | ApfsLib/DeviceMac.h 87 | ApfsLib/DeviceSparseImage.cpp 88 | ApfsLib/DeviceSparseImage.h 89 | ApfsLib/DeviceWinFile.cpp 90 | ApfsLib/DeviceWinFile.h 91 | ApfsLib/DeviceWinPhys.cpp 92 | ApfsLib/DeviceWinPhys.h 93 | ApfsLib/DiskImageFile.cpp 94 | ApfsLib/DiskImageFile.h 95 | ApfsLib/DiskStruct.h 96 | ApfsLib/Endian.h 97 | ApfsLib/Global.h 98 | ApfsLib/GptPartitionMap.cpp 99 | ApfsLib/GptPartitionMap.h 100 | ApfsLib/KeyMgmt.cpp 101 | ApfsLib/KeyMgmt.h 102 | ApfsLib/PList.cpp 103 | ApfsLib/PList.h 104 | ApfsLib/Util.cpp 105 | ApfsLib/Util.h 106 | ApfsLib/Unicode.cpp 107 | ApfsLib/Unicode.h) 108 | target_link_libraries(apfs z bz2 lzfse crypto) 109 | target_compile_definitions(apfs PUBLIC _FILE_OFFSET_BITS=64 _DARWIN_USE_64_BIT_INODE) 110 | 111 | add_executable(apfs-dump 112 | ApfsDump/Dumper.cpp 113 | ApfsDump/Dumper.h 114 | ApfsDump/Apfs.cpp) 115 | target_link_libraries(apfs-dump apfs) 116 | 117 | add_executable(apfs-dump-quick 118 | ApfsDumpQuick/ApfsDumpQuick.cpp) 119 | target_link_libraries(apfs-dump-quick apfs) 120 | 121 | add_executable(apfs-fuse 122 | apfsfuse/ApfsFuse.cpp) 123 | target_compile_definitions(apfs-fuse PRIVATE _FILE_OFFSET_BITS=64 _DARWIN_USE_64_BIT_INODE) 124 | if (APPLE) 125 | target_include_directories(apfs-fuse PRIVATE /usr/local/include/osxfuse/) 126 | # link_directories(/usr/local/lib/) 127 | target_link_libraries(apfs-fuse apfs /usr/local/lib/libosxfuse.dylib) 128 | else() 129 | if (USE_FUSE3) 130 | target_link_libraries(apfs-fuse apfs fuse3) 131 | else() 132 | target_link_libraries(apfs-fuse apfs fuse) 133 | target_compile_definitions(apfs-fuse PRIVATE USE_FUSE2) 134 | endif() 135 | endif() 136 | 137 | add_executable(apfsutil ApfsUtil/ApfsUtil.cpp) 138 | target_link_libraries(apfsutil apfs) 139 | 140 | include(GNUInstallDirs) 141 | install(TARGETS apfs-fuse RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 142 | install(TARGETS apfsutil RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 143 | -------------------------------------------------------------------------------- /Crypto/Aes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | * @brief The AES class 8 | * 9 | * Class for AES en- and decryption. Supports all AES modes specified 10 | * in NIST FIPS-197. 11 | * 12 | * Several chaining modes are supported, namely ECB, CBC, CFB and OFB. 13 | * 14 | * Usage: 15 | * 16 | * @li Set a key with SetKey. 17 | * @li If necessary, set an IV with SetIV. 18 | * @li Encrypt / Decrypt your data with the desired functions. 19 | * @li When you are done, CleanUp behind you to make life harder for hackers. 20 | */ 21 | class AES 22 | { 23 | public: 24 | AES(); 25 | ~AES(); 26 | 27 | /** 28 | * @brief Encryption key size in bits. 29 | */ 30 | enum Mode { 31 | AES_128, 32 | AES_192, 33 | AES_256 34 | }; 35 | 36 | /** 37 | * @brief Clean Up 38 | * 39 | * Deletes all data which may contain key / plaintext material. 40 | * 41 | * @note After using this, a new key must be set before additional 42 | * encryption is done. 43 | */ 44 | void CleanUp(); 45 | 46 | /** 47 | * @brief Set AES Key 48 | * 49 | * Sets the AES encryption key for this instance. Also sets the IV 50 | * to 0. 51 | * 52 | * @param key Key data. Length depends on the mode parameter. 53 | * @param mode Encryption mode (128, 192 or 256 bit key). 54 | */ 55 | void SetKey(const uint8_t *key, Mode mode); 56 | 57 | /** 58 | * @brief Set Initialization Vector 59 | * 60 | * Set the initialization vector. This also resets the counter for the 61 | * stream cipher modes (CFB, OFB). 62 | * 63 | * @param iv Initialization Vector. If NULL, a 0-vector is used. 64 | */ 65 | void SetIV(const uint8_t *iv); 66 | 67 | /** 68 | * @brief Encrypt Block 69 | * 70 | * Encrypts a block of 16 bytes with the previously selected key (ECB mode). 71 | * 72 | * @param src Plaintext block (128 bits, 16 bytes) 73 | * @param dst Encrypted block (128 bits, 16 bytes) 74 | */ 75 | void Encrypt(const uint8_t *src, uint8_t *dst); 76 | 77 | /** 78 | * @brief Decrypt Block 79 | * 80 | * Decrypts a block of 16 bytes with the previously selected key (ECB mode). 81 | * 82 | * @param src Encrypted block (128 bits, 16 bytes) 83 | * @param dst Decrypted block (128 bits, 16 bytes) 84 | */ 85 | void Decrypt(const uint8_t *src, uint8_t *dst); 86 | 87 | /** 88 | * @brief Encrypt CBC 89 | * 90 | * Encrypt data in CBC mode. 91 | * 92 | * @param src Plaintext data. 93 | * @param dst Encrypted data. 94 | * @param size Number of bytes. Must be a multiple of 16. 95 | */ 96 | void EncryptCBC(const uint8_t *src, uint8_t *dst, size_t size); 97 | 98 | /** 99 | * @brief Decrypt CBC 100 | * 101 | * Decrypt data in CBC mode. 102 | * 103 | * @param src Encrypted data. 104 | * @param dst Decrypted data. 105 | * @param size Number of bytes. Must be a multiple of 16. 106 | */ 107 | void DecryptCBC(const uint8_t *src, uint8_t *dst, size_t size); 108 | 109 | /** 110 | * @brief Encrypt CFB 111 | * 112 | * Encrypt data in CFB mode. 113 | * 114 | * @param src Plaintext data. 115 | * @param dst Encrypted data. 116 | * @param size Number of bytes. 117 | */ 118 | void EncryptCFB(const uint8_t *src, uint8_t *dst, size_t size); 119 | 120 | /** 121 | * @brief Decrypt CFB 122 | * 123 | * Decrypt data in CFB mode. 124 | * 125 | * @param src Encrypted data. 126 | * @param dst Decrypted data. 127 | * @param size Number of bytes. 128 | */ 129 | void DecryptCFB(const uint8_t *src, uint8_t *dst, size_t size); 130 | 131 | /** 132 | * @brief Crypt OFB 133 | * 134 | * En- / Decrypt data in OFB mode. 135 | * 136 | * @param src Input data. 137 | * @param dst Output data. 138 | * @param size Number of bytes. 139 | */ 140 | void CryptOFB(const uint8_t *src, uint8_t *dst, size_t size); 141 | 142 | private: 143 | /// Encryption round key. 144 | uint32_t _erk[60]; 145 | /// Decryption round key. 146 | uint32_t _drk[60]; 147 | /// Initialization vector. 148 | uint8_t _iv[16]; 149 | /// Byte counter for CFB / OFB modes. 150 | int _tp; 151 | 152 | // AES constants (depending on mode) 153 | int Nk; 154 | int Nr; 155 | int Nb; 156 | 157 | // AES tables 158 | static const uint32_t Td0[256]; 159 | static const uint32_t Td1[256]; 160 | static const uint32_t Td2[256]; 161 | static const uint32_t Td3[256]; 162 | static const uint32_t Td4[256]; 163 | static const uint32_t Te0[256]; 164 | static const uint32_t Te1[256]; 165 | static const uint32_t Te2[256]; 166 | static const uint32_t Te3[256]; 167 | static const uint32_t Te4[256]; 168 | static const uint32_t rcon[11]; 169 | 170 | /// Zero data 171 | static const uint8_t zeros[32]; 172 | }; 173 | -------------------------------------------------------------------------------- /Crypto/AesXts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "AesXts.h" 3 | 4 | #include 5 | #include 6 | 7 | AesXts::AesXts() 8 | { 9 | CleanUp(); 10 | } 11 | 12 | AesXts::~AesXts() 13 | { 14 | } 15 | 16 | void AesXts::CleanUp() 17 | { 18 | m_aes_1.CleanUp(); 19 | m_aes_2.CleanUp(); 20 | } 21 | 22 | void AesXts::SetKey(const uint8_t* key1, const uint8_t* key2) 23 | { 24 | m_aes_1.SetKey(key1, AES::AES_128); 25 | m_aes_2.SetKey(key2, AES::AES_128); 26 | } 27 | 28 | void AesXts::Encrypt(uint8_t* cipher, const uint8_t* plain, std::size_t size, uint64_t unit_no) 29 | { 30 | uint8_t pp[0x10]; 31 | uint8_t cc[0x10]; 32 | uint64_t tweak[2]; 33 | size_t k = 0; 34 | 35 | tweak[0] = htole64(unit_no); 36 | tweak[1] = 0; 37 | 38 | m_aes_2.Encrypt(reinterpret_cast(tweak), reinterpret_cast(tweak)); 39 | 40 | for (k = 0; k < size; k += 0x10) 41 | { 42 | Xor128(pp, plain + k, tweak); 43 | m_aes_1.Encrypt(pp, cc); 44 | Xor128(cipher + k, cc, tweak); 45 | MultiplyTweak(tweak); 46 | } 47 | } 48 | 49 | void AesXts::Decrypt(uint8_t* plain, const uint8_t* cipher, std::size_t size, uint64_t unit_no) 50 | { 51 | uint8_t pp[0x10]; 52 | uint8_t cc[0x10]; 53 | uint64_t tweak[2]; 54 | size_t k = 0; 55 | 56 | tweak[0] = htole64(unit_no); 57 | tweak[1] = 0; 58 | 59 | m_aes_2.Encrypt(reinterpret_cast(tweak), reinterpret_cast(tweak)); 60 | 61 | for (k = 0; k < size; k += 0x10) 62 | { 63 | Xor128(cc, cipher + k, tweak); 64 | m_aes_1.Decrypt(cc, pp); 65 | Xor128(plain + k, pp, tweak); 66 | MultiplyTweak(tweak); 67 | } 68 | } 69 | 70 | void AesXts::Xor128(void *out, const void *op1, const void *op2) 71 | { 72 | reinterpret_cast(out)[0] = reinterpret_cast(op1)[0] ^ reinterpret_cast(op2)[0]; 73 | reinterpret_cast(out)[1] = reinterpret_cast(op1)[1] ^ reinterpret_cast(op2)[1]; 74 | } 75 | 76 | void AesXts::MultiplyTweak(uint64_t* tweak) 77 | { 78 | uint8_t c1; 79 | uint8_t c2; 80 | 81 | #if (__BYTE_ORDER == __LITTLE_ENDIAN) 82 | c1 = (tweak[0] & 0x8000000000000000ULL) ? 1 : 0; 83 | c2 = (tweak[1] & 0x8000000000000000ULL) ? 0x87 : 0; 84 | 85 | tweak[0] = (tweak[0] << 1) ^ c2; 86 | tweak[1] = (tweak[1] << 1) | c1; 87 | #else 88 | uint64_t t0; 89 | uint64_t t1; 90 | 91 | t0 = le64toh(tweak[0]); 92 | t1 = le64toh(tweak[1]); 93 | 94 | c1 = (t0 & 0x8000000000000000ULL) ? 1 : 0; 95 | c2 = (t1 & 0x8000000000000000ULL) ? 0x87 : 0; 96 | t0 = (t0 << 1) ^ c2; 97 | t1 = (t1 << 1) | c1; 98 | 99 | tweak[0] = htole64(t0); 100 | tweak[1] = htole64(t1); 101 | #endif 102 | } 103 | -------------------------------------------------------------------------------- /Crypto/AesXts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Aes.h" 6 | 7 | class AesXts 8 | { 9 | public: 10 | AesXts(); 11 | ~AesXts(); 12 | 13 | void CleanUp(); 14 | 15 | void SetKey(const uint8_t *key1, const uint8_t *key2); 16 | 17 | void Encrypt(uint8_t *cipher, const uint8_t *plain, size_t size, uint64_t unit_no); 18 | void Decrypt(uint8_t *plain, const uint8_t *cipher, size_t size, uint64_t unit_no); 19 | 20 | private: 21 | void Xor128(void *out, const void *op1, const void *op2); 22 | void MultiplyTweak(uint64_t *tweak); 23 | 24 | AES m_aes_1; 25 | AES m_aes_2; 26 | }; 27 | -------------------------------------------------------------------------------- /Crypto/Asn1Der.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Asn1Der.h" 6 | 7 | const uint8_t* der_decode_tag(der_tag_t& tag, const uint8_t* der, const uint8_t* der_end) 8 | { 9 | uint64_t t; 10 | uint8_t b; 11 | uint8_t flg; 12 | 13 | tag = 0; 14 | 15 | if (der == nullptr || der >= der_end) 16 | return nullptr; 17 | flg = *der++; 18 | 19 | if ((flg & 0x1F) == 0x1F) { 20 | t = 0; 21 | do { 22 | if (der >= der_end) return nullptr; 23 | b = *der++; 24 | t = t << 7 | (b & 0x7F); 25 | } while (b & 0x80); 26 | tag = static_cast(flg & 0xE0) << 56 | (t & 0x1FFFFFFFFFFFFFFFU); 27 | } 28 | else { 29 | tag = static_cast(flg & 0xE0) << 56 | (flg & 0x1F); 30 | } 31 | return der; 32 | } 33 | 34 | const uint8_t* der_decode_len(size_t& len, const uint8_t* der, const uint8_t* der_end) 35 | { 36 | uint64_t s; 37 | uint8_t nb; 38 | unsigned k; 39 | 40 | len = 0; 41 | 42 | if (der == nullptr || der >= der_end) 43 | return nullptr; 44 | nb = *der++; 45 | 46 | if (nb & 0x80) { 47 | s = 0; 48 | nb &= 0x7F; 49 | if ((der + nb) >= der_end) return nullptr; 50 | for (k = 0; k < nb; k++) 51 | s = s << 8 | *der++; 52 | len = s; 53 | } 54 | else { 55 | len = nb; 56 | } 57 | return der; 58 | } 59 | 60 | const uint8_t* der_decode_tl(der_tag_t expected_tag, size_t& len, const uint8_t* der, const uint8_t* der_end) 61 | { 62 | der_tag_t tag; 63 | der = der_decode_tag(tag, der, der_end); 64 | if (tag != expected_tag) 65 | return nullptr; 66 | der = der_decode_len(len, der, der_end); 67 | if (der + len > der_end) 68 | return nullptr; 69 | return der; 70 | } 71 | 72 | const uint8_t* der_decode_constructed_tl(der_tag_t expected_tag, const uint8_t*& body_end, const uint8_t* der, const uint8_t* der_end) 73 | { 74 | size_t len; 75 | der = der_decode_tl(expected_tag, len, der, der_end); 76 | if (der == nullptr) 77 | return nullptr; 78 | body_end = der + len; 79 | return der; 80 | } 81 | 82 | const uint8_t* der_decode_sequence_tl(const uint8_t*& body_end, const uint8_t* der, const uint8_t* der_end) 83 | { 84 | return der_decode_constructed_tl(DER_CONSTRUCTED | DER_SEQUENCE, body_end, der, der_end); 85 | } 86 | 87 | const uint8_t* der_decode_uint(size_t n, uint64_t& val, const uint8_t* der, const uint8_t* der_end) 88 | { 89 | uint64_t v = 0; 90 | size_t k; 91 | 92 | val = 0; 93 | if (der == nullptr || (der + n) > der_end) 94 | return nullptr; 95 | for (k = 0; k < n; k++) 96 | v = v << 8 | *der++; 97 | val = v; 98 | return der; 99 | } 100 | 101 | const uint8_t * der_decode_uint64(der_tag_t expected_tag, uint64_t& val, const uint8_t* der, const uint8_t* der_end) 102 | { 103 | size_t len; 104 | 105 | val = 0; 106 | der = der_decode_tl(expected_tag, len, der, der_end); 107 | if (der == nullptr || len > 8) return nullptr; 108 | der = der_decode_uint(len, val, der, der_end); 109 | return der; 110 | } 111 | 112 | const uint8_t * der_decode_octet_string_copy(der_tag_t expected_tag, uint8_t* buf, size_t len, const uint8_t* der, const uint8_t* der_end) 113 | { 114 | size_t slen; 115 | 116 | der = der_decode_tl(expected_tag, slen, der, der_end); 117 | if (der == nullptr || slen != len || (der + slen) > der_end) return nullptr; 118 | memcpy(buf, der, slen); 119 | return der + slen; 120 | } 121 | 122 | /* 123 | static void der_dump_data(const uint8_t* data, size_t len) 124 | { 125 | size_t y; 126 | size_t x; 127 | constexpr size_t ls = 32; 128 | size_t ll; 129 | 130 | for (y = 0; y < len; y += ls) { 131 | ll = ls; 132 | if ((len - y) < ls) ll = len - y; 133 | for (x = 0; x < (ll - 1); x++) 134 | printf("%02X ", data[y + x]); 135 | printf("%02X\n", data[y + x]); 136 | } 137 | } 138 | */ 139 | 140 | static void der_dump_hex(const uint8_t* data, size_t len) 141 | { 142 | size_t n; 143 | 144 | for (n = 0; n < len; n++) 145 | printf("%02X", data[n]); 146 | printf("\n"); 147 | } 148 | 149 | static void der_dump_internal(const uint8_t* der, const uint8_t* der_end, int indent) 150 | { 151 | der_tag_t tag; 152 | size_t len; 153 | int n; 154 | // const uint8_t* der_start = der; 155 | // printf("DER %zX\n", der_end - der); 156 | 157 | while (der < der_end && der != nullptr) { 158 | // printf("[%04zX", der - der_start); 159 | der = der_decode_tag(tag, der, der_end); 160 | der = der_decode_len(len, der, der_end); 161 | // printf("/%04zX] ", der - der_start); 162 | if (der == nullptr || (der + len) > der_end) break; 163 | for (n = 0; n < indent; n++) 164 | putchar(' '); 165 | printf("%016" PRIX64 " %04zX", tag, len); 166 | if (tag & DER_CONSTRUCTED) { 167 | printf("\n"); 168 | der_dump_internal(der, der + len, indent + 2); 169 | } else { 170 | printf(" : "); 171 | der_dump_hex(der, len); 172 | } 173 | der += len; 174 | } 175 | 176 | if (der == nullptr) 177 | printf("Malformed ASN.1 DER\n"); 178 | } 179 | 180 | void der_dump(const uint8_t* data, size_t size) 181 | { 182 | // der_dump_data(data, size); 183 | der_dump_internal(data, data + size, 0); 184 | } 185 | -------------------------------------------------------------------------------- /Crypto/Asn1Der.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef uint64_t der_tag_t; 7 | 8 | static constexpr der_tag_t DER_CONSTRUCTED = 0x2000000000000000U; 9 | static constexpr der_tag_t DER_CONTEXT_SPECIFIC = 0x8000000000000000U; 10 | 11 | enum der_tag_type_t 12 | { 13 | DER_BOOLEAN = 1, 14 | DER_INTEGER = 2, 15 | DER_BIT_STRING = 3, 16 | DER_OCTET_STRING = 4, 17 | DER_NULL = 5, 18 | DER_OBJECT_IDENTIFIER = 6, 19 | DER_OBJECT_DESCRIPTOR = 7, 20 | DER_EXTERNAL = 8, 21 | DER_REAL = 9, 22 | DER_ENUMERATED = 10, 23 | DER_EMBEDDED_PDV = 11, 24 | DER_UTF8String = 12, 25 | DER_RELATIVE_OID = 13, 26 | DER_SEQUENCE = 16, 27 | DER_SET = 17, 28 | DER_NumericString = 18, 29 | DER_PrintableString = 19, 30 | DER_TeletexString = 20, 31 | DER_VideotexString = 21, 32 | DER_IA5String = 22, 33 | DER_UTCTime = 23, 34 | DER_GeneralizedTime = 24, 35 | DER_GraphicString = 25, 36 | DER_VisibleString = 26, 37 | DER_GeneralString = 27, 38 | DER_UniversalString = 28, 39 | DER_CHARACTER_STRING = 29, 40 | DER_BMPString = 30 41 | }; 42 | 43 | const uint8_t* der_decode_tag(der_tag_t& tag, const uint8_t* der, const uint8_t* der_end); 44 | const uint8_t* der_decode_len(size_t& len, const uint8_t* der, const uint8_t* der_end); 45 | const uint8_t* der_decode_tl(der_tag_t expected_tag, size_t& len, const uint8_t* der, const uint8_t* der_end); 46 | const uint8_t* der_decode_constructed_tl(der_tag_t expected_tag, const uint8_t*& body_end, const uint8_t* der, const uint8_t* der_end); 47 | const uint8_t* der_decode_sequence_tl(const uint8_t*& body_end, const uint8_t* der, const uint8_t* der_end); 48 | const uint8_t* der_decode_uint(size_t n, uint64_t& val, const uint8_t* der, const uint8_t* der_end); 49 | 50 | const uint8_t* der_decode_uint64(der_tag_t expected_tag, uint64_t& val, const uint8_t* der, const uint8_t* der_end); 51 | const uint8_t* der_decode_octet_string_copy(der_tag_t expected_tag, uint8_t* buf, size_t len, const uint8_t* der, const uint8_t* der_end); 52 | 53 | void der_dump(const uint8_t* data, size_t size); 54 | -------------------------------------------------------------------------------- /Crypto/Crypto.cpp: -------------------------------------------------------------------------------- 1 | #include "Sha1.h" 2 | #include "Sha256.h" 3 | #include "Crypto.h" 4 | // #include "Util.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | union Rfc3394_Unit { 15 | uint64_t u64[2]; 16 | uint8_t u8[16]; 17 | }; 18 | 19 | constexpr uint64_t rfc_3394_default_iv = 0xA6A6A6A6A6A6A6A6ULL; 20 | 21 | // TODO: Not tested on big-endian machines ... 22 | void Rfc3394_KeyWrap(uint8_t *crypto, const uint8_t *plain, size_t size, const uint8_t *key, AES::Mode aes_mode, uint64_t iv) 23 | { 24 | Rfc3394_Unit u; 25 | uint64_t a; 26 | uint64_t r[6]; 27 | int i; 28 | int j; 29 | int n = size / sizeof(uint64_t); 30 | uint64_t t; 31 | const uint64_t *p = reinterpret_cast(plain); 32 | uint64_t *c = reinterpret_cast(crypto); 33 | AES aes; 34 | 35 | aes.SetKey(key, aes_mode); 36 | a = iv; 37 | t = 1; 38 | 39 | for (i = 0; i < n; i++) 40 | r[i] = p[i]; 41 | 42 | for (j = 0; j < 6; j++) 43 | { 44 | for (i = 0; i < n; i++) 45 | { 46 | u.u64[1] = r[i]; 47 | u.u64[0] = a; 48 | aes.Encrypt(u.u8, u.u8); 49 | a = u.u64[0] ^ bswap_be(t); 50 | r[i] = u.u64[1]; 51 | t++; 52 | } 53 | } 54 | 55 | c[0] = a; 56 | for (i = 0; i < n; i++) 57 | c[i + 1] = r[i]; 58 | } 59 | 60 | // TODO: Not tested on big-endian machines ... 61 | bool Rfc3394_KeyUnwrap(uint8_t *plain, const uint8_t *crypto, size_t size, const uint8_t *key, AES::Mode aes_mode, uint64_t *iv) 62 | { 63 | Rfc3394_Unit u; 64 | uint64_t a; 65 | uint64_t r[6]; 66 | int i; 67 | int j; 68 | int n = size / sizeof(uint64_t); 69 | uint64_t t; 70 | const uint64_t *c = reinterpret_cast(crypto); 71 | uint64_t *p = reinterpret_cast(plain); 72 | AES aes; 73 | 74 | aes.SetKey(key, aes_mode); 75 | t = 6 * n; 76 | 77 | a = c[0]; 78 | for (i = 0; i < n; i++) 79 | r[i] = c[i + 1]; 80 | 81 | for (j = 5; j >= 0; j--) 82 | { 83 | for (i = n - 1; i >= 0; i--) 84 | { 85 | u.u64[0] = a ^ bswap_be(t); 86 | u.u64[1] = r[i]; 87 | aes.Decrypt(u.u8, u.u8); 88 | a = u.u64[0]; 89 | r[i] = u.u64[1]; 90 | t--; 91 | } 92 | } 93 | 94 | for (i = 0; i < n; i++) 95 | p[i] = r[i]; 96 | if (iv) 97 | *iv = a; 98 | 99 | return a == rfc_3394_default_iv; 100 | } 101 | 102 | void HMAC_SHA1(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *mac) 103 | { 104 | uint8_t kdata[0x40]; 105 | uint8_t digest[0x14]; 106 | constexpr uint8_t ipad = 0x36; 107 | constexpr uint8_t opad = 0x5C; 108 | Sha1 sha1; 109 | 110 | if (key_len > sizeof(kdata)) 111 | { 112 | sha1.Init(); 113 | sha1.Update(key, key_len); 114 | sha1.Final(digest); 115 | 116 | memcpy(kdata, digest, sizeof(digest)); 117 | key_len = sizeof(digest); 118 | } 119 | else 120 | { 121 | memcpy(kdata, key, key_len); 122 | } 123 | if (key_len < sizeof(kdata)) 124 | memset(kdata + key_len, 0, sizeof(kdata) - key_len); 125 | 126 | for (size_t k = 0; k < sizeof(kdata); k++) 127 | kdata[k] ^= ipad; 128 | 129 | sha1.Init(); 130 | sha1.Update(kdata, sizeof(kdata)); 131 | sha1.Update(data, data_len); 132 | sha1.Final(digest); 133 | 134 | for (size_t k = 0; k < sizeof(kdata); k++) 135 | kdata[k] ^= (ipad ^ opad); 136 | 137 | sha1.Init(); 138 | sha1.Update(kdata, sizeof(kdata)); 139 | sha1.Update(digest, sizeof(digest)); 140 | sha1.Final(mac); 141 | 142 | memset(digest, 0, sizeof(digest)); 143 | } 144 | 145 | void HMAC_SHA256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *mac) 146 | { 147 | uint8_t kdata[0x40]; 148 | uint8_t digest[0x20]; 149 | constexpr uint8_t ipad = 0x36; 150 | constexpr uint8_t opad = 0x5C; 151 | SHA256 sha256; 152 | 153 | if (key_len > sizeof(kdata)) 154 | { 155 | sha256.Init(); 156 | sha256.Update(key, key_len); 157 | sha256.Final(digest); 158 | 159 | memcpy(kdata, digest, sizeof(digest)); 160 | key_len = sizeof(digest); 161 | } 162 | else 163 | { 164 | memcpy(kdata, key, key_len); 165 | } 166 | if (key_len < sizeof(kdata)) 167 | memset(kdata + key_len, 0, sizeof(kdata) - key_len); 168 | 169 | for (size_t k = 0; k < sizeof(kdata); k++) 170 | kdata[k] ^= ipad; 171 | 172 | sha256.Init(); 173 | sha256.Update(kdata, sizeof(kdata)); 174 | sha256.Update(data, data_len); 175 | sha256.Final(digest); 176 | 177 | for (size_t k = 0; k < sizeof(kdata); k++) 178 | kdata[k] ^= (ipad ^ opad); 179 | 180 | sha256.Init(); 181 | sha256.Update(kdata, sizeof(kdata)); 182 | sha256.Update(digest, sizeof(digest)); 183 | sha256.Final(mac); 184 | 185 | memset(digest, 0, sizeof(digest)); 186 | } 187 | 188 | void PBKDF2_HMAC_SHA1(const uint8_t* pw, size_t pw_len, const uint8_t* salt, size_t salt_len, int iterations, uint8_t* derived_key, size_t dk_len) 189 | { 190 | assert(salt_len <= 0x20); 191 | assert(dk_len <= 0x20); 192 | 193 | constexpr size_t h_len = 0x14; 194 | size_t r; 195 | size_t l; 196 | uint8_t t[h_len]; 197 | uint8_t u[h_len]; 198 | uint8_t s[0x24]; 199 | size_t k; 200 | int j; 201 | uint32_t i; 202 | size_t n; 203 | 204 | r = dk_len % h_len; 205 | l = dk_len / h_len; 206 | if (r > 0) l++; 207 | 208 | for (i = 1, k = 0; k < dk_len; i++, k += h_len) 209 | { 210 | // F(P,S,c,i) 211 | 212 | memcpy(s, salt, salt_len); 213 | s[salt_len + 0] = (i >> 24) & 0xFF; 214 | s[salt_len + 1] = (i >> 16) & 0xFF; 215 | s[salt_len + 2] = (i >> 8) & 0xFF; 216 | s[salt_len + 3] = i & 0xFF; 217 | 218 | HMAC_SHA1(pw, pw_len, s, salt_len + 4, u); 219 | memcpy(t, u, sizeof(t)); 220 | 221 | for (j = 1; j < iterations; j++) 222 | { 223 | HMAC_SHA1(pw, pw_len, u, sizeof(u), u); 224 | for (n = 0; n < h_len; n++) 225 | t[n] ^= u[n]; 226 | } 227 | 228 | for (n = 0; n < h_len && (n + k) < dk_len; n++) 229 | derived_key[n + k] = t[n]; 230 | } 231 | } 232 | 233 | void PBKDF2_HMAC_SHA256(const uint8_t* pw, size_t pw_len, const uint8_t* salt, size_t salt_len, int iterations, uint8_t* derived_key, size_t dk_len) 234 | { 235 | // HMAC_SHA256(pw, key_len, salt, salt_len, mac) 236 | 237 | // l = ceil(dkLen / hLen); // No of blocks 238 | // r = dkLen - (l - 1) * hLen; // Octets in last block 239 | // T[k] = F(P, S, c, k + 1); // k = 1 .. iterations 240 | 241 | // F(P, S, c, k) = U_1 ^ U_2 ^ ... U_c 242 | // U_1 = PRF(P, S || INT(i)) 243 | // U_2 = PRF(P, U_1) 244 | // U_c = PRF(P, U_{c-1}) 245 | 246 | assert(salt_len <= 0x10); 247 | assert(dk_len <= 0x20); 248 | 249 | constexpr size_t h_len = 0x20; 250 | size_t r; 251 | size_t l; 252 | uint8_t t[h_len]; 253 | uint8_t u[h_len]; 254 | uint8_t s[0x14]; 255 | size_t k; 256 | int j; 257 | uint32_t i; 258 | size_t n; 259 | 260 | r = dk_len % h_len; 261 | l = dk_len / h_len; 262 | if (r > 0) l++; 263 | 264 | for (i = 1, k = 0; k < dk_len; i++, k += h_len) 265 | { 266 | // F(P,S,c,i) 267 | 268 | memcpy(s, salt, salt_len); 269 | s[salt_len + 0] = (i >> 24) & 0xFF; 270 | s[salt_len + 1] = (i >> 16) & 0xFF; 271 | s[salt_len + 2] = (i >> 8) & 0xFF; 272 | s[salt_len + 3] = i & 0xFF; 273 | 274 | HMAC_SHA256(pw, pw_len, s, salt_len + 4, u); 275 | memcpy(t, u, sizeof(t)); 276 | 277 | for (j = 1; j < iterations; j++) 278 | { 279 | HMAC_SHA256(pw, pw_len, u, sizeof(u), u); 280 | for (n = 0; n < h_len; n++) 281 | t[n] ^= u[n]; 282 | } 283 | 284 | for (n = 0; n < h_len && (n + k) < dk_len; n++) 285 | derived_key[n + k] = t[n]; 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /Crypto/Crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Aes.h" 6 | 7 | void Rfc3394_KeyWrap(uint8_t *crypto, const uint8_t *plain, size_t size, const uint8_t *key, AES::Mode aes_mode, uint64_t iv); 8 | bool Rfc3394_KeyUnwrap(uint8_t *plain, const uint8_t *crypto, size_t size, const uint8_t *key, AES::Mode aes_mode, uint64_t *iv); 9 | void HMAC_SHA1(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *mac); 10 | void HMAC_SHA256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *mac); 11 | void PBKDF2_HMAC_SHA1(const uint8_t* pw, size_t pw_len, const uint8_t* salt, size_t salt_len, int iterations, uint8_t* derived_key, size_t dk_len); 12 | void PBKDF2_HMAC_SHA256(const uint8_t* pw, size_t pw_len, const uint8_t* salt, size_t salt_len, int iterations, uint8_t* derived_key, size_t dk_len); 13 | -------------------------------------------------------------------------------- /Crypto/Des.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Des.h" 6 | 7 | #define rol28(v,n) ((v << n) & 0xFFFFFFF) | ((v >> (28-n)) & 0xFFFFFFF) 8 | #define ror28(v,n) ((v >> n) & 0xFFFFFFF) | ((v << (28-n)) & 0xFFFFFFF) 9 | 10 | DES::DES() 11 | { 12 | memset(m_keySchedule, 0, sizeof(m_keySchedule)); 13 | m_initVector = 0; 14 | } 15 | 16 | DES::~DES() 17 | { 18 | memset(m_keySchedule, 0, sizeof(m_keySchedule)); 19 | m_initVector = 0; 20 | } 21 | 22 | void DES::Encrypt(uint8_t *cipher, const uint8_t *plain, size_t size) 23 | { 24 | size_t off; 25 | uint64_t r; 26 | 27 | for (off = 0; off < size; off += 8) 28 | { 29 | r = BytesToU64(plain + off); 30 | r = InitialPermutation(r); 31 | r = EncryptInternal(r, m_keySchedule); 32 | r = FinalPermutation(r); 33 | U64ToBytes(cipher + off, r); 34 | } 35 | } 36 | 37 | void DES::Decrypt(uint8_t *plain, const uint8_t *cipher, size_t size) 38 | { 39 | size_t off; 40 | uint64_t r; 41 | 42 | for (off = 0; off < size; off += 8) 43 | { 44 | r = BytesToU64(cipher + off); 45 | r = InitialPermutation(r); 46 | r = DecryptInternal(r, m_keySchedule); 47 | r = FinalPermutation(r); 48 | U64ToBytes(plain + off, r); 49 | } 50 | } 51 | 52 | void DES::EncryptCBC(uint8_t *cipher, const uint8_t *plain, size_t size) 53 | { 54 | size_t off; 55 | uint64_t r; 56 | 57 | for (off = 0; off < size; off += 8) 58 | { 59 | r = BytesToU64(plain + off); 60 | r = r ^ m_initVector; 61 | r = InitialPermutation(r); 62 | r = EncryptInternal(r, m_keySchedule); 63 | r = FinalPermutation(r); 64 | m_initVector = r; 65 | U64ToBytes(cipher + off, r); 66 | } 67 | } 68 | 69 | void DES::DecryptCBC(uint8_t *plain, const uint8_t *cipher, size_t size) 70 | { 71 | size_t off; 72 | uint64_t r; 73 | uint64_t iv; 74 | 75 | for (off = 0; off < size; off += 8) 76 | { 77 | r = BytesToU64(cipher + off); 78 | iv = r; 79 | r = InitialPermutation(r); 80 | r = DecryptInternal(r, m_keySchedule); 81 | r = FinalPermutation(r); 82 | r = r ^ m_initVector; 83 | m_initVector = iv; 84 | U64ToBytes(plain + off, r); 85 | } 86 | } 87 | 88 | void DES::SetKey(const uint8_t *key) 89 | { 90 | memset(m_keySchedule, 0, sizeof(m_keySchedule)); 91 | m_initVector = 0; 92 | uint64_t k = BytesToU64(key); 93 | KeySchedule(k, m_keySchedule); 94 | } 95 | 96 | void DES::SetIV(const uint8_t *iv) 97 | { 98 | if (iv) 99 | m_initVector = BytesToU64(iv); 100 | else 101 | m_initVector = 0; 102 | } 103 | 104 | void DES::PrintBits(uint64_t v, int bits) 105 | { 106 | int i; 107 | 108 | for (i = bits - 1; i >= 0; i--) 109 | { 110 | if ((v >> i) & 1) 111 | printf("1"); 112 | else 113 | printf("0"); 114 | if ((i & 7) == 0) 115 | printf(" "); 116 | } 117 | printf("\n"); 118 | } 119 | 120 | uint64_t DES::Permute(const uint8_t *box, uint64_t v) 121 | { 122 | int i; 123 | uint64_t r; 124 | 125 | r = 0; 126 | for (i = 0; i < 64; i++) { 127 | r = (r << 1) | ((v >> (64 - box[i])) & 1); 128 | } 129 | 130 | return r; 131 | } 132 | 133 | uint64_t DES::InitialPermutation(uint64_t v) 134 | { 135 | return Permute(m_ip_box, v); 136 | } 137 | 138 | uint64_t DES::FinalPermutation(uint64_t v) 139 | { 140 | return Permute(m_fp_box, v); 141 | } 142 | 143 | uint32_t DES::Function(uint32_t r, uint64_t ks) 144 | { 145 | int i; 146 | uint64_t e; 147 | uint32_t v; 148 | uint32_t f; 149 | uint8_t s[8]; 150 | 151 | e = 0; 152 | for (i = 0; i < 48; i++) 153 | e = (e << 1) | ((r >> (32 - m_e_box[i])) & 1); 154 | 155 | e = e ^ ks; 156 | 157 | for (i = 0; i < 8; i++) 158 | s[i] = (e >> (42 - 6 * i)) & 0x3F; 159 | 160 | v = (m_s1[s[0]] << 28) | (m_s2[s[1]] << 24) | (m_s3[s[2]] << 20) | (m_s4[s[3]] << 16) | (m_s5[s[4]] << 12) | (m_s6[s[5]] << 8) | (m_s7[s[6]] << 4) | m_s8[s[7]]; 161 | 162 | f = 0; 163 | for (i = 0; i < 32; i++) 164 | f = (f << 1) | ((v >> (32 - m_p_box[i])) & 1); 165 | 166 | return f; 167 | } 168 | 169 | void DES::KeySchedule(uint64_t key, uint64_t *ks) 170 | { 171 | uint32_t c, d; 172 | uint64_t t; 173 | uint64_t k; 174 | int i; 175 | int j; 176 | 177 | t = 0; 178 | 179 | for (i = 0; i < 56; i++) 180 | t = (t << 1) | ((key >> (64 - m_pc1_box[i])) & 1); 181 | 182 | c = (t >> 28) & 0xFFFFFFF; 183 | d = t & 0xFFFFFFF; 184 | 185 | for (i = 0; i < 16; i++) { 186 | c = rol28(c, m_shifts[i]); 187 | d = rol28(d, m_shifts[i]); 188 | 189 | t = ((uint64_t)c << 28) | d; 190 | 191 | k = 0; 192 | for (j = 0; j < 48; j++) 193 | k = (k << 1) | ((t >> (56 - m_pc2_box[j])) & 1); 194 | 195 | ks[i] = k; 196 | } 197 | } 198 | 199 | uint64_t DES::EncryptInternal(uint64_t v, const uint64_t *ks) 200 | { 201 | uint32_t l, r; 202 | uint32_t t; 203 | int i; 204 | 205 | l = v >> 32; 206 | r = v & 0xFFFFFFFF; 207 | 208 | for (i = 0; i < 16; i++) { 209 | t = r; 210 | r = l ^ Function(r, ks[i]); 211 | l = t; 212 | } 213 | 214 | return ((uint64_t)r << 32) | l; 215 | } 216 | 217 | uint64_t DES::DecryptInternal(uint64_t v, const uint64_t *ks) 218 | { 219 | uint32_t l, r; 220 | uint32_t t; 221 | int i; 222 | 223 | r = v >> 32; 224 | l = v & 0xFFFFFFFF; 225 | 226 | for (i = 15; i >= 0; i--) { 227 | t = l; 228 | l = r ^ Function(l, ks[i]); 229 | r = t; 230 | } 231 | 232 | return ((uint64_t)l << 32) | r; 233 | } 234 | 235 | uint64_t DES::BytesToU64(const uint8_t *data) 236 | { 237 | uint64_t d = 0; 238 | 239 | for (int n = 0; n < 8; n++) 240 | d = (d << 8) | data[n]; 241 | 242 | return d; 243 | } 244 | 245 | void DES::U64ToBytes(uint8_t *data, uint64_t val) 246 | { 247 | for (int n = 7; n >= 0; n--) 248 | { 249 | data[n] = val & 0xFF; 250 | val >>= 8; 251 | } 252 | } 253 | 254 | 255 | const uint8_t DES::m_ip_box[64] = { 256 | 58, 50, 42, 34, 26, 18, 10, 2, 257 | 60, 52, 44, 36, 28, 20, 12, 4, 258 | 62, 54, 46, 38, 30, 22, 14, 6, 259 | 64, 56, 48, 40, 32, 24, 16, 8, 260 | 57, 49, 41, 33, 25, 17, 9, 1, 261 | 59, 51, 43, 35, 27, 19, 11, 3, 262 | 61, 53, 45, 37, 29, 21, 13, 5, 263 | 63, 55, 47, 39, 31, 23, 15, 7 264 | }; 265 | 266 | const uint8_t DES::m_fp_box[64] = { 267 | 40, 8, 48, 16, 56, 24, 64, 32, 268 | 39, 7, 47, 15, 55, 23, 63, 31, 269 | 38, 6, 46, 14, 54, 22, 62, 30, 270 | 37, 5, 45, 13, 53, 21, 61, 29, 271 | 36, 4, 44, 12, 52, 20, 60, 28, 272 | 35, 3, 43, 11, 51, 19, 59, 27, 273 | 34, 2, 42, 10, 50, 18, 58, 26, 274 | 33, 1, 41, 9, 49, 17, 57, 25 275 | }; 276 | 277 | const uint8_t DES::m_e_box[48] = { 278 | 32, 1, 2, 3, 4, 5, 279 | 4, 5, 6, 7, 8, 9, 280 | 8, 9, 10, 11, 12, 13, 281 | 12, 13, 14, 15, 16, 17, 282 | 16, 17, 18, 19, 20, 21, 283 | 20, 21, 22, 23, 24, 25, 284 | 24, 25, 26, 27, 28, 29, 285 | 28, 29, 30, 31, 32, 1 286 | }; 287 | 288 | const uint8_t DES::m_s1[64] = { 289 | 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, 290 | 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 291 | }; 292 | const uint8_t DES::m_s2[64] = { 293 | 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, 294 | 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 295 | }; 296 | const uint8_t DES::m_s3[64] = { 297 | 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, 298 | 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 299 | }; 300 | const uint8_t DES::m_s4[64] = { 301 | 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, 302 | 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 303 | }; 304 | const uint8_t DES::m_s5[64] = { 305 | 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, 306 | 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 307 | }; 308 | const uint8_t DES::m_s6[64] = { 309 | 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, 310 | 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 311 | }; 312 | const uint8_t DES::m_s7[64] = { 313 | 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, 314 | 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 315 | }; 316 | const uint8_t DES::m_s8[64] = { 317 | 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, 318 | 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 319 | }; 320 | 321 | const uint8_t DES::m_p_box[32] = { 322 | 16, 7, 20, 21, 323 | 29, 12, 28, 17, 324 | 1, 15, 23, 26, 325 | 5, 18, 31, 10, 326 | 2, 8, 24, 14, 327 | 32, 27, 3, 9, 328 | 19, 13, 30, 6, 329 | 22, 11, 4, 25 330 | }; 331 | 332 | const uint8_t DES::m_pc1_box[56] = { 333 | 57, 49, 41, 33, 25, 17, 9, 334 | 1, 58, 50, 42, 34, 26, 18, 335 | 10, 2, 59, 51, 43, 35, 27, 336 | 19, 11, 3, 60, 52, 44, 36, 337 | 338 | 63, 55, 47, 39, 31, 23, 15, 339 | 7, 62, 54, 46, 38, 30, 22, 340 | 14, 6, 61, 53, 45, 37, 29, 341 | 21, 13, 5, 28, 20, 12, 4 342 | }; 343 | 344 | const uint8_t DES::m_pc2_box[48] = { 345 | 14, 17, 11, 24, 1, 5, 346 | 3, 28, 15, 6, 21, 10, 347 | 23, 19, 12, 4, 26, 8, 348 | 16, 7, 27, 20, 13, 2, 349 | 41, 52, 31, 37, 47, 55, 350 | 30, 40, 51, 45, 33, 48, 351 | 44, 49, 39, 56, 34, 53, 352 | 46, 42, 50, 36, 29, 32 353 | }; 354 | 355 | const unsigned int DES::m_shifts[16] = { 356 | 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 357 | }; 358 | -------------------------------------------------------------------------------- /Crypto/Des.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class DES 7 | { 8 | friend class TripleDES; 9 | 10 | public: 11 | DES(); 12 | ~DES(); 13 | 14 | void Encrypt(uint8_t *cipher, const uint8_t *plain, size_t size); 15 | void Decrypt(uint8_t *plain, const uint8_t *cipher, size_t size); 16 | void EncryptCBC(uint8_t *cipher, const uint8_t *plain, size_t size); 17 | void DecryptCBC(uint8_t *plain, const uint8_t *cipher, size_t size); 18 | void SetKey(const uint8_t *key); 19 | void SetIV(const uint8_t *iv); 20 | 21 | private: 22 | static uint64_t InitialPermutation(uint64_t v); 23 | static uint64_t FinalPermutation(uint64_t v); 24 | static void KeySchedule(uint64_t key, uint64_t *ks); 25 | static uint64_t EncryptInternal(uint64_t v, const uint64_t *ks); 26 | static uint64_t DecryptInternal(uint64_t v, const uint64_t *ks); 27 | 28 | static void PrintBits(uint64_t v, int bits); 29 | static uint64_t Permute(const uint8_t *box, uint64_t v); 30 | static uint32_t Function(uint32_t r, uint64_t ks); 31 | 32 | static uint64_t BytesToU64(const uint8_t *data); 33 | static void U64ToBytes(uint8_t *data, uint64_t val); 34 | 35 | uint64_t m_keySchedule[16]; 36 | uint64_t m_initVector; 37 | 38 | static const uint8_t m_ip_box[64]; 39 | static const uint8_t m_fp_box[64]; 40 | static const uint8_t m_e_box[48]; 41 | static const uint8_t m_s1[64]; 42 | static const uint8_t m_s2[64]; 43 | static const uint8_t m_s3[64]; 44 | static const uint8_t m_s4[64]; 45 | static const uint8_t m_s5[64]; 46 | static const uint8_t m_s6[64]; 47 | static const uint8_t m_s7[64]; 48 | static const uint8_t m_s8[64]; 49 | static const uint8_t m_p_box[32]; 50 | static const uint8_t m_pc1_box[56]; 51 | static const uint8_t m_pc2_box[48]; 52 | static const unsigned int m_shifts[16]; 53 | }; 54 | -------------------------------------------------------------------------------- /Crypto/Sha1.cpp: -------------------------------------------------------------------------------- 1 | #include "Sha1.h" 2 | 3 | inline uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) 4 | { 5 | return (x & y) ^ ((~x) & z); 6 | } 7 | 8 | inline uint32_t Parity(uint32_t x, uint32_t y, uint32_t z) 9 | { 10 | return x ^ y ^ z; 11 | } 12 | 13 | inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) 14 | { 15 | return (x & y) ^ (x & z) ^ (y & z); 16 | } 17 | 18 | inline uint32_t Rotl(int sh, uint32_t v) 19 | { 20 | return (v << sh) | (v >> (32 - sh)); 21 | } 22 | 23 | Sha1::Sha1() 24 | { 25 | Init(); 26 | } 27 | 28 | Sha1::~Sha1() 29 | { 30 | } 31 | 32 | void Sha1::Init() 33 | { 34 | m_hash[0] = 0x67452301; 35 | m_hash[1] = 0xEFCDAB89; 36 | m_hash[2] = 0x98BADCFE; 37 | m_hash[3] = 0x10325476; 38 | m_hash[4] = 0xC3D2E1F0; 39 | 40 | m_bit_cnt = 0; 41 | m_buf_idx = 0; 42 | 43 | for (size_t n = 0; n < 64; n++) 44 | m_buffer[n] = 0; 45 | } 46 | 47 | void Sha1::Update(const void * ptr, size_t size) 48 | { 49 | size_t n; 50 | const uint8_t *data = reinterpret_cast(ptr); 51 | 52 | for (n = 0; n < size; n++) 53 | { 54 | m_buffer[m_buf_idx] = data[n]; 55 | m_buf_idx++; 56 | if (m_buf_idx == 64) 57 | { 58 | Round(); 59 | m_buf_idx = 0; 60 | } 61 | } 62 | 63 | m_bit_cnt += (8 * size); 64 | } 65 | 66 | void Sha1::Final(uint8_t * hash) 67 | { 68 | size_t n; 69 | 70 | m_buffer[m_buf_idx++] = 0x80; 71 | 72 | if (m_buf_idx > 56) 73 | { 74 | for (; m_buf_idx < 64; m_buf_idx++) 75 | m_buffer[m_buf_idx] = 0; 76 | Round(); 77 | m_buf_idx = 0; 78 | } 79 | 80 | for (; m_buf_idx < 56; m_buf_idx++) 81 | m_buffer[m_buf_idx] = 0; 82 | m_buffer[56] = (m_bit_cnt >> 56) & 0xFF; 83 | m_buffer[57] = (m_bit_cnt >> 48) & 0xFF; 84 | m_buffer[58] = (m_bit_cnt >> 40) & 0xFF; 85 | m_buffer[59] = (m_bit_cnt >> 32) & 0xFF; 86 | m_buffer[60] = (m_bit_cnt >> 24) & 0xFF; 87 | m_buffer[61] = (m_bit_cnt >> 16) & 0xFF; 88 | m_buffer[62] = (m_bit_cnt >> 8) & 0xFF; 89 | m_buffer[63] = (m_bit_cnt >> 0) & 0xFF; 90 | Round(); 91 | 92 | for (n = 0; n < 5; n++) 93 | { 94 | hash[4 * n + 0] = (m_hash[n] >> 24) & 0xFF; 95 | hash[4 * n + 1] = (m_hash[n] >> 16) & 0xFF; 96 | hash[4 * n + 2] = (m_hash[n] >> 8) & 0xFF; 97 | hash[4 * n + 3] = (m_hash[n] >> 0) & 0xFF; 98 | } 99 | } 100 | 101 | void Sha1::Round() 102 | { 103 | uint32_t w[80]; 104 | uint32_t a; 105 | uint32_t b; 106 | uint32_t c; 107 | uint32_t d; 108 | uint32_t e; 109 | uint32_t T; 110 | int k; 111 | 112 | for (k = 0; k < 16; k++) 113 | w[k] = (m_buffer[4 * k] << 24) | (m_buffer[4 * k + 1] << 16) | (m_buffer[4 * k + 2] << 8) | m_buffer[4 * k + 3]; 114 | for (k = 16; k < 80; k++) 115 | w[k] = Rotl(1, w[k - 3] ^ w[k - 8] ^ w[k - 14] ^ w[k - 16]); 116 | 117 | a = m_hash[0]; 118 | b = m_hash[1]; 119 | c = m_hash[2]; 120 | d = m_hash[3]; 121 | e = m_hash[4]; 122 | 123 | for (k = 0; k < 20; k++) 124 | { 125 | T = Rotl(5, a) + Ch(b, c, d) + e + m_K[0] + w[k]; 126 | e = d; 127 | d = c; 128 | c = Rotl(30, b); 129 | b = a; 130 | a = T; 131 | } 132 | 133 | for (k = 20; k < 40; k++) 134 | { 135 | T = Rotl(5, a) + Parity(b, c, d) + e + m_K[1] + w[k]; 136 | e = d; 137 | d = c; 138 | c = Rotl(30, b); 139 | b = a; 140 | a = T; 141 | } 142 | 143 | for (k = 40; k < 60; k++) 144 | { 145 | T = Rotl(5, a) + Maj(b, c, d) + e + m_K[2] + w[k]; 146 | e = d; 147 | d = c; 148 | c = Rotl(30, b); 149 | b = a; 150 | a = T; 151 | } 152 | 153 | for (k = 60; k < 80; k++) 154 | { 155 | T = Rotl(5, a) + Parity(b, c, d) + e + m_K[3] + w[k]; 156 | e = d; 157 | d = c; 158 | c = Rotl(30, b); 159 | b = a; 160 | a = T; 161 | } 162 | 163 | m_hash[0] = a + m_hash[0]; 164 | m_hash[1] = b + m_hash[1]; 165 | m_hash[2] = c + m_hash[2]; 166 | m_hash[3] = d + m_hash[3]; 167 | m_hash[4] = e + m_hash[4]; 168 | } 169 | 170 | 171 | const uint32_t Sha1::m_K[4] = 172 | { 173 | 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 174 | }; 175 | -------------------------------------------------------------------------------- /Crypto/Sha1.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Sha1 7 | { 8 | public: 9 | Sha1(); 10 | ~Sha1(); 11 | 12 | void Init(); 13 | void Update(const void *data, size_t size); 14 | void Final(uint8_t *hash); 15 | 16 | private: 17 | void Round(); 18 | 19 | uint8_t m_buffer[64]; 20 | uint32_t m_hash[5]; 21 | uint64_t m_bit_cnt; 22 | size_t m_buf_idx; 23 | 24 | static const uint32_t m_K[4]; 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /Crypto/Sha256.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Sha256.h" 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | #else 9 | static inline uint32_t _rotr(uint32_t v, int sh) 10 | { 11 | return (v >> sh) | (v << (32 - sh)); 12 | } 13 | 14 | #endif 15 | 16 | static inline uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) 17 | { 18 | return (x & y) ^ (~x & z); 19 | } 20 | 21 | static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) 22 | { 23 | return (x & y) ^ (x & z) ^ (y & z); 24 | } 25 | 26 | static inline uint32_t S0(uint32_t x) 27 | { 28 | return _rotr(x, 2) ^ _rotr(x, 13) ^ _rotr(x, 22); 29 | } 30 | 31 | static inline uint32_t S1(uint32_t x) 32 | { 33 | return _rotr(x, 6) ^ _rotr(x, 11) ^ _rotr(x, 25); 34 | } 35 | 36 | static inline uint32_t s0(uint32_t x) 37 | { 38 | return _rotr(x, 7) ^ _rotr(x, 18) ^ (x >> 3); 39 | } 40 | 41 | static inline uint32_t s1(uint32_t x) 42 | { 43 | return _rotr(x, 17) ^ _rotr(x, 19) ^ (x >> 10); 44 | } 45 | 46 | const uint32_t SHA256::m_k[64] = { 47 | 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 48 | 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 49 | 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 50 | 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 51 | 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 52 | 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 53 | 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 54 | 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 55 | }; 56 | 57 | SHA256::SHA256() 58 | { 59 | Init(); 60 | } 61 | 62 | SHA256::~SHA256() 63 | { 64 | } 65 | 66 | void SHA256::Init() 67 | { 68 | int i; 69 | 70 | m_hash[0] = 0x6A09E667; 71 | m_hash[1] = 0xBB67AE85; 72 | m_hash[2] = 0x3C6EF372; 73 | m_hash[3] = 0xA54FF53A; 74 | m_hash[4] = 0x510E527F; 75 | m_hash[5] = 0x9B05688C; 76 | m_hash[6] = 0x1F83D9AB; 77 | m_hash[7] = 0x5BE0CD19; 78 | m_bufferPtr = 0; 79 | m_byteCnt = 0; 80 | 81 | for (i = 0; i < 64; i++) 82 | m_buffer[i] = 0; 83 | } 84 | 85 | void SHA256::Round() 86 | { 87 | uint32_t a, b, c, d, e, f, g, h; 88 | uint32_t w[64]; 89 | int t; 90 | uint32_t t1, t2; 91 | 92 | for (t = 0; t < 16; t++) 93 | w[t] = (m_buffer[4*t] << 24) | (m_buffer[4*t+1] << 16) | (m_buffer[4*t+2] << 8) | m_buffer[4*t+3]; 94 | for (t = 16; t < 64; t++) 95 | w[t] = s1(w[t-2]) + w[t-7] + s0(w[t-15]) + w[t-16]; 96 | 97 | a = m_hash[0]; 98 | b = m_hash[1]; 99 | c = m_hash[2]; 100 | d = m_hash[3]; 101 | e = m_hash[4]; 102 | f = m_hash[5]; 103 | g = m_hash[6]; 104 | h = m_hash[7]; 105 | 106 | for (t = 0; t < 64; t++) { 107 | t1 = h + S1(e) + Ch(e, f, g) + m_k[t] + w[t]; 108 | t2 = S0(a) + Maj(a, b, c); 109 | h = g; 110 | g = f; 111 | f = e; 112 | e = d + t1; 113 | d = c; 114 | c = b; 115 | b = a; 116 | a = t1 + t2; 117 | } 118 | 119 | m_hash[0] += a; 120 | m_hash[1] += b; 121 | m_hash[2] += c; 122 | m_hash[3] += d; 123 | m_hash[4] += e; 124 | m_hash[5] += f; 125 | m_hash[6] += g; 126 | m_hash[7] += h; 127 | 128 | for (t = 0; t < 64; t++) 129 | m_buffer[t] = 0; 130 | 131 | m_bufferPtr = 0; 132 | } 133 | 134 | void SHA256::Update(const void *data, size_t cnt) 135 | { 136 | size_t i; 137 | const uint8_t *bdata = reinterpret_cast(data); 138 | 139 | for (i = 0; i < cnt; i++) { 140 | m_buffer[m_bufferPtr++] = bdata[i]; 141 | if (m_bufferPtr == 64) 142 | Round(); 143 | } 144 | 145 | m_byteCnt += cnt; 146 | } 147 | 148 | void SHA256::Final(uint8_t *hash) 149 | { 150 | uint32_t len_h, len_l; 151 | int i; 152 | 153 | m_buffer[m_bufferPtr] = 0x80; 154 | if (m_bufferPtr >= 55) 155 | Round(); 156 | 157 | len_h = static_cast(m_byteCnt >> 29); 158 | len_l = static_cast(m_byteCnt << 3); 159 | 160 | m_buffer[56] = (len_h >> 24) & 0xFF; 161 | m_buffer[57] = (len_h >> 16) & 0xFF; 162 | m_buffer[58] = (len_h >> 8) & 0xFF; 163 | m_buffer[59] = len_h & 0xFF; 164 | m_buffer[60] = (len_l >> 24) & 0xFF; 165 | m_buffer[61] = (len_l >> 16) & 0xFF; 166 | m_buffer[62] = (len_l >> 8) & 0xFF; 167 | m_buffer[63] = len_l & 0xFF; 168 | 169 | Round(); 170 | 171 | for (i = 0; i < 8; i++) { 172 | hash[4*i] = (m_hash[i] >> 24) & 0xFF; 173 | hash[4*i+1] = (m_hash[i] >> 16) & 0xFF; 174 | hash[4*i+2] = (m_hash[i] >> 8) & 0xFF; 175 | hash[4*i+3] = m_hash[i] & 0xFF; 176 | } 177 | 178 | Init(); 179 | } 180 | -------------------------------------------------------------------------------- /Crypto/Sha256.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class SHA256 7 | { 8 | public: 9 | SHA256(); 10 | ~SHA256(); 11 | 12 | void Init(); 13 | void Update(const void *data, size_t size); 14 | void Final(uint8_t *hash); 15 | 16 | private: 17 | void Round(); 18 | 19 | uint8_t m_buffer[64]; 20 | uint32_t m_hash[8]; 21 | uint32_t m_bufferPtr; 22 | size_t m_byteCnt; 23 | 24 | static const uint32_t m_k[64]; 25 | }; 26 | -------------------------------------------------------------------------------- /Crypto/TripleDes.cpp: -------------------------------------------------------------------------------- 1 | #include "Des.h" 2 | #include "TripleDes.h" 3 | 4 | #include 5 | 6 | TripleDES::TripleDES() 7 | { 8 | memset(m_keySchedule, 0, sizeof(m_keySchedule)); 9 | m_iv = 0; 10 | } 11 | 12 | TripleDES::~TripleDES() 13 | { 14 | memset(m_keySchedule, 0, sizeof(m_keySchedule)); 15 | m_iv = 0; 16 | } 17 | 18 | void TripleDES::Encrypt(uint8_t * cipher, const uint8_t * plain, size_t size) 19 | { 20 | size_t off; 21 | uint64_t r; 22 | 23 | for (off = 0; off < size; off += 8) 24 | { 25 | r = DES::BytesToU64(plain + off); 26 | r = DES::InitialPermutation(r); 27 | r = DES::EncryptInternal(r, m_keySchedule[0]); 28 | r = DES::DecryptInternal(r, m_keySchedule[1]); 29 | r = DES::EncryptInternal(r, m_keySchedule[2]); 30 | r = DES::FinalPermutation(r); 31 | DES::U64ToBytes(cipher + off, r); 32 | } 33 | } 34 | 35 | void TripleDES::Decrypt(uint8_t * plain, const uint8_t * cipher, size_t size) 36 | { 37 | size_t off; 38 | uint64_t r; 39 | 40 | for (off = 0; off < size; off += 8) 41 | { 42 | r = DES::BytesToU64(cipher + off); 43 | r = DES::InitialPermutation(r); 44 | r = DES::DecryptInternal(r, m_keySchedule[2]); 45 | r = DES::EncryptInternal(r, m_keySchedule[1]); 46 | r = DES::DecryptInternal(r, m_keySchedule[0]); 47 | r = DES::FinalPermutation(r); 48 | DES::U64ToBytes(plain + off, r); 49 | } 50 | } 51 | 52 | void TripleDES::EncryptCBC(uint8_t * cipher, const uint8_t * plain, size_t size) 53 | { 54 | size_t off; 55 | uint64_t r; 56 | 57 | for (off = 0; off < size; off += 8) 58 | { 59 | r = DES::BytesToU64(plain + off); 60 | r = r ^ m_iv; 61 | r = DES::InitialPermutation(r); 62 | r = DES::EncryptInternal(r, m_keySchedule[0]); 63 | r = DES::DecryptInternal(r, m_keySchedule[1]); 64 | r = DES::EncryptInternal(r, m_keySchedule[2]); 65 | r = DES::FinalPermutation(r); 66 | m_iv = r; 67 | DES::U64ToBytes(cipher + off, r); 68 | } 69 | } 70 | 71 | void TripleDES::DecryptCBC(uint8_t * plain, const uint8_t * cipher, size_t size) 72 | { 73 | size_t off; 74 | uint64_t r; 75 | uint64_t iv; 76 | 77 | for (off = 0; off < size; off += 8) 78 | { 79 | r = DES::BytesToU64(cipher + off); 80 | iv = r; 81 | r = DES::InitialPermutation(r); 82 | r = DES::DecryptInternal(r, m_keySchedule[2]); 83 | r = DES::EncryptInternal(r, m_keySchedule[1]); 84 | r = DES::DecryptInternal(r, m_keySchedule[0]); 85 | r = DES::FinalPermutation(r); 86 | r = r ^ m_iv; 87 | m_iv = iv; 88 | DES::U64ToBytes(plain + off, r); 89 | } 90 | } 91 | 92 | void TripleDES::SetKey(const uint8_t * key) 93 | { 94 | uint64_t k[3]; 95 | memset(m_keySchedule, 0, sizeof(m_keySchedule)); 96 | m_iv = 0; 97 | k[0] = DES::BytesToU64(key + 0); 98 | k[1] = DES::BytesToU64(key + 8); 99 | k[2] = DES::BytesToU64(key + 16); 100 | DES::KeySchedule(k[0], m_keySchedule[0]); 101 | DES::KeySchedule(k[1], m_keySchedule[1]); 102 | DES::KeySchedule(k[2], m_keySchedule[2]); 103 | } 104 | 105 | void TripleDES::SetIV(const uint8_t * iv) 106 | { 107 | if (iv) 108 | m_iv = DES::BytesToU64(iv); 109 | else 110 | m_iv = 0; 111 | } 112 | -------------------------------------------------------------------------------- /Crypto/TripleDes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TripleDES 7 | { 8 | public: 9 | TripleDES(); 10 | ~TripleDES(); 11 | 12 | void Encrypt(uint8_t *cipher, const uint8_t *plain, size_t size); 13 | void Decrypt(uint8_t *plain, const uint8_t *cipher, size_t size); 14 | void EncryptCBC(uint8_t *cipher, const uint8_t *plain, size_t size); 15 | void DecryptCBC(uint8_t *plain, const uint8_t *cipher, size_t size); 16 | 17 | void SetKey(const uint8_t *key); 18 | void SetIV(const uint8_t *iv); 19 | 20 | private: 21 | uint64_t m_keySchedule[3][16]; 22 | uint64_t m_iv; 23 | }; 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # APFS FUSE Driver for Linux 3 | 4 | This project is a read-only FUSE driver for the new Apple File System. It also supports software 5 | encrypted volumes and fusion drives. Firmlinks are not supported yet. 6 | 7 | Be aware that not all compression methods are supported yet (only the ones I have encountered so far). 8 | Thus, the driver may return compressed files instead of uncompressed ones. Although most of the time it 9 | should just report an error. 10 | 11 | ## Changelog 12 | 13 | | Date | Comment | 14 | |------|---------| 15 | | 2020-07-08 | Added support for mounting snapshots and sealed volumes | 16 | | 2018-04-20 | Added support for mounting DMGs | 17 | | 2018-04-14 | Added support for partition tables (GPT only) | 18 | | 2018-04-10 | Fixed and extended FileVault encryption support | 19 | | 2018-03-28 | Added support for FileVault encryption | 20 | | 2017-10-25 | Added support for encryption | 21 | | 2017-10-14 | Initial version | 22 | 23 | ## Usage 24 | 25 | ### Compile the source code 26 | The following libraries are needed (including the -dev/-devel packages): 27 | 28 | * FUSE 2.6 or greater (on 32-bit systems, FUSE 3.0 or greater) 29 | * zlib 30 | * bzip2 31 | * libattr (on some Linux distributions) 32 | 33 | Development tools: 34 | * cmake 35 | * gcc-c++ (or clang++) 36 | * git (for cloning) 37 | 38 | Example for Linux: 39 | ``` 40 | sudo apt update 41 | sudo apt install fuse libfuse3-dev bzip2 libbz2-dev cmake gcc-c++ git libattr1-dev zlib1g-dev 42 | ``` 43 | Of course these commands depend on the Linux distribution. 44 | 45 | Clone the repository: 46 | ``` 47 | git clone https://github.com/sgan81/apfs-fuse.git 48 | cd apfs-fuse 49 | git submodule init 50 | git submodule update 51 | ``` 52 | The driver uses Apple's lzfse library and includes it as a submodule. 53 | 54 | Compile the driver: 55 | ``` 56 | mkdir build 57 | cd build 58 | cmake .. 59 | ccmake . # Only if you want to change build options 60 | make 61 | ``` 62 | 63 | Note that the driver uses FUSE 3.0 by default (required on 32-bit systems). If 64 | you want do compile using FUSE 2.6, use `ccmake .` to change the option 65 | `USE_FUSE3` to `OFF`. 66 | 67 | ### Mount a drive 68 | ``` 69 | apfs-fuse 70 | ``` 71 | #### Supported options: 72 | * `-d n`: If n > 0, enable debug output (see below for details). 73 | * `-f device`: Specify secondary device for Fusion drive. 74 | * `-o opts`: Comma-separated list of mount options. 75 | * `-l`: Lax mode: when unexpected data is encountered, try to continue, even if this means 76 | returning potentially incorrect data. 77 | * `-v n`: Instead of mounting the first volume in a container, mount volume n (starting at 0). 78 | * `-r recovery_key`: Mount an encrypted volume by supplying a password or Personal Recovery Key (PRK). 79 | * `-s n`: Find the container at offset n inside the device. This is useful when using an image file 80 | instead of a disk device, and therefore partitions are not exposed. 81 | * `-p n`: Find the container at partition n inside the device. 82 | 83 | If you are using an image file containing partitions, the driver will now detect if there is a valid GPT 84 | partition table. If there is, it will look for the first APFS partition and use that one for the container. 85 | If your drive contains more than one APFS container, you can specify the partition/container id with the 86 | `-p` option. 87 | 88 | The device has to be the one containing the APFS container. If a container contains more than one volume, 89 | the volume can be specified by the `-v` option. 90 | 91 | If a volume is encrypted, the apfs-fuse command will prompt for a password, unless a password or PRK is 92 | specified on the command line. The PRK can also be used as password. 93 | 94 | It is also possible to directly mount DMG files. The driver will automatically detect if a dmg 95 | has to be mounted and take appropriate action. If a dmg is encrypted, you will be asked for the password. 96 | Note that dmg support is currently a bit slow (especially when compressed), but it should work properly. 97 | 98 | The debug flags are now a combination of bits. So to enable specific output, you just add the numbers 99 | mentioned below together, and use the result as parameter for -d. 100 | 101 | * 1 Display more information about errors 102 | * 2 Display additional generic information 103 | * 4 Display information about directory-related operations 104 | * 8 Display information about on-the-fly compression 105 | * 16 Display information about cryptographic operations (caution, displays keys as well) 106 | 107 | #### Mount options (-o ...) 108 | In addition to the mount options supported by fuse, the following mount options are supported: 109 | * uid=n: Pretend that all files have UID n. 110 | * gid=n: Pretend that all files have GID n. 111 | * vol=n: Same as -v, specify the volume number to mount if you don't want volume 0. 112 | * blksize=n: Set the physical block size (default: 512 bytes). 113 | * pass=...: Specify volume passphrase (same as -r). 114 | * xid=...: Try to mount older XID. May be useful if the container is corrupt. 115 | * snap=...: Mount snapshot with given XID. Use apfsutil to display snapshot ids. 116 | 117 | The blksize parameter is required for proper partition table parsing on some newer 118 | macs. However the current driver should be able to detect the block size automatically. 119 | 120 | If you mount a volume as root and want some user to be able to access it, use: 121 | ``` 122 | apfs-fuse -o uid=,gid=,allow_other /dev/ 123 | ``` 124 | 125 | If you want to mount a device as user, add yourself to the disk group. This might not be too safe though, 126 | as it allows any application to read and write anywhere on a drive. 127 | 128 | ### Unmount a drive 129 | As root: 130 | ``` 131 | umount 132 | ``` 133 | 134 | As user: 135 | ``` 136 | fusermount -u 137 | ``` 138 | 139 | ## Features 140 | The following features are implemented: 141 | 142 | * Can read macOS 10.13 case sensitive and insensitive volumes, as well as iOS 11 / macOS 10.12 volumes 143 | * Transparent decompression of ZLib and LZVN 144 | * Symlinks 145 | * Hardlinks (it seems ...) 146 | * Extended attributes 147 | * Software encryption (at least full-disk encryption) 148 | * Automatic detection of GPT partition tables 149 | * Direct mounting of DMG images (supports zlib/adc compression and encryption) 150 | 151 | ## Limitations 152 | These things are not supported (yet): 153 | 154 | * Transparent decompression of LZFSE 155 | * Writing 156 | * Hardware-encrypted volumes (internal drives of Macs with T2 chip) 157 | 158 | ## Debugging 159 | 160 | Since the driver is still experimental and based on analysis of a limited set of drives / volumes, crashes 161 | may unfortunately still happen. If a crash happens, providing me with useful information can be very helpful. 162 | 163 | One of the most important pieces of information is the location where the program crashed. You can find that 164 | out by debugging the tool. In order to debug the program, do the following: 165 | 166 | Change the main CMakeLists.txt as follows: In the line `set(CMAKE_BUILD_TYPE Release)`, 167 | replace `Release` with `Debug`. 168 | 169 | Rebuild everything. 170 | 171 | Run it under `gdb`. Like this: 172 | ``` 173 | gdb apfs-fuse 174 | 175 | (In gdb): 176 | set args 177 | run 178 | 179 | (And if/when it crashes): 180 | backtrace 181 | 182 | (When you're finished): 183 | quit 184 | ``` 185 | And then send the output of `backtrace` to me. Adding `-d 1` to options might help as well, but be aware that 186 | it will generate a lot of output on the text console. 187 | 188 | ### Some tools that might be useful 189 | #### apfs-dump-quick 190 | 191 | If you encounter problems with some file, it may be that I overlooked something during reverse engineering. In that 192 | case, you can use the `apfs-dump-quick` command to dump the management structures of the whole drive. It can be run 193 | as follows: 194 | ``` 195 | apfs-dump-quick 196 | ``` 197 | The tool will dump the most current version of the disk structures into a logfile. This file can become quite big, like 198 | a few 100 MB. So to limit the amount of information to report, look for the name of the file in the log. Try to find a 199 | line starting with `File` and containing the filename. The number immediately after `File` is the ID. Find all lines 200 | having this ID, and include them in your bug report. 201 | #### apfs-dump 202 | There is another command available: 203 | ``` 204 | apfs-dump 205 | ``` 206 | This tool was the one I originally used for reverse engineering. It will scan the whole volume for clusters having 207 | correct checksums (and thus being part of some management structure), and then it will try to dump them. This will 208 | take a very long time to run on big volumes, and create huge log files. So using the quick version will be much faster. 209 | #### apfsutil 210 | ``` 211 | apfsutil 212 | ``` 213 | This is a new tool that just displays some information from a container. For now, it lists the volumes a container 214 | contains, and snapshots if there are some. This tool might be extended in the future. 215 | --------------------------------------------------------------------------------