diff --git a/dump-imessages/.gitignore b/dump-imessages/.gitignore new file mode 100644 index 0000000..1944fd6 --- /dev/null +++ b/dump-imessages/.gitignore @@ -0,0 +1 @@ +*.tmp diff --git a/dump-imessages/Makefile b/dump-imessages/Makefile new file mode 100644 index 0000000..ddea342 --- /dev/null +++ b/dump-imessages/Makefile @@ -0,0 +1,7 @@ +default: dump + +dump: + ./dump.sh + +clean: + rm -rf *.tmp diff --git a/dump-imessages/README.md b/dump-imessages/README.md new file mode 100644 index 0000000..7bebe75 --- /dev/null +++ b/dump-imessages/README.md @@ -0,0 +1,5 @@ +# notes + +* iphone-dataprotection cloned from +https://code.google.com/p/iphone-dataprotection/ on 20140207 + diff --git a/dump-imessages/dump.sh b/dump-imessages/dump.sh new file mode 100755 index 0000000..2ba546f --- /dev/null +++ b/dump-imessages/dump.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +BACKUPS="" + +for B in ls ~/Library/Application\ Support/MobileSync/Backup/* ; do + BACKUPS+=" $(basename "$B")" +done + +if [ -r ${HOME}/Documents/Secure/iphone-backup-password.sh ]; then + source "${HOME}/Documents/Secure/iphone-backup-password.sh" +fi + +# expecting $IPHONE_BACKUP_PASSWORD to be set now. set it in your +# environment if not, or put a script exporting it at that path above + +echo $BACKUPS + +WORKDIR="$TMPDIR/iphone-sms-dump.workd" +if [ ! -d "$WORKDIR" ]; then + mkdir -p "$WORKDIR" +fi + +for BID in $BACKUPS ; do + if [ ! -r $WORKDIR/sms-$BID.db ]; then + TD="$(mktemp -d -t bdir)/out" + echo -e "y\n$IPHONE_BACKUP_PASSWORD" | + python ./iphone-dataprotection/python_scripts/backup_tool.py \ + "${HOME}/Library/Application Support/MobileSync/Backup/$BID" \ + "$TD" 2>&1 > /dev/null # hush + echo "extracted to $TD" + mv "$TD/HomeDomain/Library/SMS/sms.db" ./sms-$BID.db + mv "$TD/MediaDomain/Library/SMS/Attachments" ./Attachments-$BID.d + rm -rf "$TD" + fi +done + +for BID in $BACKUPS ; do + # now we process them... +done diff --git a/dump-imessages/iphone-dataprotection/.hgignore b/dump-imessages/iphone-dataprotection/.hgignore new file mode 100644 index 0000000..a166183 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/.hgignore @@ -0,0 +1,8 @@ +# use glob syntax. +syntax: glob + +*.o +*.pyc +*.dmg +*.ipsw + diff --git a/dump-imessages/iphone-dataprotection/CREDITS.txt b/dump-imessages/iphone-dataprotection/CREDITS.txt new file mode 100644 index 0000000..face27f --- /dev/null +++ b/dump-imessages/iphone-dataprotection/CREDITS.txt @@ -0,0 +1,7 @@ +comex +chronic dev team +idroid/openiboot team +iphone dev team +Jonathan Zdziarski +msftguy +planetbeing diff --git a/dump-imessages/iphone-dataprotection/README.txt b/dump-imessages/iphone-dataprotection/README.txt new file mode 100644 index 0000000..ef4afe9 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/README.txt @@ -0,0 +1 @@ +See http://code.google.com/p/iphone-dataprotection/wiki/README diff --git a/dump-imessages/iphone-dataprotection/build_ramdisk.sh b/dump-imessages/iphone-dataprotection/build_ramdisk.sh new file mode 100755 index 0000000..9c1b1fd --- /dev/null +++ b/dump-imessages/iphone-dataprotection/build_ramdisk.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +#set +e +#set -o errexit + +if [ $# -lt 4 ] +then + echo "Syntax: $0 IPSW RAMDISK KEY IV CUSTOMRAMDISK" + echo "python_scripts/kernel_patcher.py can generate a shell script with the correct parameters" + exit +fi + +if [ ! -f ramdisk_tools/restored_external ] +then + echo "ramdisk_tools/restored_external not found, check compilation output for errors" + exit -1 +fi + +IPSW=$1 +RAMDISK=$2 +KEY=$3 +IV=$4 +CUSTOMRAMDISK=$5 +if [ "$CUSTOMRAMDISK" == "" ]; then + CUSTOMRAMDISK="myramdisk.dmg" +fi +IMG3FS="./img3fs/img3fs" +IMG3MNT="/tmp/img3" + +if [ ! -f $IMG3FS ]; then + echo "img3fs is missing, install osxfuse and run make -C img3fs/" + exit -1 +fi + +if [ ! -f ssh.tar.gz ]; then + echo "Downloading ssh.tar.gz from googlecode" + curl -O http://iphone-dataprotection.googlecode.com/files/ssh.tar.gz +fi + +unzip $IPSW $RAMDISK + +if [ -d "/Volumes/ramdisk" ]; then + hdiutil eject /Volumes/ramdisk + umount $IMG3MNT +fi + +mkdir -p $IMG3MNT + +$IMG3FS -key $KEY -iv $IV $IMG3MNT $RAMDISK + +hdiutil attach -owners off $IMG3MNT/DATA.dmg + +#remove baseband files to free space +rm -rf /Volumes/ramdisk/usr/local/standalone/firmware/* +rm -rf /Volumes/ramdisk/usr/share/progressui/ +#dont replace existing files, replacing launchctl on armv6 ramdisks makes it fail somehow +tar -C /Volumes/ramdisk/ -xzkP < ssh.tar.gz +rm /Volumes/ramdisk/bin/vdir +rm /Volumes/ramdisk/bin/egrep +rm /Volumes/ramdisk/bin/grep + +#rm /Volumes/ramdisk/usr/local/bin/restored_external +cp ramdisk_tools/restored_external /Volumes/ramdisk/usr/local/bin + +cp ramdisk_tools/bruteforce ramdisk_tools/device_infos /Volumes/ramdisk/var/root +cp ramdisk_tools/scripts/* /Volumes/ramdisk/var/root +cp ramdisk_tools/ioflashstoragekit /Volumes/ramdisk/var/root + +#if present, copy ssh public key to ramdisk +if [ -f ~/.ssh/id_rsa.pub ]; then + mkdir /Volumes/ramdisk/var/root/.ssh + cp ~/.ssh/id_rsa.pub /Volumes/ramdisk/var/root/.ssh/authorized_keys +fi + +hdiutil eject /Volumes/ramdisk +umount $IMG3MNT + +mv $RAMDISK $CUSTOMRAMDISK + +#echo "$CUSTOMRAMDISK created" + diff --git a/dump-imessages/iphone-dataprotection/build_ramdisk_ios6.sh b/dump-imessages/iphone-dataprotection/build_ramdisk_ios6.sh new file mode 100755 index 0000000..bdaf833 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/build_ramdisk_ios6.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +if [ ! $(uname) == "Darwin" ] +then + echo "Script for Mac OS X only" + exit +fi + +if [ $# -lt 1 ] +then + echo "Syntax: $0 DECRYPTED_RAMDISK_DMG" + exit +fi + +if [ ! -f ssh.tar.gz ] +then + echo "Downloading ssh.tar.gz from googlecode" + curl -O http://iphone-dataprotection.googlecode.com/files/ssh.tar.gz +fi + +if [ ! -f libncurses.5.dylib ] +then + echo "Downloading libncurses.5.dylib from googlecode" + curl -O http://iphone-dataprotection.googlecode.com/files/libncurses.5.dylib +fi + +echo "Rebuilding ramdisk_tools" + +./build_tools.sh + +#compiling in a vmware shared folder can produce binaries filled with zeroes ! +if [ ! -f ramdisk_tools/restored_external ] || [ "$(file -b ramdisk_tools/restored_external)" == "data" ] +then + echo "ramdisk_tools/restored_external not found or invalid, check compilation output for errors" + exit -1 +fi + +RAMDISK=$1 + +RD_SIZE=$(du -h $RAMDISK | cut -f 1) + +if [ ! $RD_SIZE == "20M" ] +then + echo "resizing ramdisk..." + echo "hdiutil will segfault if ramdisk was already resized, thats ok" + hdiutil resize -size 20M $RAMDISK +fi + +if [ -d /Volumes/ramdisk ] +then + echo "Unmount /Volumes/ramdisk then try again" + exit -1 +fi + +echo "Attaching ramdisk" + +hdiutil attach $RAMDISK +rm -rf /Volumes/ramdisk/usr/local/standalone/firmware/* +rm -rf /Volumes/ramdisk/usr/share/progressui/ + +if [ ! -f /Volumes/ramdisk/sbin/sshd ] +then + echo "Unpacking ssh.tar.gz on ramdisk..." + tar -C /Volumes/ramdisk/ -xzkP < ssh.tar.gz + echo "^^ This tar error message is okay" +fi + +if [ ! -f /Volumes/ramdisk/usr/lib/libncurses.5.4.dylib ] +then + echo "Adding libncurses..." + cp libncurses.5.dylib /Volumes/ramdisk/usr/lib/libncurses.5.4.dylib +fi + +echo "Adding/updating ramdisk_tools binaries on ramdisk..." +cp ramdisk_tools/restored_external /Volumes/ramdisk/usr/local/bin/ +cp ramdisk_tools/bruteforce ramdisk_tools/device_infos /Volumes/ramdisk/var/root +cp ramdisk_tools/scripts/* /Volumes/ramdisk/var/root + +ls -laht /Volumes/ramdisk/usr/local/bin/ + +#if present, copy ssh public key to ramdisk +if [ -f ~/.ssh/id_rsa.pub ] && [ ! -d /Volumes/ramdisk/var/root/.ssh ] +then + mkdir /Volumes/ramdisk/var/root/.ssh + cp ~/.ssh/id_rsa.pub /Volumes/ramdisk/var/root/.ssh/authorized_keys + chmod 0600 /Volumes/ramdisk/var/root/.ssh/authorized_keys +fi + +hdiutil eject /Volumes/ramdisk diff --git a/dump-imessages/iphone-dataprotection/build_tools.sh b/dump-imessages/iphone-dataprotection/build_tools.sh new file mode 100755 index 0000000..33bff5b --- /dev/null +++ b/dump-imessages/iphone-dataprotection/build_tools.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +if [ ! $(uname) == "Darwin" ] +then + echo "Script for Mac OS X only" + exit +fi + +for VER in 5.0 5.1 6.0 6.1 7.0 +do + if [ -d "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$VER.sdk/" ]; + then + SDKVER=$VER + SDKPATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$VER.sdk/" + echo "Found iOS SDK at $SDKPATH" + break + fi +done + +if [ "$SDKVER" == "" ]; then + echo "iOS SDK not found" + exit -1 +fi + +if [ ! -f "$SDKPATH/System/Library/Frameworks/IOKit.framework/Headers/IOKitLib.h" ]; then + echo "IOKit headers missing" + + $IOKIT_HDR_109="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/IOKit.framework/Headers" + + if [ -d $IOKIT_HDR_109 ]; then + echo "Symlinking headers" + sudo ln -s $IOKIT_HDR_109 "$SDKPATH/System/Library/Frameworks/IOKit.framework/Headers" + sudo ln -s "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libkern/OSTypes.h" "$SDKPATH/usr/include/libkern/OSTypes.h" + fi + +fi + +if [ ! -f "$SDKPATH/System/Library/Frameworks/IOKit.framework/IOKit" ]; then + echo "IOKit binary missing" + + if [ -f "$SDKPATH/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit" ]; then + echo "Creating IOKit symlink for iOS 7.0 SDK" + sudo ln -s "$SDKPATH/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit" "$SDKPATH/System/Library/Frameworks/IOKit.framework/IOKit" + fi +fi + +if [ -f "$SDKPATH/System/Library/Frameworks/IOKit.framework/Headers/IOKitLib.h" ]; then + if [ -f "$SDKPATH/System/Library/Frameworks/IOKit.framework/IOKit" ]; then + export SDKVER + make -C ramdisk_tools clean + make -C ramdisk_tools + fi +fi diff --git a/dump-imessages/iphone-dataprotection/dump_data_partition.sh b/dump-imessages/iphone-dataprotection/dump_data_partition.sh new file mode 100755 index 0000000..7aa5767 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/dump_data_partition.sh @@ -0,0 +1,19 @@ +#!/bin/sh +SSHOPTS="-p 2222 -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null" + +UDID=`ssh $SSHOPTS root@localhost "./device_infos udid"` + +if [ "$UDID" == "" ]; then + exit +fi + +echo "Device UDID : $UDID" + +mkdir -p $UDID + +DATE=`date +"%Y%m%d-%H%M"` +OUT=$UDID/data_$DATE.dmg + +echo "Dumping data partition in $OUT ..." + +ssh $SSHOPTS root@localhost "dd if=/dev/rdisk0s2s1 bs=8192 || dd if=/dev/rdisk0s1s2 bs=8192" > $OUT diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/BUILD b/dump-imessages/iphone-dataprotection/emf_decrypter/BUILD new file mode 100644 index 0000000..64a50bb --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/BUILD @@ -0,0 +1,92 @@ +INSTRUCTIONS FOR BUILDING XPWN +------------------------------ + +These are very basic instructions on how to build xpwn related projects, they +are tailored to Debian based systems. They are not meant to be a substitute +for experience in programming in GNU/Linux environments, but it should be a +good starting point. + +1. Install a basic build environment (compilers, etc.): + + sudo apt-get install build-essential + +2. Install some prerequisites libraries required by xpwn: + + sudo apt-get install libcrypt-dev libz-dev libbz2-dev3 libusb-dev + +3. Install cmake. It is recommended you download and build it from the +official cmake website, since versions >= 2.6.0 are recommended. + + wget http://www.cmake.org/files/v2.6/cmake-2.6.2.tar.gz + tar zxvf cmake-2.6.2.tar.gz + cd cmake-2.6.2 + ./configure + make + sudo make install + +Now you are ready to build xpwn. It is highly recommended that you build +out-of-source (that is, the build products are not placed into the same +folders as the sources). This is much neater and cleaning up is as simple as +deleting the build products folder. + +Assuming xpwn sources are in ~/xpwn: + +4. Create a build folder + + cd ~ + mkdir build + cd build + +5. Create Makefiles + + cmake ~/xpwn + +6. Build + + make + +7. Package + + make package + +BUILDING USEFUL LIBRARIES +------------------------- + +These command-lines can be substituted in for step 6. The products are in the +subfolders (make package will not include them). + +xpwn library (for IPSW generation) + + make libXPwn.a + +Windows pwnmetheus library (for QuickPwn) + + make libpwnmetheus.dll + +HELPFUL MAKEFILE GENERATION COMMAND-LINES +----------------------------------------- + +These command-lines can be substituted in for step 5. + +Add debugging symbols: + + cmake ~/xpwn -DCMAKE_C_FLAGS=-g + +Try to only use static libraries: + + cmake ~/xpwn -DBUILD_STATIC=YES + + +CROSS-COMPILING +--------------- + +This is a complex and advanced topic, but it is possible with the appropriate +CMake toolchain files and properly configured build environment. I have +crossbuilt Windows, OS X, Linux x86, Linux x64, and iPhone binaries from one +Ubuntu machine. The source trees are properly configured for this task. + +MORE HELP +--------- + +Consult the CMake documentation and wiki and look in the CMakeLists.txt files +for hints on how things are supposed to work. diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/CMakeLists.txt b/dump-imessages/iphone-dataprotection/emf_decrypter/CMakeLists.txt new file mode 100644 index 0000000..deaa857 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 2.6) +set(CMAKE_LEGACY_CYGWIN_WIN32 0) + +project (XPwn) + +# We want win32 executables to build staticly by default, since it's more difficult to keep the shared libraries straight on Windows +IF(WIN32) + SET(BUILD_STATIC ON CACHE BOOL "Force compilation with static libraries") +ELSE(WIN32) + SET(BUILD_STATIC OFF CACHE BOOL "Force compilation with static libraries") +ENDIF(WIN32) + +IF(BUILD_STATIC) + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") +ENDIF(BUILD_STATIC) + +include_directories (${PROJECT_SOURCE_DIR}/includes) + +add_subdirectory (common) +add_subdirectory (hfs) +add_subdirectory (emf) + +IF(WIN32 OR APPLE) + SET(CPACK_GENERATOR "ZIP") +ELSE(WIN32 OR APPLE) + SET(CPACK_GENERATOR "TBZ2") +ENDIF(WIN32 OR APPLE) + +INCLUDE(CPack) diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/LICENSE b/dump-imessages/iphone-dataprotection/emf_decrypter/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/README.txt b/dump-imessages/iphone-dataprotection/emf_decrypter/README.txt new file mode 100644 index 0000000..96ef894 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/README.txt @@ -0,0 +1,13 @@ +DEPRECATED: use the python version instead + +Decrypts files data forks in raw iOS 4 disk images. +Reads encryption keys from plist file named after the volume ID. +The plist file must have at least the EMF and DKey fields set. +For now the tool decrypts the data forks but does not mark the files as +"decrypted" : running it twice on the same image will produce garbage. +Interrupting the process will also leave the image "half decrypted". + +Uses planetbeing/dev team HFS implementation +https://github.com/planetbeing/xpwn + +Only builds on Mac OS X, requires CoreFoundation for plist access. \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/common/CMakeLists.txt b/dump-imessages/iphone-dataprotection/emf_decrypter/common/CMakeLists.txt new file mode 100644 index 0000000..6ac0607 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/common/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(common abstractfile.c base64.c) + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/common/abstractfile.c b/dump-imessages/iphone-dataprotection/emf_decrypter/common/abstractfile.c new file mode 100644 index 0000000..a63f344 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/common/abstractfile.c @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include + +#include "abstractfile.h" +#include "common.h" + +size_t freadWrapper(AbstractFile* file, void* data, size_t len) { + return fread(data, 1, len, (FILE*) (file->data)); +} + +size_t fwriteWrapper(AbstractFile* file, const void* data, size_t len) { + return fwrite(data, 1, len, (FILE*) (file->data)); +} + +int fseekWrapper(AbstractFile* file, off_t offset) { + return fseeko((FILE*) (file->data), offset, SEEK_SET); +} + +off_t ftellWrapper(AbstractFile* file) { + return ftello((FILE*) (file->data)); +} + +void fcloseWrapper(AbstractFile* file) { + fclose((FILE*) (file->data)); + free(file); +} + +off_t fileGetLength(AbstractFile* file) { + off_t length; + off_t pos; + + pos = ftello((FILE*) (file->data)); + + fseeko((FILE*) (file->data), 0, SEEK_END); + length = ftello((FILE*) (file->data)); + + fseeko((FILE*) (file->data), pos, SEEK_SET); + + return length; +} + +AbstractFile* createAbstractFileFromFile(FILE* file) { + AbstractFile* toReturn; + + if(file == NULL) { + return NULL; + } + + toReturn = (AbstractFile*) malloc(sizeof(AbstractFile)); + toReturn->data = file; + toReturn->read = freadWrapper; + toReturn->write = fwriteWrapper; + toReturn->seek = fseekWrapper; + toReturn->tell = ftellWrapper; + toReturn->getLength = fileGetLength; + toReturn->close = fcloseWrapper; + toReturn->type = AbstractFileTypeFile; + return toReturn; +} + +size_t dummyRead(AbstractFile* file, void* data, size_t len) { + return 0; +} + +size_t dummyWrite(AbstractFile* file, const void* data, size_t len) { + *((off_t*) (file->data)) += len; + return len; +} + +int dummySeek(AbstractFile* file, off_t offset) { + *((off_t*) (file->data)) = offset; + return 0; +} + +off_t dummyTell(AbstractFile* file) { + return *((off_t*) (file->data)); +} + +void dummyClose(AbstractFile* file) { + free(file); +} + +AbstractFile* createAbstractFileFromDummy() { + AbstractFile* toReturn; + toReturn = (AbstractFile*) malloc(sizeof(AbstractFile)); + toReturn->data = NULL; + toReturn->read = dummyRead; + toReturn->write = dummyWrite; + toReturn->seek = dummySeek; + toReturn->tell = dummyTell; + toReturn->getLength = NULL; + toReturn->close = dummyClose; + toReturn->type = AbstractFileTypeDummy; + return toReturn; +} + +size_t memRead(AbstractFile* file, void* data, size_t len) { + MemWrapperInfo* info = (MemWrapperInfo*) (file->data); + if(info->bufferSize < (info->offset + len)) { + len = info->bufferSize - info->offset; + } + memcpy(data, (void*)((uint8_t*)(*(info->buffer)) + (uint32_t)info->offset), len); + info->offset += (size_t)len; + return len; +} + +size_t memWrite(AbstractFile* file, const void* data, size_t len) { + MemWrapperInfo* info = (MemWrapperInfo*) (file->data); + + while((info->offset + (size_t)len) > info->bufferSize) { + info->bufferSize <<= 1; + *(info->buffer) = realloc(*(info->buffer), info->bufferSize); + } + + memcpy((void*)((uint8_t*)(*(info->buffer)) + (uint32_t)info->offset), data, len); + info->offset += (size_t)len; + return len; +} + +int memSeek(AbstractFile* file, off_t offset) { + MemWrapperInfo* info = (MemWrapperInfo*) (file->data); + info->offset = (size_t)offset; + return 0; +} + +off_t memTell(AbstractFile* file) { + MemWrapperInfo* info = (MemWrapperInfo*) (file->data); + return (off_t)info->offset; +} + +off_t memGetLength(AbstractFile* file) { + MemWrapperInfo* info = (MemWrapperInfo*) (file->data); + return info->bufferSize; +} + +void memClose(AbstractFile* file) { + free(file->data); + free(file); +} + +AbstractFile* createAbstractFileFromMemory(void** buffer, size_t size) { + MemWrapperInfo* info; + AbstractFile* toReturn; + toReturn = (AbstractFile*) malloc(sizeof(AbstractFile)); + + info = (MemWrapperInfo*) malloc(sizeof(MemWrapperInfo)); + info->offset = 0; + info->buffer = buffer; + info->bufferSize = size; + + toReturn->data = info; + toReturn->read = memRead; + toReturn->write = memWrite; + toReturn->seek = memSeek; + toReturn->tell = memTell; + toReturn->getLength = memGetLength; + toReturn->close = memClose; + toReturn->type = AbstractFileTypeMem; + return toReturn; +} + +void abstractFilePrint(AbstractFile* file, const char* format, ...) { + va_list args; + char buffer[1024]; + size_t length; + + buffer[0] = '\0'; + va_start(args, format); + length = vsprintf(buffer, format, args); + va_end(args); + ASSERT(file->write(file, buffer, length) == length, "fwrite"); +} + +int absFileRead(io_func* io, off_t location, size_t size, void *buffer) { + AbstractFile* file; + file = (AbstractFile*) io->data; + file->seek(file, location); + if(file->read(file, buffer, size) == size) { + return TRUE; + } else { + return FALSE; + } +} + +int absFileWrite(io_func* io, off_t location, size_t size, void *buffer) { + AbstractFile* file; + file = (AbstractFile*) io->data; + file->seek(file, location); + if(file->write(file, buffer, size) == size) { + return TRUE; + } else { + return FALSE; + } +} + +void closeAbsFile(io_func* io) { + AbstractFile* file; + file = (AbstractFile*) io->data; + file->close(file); + free(io); +} + + +io_func* IOFuncFromAbstractFile(AbstractFile* file) { + io_func* io; + + io = (io_func*) malloc(sizeof(io_func)); + io->data = file; + io->read = &absFileRead; + io->write = &absFileWrite; + io->close = &closeAbsFile; + + return io; +} + +size_t memFileRead(AbstractFile* file, void* data, size_t len) { + MemFileWrapperInfo* info = (MemFileWrapperInfo*) (file->data); + memcpy(data, (void*)((uint8_t*)(*(info->buffer)) + (uint32_t)info->offset), len); + info->offset += (size_t)len; + return len; +} + +size_t memFileWrite(AbstractFile* file, const void* data, size_t len) { + MemFileWrapperInfo* info = (MemFileWrapperInfo*) (file->data); + + while((info->offset + (size_t)len) > info->actualBufferSize) { + info->actualBufferSize <<= 1; + *(info->buffer) = realloc(*(info->buffer), info->actualBufferSize); + } + + if((info->offset + (size_t)len) > (*(info->bufferSize))) { + memset(((uint8_t*)(*(info->buffer))) + *(info->bufferSize), 0, (info->offset + (size_t)len) - *(info->bufferSize)); + *(info->bufferSize) = info->offset + (size_t)len; + } + + memcpy((void*)((uint8_t*)(*(info->buffer)) + (uint32_t)info->offset), data, len); + info->offset += (size_t)len; + return len; +} + +int memFileSeek(AbstractFile* file, off_t offset) { + MemFileWrapperInfo* info = (MemFileWrapperInfo*) (file->data); + info->offset = (size_t)offset; + return 0; +} + +off_t memFileTell(AbstractFile* file) { + MemFileWrapperInfo* info = (MemFileWrapperInfo*) (file->data); + return (off_t)info->offset; +} + +off_t memFileGetLength(AbstractFile* file) { + MemFileWrapperInfo* info = (MemFileWrapperInfo*) (file->data); + return *(info->bufferSize); +} + +void memFileClose(AbstractFile* file) { + free(file->data); + free(file); +} + +AbstractFile* createAbstractFileFromMemoryFile(void** buffer, size_t* size) { + MemFileWrapperInfo* info; + AbstractFile* toReturn; + toReturn = (AbstractFile*) malloc(sizeof(AbstractFile)); + + info = (MemFileWrapperInfo*) malloc(sizeof(MemFileWrapperInfo)); + info->offset = 0; + info->buffer = buffer; + info->bufferSize = size; + info->actualBufferSize = (1024 < (*size)) ? (*size) : 1024; + if(info->actualBufferSize != *(info->bufferSize)) { + *(info->buffer) = realloc(*(info->buffer), info->actualBufferSize); + } + + toReturn->data = info; + toReturn->read = memFileRead; + toReturn->write = memFileWrite; + toReturn->seek = memFileSeek; + toReturn->tell = memFileTell; + toReturn->getLength = memFileGetLength; + toReturn->close = memFileClose; + toReturn->type = AbstractFileTypeMemFile; + return toReturn; +} + +AbstractFile* createAbstractFileFromMemoryFileBuffer(void** buffer, size_t* size, size_t actualBufferSize) { + MemFileWrapperInfo* info; + AbstractFile* toReturn; + toReturn = (AbstractFile*) malloc(sizeof(AbstractFile)); + + info = (MemFileWrapperInfo*) malloc(sizeof(MemFileWrapperInfo)); + info->offset = 0; + info->buffer = buffer; + info->bufferSize = size; + info->actualBufferSize = actualBufferSize; + + toReturn->data = info; + toReturn->read = memFileRead; + toReturn->write = memFileWrite; + toReturn->seek = memFileSeek; + toReturn->tell = memFileTell; + toReturn->getLength = memFileGetLength; + toReturn->close = memFileClose; + toReturn->type = AbstractFileTypeMemFile; + return toReturn; +} + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/common/base64.c b/dump-imessages/iphone-dataprotection/emf_decrypter/common/base64.c new file mode 100644 index 0000000..4e745aa --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/common/base64.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#include + +unsigned char* decodeBase64(char* toDecode, size_t* dataLength) { + uint8_t buffer[4]; + uint8_t charsInBuffer; + unsigned char* curChar; + unsigned char* decodeBuffer; + unsigned int decodeLoc; + unsigned int decodeBufferSize; + uint8_t bytesToDrop; + + curChar = (unsigned char*) toDecode; + charsInBuffer = 0; + + decodeBufferSize = 100; + decodeLoc = 0; + decodeBuffer = (unsigned char*) malloc(decodeBufferSize); + + bytesToDrop = 0; + + while((*curChar) != '\0') { + if((*curChar) >= 'A' && (*curChar) <= 'Z') { + buffer[charsInBuffer] = (*curChar) - 'A'; + charsInBuffer++; + } + + if((*curChar) >= 'a' && (*curChar) <= 'z') { + buffer[charsInBuffer] = ((*curChar) - 'a') + ('Z' - 'A' + 1); + charsInBuffer++; + } + + if((*curChar) >= '0' && (*curChar) <= '9') { + buffer[charsInBuffer] = ((*curChar) - '0') + ('Z' - 'A' + 1) + ('z' - 'a' + 1); + charsInBuffer++; + } + + if((*curChar) == '+') { + buffer[charsInBuffer] = ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1); + charsInBuffer++; + } + + if((*curChar) == '/') { + buffer[charsInBuffer] = ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1) + 1; + charsInBuffer++; + } + + if((*curChar) == '=') { + bytesToDrop++; + } + + if(charsInBuffer == 4) { + charsInBuffer = 0; + + if((decodeLoc + 3) >= decodeBufferSize) { + decodeBufferSize <<= 1; + decodeBuffer = (unsigned char*) realloc(decodeBuffer, decodeBufferSize); + } + decodeBuffer[decodeLoc] = ((buffer[0] << 2) & 0xFC) + ((buffer[1] >> 4) & 0x3F); + decodeBuffer[decodeLoc + 1] = ((buffer[1] << 4) & 0xF0) + ((buffer[2] >> 2) & 0x0F); + decodeBuffer[decodeLoc + 2] = ((buffer[2] << 6) & 0xC0) + (buffer[3] & 0x3F); + + decodeLoc += 3; + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + } + + curChar++; + } + + if(bytesToDrop != 0) { + if((decodeLoc + 3) >= decodeBufferSize) { + decodeBufferSize <<= 1; + decodeBuffer = (unsigned char*) realloc(decodeBuffer, decodeBufferSize); + } + + decodeBuffer[decodeLoc] = ((buffer[0] << 2) & 0xFC) | ((buffer[1] >> 4) & 0x3F); + + if(bytesToDrop <= 2) + decodeBuffer[decodeLoc + 1] = ((buffer[1] << 4) & 0xF0) | ((buffer[2] >> 2) & 0x0F); + + if(bytesToDrop <= 1) + decodeBuffer[decodeLoc + 2] = ((buffer[2] << 6) & 0xC0) | (buffer[3] & 0x3F); + + *dataLength = decodeLoc + 3 - bytesToDrop; + } else { + *dataLength = decodeLoc; + } + + return decodeBuffer; +} + +void writeBase64(AbstractFile* file, unsigned char* data, size_t dataLength, int tabLength, int width) { + char* buffer; + buffer = convertBase64(data, dataLength, tabLength, width); + file->write(file, buffer, strlen(buffer)); + free(buffer); +} + +#define CHECK_BUFFER_SIZE() \ + if(pos == bufferSize) { \ + bufferSize <<= 1; \ + buffer = (unsigned char*) realloc(buffer, bufferSize); \ + } + +#define CHECK_LINE_END_STRING() \ + CHECK_BUFFER_SIZE() \ + if(width == lineLength) { \ + buffer[pos++] = '\n'; \ + CHECK_BUFFER_SIZE() \ + for(j = 0; j < tabLength; j++) { \ + buffer[pos++] = '\t'; \ + CHECK_BUFFER_SIZE() \ + } \ + lineLength = 0; \ + } else { \ + lineLength++; \ + } + +char* convertBase64(unsigned char* data, size_t dataLength, int tabLength, int width) { + const char* dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + unsigned char* buffer; + size_t pos; + size_t bufferSize; + int i, j; + int lineLength; + + bufferSize = 100; + buffer = (unsigned char*) malloc(bufferSize); + pos = 0; + lineLength = 0; + + for(i = 0; i < tabLength; i++) { + CHECK_BUFFER_SIZE() + buffer[pos++] = '\t'; + } + i = 0; + while(dataLength >= 3) { + dataLength -= 3; + buffer[pos++] = dictionary[(data[i] >> 2) & 0x3F]; + CHECK_LINE_END_STRING(); + buffer[pos++] = dictionary[(((data[i] << 4) & 0x30) | ((data[i+1] >> 4) & 0x0F)) & 0x3F]; + CHECK_LINE_END_STRING(); + buffer[pos++] = dictionary[(((data[i+1] << 2) & 0x3C) | ((data[i+2] >> 6) & 0x03)) & 0x03F]; + CHECK_LINE_END_STRING(); + buffer[pos++] = dictionary[data[i+2] & 0x3F]; + CHECK_LINE_END_STRING(); + i += 3; + } + + if(dataLength == 2) { + buffer[pos++] = dictionary[(data[i] >> 2) & 0x3F]; + CHECK_LINE_END_STRING(); + buffer[pos++] = dictionary[(((data[i] << 4) & 0x30) | ((data[i+1] >> 4) & 0x0F)) & 0x3F]; + CHECK_LINE_END_STRING(); + buffer[pos++] = dictionary[(data[i+1] << 2) & 0x3C]; + CHECK_LINE_END_STRING(); + buffer[pos++] = '='; + } else if(dataLength == 1) { + buffer[pos++] = dictionary[(data[i] >> 2) & 0x3F]; + CHECK_LINE_END_STRING(); + buffer[pos++] = dictionary[(data[i] << 4) & 0x30]; + CHECK_LINE_END_STRING(); + buffer[pos++] = '='; + CHECK_LINE_END_STRING(); + buffer[pos++] = '='; + } + + CHECK_BUFFER_SIZE(); + buffer[pos++] = '\n'; + + CHECK_BUFFER_SIZE(); + buffer[pos++] = '\0'; + + return (char*) buffer; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/emf/CMakeLists.txt b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/CMakeLists.txt new file mode 100644 index 0000000..bf1f9d1 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/CMakeLists.txt @@ -0,0 +1,12 @@ +link_directories(${PROJECT_BINARY_DIR}/common ${PROJECT_BINARY_DIR}/hfs) + +#set(COREFOUNDATION_LIBRARY CoreFoundation) + +IF (APPLE) + FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) +ENDIF (APPLE) + +add_executable(emf_decrypter emf_decrypter.c emf_init.c) +target_link_libraries (emf_decrypter hfs common crypto ${COREFOUNDATION_LIBRARY}) + +install(TARGETS emf_decrypter DESTINATION .) diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf.h b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf.h new file mode 100644 index 0000000..d6ef84b --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf.h @@ -0,0 +1,42 @@ +//As of iOS 4, class keys 1 to 4 are used for files, class 5 usage is unknown +#define MAX_CLASS_KEYS 5 +#define CLASS_DKEY 4 + +typedef struct EMFInfo +{ + Volume* volume; + uint64_t volume_id; + uint64_t volume_offset; + uint32_t classKeys_bitset; + AES_KEY emfkey; + AES_KEY classKeys[MAX_CLASS_KEYS]; +}EMFInfo; + +EMFInfo* EMF_init(Volume*, char*); + +#define CPROTECT_V2_LENGTH 0x38 //56 +#define CP_WRAPPEDKEYSIZE 40 /* 2x4 = 8, 8x8 = 64 */ + +//http://www.opensource.apple.com/source/xnu/xnu-1699.22.73/bsd/sys/cprotect.h +typedef struct cprotect_xattr_v2 +{ + uint16_t xattr_major_version; // =2 + uint16_t xattr_minor_version; // =0 + uint32_t flags; // leaks stack dword in one code path (cp_handle_vnop) + uint32_t persistent_class; + uint32_t key_size; //0x28 + uint8_t persistent_key[0x28]; +} cprotect_xattr_v2; + +#define CPROTECT_V4_LENGTH 0x4C //76 + +typedef struct cprotect_xattr_v4 +{ + uint16_t xattr_major_version; // =4 + uint16_t xattr_minor_version; // =0 + uint32_t xxx_length; // 0xc + uint32_t protection_class_id; + uint32_t wrapped_length; //0x28 + uint8_t xxx_junk[20]; //uninitialized ? + uint8_t wrapped_key[0x28]; +} cprotect_xattr_v4; \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf_decrypter.c b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf_decrypter.c new file mode 100644 index 0000000..bdaf1e4 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf_decrypter.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "emf.h" + +char endianness; + +void TestByteOrder() +{ + short int word = 0x0001; + char *byte = (char *) &word; + endianness = byte[0] ? IS_LITTLE_ENDIAN : IS_BIG_ENDIAN; +} + +void iv_for_lba(uint32_t lba, uint32_t* iv) +{ + int i; + for(i = 0; i < 4; i++) + { + if(lba & 1) + lba = 0x80000061 ^ (lba >> 1); + else + lba = lba >> 1; + iv[i] = lba; + } +} + +int EMF_unwrap_filekey_forclass(EMFInfo* emf, uint8_t* wrapped_file_key, uint32_t protection_class_id, AES_KEY* file_key) +{ + uint8_t fk[32]={0}; + + if (protection_class_id < 1 || protection_class_id >= MAX_CLASS_KEYS) + return -1; + + if ((emf->classKeys_bitset & (1 << protection_class_id)) == 0) + { + printf("Class key %d not available\n", protection_class_id); + return -1; + } + + if(AES_unwrap_key(&(emf->classKeys[protection_class_id-1]), NULL, fk, wrapped_file_key, 40)!= 32) + { + fprintf(stderr, "EMF_unwrap_filekey_forclass unwrap FAIL, protection_class_id=%d\n", protection_class_id); + return -1; + } + AES_set_decrypt_key(fk, 32*8, file_key); + + return 0; +} + +void EMF_fix_and_decrypt_block(EMFInfo* emf, uint8_t* buffer, uint32_t lba, uint32_t blockSize, AES_KEY* filekey) +{ + uint32_t volumeOffset = emf->volume_offset; + uint32_t iv[4]; + + //reencrypt with emf key to get correct ciphertext + iv_for_lba(volumeOffset + lba, iv); + AES_cbc_encrypt(buffer, buffer, blockSize, &(emf->emfkey), (uint8_t*) iv, AES_ENCRYPT); + + //decrypt with file key + iv_for_lba(volumeOffset + lba, iv); + AES_cbc_encrypt(buffer, buffer, blockSize, filekey, (uint8_t*) iv, AES_DECRYPT); +} + +int EMF_decrypt_file_blocks(EMFInfo* emf, HFSPlusCatalogFile* file, uint8_t* wrapped_file_key, uint32_t protection_class) +{ + AES_KEY filekey; + + if( EMF_unwrap_filekey_forclass(emf, wrapped_file_key, protection_class, &filekey)) + { + return -1; + } + + io_func* io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, emf->volume); + if(io == NULL) + { + fprintf(stderr, "openRawFile %d FAIL!\n", file->fileID); + return -1; + } + RawFile* rawFile = (RawFile*) io->data; + Extent* extent = rawFile->extents; + uint32_t blockSize = emf->volume->volumeHeader->blockSize; + uint32_t i; + uint8_t* buffer = malloc(blockSize); + + if(buffer == NULL) + return -1; + + //decrypt all blocks in all extents + //the last block can contain stuff from erased files maybe ? + while( extent != NULL) + { + for(i=0; i < extent->blockCount; i++) + { + if(READ(emf->volume->image, (extent->startBlock + i) * blockSize, blockSize, buffer)) + { + EMF_fix_and_decrypt_block(emf, buffer, extent->startBlock + i, blockSize, &filekey); + + //write back to image + WRITE(emf->volume->image, (extent->startBlock + i) * blockSize, blockSize, buffer); + } + } + extent = extent->next; + } + + free(buffer); + return 0; +} + +int EMF_decrypt_folder(EMFInfo* emf, HFSCatalogNodeID folderID) +{ + CatalogRecordList* list; + CatalogRecordList* theList; + HFSPlusCatalogFolder* folder; + HFSPlusCatalogFile* file; + char* name; + cprotect_xattr_v2* cprotect_xattr; + uint8_t* wrapped_file_key; + + theList = list = getFolderContents(folderID, emf->volume); + + while(list != NULL) + { + name = unicodeToAscii(&list->name); + + if(list->record->recordType == kHFSPlusFolderRecord) + { + folder = (HFSPlusCatalogFolder*)list->record; + EMF_decrypt_folder(emf, folder->folderID); + } + else if(list->record->recordType == kHFSPlusFileRecord) + { + file = (HFSPlusCatalogFile*)list->record; + + size_t attr_len = getAttribute(emf->volume, file->fileID, "com.apple.system.cprotect", (uint8_t**) &cprotect_xattr); + + if(cprotect_xattr != NULL && attr_len > 0) + { + if (cprotect_xattr->xattr_major_version == 2 && attr_len == CPROTECT_V2_LENGTH) + { + printf("Decrypting %s\n", name); + if(!EMF_decrypt_file_blocks(emf, file, cprotect_xattr->persistent_key, cprotect_xattr->persistent_class)) + { + //TODO HAX: update cprotect xattr version field (bit1) to mark file as decrypted ? + //cprotect_xattr->version |= 1; + //setAttribute(volume, file->fileID, "com.apple.system.cprotect", (uint8_t*) cprotect_xattr, CPROTECT_V2_LENGTH); + } + } + else if (cprotect_xattr->xattr_major_version == 4 && attr_len == CPROTECT_V4_LENGTH) + { + //not just yet :) + } + else if (cprotect_xattr->xattr_major_version & 1) + { + //TODO: file already decrypted by this tool ? + } + else + { + fprintf(stderr, "Unknown cprotect xattr version/length : %x/%zx\n", cprotect_xattr->xattr_major_version, attr_len); + } + } + } + + free(name); + list = list->next; + } + releaseCatalogRecordList(theList); +} + +int main(int argc, const char *argv[]) { + io_func* io; + Volume* volume; + + TestByteOrder(); + + if(argc < 2) { + printf("usage: %s \n", argv[0]); + return 0; + } + + io = openFlatFile(argv[1]); + + if(io == NULL) { + fprintf(stderr, "error: Cannot open image-file.\n"); + return 1; + } + + volume = openVolume(io); + if(volume == NULL) { + fprintf(stderr, "error: Cannot open volume.\n"); + CLOSE(io); + return 1; + } + printf("WARNING ! This tool will modify the hfs image and possibly wreck it if something goes wrong !\n" + "Make sure to backup the image before proceeding\n"); + printf("Press a key to continue or CTRL-C to abort\n"); + getchar(); + + char* dir = dirname((char*)argv[1]); + EMFInfo* emf = EMF_init(volume, dir); + + if(emf != NULL) + { + EMF_decrypt_folder(emf, kHFSRootFolderID); + } + + closeVolume(volume); + CLOSE(io); + + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf_init.c b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf_init.c new file mode 100644 index 0000000..c75a7fe --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/emf/emf_init.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include "hfs/hfslib.h" +#include "emf.h" + +size_t ConvertHexaCFString(CFStringRef s, uint8_t** bytes) +{ + uint32_t len = CFStringGetLength(s); + uint8_t* hex = malloc(len+1); + + if(hex == NULL) + return 0; + + if(!CFStringGetCString(s, hex, len+1, kCFStringEncodingASCII)) + { + free(hex); + return 0; + } + size_t size = 0; + hexToBytes(hex, bytes, &size); + free(hex); + return size; +} + +void grabClassKey(const void *key, const void *value, void *context) +{ + EMFInfo* emf = (EMFInfo*) context; + uint8_t* class_key = NULL; + + if(CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFStringGetTypeID()) + return; + + SInt32 clas = CFStringGetIntValue((CFStringRef)key); + + if(clas > 0 && clas <= MAX_CLASS_KEYS && CFStringGetLength((CFStringRef) value) == 64) + { + if(ConvertHexaCFString(value, &class_key) == 32) + { + AES_set_decrypt_key(class_key, 32*8, &(emf->classKeys[clas-1])); + free(class_key); + emf->classKeys_bitset |= 1 << clas; + } + } + +} + +EMFInfo* EMF_init(Volume* volume, char* imagePath) +{ + uint8_t* emfk = NULL; + uint8_t* dkey = NULL; + + uint64_t volume_id = *((uint64_t*) (&volume->volumeHeader->finderInfo[6])); + FLIPENDIAN(volume_id); + + if(imagePath == NULL) + imagePath = "."; + + printf("Volume identifier : %llx\n", volume_id); + printf("Searching for %s/%llx.plist\n", imagePath, volume_id); + + CFStringRef path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/%llx.plist"), imagePath, volume_id); + + CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, FALSE); + CFRelease(path); + + CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, fileURL); + CFRelease(fileURL); + + if(stream == NULL) + { + return NULL; + } + if(!CFReadStreamOpen(stream)) + { + fprintf(stderr, "Cannot open file\n"); + return NULL; + } + CFPropertyListRef dict = CFPropertyListCreateWithStream(NULL, stream, 0, kCFPropertyListImmutable, NULL, NULL); + + CFRelease(stream); + + if (dict == NULL || CFGetTypeID(dict) != CFDictionaryGetTypeID()) + return NULL; + + CFStringRef emfHex = CFDictionaryGetValue(dict, CFSTR("EMF")); + CFStringRef dkeyHex = CFDictionaryGetValue(dict, CFSTR("DKey")); + CFNumberRef dataVolumeOffset = CFDictionaryGetValue (dict, CFSTR("dataVolumeOffset")); + + if (emfHex == NULL || CFGetTypeID(emfHex) != CFStringGetTypeID()) + return NULL; + if (dkeyHex == NULL || CFGetTypeID(dkeyHex) != CFStringGetTypeID()) + return NULL; + if (dataVolumeOffset == NULL || CFGetTypeID(dataVolumeOffset) != CFNumberGetTypeID()) + return NULL; + + EMFInfo* emf = malloc(sizeof(EMFInfo)); + + if(emf == NULL) + return NULL; + + memset(emf, 0, sizeof(EMFInfo)); + + emf->volume = volume; + + CFNumberGetValue(dataVolumeOffset, kCFNumberLongType, &emf->volume_offset); + + printf("Data partition offset = %llx\n", emf->volume_offset); + + if(ConvertHexaCFString(emfHex, &emfk) != 32) + { + fprintf(stderr, "Invalid EMF key\n"); + free(emf); + return NULL; + } + if(ConvertHexaCFString(dkeyHex, &dkey) != 32) + { + fprintf(stderr, "Invalid DKey key\n"); + free(emf); + return NULL; + } + + AES_set_encrypt_key(emfk, 32*8, &(emf->emfkey)); + AES_set_decrypt_key(dkey, 32*8, &(emf->classKeys[CLASS_DKEY-1])); + emf->classKeys_bitset |= 1 << CLASS_DKEY; + + CFDictionaryRef classKeys = CFDictionaryGetValue(dict, CFSTR("classKeys")); + + if(classKeys != NULL && CFGetTypeID(classKeys) == CFDictionaryGetTypeID()) + { + printf("Reading class keys, NSProtectionComplete files should be decrypted OK\n"); + CFDictionaryApplyFunction(classKeys, grabClassKey, (void*) emf); + } + else + { + printf("Only NSProtectionNone files will be decrypted\n"); + } + + free(emfk); + free(dkey); + return emf; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/CMakeLists.txt b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/CMakeLists.txt new file mode 100644 index 0000000..3eaa5d2 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/CMakeLists.txt @@ -0,0 +1,18 @@ +INCLUDE(FindZLIB) + +IF(NOT ZLIB_FOUND) + message(FATAL_ERROR "zlib is required for hfs!") +ENDIF(NOT ZLIB_FOUND) + +include_directories(${ZLIB_INCLUDE_DIR}) +link_directories(${ZLIB_LIBRARIES}) + +link_directories (${PROJECT_BINARY_DIR}/common) +add_library(hfs btree.c catalog.c extents.c xattr.c fastunicodecompare.c flatfile.c hfslib.c rawfile.c utility.c volume.c hfscompress.c) +target_link_libraries(hfs common z) + +add_executable(hfsplus hfs.c) +target_link_libraries (hfsplus hfs) + +install(TARGETS hfsplus DESTINATION .) + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/btree.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/btree.c new file mode 100644 index 0000000..6d8f494 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/btree.c @@ -0,0 +1,1556 @@ +#include +#include + +BTNodeDescriptor* readBTNodeDescriptor(uint32_t num, BTree* tree) { + BTNodeDescriptor* descriptor; + + descriptor = (BTNodeDescriptor*) malloc(sizeof(BTNodeDescriptor)); + + if(!READ(tree->io, num * tree->headerRec->nodeSize, sizeof(BTNodeDescriptor), descriptor)) + return NULL; + + FLIPENDIAN(descriptor->fLink); + FLIPENDIAN(descriptor->bLink); + FLIPENDIAN(descriptor->numRecords); + + return descriptor; +} + +static int writeBTNodeDescriptor(BTNodeDescriptor* descriptor, uint32_t num, BTree* tree) { + BTNodeDescriptor myDescriptor; + + myDescriptor = *descriptor; + + FLIPENDIAN(myDescriptor.fLink); + FLIPENDIAN(myDescriptor.bLink); + FLIPENDIAN(myDescriptor.numRecords); + + if(!WRITE(tree->io, num * tree->headerRec->nodeSize, sizeof(BTNodeDescriptor), &myDescriptor)) + return FALSE; + + return TRUE; +} + +BTHeaderRec* readBTHeaderRec(io_func* io) { + BTHeaderRec* headerRec; + + headerRec = (BTHeaderRec*) malloc(sizeof(BTHeaderRec)); + + if(!READ(io, sizeof(BTNodeDescriptor), sizeof(BTHeaderRec), headerRec)) { + free(headerRec); + return NULL; + } + + FLIPENDIAN(headerRec->treeDepth); + FLIPENDIAN(headerRec->rootNode); + FLIPENDIAN(headerRec->leafRecords); + FLIPENDIAN(headerRec->firstLeafNode); + FLIPENDIAN(headerRec->lastLeafNode); + FLIPENDIAN(headerRec->nodeSize); + FLIPENDIAN(headerRec->maxKeyLength); + FLIPENDIAN(headerRec->totalNodes); + FLIPENDIAN(headerRec->freeNodes); + FLIPENDIAN(headerRec->clumpSize); + FLIPENDIAN(headerRec->attributes); + + /*printf("treeDepth: %d\n", headerRec->treeDepth); + printf("rootNode: %d\n", headerRec->rootNode); + printf("leafRecords: %d\n", headerRec->leafRecords); + printf("firstLeafNode: %d\n", headerRec->firstLeafNode); + printf("lastLeafNode: %d\n", headerRec->lastLeafNode); + printf("nodeSize: %d\n", headerRec->nodeSize); + printf("maxKeyLength: %d\n", headerRec->maxKeyLength); + printf("totalNodes: %d\n", headerRec->totalNodes); + printf("freeNodes: %d\n", headerRec->freeNodes); + printf("clumpSize: %d\n", headerRec->clumpSize); + printf("bTreeType: 0x%x\n", headerRec->btreeType); + printf("keyCompareType: 0x%x\n", headerRec->keyCompareType); + printf("attributes: 0x%x\n", headerRec->attributes); + fflush(stdout);*/ + + return headerRec; +} + +static int writeBTHeaderRec(BTree* tree) { + BTHeaderRec headerRec; + + headerRec = *tree->headerRec; + + FLIPENDIAN(headerRec.treeDepth); + FLIPENDIAN(headerRec.rootNode); + FLIPENDIAN(headerRec.leafRecords); + FLIPENDIAN(headerRec.firstLeafNode); + FLIPENDIAN(headerRec.lastLeafNode); + FLIPENDIAN(headerRec.nodeSize); + FLIPENDIAN(headerRec.maxKeyLength); + FLIPENDIAN(headerRec.totalNodes); + FLIPENDIAN(headerRec.freeNodes); + FLIPENDIAN(headerRec.clumpSize); + FLIPENDIAN(headerRec.attributes); + + if(!WRITE(tree->io, sizeof(BTNodeDescriptor), sizeof(BTHeaderRec), &headerRec)) + return FALSE; + + return TRUE; +} + + +BTree* openBTree(io_func* io, compareFunc compare, dataReadFunc keyRead, keyWriteFunc keyWrite, keyPrintFunc keyPrint, dataReadFunc dataRead) { + BTree* tree; + + tree = (BTree*) malloc(sizeof(BTree)); + tree->io = io; + tree->headerRec = readBTHeaderRec(tree->io); + + if(tree->headerRec == NULL) { + free(tree); + return NULL; + } + + tree->compare = compare; + tree->keyRead = keyRead; + tree->keyWrite = keyWrite; + tree->keyPrint = keyPrint; + tree->dataRead = dataRead; + + return tree; +} + +void closeBTree(BTree* tree) { + (*tree->io->close)(tree->io); + free(tree->headerRec); + free(tree); +} + +off_t getRecordOffset(int num, uint32_t nodeNum, BTree* tree) { + uint16_t offset; + off_t nodeOffset; + + nodeOffset = nodeNum * tree->headerRec->nodeSize; + + if(!READ(tree->io, nodeOffset + tree->headerRec->nodeSize - (sizeof(uint16_t) * (num + 1)), sizeof(uint16_t), &offset)) { + hfs_panic("cannot get record offset!"); + } + + FLIPENDIAN(offset); + + //printf("%d: %d %d\n", nodeOffset + tree->headerRec->nodeSize - (sizeof(uint16_t) * (num + 1)), nodeOffset + offset, offset); + + return (nodeOffset + offset); +} + +static off_t getFreeSpace(uint32_t nodeNum, BTNodeDescriptor* descriptor, BTree* tree) { + uint16_t num; + off_t nodeOffset; + off_t freespaceOffsetOffset; + uint16_t offset; + off_t freespaceOffset; + + num = descriptor->numRecords; + + nodeOffset = nodeNum * tree->headerRec->nodeSize; + freespaceOffsetOffset = nodeOffset + tree->headerRec->nodeSize - (sizeof(uint16_t) * (num + 1)); + + if(!READ(tree->io, freespaceOffsetOffset, sizeof(uint16_t), &offset)) { + hfs_panic("cannot get record offset!"); + } + + FLIPENDIAN(offset); + + freespaceOffset = nodeOffset + offset; + + return (freespaceOffsetOffset - freespaceOffset); +} + +off_t getNodeNumberFromPointerRecord(off_t offset, io_func* io) { + uint32_t nodeNum; + + if(!READ(io, offset, sizeof(uint32_t), &nodeNum)) { + hfs_panic("cannot get node number from pointer record!"); + } + + FLIPENDIAN(nodeNum); + + return nodeNum; +} + +static void* searchNode(BTree* tree, uint32_t root, BTKey* searchKey, int *exact, uint32_t *nodeNumber, int *recordNumber) { + BTNodeDescriptor* descriptor; + BTKey* key; + off_t recordOffset; + off_t recordDataOffset; + off_t lastRecordDataOffset; + + int res; + int i; + + descriptor = readBTNodeDescriptor(root, tree); + + if(descriptor == NULL) + return NULL; + + lastRecordDataOffset = 0; + + for(i = 0; i < descriptor->numRecords; i++) { + recordOffset = getRecordOffset(i, root, tree); + key = READ_KEY(tree, recordOffset, tree->io); + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + + res = COMPARE(tree, key, searchKey); + free(key); + if(res == 0) { + if(descriptor->kind == kBTLeafNode) { + if(nodeNumber != NULL) + *nodeNumber = root; + + if(recordNumber != NULL) + *recordNumber = i; + + if(exact != NULL) + *exact = TRUE; + + free(descriptor); + + return READ_DATA(tree, recordDataOffset, tree->io); + } else { + + free(descriptor); + return searchNode(tree, getNodeNumberFromPointerRecord(recordDataOffset, tree->io), searchKey, exact, nodeNumber, recordNumber); + } + } else if(res > 0) { + break; + } + + lastRecordDataOffset = recordDataOffset; + } + + if(lastRecordDataOffset == 0) { + hfs_panic("BTree inconsistent!"); + return NULL; + } + + if(descriptor->kind == kBTLeafNode) { + if(nodeNumber != NULL) + *nodeNumber = root; + + if(recordNumber != NULL) + *recordNumber = i; + + if(exact != NULL) + *exact = FALSE; + + free(descriptor); + return READ_DATA(tree, lastRecordDataOffset, tree->io); + } else if(descriptor->kind == kBTIndexNode) { + + free(descriptor); + return searchNode(tree, getNodeNumberFromPointerRecord(lastRecordDataOffset, tree->io), searchKey, exact, nodeNumber, recordNumber); + } else { + if(nodeNumber != NULL) + *nodeNumber = root; + + if(recordNumber != NULL) + *recordNumber = i; + + if(exact != NULL) + *exact = FALSE; + + free(descriptor); + return NULL; + } +} + +void* search(BTree* tree, BTKey* searchKey, int *exact, uint32_t *nodeNumber, int *recordNumber) { + return searchNode(tree, tree->headerRec->rootNode, searchKey, exact, nodeNumber, recordNumber); +} + +static uint32_t linearCheck(uint32_t* heightTable, unsigned char* map, BTree* tree, uint32_t *errCount) { + uint8_t i; + uint8_t j; + uint32_t node; + + uint32_t count; + uint32_t leafRecords; + + BTNodeDescriptor* descriptor; + + uint32_t prevNode; + + off_t recordOffset; + BTKey* key; + BTKey* previousKey; + + count = 0; + + leafRecords = 0; + + for(i = 0; i <= tree->headerRec->treeDepth; i++) { + node = heightTable[i]; + if(node != 0) { + descriptor = readBTNodeDescriptor(node, tree); + while(descriptor->bLink != 0) { + node = descriptor->bLink; + free(descriptor); + descriptor = readBTNodeDescriptor(node, tree); + } + free(descriptor); + + prevNode = 0; + previousKey = NULL; + + if(i == 1) { + if(node != tree->headerRec->firstLeafNode) { + printf("BTREE CONSISTENCY ERROR: First leaf node (%d) is not correct. Should be: %d\n", tree->headerRec->firstLeafNode, node); + (*errCount)++; + } + } + + while(node != 0) { + descriptor = readBTNodeDescriptor(node, tree); + if(descriptor->bLink != prevNode) { + printf("BTREE CONSISTENCY ERROR: Node %d is not properly linked with previous node %d\n", node, prevNode); + (*errCount)++; + } + + if(descriptor->height != i) { + printf("BTREE CONSISTENCY ERROR: Node %d (%d) is not properly linked with nodes of the same height %d\n", node, descriptor->height, i); + (*errCount)++; + } + + if((map[node / 8] & (1 << (7 - (node % 8)))) == 0) { + printf("BTREE CONSISTENCY ERROR: Node %d not marked allocated\n", node); + (*errCount)++; + } + + /*if(descriptor->kind == kBTIndexNode && descriptor->numRecords < 2) { + printf("BTREE CONSISTENCY ERROR: Node %d does not have at least two children\n", node); + (*errCount)++; + }*/ + + for(j = 0; j < descriptor->numRecords; j++) { + recordOffset = getRecordOffset(j, node, tree); + key = READ_KEY(tree, recordOffset, tree->io); + if(previousKey != NULL) { + if(COMPARE(tree, key, previousKey) < 0) { + printf("BTREE CONSISTENCY ERROR: Ordering not preserved during linear check for record %d node %d: ", j, node); + (*errCount)++; + tree->keyPrint(previousKey); + printf(" < "); + tree->keyPrint(key); + printf("\n"); + } + free(previousKey); + } + + if(i == 1) { + leafRecords++; + } + previousKey = key; + } + + count++; + + prevNode = node; + node = descriptor->fLink; + free(descriptor); + } + + if(i == 1) { + if(prevNode != tree->headerRec->lastLeafNode) { + printf("BTREE CONSISTENCY ERROR: Last leaf node (%d) is not correct. Should be: %d\n", tree->headerRec->lastLeafNode, node); + (*errCount)++; + } + } + + free(previousKey); + } + } + + if(leafRecords != tree->headerRec->leafRecords) { + printf("BTREE CONSISTENCY ERROR: leafRecords (%d) is not correct. Should be: %d\n", tree->headerRec->leafRecords, leafRecords); + (*errCount)++; + } + + return count; +} + +static uint32_t traverseNode(uint32_t nodeNum, BTree* tree, unsigned char* map, int parentHeight, BTKey** firstKey, BTKey** lastKey, + uint32_t* heightTable, uint32_t* errCount, int displayTree) { + BTNodeDescriptor* descriptor; + BTKey* key; + BTKey* previousKey; + BTKey* retFirstKey; + BTKey* retLastKey; + int i, j; + + int res; + + uint32_t count; + + off_t recordOffset; + off_t recordDataOffset; + + off_t lastrecordDataOffset; + + descriptor = readBTNodeDescriptor(nodeNum, tree); + + previousKey = NULL; + + count = 1; + + if(displayTree) { + for(i = 0; i < descriptor->height; i++) { + printf(" "); + } + } + + if(descriptor->kind == kBTLeafNode) { + if(displayTree) + printf("Leaf %d: %d", nodeNum, descriptor->numRecords); + + if(descriptor->height != 1) { + printf("BTREE CONSISTENCY ERROR: Leaf node %d does not have height 1\n", nodeNum); fflush(stdout); + (*errCount)++; + } + } else if(descriptor->kind == kBTIndexNode) { + if(displayTree) + printf("Index %d: %d", nodeNum, descriptor->numRecords); + + } else { + printf("BTREE CONSISTENCY ERROR: Unexpected node %d has kind %d\n", nodeNum, descriptor->kind); fflush(stdout); + (*errCount)++; + } + + if(displayTree) { + printf("\n"); fflush(stdout); + } + + if((map[nodeNum / 8] & (1 << (7 - (nodeNum % 8)))) == 0) { + printf("BTREE CONSISTENCY ERROR: Node %d not marked allocated\n", nodeNum); fflush(stdout); + (*errCount)++; + } + + if(nodeNum == tree->headerRec->rootNode) { + if(descriptor->height != tree->headerRec->treeDepth) { + printf("BTREE CONSISTENCY ERROR: Root node %d (%d) does not have the proper height (%d)\n", nodeNum, + descriptor->height, tree->headerRec->treeDepth); fflush(stdout); + (*errCount)++; + } + } else { + if(descriptor->height != (parentHeight - 1)) { + printf("BTREE CONSISTENCY ERROR: Node %d does not have the proper height\n", nodeNum); fflush(stdout); + (*errCount)++; + } + } + + /*if(descriptor->kind == kBTIndexNode && descriptor->numRecords < 2) { + printf("BTREE CONSISTENCY ERROR: Node %d does not have at least two children\n", nodeNum); + (*errCount)++; + }*/ + + heightTable[descriptor->height] = nodeNum; + lastrecordDataOffset = 0; + + for(i = 0; i < descriptor->numRecords; i++) { + recordOffset = getRecordOffset(i, nodeNum, tree); + key = READ_KEY(tree, recordOffset, tree->io); + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + + if((recordDataOffset - (nodeNum * tree->headerRec->nodeSize)) > (tree->headerRec->nodeSize - (sizeof(uint16_t) * (descriptor->numRecords + 1)))) { + printf("BTREE CONSISTENCY ERROR: Record data extends past offsets in node %d record %d\n", nodeNum, i); fflush(stdout); + (*errCount)++; + } + + if(i == 0) { + *firstKey = READ_KEY(tree, recordOffset, tree->io); + } + + if(i == (descriptor->numRecords - 1)) { + *lastKey = READ_KEY(tree, recordOffset, tree->io); + } + + if(previousKey != NULL) { + res = COMPARE(tree, key, previousKey); + if(res < 0) { + printf("BTREE CONSISTENCY ERROR(traverse): Ordering between records within node not preserved in record %d node %d for ", i, nodeNum); + (*errCount)++; + tree->keyPrint(previousKey); + printf(" < "); + tree->keyPrint(key); + printf("\n"); fflush(stdout); + } + free(previousKey); + previousKey = NULL; + } + + if(displayTree) { + for(j = 0; j < (descriptor->height - 1); j++) { + printf(" "); + } + tree->keyPrint(key); + printf("\n"); + } + + if(descriptor->kind == kBTIndexNode) { + count += traverseNode(getNodeNumberFromPointerRecord(recordDataOffset, tree->io), + tree, map, descriptor->height, &retFirstKey, &retLastKey, heightTable, errCount, displayTree); + + if(COMPARE(tree, retFirstKey, key) != 0) { + printf("BTREE CONSISTENCY ERROR: Index node key does not match first key in record %d node %d\n", i, nodeNum); fflush(stdout); + (*errCount)++; + } + if(COMPARE(tree, retLastKey, key) < 0) { + printf("BTREE CONSISTENCY ERROR: Last key is less than the index node key in record %d node %d\n", i, nodeNum); fflush(stdout); + (*errCount)++; + } + free(retFirstKey); + free(key); + previousKey = retLastKey; + } else { + previousKey = key; + } + + if(recordOffset < lastrecordDataOffset) { + printf("BTREE CONSISTENCY ERROR: Record offsets are not in order in node %d starting at record %d\n", nodeNum, i); fflush(stdout); + (*errCount)++; + } + + lastrecordDataOffset = recordDataOffset; + } + + if(previousKey != NULL) free(previousKey); + + free(descriptor); + + return count; + +} + +static unsigned char* mapNodes(BTree* tree, uint32_t* numMapNodes, uint32_t* errCount) { + unsigned char *map; + + BTNodeDescriptor* descriptor; + + unsigned char byte; + + uint32_t totalNodes; + uint32_t freeNodes; + + uint32_t byteNumber; + uint32_t byteTracker; + + uint32_t mapNode; + + off_t mapRecordStart; + off_t mapRecordLength; + + int i; + + map = (unsigned char *)malloc(tree->headerRec->totalNodes/8 + 1); + + byteTracker = 0; + freeNodes = 0; + totalNodes = 0; + + mapRecordStart = getRecordOffset(2, 0, tree); + mapRecordLength = tree->headerRec->nodeSize - 256; + byteNumber = 0; + mapNode = 0; + + *numMapNodes = 0; + + while(TRUE) { + while(byteNumber < mapRecordLength) { + READ(tree->io, mapRecordStart + byteNumber, 1, &byte); + map[byteTracker] = byte; + byteTracker++; + byteNumber++; + for(i = 0; i < 8; i++) { + if((byte & (1 << (7 - i))) == 0) { + freeNodes++; + } + totalNodes++; + + if(totalNodes == tree->headerRec->totalNodes) + goto done; + } + } + + descriptor = readBTNodeDescriptor(mapNode, tree); + mapNode = descriptor->fLink; + free(descriptor); + + (*numMapNodes)++; + + if(mapNode == 0) { + printf("BTREE CONSISTENCY ERROR: Not enough map nodes allocated! Allocated for: %d, needed: %d\n", totalNodes, tree->headerRec->totalNodes); + (*errCount)++; + break; + } + + mapRecordStart = mapNode * tree->headerRec->nodeSize + 14; + mapRecordLength = tree->headerRec->nodeSize - 20; + byteNumber = 0; + } + + done: + + if(freeNodes != tree->headerRec->freeNodes) { + printf("BTREE CONSISTENCY ERROR: Free nodes %d differ from actually allocated %d\n", tree->headerRec->freeNodes, freeNodes); + (*errCount)++; + } + + return map; +} + +int debugBTree(BTree* tree, int displayTree) { + unsigned char* map; + uint32_t *heightTable; + BTKey* retFirstKey; + BTKey* retLastKey; + + uint32_t numMapNodes; + uint32_t traverseCount; + uint32_t linearCount; + uint32_t errorCount; + + uint8_t i; + + errorCount = 0; + + printf("Mapping nodes...\n"); fflush(stdout); + map = mapNodes(tree, &numMapNodes, &errorCount); + + printf("Initializing height table...\n"); fflush(stdout); + heightTable = (uint32_t*) malloc(sizeof(uint32_t) * (tree->headerRec->treeDepth + 1)); + for(i = 0; i <= tree->headerRec->treeDepth; i++) { + heightTable[i] = 0; + } + + if(tree->headerRec->rootNode == 0) { + if(tree->headerRec->firstLeafNode == 0 && tree->headerRec->lastLeafNode == 0) { + traverseCount = 0; + linearCount = 0; + } else { + printf("BTREE CONSISTENCY ERROR: First leaf node (%d) and last leaf node (%d) inconsistent with empty BTree\n", + tree->headerRec->firstLeafNode, tree->headerRec->lastLeafNode); + + // Try to see if we can get a linear count + if(tree->headerRec->firstLeafNode != 0) + heightTable[1] = tree->headerRec->firstLeafNode; + else + heightTable[1] = tree->headerRec->lastLeafNode; + + linearCount = linearCheck(heightTable, map, tree, &errorCount); + } + } else { + printf("Performing tree traversal...\n"); fflush(stdout); + traverseCount = traverseNode(tree->headerRec->rootNode, tree, map, 0, &retFirstKey, &retLastKey, heightTable, &errorCount, displayTree); + + free(retFirstKey); + free(retLastKey); + + printf("Performing linear traversal...\n"); fflush(stdout); + linearCount = linearCheck(heightTable, map, tree, &errorCount); + } + + printf("Total traverse nodes: %d\n", traverseCount); fflush(stdout); + printf("Total linear nodes: %d\n", linearCount); fflush(stdout); + printf("Error count: %d\n", errorCount); fflush(stdout); + + if(traverseCount != linearCount) { + printf("BTREE CONSISTENCY ERROR: Linear count and traverse count are inconsistent\n"); + } + + if(traverseCount != (tree->headerRec->totalNodes - tree->headerRec->freeNodes - numMapNodes - 1)) { + printf("BTREE CONSISTENCY ERROR: Free nodes and total nodes (%d) and traverse count are inconsistent\n", + tree->headerRec->totalNodes - tree->headerRec->freeNodes); + } + + free(heightTable); + free(map); + + return errorCount; +} + +static uint32_t findFree(BTree* tree) { + unsigned char byte; + uint32_t byteNumber; + uint32_t mapNode; + + BTNodeDescriptor* descriptor; + + off_t mapRecordStart; + off_t mapRecordLength; + + int i; + + mapRecordStart = getRecordOffset(2, 0, tree); + mapRecordLength = tree->headerRec->nodeSize - 256; + mapNode = 0; + byteNumber = 0; + + while(TRUE) { + while(byteNumber < mapRecordLength) { + READ(tree->io, mapRecordStart + byteNumber, 1, &byte); + if(byte != 0xFF) { + for(i = 0; i < 8; i++) { + if((byte & (1 << (7 - i))) == 0) { + byte |= (1 << (7 - i)); + tree->headerRec->freeNodes--; + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + ASSERT(WRITE(tree->io, mapRecordStart + byteNumber, 1, &byte), "WRITE"); + return ((byteNumber * 8) + i); + } + } + } + byteNumber++; + } + + descriptor = readBTNodeDescriptor(mapNode, tree); + mapNode = descriptor->fLink; + free(descriptor); + + if(mapNode == 0) { + return 0; + } + + mapRecordStart = mapNode * tree->headerRec->nodeSize + 14; + mapRecordLength = tree->headerRec->nodeSize - 20; + byteNumber = 0; + } +} + +static int markUsed(uint32_t node, BTree* tree) { + BTNodeDescriptor* descriptor; + uint32_t mapNode; + uint32_t byteNumber; + + unsigned char byte; + + mapNode = 0; + byteNumber = node / 8; + + if(byteNumber >= (tree->headerRec->nodeSize - 256)) { + while(TRUE) { + descriptor = readBTNodeDescriptor(mapNode, tree); + mapNode = descriptor->fLink; + free(descriptor); + + if(byteNumber > (tree->headerRec->nodeSize - 20)) { + byteNumber -= tree->headerRec->nodeSize - 20; + } else { + break; + } + } + } + + ASSERT(READ(tree->io, mapNode * tree->headerRec->nodeSize + 14 + byteNumber, 1, &byte), "READ"); + byte |= (1 << (7 - (node % 8))); + ASSERT(WRITE(tree->io, mapNode * tree->headerRec->nodeSize + 14 + byteNumber, 1, &byte), "WRITE"); + + return TRUE; +} + +static int growBTree(BTree* tree) { + int i; + unsigned char* buffer; + uint16_t offset; + + uint32_t byteNumber; + uint32_t mapNode; + int increasedNodes; + + off_t newNodeOffset; + uint32_t newNodesStart; + + BTNodeDescriptor* descriptor; + BTNodeDescriptor newDescriptor; + + allocate((RawFile*)(tree->io->data), ((RawFile*)(tree->io->data))->forkData->logicalSize + ((RawFile*)(tree->io->data))->forkData->clumpSize); + increasedNodes = (((RawFile*)(tree->io->data))->forkData->logicalSize/tree->headerRec->nodeSize) - tree->headerRec->totalNodes; + + newNodesStart = tree->headerRec->totalNodes / tree->headerRec->nodeSize; + + tree->headerRec->freeNodes += increasedNodes; + tree->headerRec->totalNodes += increasedNodes; + + byteNumber = tree->headerRec->totalNodes / 8; + mapNode = 0; + + buffer = (unsigned char*) malloc(tree->headerRec->nodeSize - 20); + for(i = 0; i < (tree->headerRec->nodeSize - 20); i++) { + buffer[i] = 0; + } + + if(byteNumber < (tree->headerRec->nodeSize - 256)) { + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderREc"); + free(buffer); + return TRUE; + } else { + byteNumber -= tree->headerRec->nodeSize - 256; + + while(TRUE) { + descriptor = readBTNodeDescriptor(mapNode, tree); + + if(descriptor->fLink == 0) { + descriptor->fLink = newNodesStart; + ASSERT(writeBTNodeDescriptor(descriptor, mapNode, tree), "writeBTNodeDescriptor"); + + newDescriptor.fLink = 0; + newDescriptor.bLink = 0; + newDescriptor.kind = kBTMapNode; + newDescriptor.height = 0; + newDescriptor.numRecords = 1; + newDescriptor.reserved = 0; + ASSERT(writeBTNodeDescriptor(&newDescriptor, descriptor->fLink, tree), "writeBTNodeDescriptor"); + + newNodeOffset = descriptor->fLink * tree->headerRec->nodeSize; + + ASSERT(WRITE(tree->io, newNodeOffset + 14, tree->headerRec->nodeSize - 20, buffer), "WRITE"); + offset = 14; + FLIPENDIAN(offset); + ASSERT(WRITE(tree->io, newNodeOffset + tree->headerRec->nodeSize - 2, sizeof(offset), &offset), "WRITE"); + offset = 14 + tree->headerRec->nodeSize - 20; + FLIPENDIAN(offset); + ASSERT(WRITE(tree->io, newNodeOffset + tree->headerRec->nodeSize - 4, sizeof(offset), &offset), "WRITE"); + + // mark the map node as being used + ASSERT(markUsed(newNodesStart, tree), "markUsed"); + tree->headerRec->freeNodes--; + newNodesStart++; + } + mapNode = descriptor->fLink; + + if(byteNumber > (tree->headerRec->nodeSize - 20)) { + byteNumber -= tree->headerRec->nodeSize - 20; + } else { + free(buffer); + + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + return TRUE; + } + } + } + + return FALSE; +} + +static uint32_t getNewNode(BTree* tree) { + if(tree->headerRec->freeNodes == 0) { + growBTree(tree); + } + + return findFree(tree); +} + +static uint32_t removeNode(BTree* tree, uint32_t node) { + unsigned char byte; + off_t mapRecordStart; + uint32_t mapNode; + size_t mapRecordLength; + BTNodeDescriptor *descriptor; + BTNodeDescriptor *oDescriptor; + + mapRecordStart = getRecordOffset(2, 0, tree); + mapRecordLength = tree->headerRec->nodeSize - 256; + mapNode = 0; + + while((node / 8) >= mapRecordLength) { + descriptor = readBTNodeDescriptor(mapNode, tree); + mapNode = descriptor->fLink; + free(descriptor); + + if(mapNode == 0) { + hfs_panic("Cannot remove node because I can't map it!"); + return 0; + } + + mapRecordStart = mapNode * tree->headerRec->nodeSize + 14; + mapRecordLength = tree->headerRec->nodeSize - 20; + node -= mapRecordLength * 8; + } + + READ(tree->io, mapRecordStart + (node / 8), 1, &byte); + + byte &= ~(1 << (7 - (node % 8))); + + tree->headerRec->freeNodes++; + + descriptor = readBTNodeDescriptor(node, tree); + + if(tree->headerRec->firstLeafNode == node) { + tree->headerRec->firstLeafNode = descriptor->fLink; + } + + if(tree->headerRec->lastLeafNode == node) { + tree->headerRec->lastLeafNode = descriptor->bLink; + } + + if(node == tree->headerRec->rootNode) { + tree->headerRec->rootNode = 0; + } + + if(descriptor->bLink != 0) { + oDescriptor = readBTNodeDescriptor(descriptor->bLink, tree); + oDescriptor->fLink = descriptor->fLink; + ASSERT(writeBTNodeDescriptor(oDescriptor, descriptor->bLink, tree), "writeBTNodeDescriptor"); + free(oDescriptor); + } + + if(descriptor->fLink != 0) { + oDescriptor = readBTNodeDescriptor(descriptor->fLink, tree); + oDescriptor->bLink = descriptor->bLink; + ASSERT(writeBTNodeDescriptor(oDescriptor, descriptor->fLink, tree), "writeBTNodeDescriptor"); + free(oDescriptor); + } + + free(descriptor); + + ASSERT(WRITE(tree->io, mapRecordStart + (node / 8), 1, &byte), "WRITE"); + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + + return TRUE; +} + +static uint32_t splitNode(uint32_t node, BTNodeDescriptor* descriptor, BTree* tree) { + int nodesToMove; + + int i; + off_t internalOffset; + + BTNodeDescriptor* fDescriptor; + + BTNodeDescriptor newDescriptor; + uint32_t newNodeNum; + off_t newNodeOffset; + + off_t toMove; + size_t toMoveLength; + unsigned char *buffer; + + off_t offsetsToMove; + size_t offsetsToMoveLength; + uint16_t *offsetsBuffer; + + nodesToMove = descriptor->numRecords - (descriptor->numRecords/2); + + toMove = getRecordOffset(descriptor->numRecords/2, node, tree); + toMoveLength = getRecordOffset(descriptor->numRecords, node, tree) - toMove; + buffer = (unsigned char *)malloc(toMoveLength); + ASSERT(READ(tree->io, toMove, toMoveLength, buffer), "READ"); + + offsetsToMove = (node * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * (descriptor->numRecords + 1)); + offsetsToMoveLength = sizeof(uint16_t) * (nodesToMove + 1); + offsetsBuffer = (uint16_t *)malloc(offsetsToMoveLength); + ASSERT(READ(tree->io, offsetsToMove, offsetsToMoveLength, offsetsBuffer), "READ"); + + for(i = 0; i < (nodesToMove + 1); i++) { + FLIPENDIAN(offsetsBuffer[i]); + } + + internalOffset = offsetsBuffer[nodesToMove] - 14; + + for(i = 0; i < (nodesToMove + 1); i++) { + offsetsBuffer[i] -= internalOffset; + FLIPENDIAN(offsetsBuffer[i]); + } + + newNodeNum = getNewNode(tree); + newNodeOffset = newNodeNum * tree->headerRec->nodeSize; + + newDescriptor.fLink = descriptor->fLink; + newDescriptor.bLink = node; + newDescriptor.kind = descriptor->kind; + newDescriptor.height = descriptor->height; + newDescriptor.numRecords = nodesToMove; + newDescriptor.reserved = 0; + ASSERT(writeBTNodeDescriptor(&newDescriptor, newNodeNum, tree), "writeBTNodeDescriptor"); + + if(newDescriptor.fLink != 0) { + fDescriptor = readBTNodeDescriptor(newDescriptor.fLink, tree); + fDescriptor->bLink = newNodeNum; + ASSERT(writeBTNodeDescriptor(fDescriptor, newDescriptor.fLink, tree), "writeBTNodeDescriptor"); + free(fDescriptor); + } + + descriptor->fLink = newNodeNum; + descriptor->numRecords = descriptor->numRecords/2; + ASSERT(writeBTNodeDescriptor(descriptor, node, tree), "writeBTNodeDescriptor"); + + ASSERT(WRITE(tree->io, newNodeOffset + 14, toMoveLength, buffer), "WRITE"); + ASSERT(WRITE(tree->io, newNodeOffset + tree->headerRec->nodeSize - (sizeof(uint16_t) * (nodesToMove + 1)), offsetsToMoveLength, offsetsBuffer), "WRITE"); + + // The offset for the existing descriptor's new numRecords will happen to be where the old data was, which is now where the free space starts + // So we don't have to manually set the free space offset + + free(buffer); + free(offsetsBuffer); + + if(descriptor->kind == kBTLeafNode && node == tree->headerRec->lastLeafNode) { + tree->headerRec->lastLeafNode = newNodeNum; + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + } + + return newNodeNum; +} + +static int moveRecordsDown(BTree* tree, BTNodeDescriptor* descriptor, int record, uint32_t node, int length, int moveOffsets) { + off_t firstRecordStart; + off_t lastRecordEnd; + unsigned char* records; + + off_t firstOffsetStart; + off_t lastOffsetEnd; + uint16_t* offsets; + + int i; + + firstRecordStart = getRecordOffset(record, node, tree); + lastRecordEnd = getRecordOffset(descriptor->numRecords, node, tree); + + records = (unsigned char*)malloc(lastRecordEnd - firstRecordStart); + + ASSERT(READ(tree->io, firstRecordStart, lastRecordEnd - firstRecordStart, records), "READ"); + firstOffsetStart = (node * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * (descriptor->numRecords + 1)); + lastOffsetEnd = (node * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * record); + + offsets = (uint16_t*)malloc(lastOffsetEnd - firstOffsetStart); + ASSERT(READ(tree->io, firstOffsetStart, lastOffsetEnd - firstOffsetStart, offsets), "READ"); + + for(i = 0; i < (lastOffsetEnd - firstOffsetStart)/sizeof(uint16_t); i++) { + FLIPENDIAN(offsets[i]); + offsets[i] += length; + FLIPENDIAN(offsets[i]); + } + + ASSERT(WRITE(tree->io, firstRecordStart + length, lastRecordEnd - firstRecordStart, records), "WRITE"); + + if(moveOffsets > 0) { + ASSERT(WRITE(tree->io, firstOffsetStart - sizeof(uint16_t), lastOffsetEnd - firstOffsetStart, offsets), "WRITE"); + } else if(moveOffsets < 0) { + ASSERT(WRITE(tree->io, firstOffsetStart + sizeof(uint16_t), lastOffsetEnd - firstOffsetStart, offsets), "WRITE"); + } else { + ASSERT(WRITE(tree->io, firstOffsetStart, lastOffsetEnd - firstOffsetStart, offsets), "WRITE"); + } + + free(records); + free(offsets); + + return TRUE; +} + +static int doAddRecord(BTree* tree, uint32_t root, BTKey* searchKey, size_t length, unsigned char* content) { + BTNodeDescriptor* descriptor; + BTKey* key; + off_t recordOffset; + off_t recordDataOffset; + off_t lastRecordDataOffset; + + uint16_t offset; + + int res; + int i; + + descriptor = readBTNodeDescriptor(root, tree); + + if(descriptor == NULL) + return FALSE; + + lastRecordDataOffset = 0; + + for(i = 0; i < descriptor->numRecords; i++) { + recordOffset = getRecordOffset(i, root, tree); + key = READ_KEY(tree, recordOffset, tree->io); + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + + res = COMPARE(tree, key, searchKey); + if(res == 0) { + free(key); + free(descriptor); + + return FALSE; + } else if(res > 0) { + free(key); + + break; + } + + free(key); + + lastRecordDataOffset = recordDataOffset; + } + + if(i != descriptor->numRecords) { + // first, move everyone else down + + moveRecordsDown(tree, descriptor, i, root, sizeof(searchKey->keyLength) + searchKey->keyLength + length, 1); + + // then insert ourself + ASSERT(WRITE_KEY(tree, recordOffset, searchKey, tree->io), "WRITE_KEY"); + ASSERT(WRITE(tree->io, recordOffset + sizeof(searchKey->keyLength) + searchKey->keyLength, length, content), "WRITE"); + + offset = recordOffset - (root * tree->headerRec->nodeSize); + FLIPENDIAN(offset); + ASSERT(WRITE(tree->io, (root * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * (i + 1)), + sizeof(uint16_t), &offset), "WRITE"); + } else { + // just insert ourself at the end + recordOffset = getRecordOffset(i, root, tree); + ASSERT(WRITE_KEY(tree, recordOffset, searchKey, tree->io), "WRITE_KEY"); + ASSERT(WRITE(tree->io, recordOffset + sizeof(uint16_t) + searchKey->keyLength, length, content), "WRITE"); + + // write the new free offset + offset = (recordOffset + sizeof(searchKey->keyLength) + searchKey->keyLength + length) - (root * tree->headerRec->nodeSize); + FLIPENDIAN(offset); + ASSERT(WRITE(tree->io, (root * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * (descriptor->numRecords + 2)), + sizeof(uint16_t), &offset), "WRITE"); + } + + descriptor->numRecords++; + + if(descriptor->height == 1) { + tree->headerRec->leafRecords++; + } + + ASSERT(writeBTNodeDescriptor(descriptor, root, tree), "writeBTNodeDescriptor"); + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + + free(descriptor); + return TRUE; +} + +static int addRecord(BTree* tree, uint32_t root, BTKey* searchKey, size_t length, unsigned char* content, int* callAgain) { + BTNodeDescriptor* descriptor; + BTKey* key; + off_t recordOffset; + off_t recordDataOffset; + off_t lastRecordDataOffset; + + size_t freeSpace; + + int res; + int i; + + uint32_t newNode; + uint32_t newNodeBigEndian; + + uint32_t nodeBigEndian; + + descriptor = readBTNodeDescriptor(root, tree); + + if(descriptor == NULL) + return 0; + + freeSpace = getFreeSpace(root, descriptor, tree); + + lastRecordDataOffset = 0; + + for(i = 0; i < descriptor->numRecords; i++) { + recordOffset = getRecordOffset(i, root, tree); + key = READ_KEY(tree, recordOffset, tree->io); + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + + res = COMPARE(tree, key, searchKey); + if(res == 0) { + free(key); + free(descriptor); + + return 0; + } else if(res > 0) { + free(key); + + break; + } + + free(key); + + lastRecordDataOffset = recordDataOffset; + } + + if(descriptor->kind == kBTLeafNode) { + if(freeSpace < (sizeof(searchKey->keyLength) + searchKey->keyLength + length + sizeof(uint16_t))) { + newNode = splitNode(root, descriptor, tree); + if(i < descriptor->numRecords) { + doAddRecord(tree, root, searchKey, length, content); + } else { + doAddRecord(tree, newNode, searchKey, length, content); + } + + free(descriptor); + return newNode; + } else { + doAddRecord(tree, root, searchKey, length, content); + + free(descriptor); + return 0; + } + } else { + if(lastRecordDataOffset == 0) { + if(descriptor->numRecords == 0) { + hfs_panic("empty index node in btree"); + return 0; + } + + key = READ_KEY(tree, (root * tree->headerRec->nodeSize) + 14, tree->io); + + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + nodeBigEndian = getNodeNumberFromPointerRecord(recordDataOffset, tree->io); + + FLIPENDIAN(nodeBigEndian); + + free(key); + key = READ_KEY(tree, (root * tree->headerRec->nodeSize) + 14, tree->io); + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + + if(searchKey->keyLength != key->keyLength) { + if(searchKey->keyLength > key->keyLength && freeSpace < (searchKey->keyLength - key->keyLength)) { + // very unlikely. We need to split this node before we can resize the key of this index. Do that first, and tell them to call again. + *callAgain = TRUE; + return splitNode(root, descriptor, tree); + } + + moveRecordsDown(tree, descriptor, 1, root, searchKey->keyLength - key->keyLength, 0); + } + + free(key); + + ASSERT(WRITE_KEY(tree, recordOffset, searchKey, tree->io), "WRITE_KEY"); + ASSERT(WRITE(tree->io, recordOffset + sizeof(uint16_t) + searchKey->keyLength, sizeof(uint32_t), &nodeBigEndian), "WRITE"); + + FLIPENDIAN(nodeBigEndian); + + newNode = addRecord(tree, nodeBigEndian, searchKey, length, content, callAgain); + } else { + newNode = addRecord(tree, getNodeNumberFromPointerRecord(lastRecordDataOffset, tree->io), searchKey, length, content, callAgain); + } + + if(newNode == 0) { + free(descriptor); + return 0; + } else { + newNodeBigEndian = newNode; + key = READ_KEY(tree, newNode * tree->headerRec->nodeSize + 14, tree->io); + FLIPENDIAN(newNodeBigEndian); + + if(freeSpace < (sizeof(key->keyLength) + key->keyLength + sizeof(newNodeBigEndian) + sizeof(uint16_t))) { + newNode = splitNode(root, descriptor, tree); + + if(i < descriptor->numRecords) { + doAddRecord(tree, root, key, sizeof(newNodeBigEndian), (unsigned char*)(&newNodeBigEndian)); + } else { + doAddRecord(tree, newNode, key, sizeof(newNodeBigEndian), (unsigned char*)(&newNodeBigEndian)); + } + + free(key); + free(descriptor); + return newNode; + } else { + doAddRecord(tree, root, key, sizeof(newNodeBigEndian), (unsigned char*)(&newNodeBigEndian)); + + free(key); + free(descriptor); + return 0; + } + } + } +} + +static int increaseHeight(BTree* tree, uint32_t newNode) { + uint32_t oldRoot; + uint32_t newRoot; + BTNodeDescriptor newDescriptor; + + BTKey* oldRootKey; + BTKey* newNodeKey; + + uint16_t oldRootOffset; + uint16_t newNodeOffset; + uint16_t freeOffset; + + oldRoot = tree->headerRec->rootNode; + + oldRootKey = READ_KEY(tree, (oldRoot * tree->headerRec->nodeSize) + 14, tree->io); + newNodeKey = READ_KEY(tree, (newNode * tree->headerRec->nodeSize) + 14, tree->io); + + newRoot = getNewNode(tree); + + newDescriptor.fLink = 0; + newDescriptor.bLink = 0; + newDescriptor.kind = kBTIndexNode; + newDescriptor.height = tree->headerRec->treeDepth + 1; + newDescriptor.numRecords = 2; + newDescriptor.reserved = 0; + + oldRootOffset = 14; + newNodeOffset = oldRootOffset + sizeof(oldRootKey->keyLength) + oldRootKey->keyLength + sizeof(uint32_t); + freeOffset = newNodeOffset + sizeof(newNodeKey->keyLength) + newNodeKey->keyLength + sizeof(uint32_t); + + tree->headerRec->rootNode = newRoot; + tree->headerRec->treeDepth = newDescriptor.height; + + ASSERT(WRITE_KEY(tree, newRoot * tree->headerRec->nodeSize + oldRootOffset, oldRootKey, tree->io), "WRITE_KEY"); + FLIPENDIAN(oldRoot); + ASSERT(WRITE(tree->io, newRoot * tree->headerRec->nodeSize + oldRootOffset + sizeof(oldRootKey->keyLength) + oldRootKey->keyLength, + sizeof(uint32_t), &oldRoot), "WRITE"); + + ASSERT(WRITE_KEY(tree, newRoot * tree->headerRec->nodeSize + newNodeOffset, newNodeKey, tree->io), "WRITE_KEY"); + FLIPENDIAN(newNode); + ASSERT(WRITE(tree->io, newRoot * tree->headerRec->nodeSize + newNodeOffset + sizeof(newNodeKey->keyLength) + newNodeKey->keyLength, + sizeof(uint32_t), &newNode), "WRITE"); + + FLIPENDIAN(oldRootOffset); + ASSERT(WRITE(tree->io, (newRoot * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * 1), + sizeof(uint16_t), &oldRootOffset), "WRITE"); + + FLIPENDIAN(newNodeOffset); + ASSERT(WRITE(tree->io, (newRoot * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * 2), + sizeof(uint16_t), &newNodeOffset), "WRITE"); + + FLIPENDIAN(freeOffset); + ASSERT(WRITE(tree->io, (newRoot * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * 3), + sizeof(uint16_t), &freeOffset), "WRITE"); + + ASSERT(writeBTNodeDescriptor(&newDescriptor, tree->headerRec->rootNode, tree), "writeBTNodeDescriptor"); + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + + free(oldRootKey); + free(newNodeKey); + return TRUE; +} + +int addToBTree(BTree* tree, BTKey* searchKey, size_t length, unsigned char* content) { + int callAgain; + BTNodeDescriptor newDescriptor; + uint16_t offset; + uint16_t freeOffset; + uint32_t newNode; + + if(tree->headerRec->rootNode != 0) { + do { + callAgain = FALSE; + newNode = addRecord(tree, tree->headerRec->rootNode, searchKey, length, content, &callAgain); + if(newNode != 0) { + increaseHeight(tree, newNode); + } + } while(callAgain); + } else { + // add the first leaf node + tree->headerRec->rootNode = getNewNode(tree); + tree->headerRec->firstLeafNode = tree->headerRec->rootNode; + tree->headerRec->lastLeafNode = tree->headerRec->rootNode; + tree->headerRec->leafRecords = 1; + tree->headerRec->treeDepth = 1; + + newDescriptor.fLink = 0; + newDescriptor.bLink = 0; + newDescriptor.kind = kBTLeafNode; + newDescriptor.height = 1; + newDescriptor.numRecords = 1; + newDescriptor.reserved = 0; + + offset = 14; + freeOffset = offset + sizeof(searchKey->keyLength) + searchKey->keyLength + length; + + ASSERT(WRITE_KEY(tree, tree->headerRec->rootNode * tree->headerRec->nodeSize + offset, searchKey, tree->io), "WRITE_KEY"); + ASSERT(WRITE(tree->io, tree->headerRec->rootNode * tree->headerRec->nodeSize + offset + sizeof(searchKey->keyLength) + searchKey->keyLength, + length, content), "WRITE"); + + FLIPENDIAN(offset); + ASSERT(WRITE(tree->io, (tree->headerRec->rootNode * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * 1), + sizeof(uint16_t), &offset), "WRITE"); + + FLIPENDIAN(freeOffset); + ASSERT(WRITE(tree->io, (tree->headerRec->rootNode * tree->headerRec->nodeSize) + tree->headerRec->nodeSize - (sizeof(uint16_t) * 2), + sizeof(uint16_t), &freeOffset), "WRITE"); + + ASSERT(writeBTNodeDescriptor(&newDescriptor, tree->headerRec->rootNode, tree), "writeBTNodeDescriptor"); + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + } + + return TRUE; +} + +static uint32_t removeRecord(BTree* tree, uint32_t root, BTKey* searchKey, int* callAgain, int* gone) { + BTNodeDescriptor* descriptor; + int length; + int i; + int res; + + uint32_t newNode; + uint32_t nodeToTraverse; + uint32_t newNodeBigEndian; + + BTKey* key; + off_t recordOffset; + off_t recordDataOffset; + off_t lastRecordDataOffset; + + int childGone; + int checkForChangedKey ; + + size_t freeSpace; + + descriptor = readBTNodeDescriptor(root, tree); + + freeSpace = getFreeSpace(root, descriptor, tree); + + nodeToTraverse = 0; + lastRecordDataOffset = 0; + newNode = 0; + + (*gone) = FALSE; + checkForChangedKey = FALSE; + + for(i = 0; i < descriptor->numRecords; i++) { + recordOffset = getRecordOffset(i, root, tree); + key = READ_KEY(tree, recordOffset, tree->io); + recordDataOffset = recordOffset + key->keyLength + sizeof(key->keyLength); + + res = COMPARE(tree, key, searchKey); + if(res == 0) { + free(key); + + if(descriptor->kind == kBTLeafNode) { + if(i != (descriptor->numRecords - 1)) { + length = getRecordOffset(i + 1, root, tree) - recordOffset; + moveRecordsDown(tree, descriptor, i + 1, root, -length, -1); + } // don't need to do that if we're the last record, because our old offset pointer will be the new free space pointer + + descriptor->numRecords--; + tree->headerRec->leafRecords--; + ASSERT(writeBTNodeDescriptor(descriptor, root, tree), "writeBTNodeDescriptor"); + ASSERT(writeBTHeaderRec(tree), "writeBTHeaderRec"); + + if(descriptor->numRecords >= 1) { + free(descriptor); + return 0; + } else { + free(descriptor); + removeNode(tree, root); + (*gone) = TRUE; + return 0; + } + } else { + nodeToTraverse = getNodeNumberFromPointerRecord(recordDataOffset, tree->io); + checkForChangedKey = TRUE; + break; + } + } else if(res > 0) { + free(key); + + if(lastRecordDataOffset == 0 || descriptor->kind == kBTLeafNode) { + // not found; + free(descriptor); + return 0; + } else { + nodeToTraverse = getNodeNumberFromPointerRecord(lastRecordDataOffset, tree->io); + break; + } + } + + lastRecordDataOffset = recordDataOffset; + + free(key); + } + + if(nodeToTraverse == 0) { + nodeToTraverse = getNodeNumberFromPointerRecord(lastRecordDataOffset, tree->io); + } + + if(i == descriptor->numRecords) { + i = descriptor->numRecords - 1; + } + + newNode = removeRecord(tree, nodeToTraverse, searchKey, callAgain, &childGone); + + if(childGone) { + if(i != (descriptor->numRecords - 1)) { + length = getRecordOffset(i + 1, root, tree) - recordOffset; + moveRecordsDown(tree, descriptor, i + 1, root, -length, -1); + } // don't need to do that if we're the last record, because our old offset pointer will be the new free space pointer + + descriptor->numRecords--; + ASSERT(writeBTNodeDescriptor(descriptor, root, tree), "writeBTNodeDescriptor"); + } else { + if(checkForChangedKey) { + // we will remove the first item in the child node, so our index has to change + + key = READ_KEY(tree, getRecordOffset(0, nodeToTraverse, tree), tree->io); + + if(searchKey->keyLength != key->keyLength) { + if(key->keyLength > searchKey->keyLength && freeSpace < (key->keyLength - searchKey->keyLength)) { + // very unlikely. We need to split this node before we can resize the key of this index. Do that first, and tell them to call again. + *callAgain = TRUE; + return splitNode(root, descriptor, tree); + } + + moveRecordsDown(tree, descriptor, i + 1, root, key->keyLength - searchKey->keyLength, 0); + } + + ASSERT(WRITE_KEY(tree, recordOffset, key, tree->io), "WRITE_KEY"); + FLIPENDIAN(nodeToTraverse); + ASSERT(WRITE(tree->io, recordOffset + sizeof(uint16_t) + key->keyLength, sizeof(uint32_t), &nodeToTraverse), "WRITE"); + FLIPENDIAN(nodeToTraverse); + + free(key); + } + } + + if(newNode == 0) { + if(descriptor->numRecords == 0) { + removeNode(tree, root); + (*gone) = TRUE; + } + + free(descriptor); + return 0; + } else { + newNodeBigEndian = newNode; + key = READ_KEY(tree, newNode * tree->headerRec->nodeSize + 14, tree->io); + FLIPENDIAN(newNodeBigEndian); + + if(freeSpace < (sizeof(key->keyLength) + key->keyLength + sizeof(newNodeBigEndian) + sizeof(uint16_t))) { + newNode = splitNode(root, descriptor, tree); + + if(i < descriptor->numRecords) { + doAddRecord(tree, root, key, sizeof(newNodeBigEndian), (unsigned char*)(&newNodeBigEndian)); + } else { + doAddRecord(tree, newNode, key, sizeof(newNodeBigEndian), (unsigned char*)(&newNodeBigEndian)); + } + + if(descriptor->numRecords == 0) { + removeNode(tree, root); + (*gone) = TRUE; + } + + free(key); + free(descriptor); + return newNode; + } else { + doAddRecord(tree, root, key, sizeof(newNodeBigEndian), (unsigned char*)(&newNodeBigEndian)); + + free(key); + free(descriptor); + return 0; + } + } + + return FALSE; +} + +int removeFromBTree(BTree* tree, BTKey* searchKey) { + int callAgain; + int gone; + uint32_t newNode; + + do { + callAgain = FALSE; + newNode = removeRecord(tree, tree->headerRec->rootNode, searchKey, &callAgain, &gone); + if(newNode != 0) { + increaseHeight(tree, newNode); + } + } while(callAgain); + + return TRUE; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/catalog.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/catalog.c new file mode 100644 index 0000000..187dc31 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/catalog.c @@ -0,0 +1,1165 @@ +#include +#include +#include +#include + +static inline void flipBSDInfo(HFSPlusBSDInfo* info) { + FLIPENDIAN(info->ownerID); + FLIPENDIAN(info->groupID); + FLIPENDIAN(info->fileMode); + FLIPENDIAN(info->special); +} + +static inline void flipPoint(Point* point) { + FLIPENDIAN(point->v); + FLIPENDIAN(point->h); +} + +static inline void flipRect(Rect* rect) { + FLIPENDIAN(rect->top); + FLIPENDIAN(rect->left); + FLIPENDIAN(rect->bottom); + FLIPENDIAN(rect->right); +} + +static inline void flipFolderInfo(FolderInfo* info) { + flipRect(&info->windowBounds); + FLIPENDIAN(info->finderFlags); + flipPoint(&info->location); +} + +static inline void flipExtendedFolderInfo(ExtendedFolderInfo* info) { + flipPoint(&info->scrollPosition); + FLIPENDIAN(info->extendedFinderFlags); + FLIPENDIAN(info->putAwayFolderID); +} + +static inline void flipFileInfo(FileInfo* info) { + FLIPENDIAN(info->fileType); + FLIPENDIAN(info->fileCreator); + FLIPENDIAN(info->finderFlags); + flipPoint(&info->location); +} + +static inline void flipExtendedFileInfo(ExtendedFileInfo* info) { + FLIPENDIAN(info->extendedFinderFlags); + FLIPENDIAN(info->putAwayFolderID); +} + +void flipCatalogFolder(HFSPlusCatalogFolder* record) { + FLIPENDIAN(record->recordType); + FLIPENDIAN(record->flags); + FLIPENDIAN(record->valence); + FLIPENDIAN(record->folderID); + FLIPENDIAN(record->createDate); + FLIPENDIAN(record->contentModDate); + FLIPENDIAN(record->attributeModDate); + FLIPENDIAN(record->accessDate); + FLIPENDIAN(record->backupDate); + + flipBSDInfo(&record->permissions); + flipFolderInfo(&record->userInfo); + flipExtendedFolderInfo(&record->finderInfo); + + FLIPENDIAN(record->textEncoding); + FLIPENDIAN(record->folderCount); +} + +void flipCatalogFile(HFSPlusCatalogFile* record) { + FLIPENDIAN(record->recordType); + FLIPENDIAN(record->flags); + FLIPENDIAN(record->fileID); + FLIPENDIAN(record->createDate); + FLIPENDIAN(record->contentModDate); + FLIPENDIAN(record->attributeModDate); + FLIPENDIAN(record->accessDate); + FLIPENDIAN(record->backupDate); + + flipBSDInfo(&record->permissions); + flipFileInfo(&record->userInfo); + flipExtendedFileInfo(&record->finderInfo); + + FLIPENDIAN(record->textEncoding); + + flipForkData(&record->dataFork); + flipForkData(&record->resourceFork); +} + +void flipCatalogThread(HFSPlusCatalogThread* record, int out) { + int i; + int nameLength; + + FLIPENDIAN(record->recordType); + FLIPENDIAN(record->parentID); + if(out) { + nameLength = record->nodeName.length; + FLIPENDIAN(record->nodeName.length); + } else { + FLIPENDIAN(record->nodeName.length); + nameLength = record->nodeName.length; + } + + for(i = 0; i < nameLength; i++) { + if(out) { + if(record->nodeName.unicode[i] == ':') { + record->nodeName.unicode[i] = '/'; + } + FLIPENDIAN(record->nodeName.unicode[i]); + } else { + FLIPENDIAN(record->nodeName.unicode[i]); + if(record->nodeName.unicode[i] == '/') { + record->nodeName.unicode[i] = ':'; + } + } + } +} + +#define UNICODE_START (sizeof(uint16_t) + sizeof(HFSCatalogNodeID) + sizeof(uint16_t)) + +static void catalogKeyPrint(BTKey* toPrint) { + HFSPlusCatalogKey* key; + + key = (HFSPlusCatalogKey*) toPrint; + + printf("%d:", key->parentID); + printUnicode(&key->nodeName); +} + +static int catalogCompare(BTKey* vLeft, BTKey* vRight) { + HFSPlusCatalogKey* left; + HFSPlusCatalogKey* right; + uint16_t i; + + uint16_t cLeft; + uint16_t cRight; + + left = (HFSPlusCatalogKey*) vLeft; + right =(HFSPlusCatalogKey*) vRight; + + if(left->parentID < right->parentID) { + return -1; + } else if(left->parentID > right->parentID) { + return 1; + } else { + for(i = 0; i < left->nodeName.length; i++) { + if(i >= right->nodeName.length) { + return 1; + } else { + /* ugly hack to support weird : to / conversion on iPhone */ + if(left->nodeName.unicode[i] == ':') { + cLeft = '/'; + } else { + cLeft = left->nodeName.unicode[i] ; + } + + if(right->nodeName.unicode[i] == ':') { + cRight = '/'; + } else { + cRight = right->nodeName.unicode[i]; + } + + if(cLeft < cRight) + return -1; + else if(cLeft > cRight) + return 1; + } + } + + if(i < right->nodeName.length) { + return -1; + } else { + /* do a safety check on key length. Otherwise, bad things may happen later on when we try to add or remove with this key */ + /*if(left->keyLength == right->keyLength) { + return 0; + } else if(left->keyLength < right->keyLength) { + return -1; + } else { + return 1; + }*/ + return 0; + } + } +} + +static int catalogCompareCS(BTKey* vLeft, BTKey* vRight) { + HFSPlusCatalogKey* left; + HFSPlusCatalogKey* right; + + left = (HFSPlusCatalogKey*) vLeft; + right =(HFSPlusCatalogKey*) vRight; + + if(left->parentID < right->parentID) { + return -1; + } else if(left->parentID > right->parentID) { + return 1; + } else { + return FastUnicodeCompare(left->nodeName.unicode, left->nodeName.length, right->nodeName.unicode, right->nodeName.length); + } +} + +static BTKey* catalogKeyRead(off_t offset, io_func* io) { + HFSPlusCatalogKey* key; + uint16_t i; + + key = (HFSPlusCatalogKey*) malloc(sizeof(HFSPlusCatalogKey)); + + if(!READ(io, offset, UNICODE_START, key)) + return NULL; + + FLIPENDIAN(key->keyLength); + FLIPENDIAN(key->parentID); + FLIPENDIAN(key->nodeName.length); + + if(!READ(io, offset + UNICODE_START, key->nodeName.length * sizeof(uint16_t), ((unsigned char *)key) + UNICODE_START)) + return NULL; + + for(i = 0; i < key->nodeName.length; i++) { + FLIPENDIAN(key->nodeName.unicode[i]); + if(key->nodeName.unicode[i] == '/') /* ugly hack that iPhone seems to do */ + key->nodeName.unicode[i] = ':'; + } + + return (BTKey*)key; +} + +static int catalogKeyWrite(off_t offset, BTKey* toWrite, io_func* io) { + HFSPlusCatalogKey* key; + uint16_t i; + uint16_t keyLength; + uint16_t nodeNameLength; + + keyLength = toWrite->keyLength + sizeof(uint16_t); + key = (HFSPlusCatalogKey*) malloc(keyLength); + memcpy(key, toWrite, keyLength); + + nodeNameLength = key->nodeName.length; + + FLIPENDIAN(key->keyLength); + FLIPENDIAN(key->parentID); + FLIPENDIAN(key->nodeName.length); + + for(i = 0; i < nodeNameLength; i++) { + if(key->nodeName.unicode[i] == ':') /* ugly hack that iPhone seems to do */ + key->nodeName.unicode[i] = '/'; + + FLIPENDIAN(key->nodeName.unicode[i]); + } + + if(!WRITE(io, offset, keyLength, key)) + return FALSE; + + free(key); + + return TRUE; +} + +static BTKey* catalogDataRead(off_t offset, io_func* io) { + int16_t recordType; + HFSPlusCatalogRecord* record; + uint16_t nameLength; + + if(!READ(io, offset, sizeof(int16_t), &recordType)) + return NULL; + + FLIPENDIAN(recordType); fflush(stdout); + + switch(recordType) { + case kHFSPlusFolderRecord: + record = (HFSPlusCatalogRecord*) malloc(sizeof(HFSPlusCatalogFolder)); + if(!READ(io, offset, sizeof(HFSPlusCatalogFolder), record)) + return NULL; + flipCatalogFolder((HFSPlusCatalogFolder*)record); + break; + + case kHFSPlusFileRecord: + record = (HFSPlusCatalogRecord*) malloc(sizeof(HFSPlusCatalogFile)); + if(!READ(io, offset, sizeof(HFSPlusCatalogFile), record)) + return NULL; + flipCatalogFile((HFSPlusCatalogFile*)record); + break; + + case kHFSPlusFolderThreadRecord: + case kHFSPlusFileThreadRecord: + record = (HFSPlusCatalogRecord*) malloc(sizeof(HFSPlusCatalogThread)); + + if(!READ(io, offset + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t), sizeof(uint16_t), &nameLength)) + return NULL; + + FLIPENDIAN(nameLength); + + if(!READ(io, offset, sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * nameLength), record)) + return NULL; + + flipCatalogThread((HFSPlusCatalogThread*)record, FALSE); + break; + } + + return (BTKey*)record; +} + +void ASCIIToUnicode(const char* ascii, HFSUniStr255* unistr) { + int count; + + count = 0; + while(ascii[count] != '\0') { + unistr->unicode[count] = ascii[count]; + count++; + } + + unistr->length = count; +} + +HFSPlusCatalogRecord* getRecordByCNID(HFSCatalogNodeID CNID, Volume* volume) { + HFSPlusCatalogKey key; + HFSPlusCatalogThread* thread; + HFSPlusCatalogRecord* record; + int exact; + + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + key.parentID = CNID; + key.nodeName.length = 0; + + thread = (HFSPlusCatalogThread*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + + if(thread == NULL) { + return NULL; + } + + if(exact == FALSE) { + free(thread); + return NULL; + } + + key.parentID = thread->parentID; + key.nodeName = thread->nodeName; + + free(thread); + + record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + + if(record == NULL || exact == FALSE) + return NULL; + else + return record; +} + +CatalogRecordList* getFolderContents(HFSCatalogNodeID CNID, Volume* volume) { + BTree* tree; + HFSPlusCatalogThread* record; + HFSPlusCatalogKey key; + uint32_t nodeNumber; + int recordNumber; + + BTNodeDescriptor* descriptor; + off_t recordOffset; + off_t recordDataOffset; + HFSPlusCatalogKey* currentKey; + + CatalogRecordList* list; + CatalogRecordList* lastItem; + CatalogRecordList* item; + + char pathBuffer[1024]; + HFSPlusCatalogRecord* toReturn; + HFSPlusCatalogKey nkey; + int exact; + + tree = volume->catalogTree; + + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + key.parentID = CNID; + key.nodeName.length = 0; + + list = NULL; + + record = (HFSPlusCatalogThread*) search(tree, (BTKey*)(&key), NULL, &nodeNumber, &recordNumber); + + if(record == NULL) + return NULL; + + free(record); + + ++recordNumber; + + while(nodeNumber != 0) { + descriptor = readBTNodeDescriptor(nodeNumber, tree); + + while(recordNumber < descriptor->numRecords) { + recordOffset = getRecordOffset(recordNumber, nodeNumber, tree); + currentKey = (HFSPlusCatalogKey*) READ_KEY(tree, recordOffset, tree->io); + recordDataOffset = recordOffset + currentKey->keyLength + sizeof(currentKey->keyLength); + + if(currentKey->parentID == CNID) { + item = (CatalogRecordList*) malloc(sizeof(CatalogRecordList)); + item->name = currentKey->nodeName; + item->record = (HFSPlusCatalogRecord*) READ_DATA(tree, recordDataOffset, tree->io); + + if(item->record->recordType == kHFSPlusFileRecord && (((HFSPlusCatalogFile*)item->record)->userInfo.fileType) == kHardLinkFileType) { + sprintf(pathBuffer, "iNode%d", ((HFSPlusCatalogFile*)item->record)->permissions.special.iNodeNum); + nkey.parentID = volume->metadataDir; + ASCIIToUnicode(pathBuffer, &nkey.nodeName); + nkey.keyLength = sizeof(nkey.parentID) + sizeof(nkey.nodeName.length) + (sizeof(uint16_t) * nkey.nodeName.length); + + toReturn = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&nkey), &exact, NULL, NULL); + + free(item->record); + item->record = toReturn; + } + item->next = NULL; + + if(list == NULL) { + list = item; + } else { + lastItem->next = item; + } + + lastItem = item; + free(currentKey); + } else { + free(currentKey); + free(descriptor); + return list; + } + + recordNumber++; + } + + nodeNumber = descriptor->fLink; + recordNumber = 0; + + free(descriptor); + } + + return list; +} + +void releaseCatalogRecordList(CatalogRecordList* list) { + CatalogRecordList* next; + while(list) { + next = list->next; + free(list->record); + free(list); + list = next; + } +} + +HFSPlusCatalogRecord* getLinkTarget(HFSPlusCatalogRecord* record, HFSCatalogNodeID parentID, HFSPlusCatalogKey *key, Volume* volume) { + io_func* io; + char pathBuffer[1024]; + HFSPlusCatalogRecord* toReturn; + HFSPlusCatalogKey nkey; + int exact; + + if(record->recordType == kHFSPlusFileRecord && (((HFSPlusCatalogFile*)record)->permissions.fileMode & S_IFLNK) == S_IFLNK) { + io = openRawFile(((HFSPlusCatalogFile*)record)->fileID, &(((HFSPlusCatalogFile*)record)->dataFork), record, volume); + READ(io, 0, (((HFSPlusCatalogFile*)record)->dataFork).logicalSize, pathBuffer); + CLOSE(io); + pathBuffer[(((HFSPlusCatalogFile*)record)->dataFork).logicalSize] = '\0'; + toReturn = getRecordFromPath3(pathBuffer, volume, NULL, key, TRUE, TRUE, parentID); + free(record); + return toReturn; + } else if(record->recordType == kHFSPlusFileRecord && (((HFSPlusCatalogFile*)record)->userInfo.fileType) == kHardLinkFileType) { + sprintf(pathBuffer, "iNode%d", ((HFSPlusCatalogFile*)record)->permissions.special.iNodeNum); + nkey.parentID = volume->metadataDir; + ASCIIToUnicode(pathBuffer, &nkey.nodeName); + nkey.keyLength = sizeof(nkey.parentID) + sizeof(nkey.nodeName.length) + (sizeof(uint16_t) * nkey.nodeName.length); + + toReturn = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&nkey), &exact, NULL, NULL); + + free(record); + + return toReturn; + } else { + return record; + } +} + +static const uint16_t METADATA_DIR[] = {0, 0, 0, 0, 'H', 'F', 'S', '+', ' ', 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'D', 'a', 't', 'a'}; + +HFSCatalogNodeID getMetadataDirectoryID(Volume* volume) { + HFSPlusCatalogKey key; + HFSPlusCatalogFolder* record; + int exact; + HFSCatalogNodeID id; + + key.nodeName.length = sizeof(METADATA_DIR) / sizeof(uint16_t); + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length) + sizeof(METADATA_DIR); + key.parentID = kHFSRootFolderID; + memcpy(key.nodeName.unicode, METADATA_DIR, sizeof(METADATA_DIR)); + + record = (HFSPlusCatalogFolder*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + id = record->folderID; + + free(record); + + return id; +} + +HFSPlusCatalogRecord* getRecordFromPath(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey) { + return getRecordFromPath2(path, volume, name, retKey, TRUE); +} + +HFSPlusCatalogRecord* getRecordFromPath2(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey, char traverse) { + return getRecordFromPath3(path, volume, name, retKey, TRUE, TRUE, kHFSRootFolderID); +} + +HFSPlusCatalogRecord* getRecordFromPath3(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey, char traverse, char returnLink, HFSCatalogNodeID parentID) { + HFSPlusCatalogKey key; + HFSPlusCatalogRecord* record; + + char* origPath; + char* myPath; + char* word; + char* pathLimit; + + uint32_t realParent; + + int exact; + + if(path[0] == '\0' || (path[0] == '/' && path[1] == '\0')) { + if(name != NULL) + *name = (char*)path; + + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + key.parentID = kHFSRootFolderID; + key.nodeName.length = 0; + + record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + key.parentID = ((HFSPlusCatalogThread*)record)->parentID; + key.nodeName = ((HFSPlusCatalogThread*)record)->nodeName; + + free(record); + + record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + return record; + } + + myPath = strdup(path); + origPath = myPath; + + record = NULL; + + if(path[0] == '/') { + key.parentID = kHFSRootFolderID; + } else { + key.parentID = parentID; + } + + pathLimit = myPath + strlen(myPath); + + for(word = (char*)strtok(myPath, "/"); word && (word < pathLimit); + word = ((word + strlen(word) + 1) < pathLimit) ? (char*)strtok(word + strlen(word) + 1, "/") : NULL) { + + if(name != NULL) + *name = (char*)(path + (word - origPath)); + + if(record != NULL) { + free(record); + record = NULL; + } + + if(word[0] == '\0') { + continue; + } + + ASCIIToUnicode(word, &key.nodeName); + + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length) + (sizeof(uint16_t) * key.nodeName.length); + record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + + if(record == NULL || exact == FALSE) { + free(origPath); + if(record != NULL) { + free(record); + } + return NULL; + } + + if(traverse) { + if(((word + strlen(word) + 1) < pathLimit) || returnLink) { + record = getLinkTarget(record, key.parentID, &key, volume); + if(record == NULL || exact == FALSE) { + free(origPath); + return NULL; + } + } + } + + if(record->recordType == kHFSPlusFileRecord) { + if((word + strlen(word) + 1) >= pathLimit) { + free(origPath); + + if(retKey != NULL) { + memcpy(retKey, &key, sizeof(HFSPlusCatalogKey)); + } + + return record; + } else { + free(origPath); + free(record); + return NULL; + } + } + + if(record->recordType != kHFSPlusFolderRecord) + hfs_panic("inconsistent catalog tree!"); + + realParent = key.parentID; + key.parentID = ((HFSPlusCatalogFolder*)record)->folderID; + } + + if(retKey != NULL) { + memcpy(retKey, &key, sizeof(HFSPlusCatalogKey)); + retKey->parentID = realParent; + } + + free(origPath); + return record; +} + +int updateCatalog(Volume* volume, HFSPlusCatalogRecord* catalogRecord) { + HFSPlusCatalogKey key; + HFSPlusCatalogRecord* record; + HFSPlusCatalogFile file; + HFSPlusCatalogFolder folder; + int exact; + + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + if(catalogRecord->recordType == kHFSPlusFolderRecord) { + key.parentID = ((HFSPlusCatalogFolder*)catalogRecord)->folderID; + } else if(catalogRecord->recordType == kHFSPlusFileRecord) { + key.parentID = ((HFSPlusCatalogFile*)catalogRecord)->fileID; + } else { + /* unexpected */ + return FALSE; + } + key.nodeName.length = 0; + + record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + + key.parentID = ((HFSPlusCatalogThread*)record)->parentID; + key.nodeName = ((HFSPlusCatalogThread*)record)->nodeName; + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length) + (sizeof(uint16_t) * key.nodeName.length); + + free(record); + + record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); + + removeFromBTree(volume->catalogTree, (BTKey*)(&key)); + + switch(record->recordType) { + case kHFSPlusFolderRecord: + memcpy(&folder, catalogRecord, sizeof(HFSPlusCatalogFolder)); + flipCatalogFolder(&folder); + free(record); + return addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFolder), (unsigned char *)(&folder)); + break; + + case kHFSPlusFileRecord: + memcpy(&file, catalogRecord, sizeof(HFSPlusCatalogFile)); + flipCatalogFile(&file); + free(record); + return addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFile), (unsigned char *)(&file)); + break; + } + + return TRUE; +} + +int move(const char* source, const char* dest, Volume* volume) { + HFSPlusCatalogRecord* srcRec; + HFSPlusCatalogFolder* srcFolderRec; + HFSPlusCatalogFolder* destRec; + char* destPath; + char* destName; + char* curChar; + char* lastSeparator; + + int i; + int threadLength; + + HFSPlusCatalogKey srcKey; + HFSPlusCatalogKey destKey; + HFSPlusCatalogThread* thread; + + srcRec = getRecordFromPath3(source, volume, NULL, &srcKey, TRUE, FALSE, kHFSRootFolderID); + if(srcRec == NULL) { + free(srcRec); + return FALSE; + } + + srcFolderRec = (HFSPlusCatalogFolder*) getRecordByCNID(srcKey.parentID, volume); + + if(srcFolderRec == NULL || srcFolderRec->recordType != kHFSPlusFolderRecord) { + free(srcRec); + free(srcFolderRec); + return FALSE; + } + + destPath = strdup(dest); + + curChar = destPath; + lastSeparator = NULL; + + while((*curChar) != '\0') { + if((*curChar) == '/') + lastSeparator = curChar; + curChar++; + } + + if(lastSeparator == NULL) { + destRec = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); + destName = destPath; + } else { + destName = lastSeparator + 1; + *lastSeparator = '\0'; + destRec = (HFSPlusCatalogFolder*) getRecordFromPath(destPath, volume, NULL, NULL); + + if(destRec == NULL || destRec->recordType != kHFSPlusFolderRecord) { + free(destPath); + free(srcRec); + free(destRec); + free(srcFolderRec); + return FALSE; + } + } + + removeFromBTree(volume->catalogTree, (BTKey*)(&srcKey)); + + srcKey.nodeName.length = 0; + if(srcRec->recordType == kHFSPlusFolderRecord) { + srcKey.parentID = ((HFSPlusCatalogFolder*)srcRec)->folderID; + } else if(srcRec->recordType == kHFSPlusFileRecord) { + srcKey.parentID = ((HFSPlusCatalogFile*)srcRec)->fileID; + } else { + /* unexpected */ + return FALSE; + } + srcKey.keyLength = sizeof(srcKey.parentID) + sizeof(srcKey.nodeName.length); + + removeFromBTree(volume->catalogTree, (BTKey*)(&srcKey)); + + + destKey.nodeName.length = strlen(destName); + + threadLength = sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * destKey.nodeName.length); + thread = (HFSPlusCatalogThread*) malloc(threadLength); + thread->reserved = 0; + destKey.parentID = destRec->folderID; + thread->parentID = destKey.parentID; + thread->nodeName.length = destKey.nodeName.length; + for(i = 0; i < destKey.nodeName.length; i++) { + destKey.nodeName.unicode[i] = destName[i]; + thread->nodeName.unicode[i] = destName[i]; + } + + destKey.keyLength = sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * destKey.nodeName.length); + + switch(srcRec->recordType) { + case kHFSPlusFolderRecord: + thread->recordType = kHFSPlusFolderThreadRecord; + flipCatalogFolder((HFSPlusCatalogFolder*)srcRec); + addToBTree(volume->catalogTree, (BTKey*)(&destKey), sizeof(HFSPlusCatalogFolder), (unsigned char *)(srcRec)); + break; + + case kHFSPlusFileRecord: + thread->recordType = kHFSPlusFileThreadRecord; + flipCatalogFile((HFSPlusCatalogFile*)srcRec); + addToBTree(volume->catalogTree, (BTKey*)(&destKey), sizeof(HFSPlusCatalogFile), (unsigned char *)(srcRec)); + break; + } + + destKey.nodeName.length = 0; + destKey.parentID = srcKey.parentID; + destKey.keyLength = sizeof(destKey.parentID) + sizeof(destKey.nodeName.length); + + flipCatalogThread(thread, TRUE); + addToBTree(volume->catalogTree, (BTKey*)(&destKey), threadLength, (unsigned char *)(thread)); + + /* adjust valence */ + srcFolderRec->valence--; + updateCatalog(volume, (HFSPlusCatalogRecord*) srcFolderRec); + destRec->valence++; + updateCatalog(volume, (HFSPlusCatalogRecord*) destRec); + + free(thread); + free(destPath); + free(srcRec); + free(destRec); + free(srcFolderRec); + + return TRUE; +} + +int removeFile(const char* fileName, Volume* volume) { + HFSPlusCatalogRecord* record; + HFSPlusCatalogKey key; + io_func* io; + HFSPlusCatalogFolder* parentFolder; + + record = getRecordFromPath3(fileName, volume, NULL, &key, TRUE, FALSE, kHFSRootFolderID); + if(record != NULL) { + parentFolder = (HFSPlusCatalogFolder*) getRecordByCNID(key.parentID, volume); + if(parentFolder != NULL) { + if(parentFolder->recordType != kHFSPlusFolderRecord) { + ASSERT(FALSE, "parent not folder"); + free(parentFolder); + return FALSE; + } + } else { + ASSERT(FALSE, "can't find parent"); + return FALSE; + } + + if(record->recordType == kHFSPlusFileRecord) { + io = openRawFile(((HFSPlusCatalogFile*)record)->fileID, &((HFSPlusCatalogFile*)record)->dataFork, record, volume); + allocate((RawFile*)io->data, 0); + CLOSE(io); + + removeFromBTree(volume->catalogTree, (BTKey*)(&key)); + XAttrList* next; + XAttrList* attrs = getAllExtendedAttributes(((HFSPlusCatalogFile*)record)->fileID, volume); + if(attrs != NULL) { + while(attrs != NULL) { + next = attrs->next; + unsetAttribute(volume, ((HFSPlusCatalogFile*)record)->fileID, attrs->name); + free(attrs->name); + free(attrs); + attrs = next; + } + } + + + key.nodeName.length = 0; + key.parentID = ((HFSPlusCatalogFile*)record)->fileID; + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + removeFromBTree(volume->catalogTree, (BTKey*)(&key)); + + volume->volumeHeader->fileCount--; + } else { + if(((HFSPlusCatalogFolder*)record)->valence > 0) { + free(record); + free(parentFolder); + ASSERT(FALSE, "folder not empty"); + return FALSE; + } else { + removeFromBTree(volume->catalogTree, (BTKey*)(&key)); + XAttrList* next; + XAttrList* attrs = getAllExtendedAttributes(((HFSPlusCatalogFolder*)record)->folderID, volume); + if(attrs != NULL) { + while(attrs != NULL) { + next = attrs->next; + unsetAttribute(volume, ((HFSPlusCatalogFolder*)record)->folderID, attrs->name); + free(attrs->name); + free(attrs); + attrs = next; + } + } + + key.nodeName.length = 0; + key.parentID = ((HFSPlusCatalogFolder*)record)->folderID; + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + removeFromBTree(volume->catalogTree, (BTKey*)(&key)); + } + + parentFolder->folderCount--; + volume->volumeHeader->folderCount--; + } + parentFolder->valence--; + updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); + updateVolume(volume); + + free(record); + free(parentFolder); + + return TRUE; + } else { + free(parentFolder); + ASSERT(FALSE, "cannot find record"); + return FALSE; + } +} + +int makeSymlink(const char* pathName, const char* target, Volume* volume) { + io_func* io; + HFSPlusCatalogFile* record; + + record = (HFSPlusCatalogFile*) getRecordFromPath3(pathName, volume, NULL, NULL, TRUE, FALSE, kHFSRootFolderID); + + if(!record) { + newFile(pathName, volume); + record = (HFSPlusCatalogFile*) getRecordFromPath(pathName, volume, NULL, NULL); + if(!record) { + return FALSE; + } + record->permissions.fileMode |= S_IFLNK; + record->userInfo.fileType = kSymLinkFileType; + record->userInfo.fileCreator = kSymLinkCreator; + updateCatalog(volume, (HFSPlusCatalogRecord*) record); + } else { + if(record->recordType != kHFSPlusFileRecord || (((HFSPlusCatalogFile*)record)->permissions.fileMode & S_IFLNK) != S_IFLNK) { + free(record); + return FALSE; + } + } + + io = openRawFile(record->fileID, &record->dataFork, (HFSPlusCatalogRecord*) record, volume); + WRITE(io, 0, strlen(target), (void*) target); + CLOSE(io); + free(record); + + return TRUE; +} + +HFSCatalogNodeID newFolder(const char* pathName, Volume* volume) { + HFSPlusCatalogFolder* parentFolder; + HFSPlusCatalogFolder folder; + HFSPlusCatalogKey key; + HFSPlusCatalogThread thread; + + uint32_t newFolderID; + + int threadLength; + + char* path; + char* name; + char* curChar; + char* lastSeparator; + + path = strdup(pathName); + + curChar = path; + lastSeparator = NULL; + + while((*curChar) != '\0') { + if((*curChar) == '/') + lastSeparator = curChar; + curChar++; + } + + if(lastSeparator == NULL) { + parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); + name = path; + } else { + name = lastSeparator + 1; + *lastSeparator = '\0'; + parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath(path, volume, NULL, NULL); + } + + if(parentFolder == NULL || parentFolder->recordType != kHFSPlusFolderRecord) { + free(path); + free(parentFolder); + return FALSE; + } + + newFolderID = volume->volumeHeader->nextCatalogID++; + volume->volumeHeader->folderCount++; + + folder.recordType = kHFSPlusFolderRecord; + folder.flags = kHFSHasFolderCountMask; + folder.valence = 0; + folder.folderID = newFolderID; + folder.createDate = UNIX_TO_APPLE_TIME(time(NULL)); + folder.contentModDate = folder.createDate; + folder.attributeModDate = folder.createDate; + folder.accessDate = folder.createDate; + folder.backupDate = folder.createDate; + folder.permissions.ownerID = parentFolder->permissions.ownerID; + folder.permissions.groupID = parentFolder->permissions.groupID; + folder.permissions.adminFlags = 0; + folder.permissions.ownerFlags = 0; + folder.permissions.fileMode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + folder.permissions.special.iNodeNum = 0; + memset(&folder.userInfo, 0, sizeof(folder.userInfo)); + memset(&folder.finderInfo, 0, sizeof(folder.finderInfo)); + folder.textEncoding = 0; + folder.folderCount = 0; + + key.parentID = parentFolder->folderID; + ASCIIToUnicode(name, &key.nodeName); + key.keyLength = sizeof(key.parentID) + STR_SIZE(key.nodeName); + + thread.recordType = kHFSPlusFolderThreadRecord; + thread.reserved = 0; + thread.parentID = parentFolder->folderID; + ASCIIToUnicode(name, &thread.nodeName); + threadLength = sizeof(thread.recordType) + sizeof(thread.reserved) + sizeof(thread.parentID) + STR_SIZE(thread.nodeName); + flipCatalogThread(&thread, TRUE); + flipCatalogFolder(&folder); + + ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFolder), (unsigned char *)(&folder)), "addToBTree"); + key.nodeName.length = 0; + key.parentID = newFolderID; + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), threadLength, (unsigned char *)(&thread)), "addToBTree"); + + parentFolder->folderCount++; + parentFolder->valence++; + updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); + + updateVolume(volume); + + free(parentFolder); + free(path); + + return newFolderID; +} + +HFSCatalogNodeID newFile(const char* pathName, Volume* volume) { + HFSPlusCatalogFolder* parentFolder; + HFSPlusCatalogFile file; + HFSPlusCatalogKey key; + HFSPlusCatalogThread thread; + + uint32_t newFileID; + + int threadLength; + + char* path; + char* name; + char* curChar; + char* lastSeparator; + + path = strdup(pathName); + + curChar = path; + lastSeparator = NULL; + + while((*curChar) != '\0') { + if((*curChar) == '/') + lastSeparator = curChar; + curChar++; + } + + if(lastSeparator == NULL) { + parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); + name = path; + } else { + name = lastSeparator + 1; + *lastSeparator = '\0'; + parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath(path, volume, NULL, NULL); + } + + if(parentFolder == NULL || parentFolder->recordType != kHFSPlusFolderRecord) { + free(path); + free(parentFolder); + return FALSE; + } + + newFileID = volume->volumeHeader->nextCatalogID++; + volume->volumeHeader->fileCount++; + + file.recordType = kHFSPlusFileRecord; + file.flags = kHFSThreadExistsMask; + file.reserved1 = 0; + file.fileID = newFileID; + file.createDate = UNIX_TO_APPLE_TIME(time(NULL)); + file.contentModDate = file.createDate; + file.attributeModDate = file.createDate; + file.accessDate = file.createDate; + file.backupDate = file.createDate; + file.permissions.ownerID = parentFolder->permissions.ownerID; + file.permissions.groupID = parentFolder->permissions.groupID; + file.permissions.adminFlags = 0; + file.permissions.ownerFlags = 0; + file.permissions.fileMode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + file.permissions.special.iNodeNum = 0; + memset(&file.userInfo, 0, sizeof(file.userInfo)); + memset(&file.finderInfo, 0, sizeof(file.finderInfo)); + file.textEncoding = 0; + file.reserved2 = 0; + memset(&file.dataFork, 0, sizeof(file.dataFork)); + memset(&file.resourceFork, 0, sizeof(file.resourceFork)); + + key.parentID = parentFolder->folderID; + ASCIIToUnicode(name, &key.nodeName); + key.keyLength = sizeof(key.parentID) + STR_SIZE(key.nodeName); + + thread.recordType = kHFSPlusFileThreadRecord; + thread.reserved = 0; + thread.parentID = parentFolder->folderID; + ASCIIToUnicode(name, &thread.nodeName); + threadLength = sizeof(thread.recordType) + sizeof(thread.reserved) + sizeof(thread.parentID) + STR_SIZE(thread.nodeName); + flipCatalogThread(&thread, TRUE); + flipCatalogFile(&file); + + ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFile), (unsigned char *)(&file)), "addToBTree"); + key.nodeName.length = 0; + key.parentID = newFileID; + key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); + ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), threadLength, (unsigned char *)(&thread)), "addToBTree"); + + parentFolder->valence++; + updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); + + updateVolume(volume); + + free(parentFolder); + free(path); + + return newFileID; +} + +int chmodFile(const char* pathName, int mode, Volume* volume) { + HFSPlusCatalogRecord* record; + + record = getRecordFromPath(pathName, volume, NULL, NULL); + + if(record == NULL) { + return FALSE; + } + + if(record->recordType == kHFSPlusFolderRecord) { + ((HFSPlusCatalogFolder*)record)->permissions.fileMode = (((HFSPlusCatalogFolder*)record)->permissions.fileMode & 0770000) | mode; + } else if(record->recordType == kHFSPlusFileRecord) { + ((HFSPlusCatalogFile*)record)->permissions.fileMode = (((HFSPlusCatalogFolder*)record)->permissions.fileMode & 0770000) | mode; + } else { + return FALSE; + } + + updateCatalog(volume, record); + + free(record); + + return TRUE; +} + +int chownFile(const char* pathName, uint32_t owner, uint32_t group, Volume* volume) { + HFSPlusCatalogRecord* record; + + record = getRecordFromPath(pathName, volume, NULL, NULL); + + if(record == NULL) { + return FALSE; + } + + if(record->recordType == kHFSPlusFolderRecord) { + ((HFSPlusCatalogFolder*)record)->permissions.ownerID = owner; + ((HFSPlusCatalogFolder*)record)->permissions.groupID = group; + } else if(record->recordType == kHFSPlusFileRecord) { + ((HFSPlusCatalogFile*)record)->permissions.ownerID = owner; + ((HFSPlusCatalogFile*)record)->permissions.groupID = group; + } else { + return FALSE; + } + + updateCatalog(volume, record); + + free(record); + + return TRUE; +} + + +BTree* openCatalogTree(io_func* file) { + BTree* btree; + + btree = openBTree(file, &catalogCompare, &catalogKeyRead, &catalogKeyWrite, &catalogKeyPrint, &catalogDataRead); + + if(btree->headerRec->keyCompareType == kHFSCaseFolding) { + btree->compare = &catalogCompareCS; + } + + return btree; +} + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/extents.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/extents.c new file mode 100644 index 0000000..43a6152 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/extents.c @@ -0,0 +1,119 @@ +#include +#include +#include + +static inline void flipExtentDescriptor(HFSPlusExtentDescriptor* extentDescriptor) { + FLIPENDIAN(extentDescriptor->startBlock); + FLIPENDIAN(extentDescriptor->blockCount); +} + +void flipExtentRecord(HFSPlusExtentRecord* extentRecord) { + HFSPlusExtentDescriptor *extentDescriptor; + extentDescriptor = (HFSPlusExtentDescriptor*)extentRecord; + + flipExtentDescriptor(&extentDescriptor[0]); + flipExtentDescriptor(&extentDescriptor[1]); + flipExtentDescriptor(&extentDescriptor[2]); + flipExtentDescriptor(&extentDescriptor[3]); + flipExtentDescriptor(&extentDescriptor[4]); + flipExtentDescriptor(&extentDescriptor[5]); + flipExtentDescriptor(&extentDescriptor[6]); + flipExtentDescriptor(&extentDescriptor[7]); +} + +static int extentCompare(BTKey* vLeft, BTKey* vRight) { + HFSPlusExtentKey* left; + HFSPlusExtentKey* right; + + left = (HFSPlusExtentKey*) vLeft; + right =(HFSPlusExtentKey*) vRight; + + if(left->forkType < right->forkType) { + return -1; + } else if(left->forkType > right->forkType) { + return 1; + } else { + if(left->fileID < right->fileID) { + return -1; + } else if(left->fileID > right->fileID) { + return 1; + } else { + if(left->startBlock < right->startBlock) { + return -1; + } else if(left->startBlock > right->startBlock) { + return 1; + } else { + /* do a safety check on key length. Otherwise, bad things may happen later on when we try to add or remove with this key */ + if(left->keyLength == right->keyLength) { + return 0; + } else if(left->keyLength < right->keyLength) { + return -1; + } else { + return 1; + } + return 0; + } + } + } +} + +static BTKey* extentKeyRead(off_t offset, io_func* io) { + HFSPlusExtentKey* key; + + key = (HFSPlusExtentKey*) malloc(sizeof(HFSPlusExtentKey)); + + if(!READ(io, offset, sizeof(HFSPlusExtentKey), key)) + return NULL; + + FLIPENDIAN(key->keyLength); + FLIPENDIAN(key->forkType); + FLIPENDIAN(key->fileID); + FLIPENDIAN(key->startBlock); + + return (BTKey*)key; +} + +static int extentKeyWrite(off_t offset, BTKey* toWrite, io_func* io) { + HFSPlusExtentKey* key; + + key = (HFSPlusExtentKey*) malloc(sizeof(HFSPlusExtentKey)); + + memcpy(key, toWrite, sizeof(HFSPlusExtentKey)); + + FLIPENDIAN(key->keyLength); + FLIPENDIAN(key->forkType); + FLIPENDIAN(key->fileID); + FLIPENDIAN(key->startBlock); + + if(!WRITE(io, offset, sizeof(HFSPlusExtentKey), key)) + return FALSE; + + free(key); + + return TRUE; +} + +static void extentKeyPrint(BTKey* toPrint) { + HFSPlusExtentKey* key; + + key = (HFSPlusExtentKey*)toPrint; + + printf("extent%d:%d:%d", key->forkType, key->fileID, key->startBlock); +} + +static BTKey* extentDataRead(off_t offset, io_func* io) { + HFSPlusExtentRecord* record; + + record = (HFSPlusExtentRecord*) malloc(sizeof(HFSPlusExtentRecord)); + + if(!READ(io, offset, sizeof(HFSPlusExtentRecord), record)) + return NULL; + + flipExtentRecord(record); + + return (BTKey*)record; +} + +BTree* openExtentsTree(io_func* file) { + return openBTree(file, &extentCompare, &extentKeyRead, &extentKeyWrite, &extentKeyPrint, &extentDataRead); +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/fastunicodecompare.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/fastunicodecompare.c new file mode 100644 index 0000000..25793b3 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/fastunicodecompare.c @@ -0,0 +1,418 @@ +#include +#include + +/* This routine is taken from Apple's TN 1150, with adaptations for C */ + +/* The lower case table consists of a 256-entry high-byte table followed by + some number of 256-entry subtables. The high-byte table contains either an + offset to the subtable for characters with that high byte or zero, which + means that there are no case mappings or ignored characters in that block. + Ignored characters are mapped to zero. + */ + +uint16_t gLowerCaseTable[] = { + + /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00, + + /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF, + /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, + + /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, + 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, + /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, + 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, + /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127, + 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, + 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140, + /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, + 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, + /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, + 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, + /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167, + 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, + /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, + 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, + /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, + 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259, + /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, + 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275, + /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8, + 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF, + /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, + 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF, + /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, + 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF, + /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, + 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, + /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7, + 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, + /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, + 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, + + /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, + 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, + 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, + /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, + 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, + /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, + /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, + 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, + 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF, + /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, + 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + + /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407, + 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F, + /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, + 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, + /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, + 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F, + /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477, + 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F, + /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, + 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, + 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F, + /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, + 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF, + /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, + 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF, + /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8, + 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, + 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, + /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, + 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, + /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, + 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, + + /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, + 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, + /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, + 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, + /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, + 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, + /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, + 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, + 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, + 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, + /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, + 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, + 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, + 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, + /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, + 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, + /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, + 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, + /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, + 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, + /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, + /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, + 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, + + /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, + 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, + /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, + 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, + /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, + 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, + /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, + 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, + /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, + 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, + /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, + 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, + /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, + 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, + /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, + 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, + /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, + 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, + /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, + 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, + /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, + 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, + 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7, + 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, + /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, + 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, + 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, + 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, + + /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, + 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017, + 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F, + /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027, + 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F, + /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, + 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F, + /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047, + 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F, + /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, + 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, + /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067, + 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, + 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F, + /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, + 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F, + /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097, + 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F, + /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7, + 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF, + /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, + 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF, + /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, + 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF, + /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, + 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF, + /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, + 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, + /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7, + 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF, + + /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, + 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F, + /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, + 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F, + /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, + 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F, + /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, + 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F, + /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, + 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F, + /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, + 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F, + /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, + 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, + 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, + 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F, + /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, + 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F, + /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7, + 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF, + /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7, + 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF, + /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7, + 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF, + /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7, + 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF, + /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7, + 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF, + /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7, + 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF, + + /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, + 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, + /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17, + 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F, + /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, + 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F, + /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, + 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, + /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47, + 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, + /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57, + 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, + /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67, + 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F, + /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77, + 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, + /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87, + 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F, + /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97, + 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F, + /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, + 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, + /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, + 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, + /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, + 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF, + /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7, + 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF, + /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7, + 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF, + /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, + 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000, + + /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, + 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, + /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, + 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, + /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, + 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, + 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, + /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, + 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, + 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F, + /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, + 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, + /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, + 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, + /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, + 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, + /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, + 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, + /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7, + 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF, + /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7, + 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF, + /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7, + 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF, + /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7, + 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF, + /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7, + 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF, + /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, + 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, +}; + +int32_t FastUnicodeCompare ( register uint16_t str1[], register uint16_t length1, + register uint16_t str2[], register uint16_t length2) +{ + register uint16_t c1,c2; + register uint16_t temp; + register uint16_t* lowerCaseTable; + + lowerCaseTable = gLowerCaseTable; + + while (1) { + c1 = 0; + c2 = 0; + while (length1 && c1 == 0) { + c1 = *(str1++); + --length1; + if ((temp = lowerCaseTable[c1>>8]) != 0) + c1 = lowerCaseTable[temp + (c1 & 0x00FF)]; + } + while (length2 && c2 == 0) { + c2 = *(str2++); + --length2; + if ((temp = lowerCaseTable[c2>>8]) != 0) + c2 = lowerCaseTable[temp + (c2 & 0x00FF)]; + } + if (c1 == ':') { + c1 = '/'; + } + if (c2 == ':') { + c2 = '/'; + } + if (c1 != c2) + break; + if (c1 == 0) + return 0; + } + if (c1 < c2) + return -1; + else + return 1; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/flatfile.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/flatfile.c new file mode 100644 index 0000000..8a15570 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/flatfile.c @@ -0,0 +1,104 @@ +#include +#include + +static int flatFileRead(io_func* io, off_t location, size_t size, void *buffer) { + FILE* file; + file = (FILE*) io->data; + + if(size == 0) { + return TRUE; + } + + //printf("%d %d\n", location, size); fflush(stdout); + + if(fseeko(file, location, SEEK_SET) != 0) { + perror("fseek"); + return FALSE; + } + + if(fread(buffer, size, 1, file) != 1) { + perror("fread"); + return FALSE; + } else { + return TRUE; + } +} + +static int flatFileWrite(io_func* io, off_t location, size_t size, void *buffer) { + FILE* file; + + /*int i; + + printf("write: %lld %d - ", location, size); fflush(stdout); + + for(i = 0; i < size; i++) { + printf("%x ", ((unsigned char*)buffer)[i]); + fflush(stdout); + } + printf("\n"); fflush(stdout);*/ + + if(size == 0) { + return TRUE; + } + + file = (FILE*) io->data; + + if(fseeko(file, location, SEEK_SET) != 0) { + perror("fseek"); + return FALSE; + } + + if(fwrite(buffer, size, 1, file) != 1) { + perror("fwrite"); + return FALSE; + } else { + return TRUE; + } + + return TRUE; +} + +static void closeFlatFile(io_func* io) { + FILE* file; + + file = (FILE*) io->data; + + fclose(file); + free(io); +} + +io_func* openFlatFile(const char* fileName) { + io_func* io; + + io = (io_func*) malloc(sizeof(io_func)); + io->data = fopen(fileName, "rb+"); + + if(io->data == NULL) { + perror("fopen"); + return NULL; + } + + io->read = &flatFileRead; + io->write = &flatFileWrite; + io->close = &closeFlatFile; + + return io; +} + +io_func* openFlatFileRO(const char* fileName) { + io_func* io; + + io = (io_func*) malloc(sizeof(io_func)); + io->data = fopen(fileName, "rb"); + + if(io->data == NULL) { + perror("fopen"); + return NULL; + } + + io->read = &flatFileRead; + io->write = &flatFileWrite; + io->close = &closeFlatFile; + + return io; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfs.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfs.c new file mode 100644 index 0000000..e3ceaa3 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfs.c @@ -0,0 +1,333 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "abstractfile.h" +#include + +char endianness; + + +void cmd_ls(Volume* volume, int argc, const char *argv[]) { + if(argc > 1) + hfs_ls(volume, argv[1]); + else + hfs_ls(volume, "/"); +} + +void cmd_cat(Volume* volume, int argc, const char *argv[]) { + HFSPlusCatalogRecord* record; + AbstractFile* stdoutFile; + + record = getRecordFromPath(argv[1], volume, NULL, NULL); + + stdoutFile = createAbstractFileFromFile(stdout); + + if(record != NULL) { + if(record->recordType == kHFSPlusFileRecord) + writeToFile((HFSPlusCatalogFile*)record, stdoutFile, volume); + else + printf("Not a file\n"); + } else { + printf("No such file or directory\n"); + } + + free(record); + free(stdoutFile); +} + +void cmd_extract(Volume* volume, int argc, const char *argv[]) { + HFSPlusCatalogRecord* record; + AbstractFile *outFile; + + if(argc < 3) { + printf("Not enough arguments"); + return; + } + + outFile = createAbstractFileFromFile(fopen(argv[2], "wb")); + + if(outFile == NULL) { + printf("cannot create file"); + } + + record = getRecordFromPath(argv[1], volume, NULL, NULL); + + if(record != NULL) { + if(record->recordType == kHFSPlusFileRecord) + writeToFile((HFSPlusCatalogFile*)record, outFile, volume); + else + printf("Not a file\n"); + } else { + printf("No such file or directory\n"); + } + + outFile->close(outFile); + free(record); +} + +void cmd_mv(Volume* volume, int argc, const char *argv[]) { + if(argc > 2) { + move(argv[1], argv[2], volume); + } else { + printf("Not enough arguments"); + } +} + +void cmd_symlink(Volume* volume, int argc, const char *argv[]) { + if(argc > 2) { + makeSymlink(argv[1], argv[2], volume); + } else { + printf("Not enough arguments"); + } +} + +void cmd_mkdir(Volume* volume, int argc, const char *argv[]) { + if(argc > 1) { + newFolder(argv[1], volume); + } else { + printf("Not enough arguments"); + } +} + +void cmd_add(Volume* volume, int argc, const char *argv[]) { + AbstractFile *inFile; + + if(argc < 3) { + printf("Not enough arguments"); + return; + } + + inFile = createAbstractFileFromFile(fopen(argv[1], "rb")); + + if(inFile == NULL) { + printf("file to add not found"); + } + + add_hfs(volume, inFile, argv[2]); +} + +void cmd_rm(Volume* volume, int argc, const char *argv[]) { + if(argc > 1) { + removeFile(argv[1], volume); + } else { + printf("Not enough arguments"); + } +} + +void cmd_chmod(Volume* volume, int argc, const char *argv[]) { + int mode; + + if(argc > 2) { + sscanf(argv[1], "%o", &mode); + chmodFile(argv[2], mode, volume); + } else { + printf("Not enough arguments"); + } +} + +void cmd_extractall(Volume* volume, int argc, const char *argv[]) { + HFSPlusCatalogRecord* record; + char cwd[1024]; + char* name; + + ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory"); + + if(argc > 1) + record = getRecordFromPath(argv[1], volume, &name, NULL); + else + record = getRecordFromPath("/", volume, &name, NULL); + + if(argc > 2) { + ASSERT(chdir(argv[2]) == 0, "chdir"); + } + + if(record != NULL) { + if(record->recordType == kHFSPlusFolderRecord) + extractAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume); + else + printf("Not a folder\n"); + } else { + printf("No such file or directory\n"); + } + free(record); + + ASSERT(chdir(cwd) == 0, "chdir"); +} + + +void cmd_rmall(Volume* volume, int argc, const char *argv[]) { + HFSPlusCatalogRecord* record; + char* name; + char initPath[1024]; + int lastCharOfPath; + + if(argc > 1) { + record = getRecordFromPath(argv[1], volume, &name, NULL); + strcpy(initPath, argv[1]); + lastCharOfPath = strlen(argv[1]) - 1; + if(argv[1][lastCharOfPath] != '/') { + initPath[lastCharOfPath + 1] = '/'; + initPath[lastCharOfPath + 2] = '\0'; + } + } else { + record = getRecordFromPath("/", volume, &name, NULL); + initPath[0] = '/'; + initPath[1] = '\0'; + } + + if(record != NULL) { + if(record->recordType == kHFSPlusFolderRecord) { + removeAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume, initPath); + } else { + printf("Not a folder\n"); + } + } else { + printf("No such file or directory\n"); + } + free(record); +} + +void cmd_addall(Volume* volume, int argc, const char *argv[]) { + if(argc < 2) { + printf("Not enough arguments"); + return; + } + + if(argc > 2) { + addall_hfs(volume, argv[1], argv[2]); + } else { + addall_hfs(volume, argv[1], "/"); + } +} + +void cmd_grow(Volume* volume, int argc, const char *argv[]) { + uint64_t newSize; + + if(argc < 2) { + printf("Not enough arguments\n"); + return; + } + + newSize = 0; + sscanf(argv[1], "%" PRId64, &newSize); + + grow_hfs(volume, newSize); + + printf("grew volume: %" PRId64 "\n", newSize); +} + +void cmd_getattr(Volume* volume, int argc, const char *argv[]) { + HFSPlusCatalogRecord* record; + + if(argc < 3) { + printf("Not enough arguments"); + return; + } + + record = getRecordFromPath(argv[1], volume, NULL, NULL); + + if(record != NULL) { + HFSCatalogNodeID id; + uint8_t* data; + size_t size; + if(record->recordType == kHFSPlusFileRecord) + id = ((HFSPlusCatalogFile*)record)->fileID; + else + id = ((HFSPlusCatalogFolder*)record)->folderID; + + size = getAttribute(volume, id, argv[2], &data); + + if(size > 0) { + fwrite(data, size, 1, stdout); + free(data); + } else { + printf("No such attribute\n"); + } + } else { + printf("No such file or directory\n"); + } + + free(record); +} + +void TestByteOrder() +{ + short int word = 0x0001; + char *byte = (char *) &word; + endianness = byte[0] ? IS_LITTLE_ENDIAN : IS_BIG_ENDIAN; +} + + +int main(int argc, const char *argv[]) { + io_func* io; + Volume* volume; + + TestByteOrder(); + + if(argc < 3) { + printf("usage: %s \n", argv[0]); + return 0; + } + + io = openFlatFile(argv[1]); + if(io == NULL) { + fprintf(stderr, "error: Cannot open image-file.\n"); + return 1; + } + + volume = openVolume(io); + if(volume == NULL) { + fprintf(stderr, "error: Cannot open volume.\n"); + CLOSE(io); + return 1; + } + + if(argc > 1) { + if(strcmp(argv[2], "ls") == 0) { + cmd_ls(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "cat") == 0) { + cmd_cat(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "mv") == 0) { + cmd_mv(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "symlink") == 0) { + cmd_symlink(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "mkdir") == 0) { + cmd_mkdir(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "add") == 0) { + cmd_add(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "rm") == 0) { + cmd_rm(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "chmod") == 0) { + cmd_chmod(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "extract") == 0) { + cmd_extract(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "extractall") == 0) { + cmd_extractall(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "rmall") == 0) { + cmd_rmall(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "addall") == 0) { + cmd_addall(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "grow") == 0) { + cmd_grow(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "getattr") == 0) { + cmd_getattr(volume, argc - 2, argv + 2); + } else if(strcmp(argv[2], "debug") == 0) { + if(argc > 3 && strcmp(argv[3], "verbose") == 0) { + debugBTree(volume->catalogTree, TRUE); + } else { + debugBTree(volume->catalogTree, FALSE); + } + } + } + + closeVolume(volume); + CLOSE(io); + + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfscompress.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfscompress.c new file mode 100644 index 0000000..6e8682b --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfscompress.c @@ -0,0 +1,292 @@ +#include +#include "common.h" +#include +#include + +void flipHFSPlusDecmpfs(HFSPlusDecmpfs* compressData) { + FLIPENDIANLE(compressData->magic); + FLIPENDIANLE(compressData->flags); + FLIPENDIANLE(compressData->size); +} + +void flipRsrcHead(HFSPlusCmpfRsrcHead* data) { + FLIPENDIAN(data->headerSize); + FLIPENDIAN(data->totalSize); + FLIPENDIAN(data->dataSize); + FLIPENDIAN(data->flags); +} + +void flipRsrcBlockHead(HFSPlusCmpfRsrcBlockHead* data) { + FLIPENDIAN(data->dataSize); + FLIPENDIANLE(data->numBlocks); +} + +void flipRsrcBlock(HFSPlusCmpfRsrcBlock* data) { + FLIPENDIANLE(data->offset); + FLIPENDIANLE(data->size); +} + +void flipHFSPlusCmpfEnd(HFSPlusCmpfEnd* data) { + FLIPENDIAN(data->unk1); + FLIPENDIAN(data->unk2); + FLIPENDIAN(data->unk3); + FLIPENDIAN(data->magic); + FLIPENDIAN(data->flags); + FLIPENDIANLE(data->size); + FLIPENDIANLE(data->unk4); +} + +static int compressedRead(io_func* io, off_t location, size_t size, void *buffer) { + HFSPlusCompressed* data = (HFSPlusCompressed*) io->data; + size_t toRead; + + while(size > 0) { + if(data->cached && location >= data->cachedStart && location < data->cachedEnd) { + if((data->cachedEnd - location) < size) + toRead = data->cachedEnd - location; + else + toRead = size; + + memcpy(buffer, data->cached + (location - data->cachedStart), toRead); + + size -= toRead; + location += toRead; + buffer = ((uint8_t*) buffer) + toRead; + } + + if(size == 0) + break; + + // Try to cache + uLongf actualSize; + uint32_t block = location / 0x10000; + uint8_t* compressed = (uint8_t*) malloc(data->blocks->blocks[block].size); + if(!READ(data->io, data->rsrcHead.headerSize + sizeof(uint32_t) + data->blocks->blocks[block].offset, data->blocks->blocks[block].size, compressed)) { + hfs_panic("error reading"); + } + + if(data->cached) + free(data->cached); + + data->cached = (uint8_t*) malloc(0x10000); + actualSize = 0x10000; + uncompress(data->cached, &actualSize, compressed, data->blocks->blocks[block].size); + data->cachedStart = block * 0x10000; + data->cachedEnd = data->cachedStart + actualSize; + free(compressed); + } + + return TRUE; +} + +static int compressedWrite(io_func* io, off_t location, size_t size, void *buffer) { + HFSPlusCompressed* data = (HFSPlusCompressed*) io->data; + + if(data->cachedStart != 0 || data->cachedEnd != data->decmpfs->size) { + // Cache entire file + uint8_t* newCache = (uint8_t*) malloc(data->decmpfs->size); + compressedRead(io, 0, data->decmpfs->size, newCache); + if(data->cached) + free(data->cached); + + data->cached = newCache; + data->cachedStart = 0; + data->cachedEnd = data->decmpfs->size; + } + + if((location + size) > data->decmpfs->size) { + data->decmpfs->size = location + size; + data->cached = (uint8_t*) realloc(data->cached, data->decmpfs->size); + data->cachedEnd = data->decmpfs->size; + } + + memcpy(data->cached + location, buffer, size); + + data->dirty = TRUE; + return TRUE; +} + +static void closeHFSPlusCompressed(io_func* io) { + HFSPlusCompressed* data = (HFSPlusCompressed*) io->data; + + if(data->io) + CLOSE(data->io); + + if(data->dirty) { + if(data->blocks) + free(data->blocks); + + data->decmpfs->magic = CMPFS_MAGIC; + data->decmpfs->flags = 0x4; + data->decmpfsSize = sizeof(HFSPlusDecmpfs); + + uint32_t numBlocks = (data->decmpfs->size + 0xFFFF) / 0x10000; + uint32_t blocksSize = sizeof(HFSPlusCmpfRsrcBlockHead) + (numBlocks * sizeof(HFSPlusCmpfRsrcBlock)); + data->blocks = (HFSPlusCmpfRsrcBlockHead*) malloc(sizeof(HFSPlusCmpfRsrcBlockHead) + (numBlocks * sizeof(HFSPlusCmpfRsrcBlock))); + data->blocks->numBlocks = numBlocks; + data->blocks->dataSize = blocksSize - sizeof(uint32_t); // without the front dataSize in BlockHead. + + data->rsrcHead.headerSize = 0x100; + data->rsrcHead.dataSize = blocksSize; + data->rsrcHead.totalSize = data->rsrcHead.headerSize + data->rsrcHead.dataSize; + data->rsrcHead.flags = 0x32; + + uint8_t* buffer = (uint8_t*) malloc((0x10000 * 1.1) + 12); + uint32_t curFileOffset = data->blocks->dataSize; + uint32_t i; + for(i = 0; i < numBlocks; i++) { + data->blocks->blocks[i].offset = curFileOffset; + uLongf actualSize = (0x10000 * 1.1) + 12; + compress(buffer, &actualSize, data->cached + (0x10000 * i), + (data->decmpfs->size - (0x10000 * i)) > 0x10000 ? 0x10000 : (data->decmpfs->size - (0x10000 * i))); + data->blocks->blocks[i].size = actualSize; + + // check if we can fit the whole thing into an inline extended attribute + // a little fudge factor here since sizeof(HFSPlusAttrKey) is bigger than it ought to be, since only 127 characters are strictly allowed + if(numBlocks <= 1 && (actualSize + sizeof(HFSPlusDecmpfs) + sizeof(HFSPlusAttrKey)) <= 0x1000) { + data->decmpfs->flags = 0x3; + memcpy(data->decmpfs->data, buffer, actualSize); + data->decmpfsSize = sizeof(HFSPlusDecmpfs) + actualSize; + printf("inline data\n"); + break; + } else { + if(i == 0) { + data->io = openRawFile(data->file->fileID, &data->file->resourceFork, (HFSPlusCatalogRecord*)data->file, data->volume); + if(!data->io) { + hfs_panic("error opening resource fork"); + } + } + + WRITE(data->io, data->rsrcHead.headerSize + sizeof(uint32_t) + data->blocks->blocks[i].offset, data->blocks->blocks[i].size, buffer); + + curFileOffset += data->blocks->blocks[i].size; + data->blocks->dataSize += data->blocks->blocks[i].size; + data->rsrcHead.dataSize += data->blocks->blocks[i].size; + data->rsrcHead.totalSize += data->blocks->blocks[i].size; + } + } + + free(buffer); + + if(data->decmpfs->flags == 0x4) { + flipRsrcHead(&data->rsrcHead); + WRITE(data->io, 0, sizeof(HFSPlusCmpfRsrcHead), &data->rsrcHead); + flipRsrcHead(&data->rsrcHead); + + for(i = 0; i < data->blocks->numBlocks; i++) { + flipRsrcBlock(&data->blocks->blocks[i]); + } + flipRsrcBlockHead(data->blocks); + WRITE(data->io, data->rsrcHead.headerSize, blocksSize, data->blocks); + flipRsrcBlockHead(data->blocks); + for(i = 0; i < data->blocks->numBlocks; i++) { + flipRsrcBlock(&data->blocks->blocks[i]); + } + + HFSPlusCmpfEnd end; + memset(&end, 0, sizeof(HFSPlusCmpfEnd)); + end.unk1 = 0x1C; + end.unk2 = 0x32; + end.unk3 = 0x0; + end.magic = CMPFS_MAGIC; + end.flags = 0xA; + end.size = 0xFFFF01; + end.unk4 = 0x0; + + flipHFSPlusCmpfEnd(&end); + WRITE(data->io, data->rsrcHead.totalSize, sizeof(HFSPlusCmpfEnd), &end); + flipHFSPlusCmpfEnd(&end); + + CLOSE(data->io); + } + + flipHFSPlusDecmpfs(data->decmpfs); + setAttribute(data->volume, data->file->fileID, "com.apple.decmpfs", (uint8_t*)(data->decmpfs), data->decmpfsSize); + flipHFSPlusDecmpfs(data->decmpfs); + } + + if(data->cached) + free(data->cached); + + if(data->blocks) + free(data->blocks); + + free(data->decmpfs); + free(data); + free(io); +} + +io_func* openHFSPlusCompressed(Volume* volume, HFSPlusCatalogFile* file) { + io_func* io; + HFSPlusCompressed* data; + uLongf actualSize; + + io = (io_func*) malloc(sizeof(io_func)); + data = (HFSPlusCompressed*) malloc(sizeof(HFSPlusCompressed)); + + data->volume = volume; + data->file = file; + + io->data = data; + io->read = &compressedRead; + io->write = &compressedWrite; + io->close = &closeHFSPlusCompressed; + + data->cached = NULL; + data->cachedStart = 0; + data->cachedEnd = 0; + data->io = NULL; + data->blocks = NULL; + data->dirty = FALSE; + + data->decmpfsSize = getAttribute(volume, file->fileID, "com.apple.decmpfs", (uint8_t**)(&data->decmpfs)); + if(data->decmpfsSize == 0) { + data->decmpfs = (HFSPlusDecmpfs*) malloc(0x1000); + data->decmpfs->size = 0; + return io; // previously not compressed file + } + + flipHFSPlusDecmpfs(data->decmpfs); + + if(data->decmpfs->flags == 0x3) { + data->cached = (uint8_t*) malloc(data->decmpfs->size); + actualSize = data->decmpfs->size; + uncompress(data->cached, &actualSize, data->decmpfs->data, data->decmpfsSize - sizeof(HFSPlusDecmpfs)); + if(actualSize != data->decmpfs->size) { + fprintf(stderr, "decmpfs: size mismatch\n"); + } + data->cachedStart = 0; + data->cachedEnd = actualSize; + } else { + data->io = openRawFile(file->fileID, &file->resourceFork, (HFSPlusCatalogRecord*)file, volume); + if(!data->io) { + hfs_panic("error opening resource fork"); + } + + if(!READ(data->io, 0, sizeof(HFSPlusCmpfRsrcHead), &data->rsrcHead)) { + hfs_panic("error reading"); + } + + flipRsrcHead(&data->rsrcHead); + + data->blocks = (HFSPlusCmpfRsrcBlockHead*) malloc(sizeof(HFSPlusCmpfRsrcBlockHead)); + if(!READ(data->io, data->rsrcHead.headerSize, sizeof(HFSPlusCmpfRsrcBlockHead), data->blocks)) { + hfs_panic("error reading"); + } + + flipRsrcBlockHead(data->blocks); + + data->blocks = (HFSPlusCmpfRsrcBlockHead*) realloc(data->blocks, sizeof(HFSPlusCmpfRsrcBlockHead) + (sizeof(HFSPlusCmpfRsrcBlock) * data->blocks->numBlocks)); + if(!READ(data->io, data->rsrcHead.headerSize + sizeof(HFSPlusCmpfRsrcBlockHead), sizeof(HFSPlusCmpfRsrcBlock) * data->blocks->numBlocks, data->blocks->blocks)) { + hfs_panic("error reading"); + } + + int i; + for(i = 0; i < data->blocks->numBlocks; i++) { + flipRsrcBlock(&data->blocks->blocks[i]); + } + } + + return io; +} + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfslib.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfslib.c new file mode 100644 index 0000000..2b0274b --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/hfslib.c @@ -0,0 +1,723 @@ +#include +#include +#include +#include +#include +#include "common.h" +#include +#include +#include "abstractfile.h" +#include +#include + +#define BUFSIZE 1024*1024 + +static int silence = 0; + +void hfs_setsilence(int s) { + silence = s; +} + +void writeToFile(HFSPlusCatalogFile* file, AbstractFile* output, Volume* volume) { + unsigned char* buffer; + io_func* io; + off_t curPosition; + size_t bytesLeft; + + buffer = (unsigned char*) malloc(BUFSIZE); + + if(file->permissions.ownerFlags & UF_COMPRESSED) { + io = openHFSPlusCompressed(volume, file); + if(io == NULL) { + hfs_panic("error opening file"); + free(buffer); + return; + } + + curPosition = 0; + bytesLeft = ((HFSPlusCompressed*) io->data)->decmpfs->size; + } else { + io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, volume); + if(io == NULL) { + hfs_panic("error opening file"); + free(buffer); + return; + } + + curPosition = 0; + bytesLeft = file->dataFork.logicalSize; + } + while(bytesLeft > 0) { + if(bytesLeft > BUFSIZE) { + if(!READ(io, curPosition, BUFSIZE, buffer)) { + hfs_panic("error reading"); + } + if(output->write(output, buffer, BUFSIZE) != BUFSIZE) { + hfs_panic("error writing"); + } + curPosition += BUFSIZE; + bytesLeft -= BUFSIZE; + } else { + if(!READ(io, curPosition, bytesLeft, buffer)) { + hfs_panic("error reading"); + } + if(output->write(output, buffer, bytesLeft) != bytesLeft) { + hfs_panic("error writing"); + } + curPosition += bytesLeft; + bytesLeft -= bytesLeft; + } + } + CLOSE(io); + + free(buffer); +} + +void writeToHFSFile(HFSPlusCatalogFile* file, AbstractFile* input, Volume* volume) { + unsigned char *buffer; + io_func* io; + off_t curPosition; + off_t bytesLeft; + + buffer = (unsigned char*) malloc(BUFSIZE); + + bytesLeft = input->getLength(input); + + if(file->permissions.ownerFlags & UF_COMPRESSED) { + io = openHFSPlusCompressed(volume, file); + if(io == NULL) { + hfs_panic("error opening file"); + free(buffer); + return; + } + } else { + io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, volume); + if(io == NULL) { + hfs_panic("error opening file"); + free(buffer); + return; + } + allocate((RawFile*)io->data, bytesLeft); + } + + curPosition = 0; + + while(bytesLeft > 0) { + if(bytesLeft > BUFSIZE) { + if(input->read(input, buffer, BUFSIZE) != BUFSIZE) { + hfs_panic("error reading"); + } + if(!WRITE(io, curPosition, BUFSIZE, buffer)) { + hfs_panic("error writing"); + } + curPosition += BUFSIZE; + bytesLeft -= BUFSIZE; + } else { + if(input->read(input, buffer, (size_t)bytesLeft) != (size_t)bytesLeft) { + hfs_panic("error reading"); + } + if(!WRITE(io, curPosition, (size_t)bytesLeft, buffer)) { + hfs_panic("error reading"); + } + curPosition += bytesLeft; + bytesLeft -= bytesLeft; + } + } + + CLOSE(io); + + free(buffer); +} + +void get_hfs(Volume* volume, const char* inFileName, AbstractFile* output) { + HFSPlusCatalogRecord* record; + + record = getRecordFromPath(inFileName, volume, NULL, NULL); + + if(record != NULL) { + if(record->recordType == kHFSPlusFileRecord) + writeToFile((HFSPlusCatalogFile*)record, output, volume); + else { + printf("Not a file\n"); + exit(0); + } + } else { + printf("No such file or directory\n"); + exit(0); + } + + free(record); +} + +int add_hfs(Volume* volume, AbstractFile* inFile, const char* outFileName) { + HFSPlusCatalogRecord* record; + int ret; + + record = getRecordFromPath(outFileName, volume, NULL, NULL); + + if(record != NULL) { + if(record->recordType == kHFSPlusFileRecord) { + writeToHFSFile((HFSPlusCatalogFile*)record, inFile, volume); + ret = TRUE; + } else { + printf("Not a file\n"); + exit(0); + } + } else { + if(newFile(outFileName, volume)) { + record = getRecordFromPath(outFileName, volume, NULL, NULL); + writeToHFSFile((HFSPlusCatalogFile*)record, inFile, volume); + ret = TRUE; + } else { + ret = FALSE; + } + } + + inFile->close(inFile); + if(record != NULL) { + free(record); + } + + return ret; +} + +void grow_hfs(Volume* volume, uint64_t newSize) { + uint32_t newBlocks; + uint32_t blocksToGrow; + uint64_t newMapSize; + uint64_t i; + unsigned char zero; + + zero = 0; + + newBlocks = newSize / volume->volumeHeader->blockSize; + + if(newBlocks <= volume->volumeHeader->totalBlocks) { + printf("Cannot shrink volume\n"); + return; + } + + blocksToGrow = newBlocks - volume->volumeHeader->totalBlocks; + newMapSize = newBlocks / 8; + + if(volume->volumeHeader->allocationFile.logicalSize < newMapSize) { + if(volume->volumeHeader->freeBlocks + < ((newMapSize - volume->volumeHeader->allocationFile.logicalSize) / volume->volumeHeader->blockSize)) { + printf("Not enough room to allocate new allocation map blocks\n"); + exit(0); + } + + allocate((RawFile*) (volume->allocationFile->data), newMapSize); + } + + /* unreserve last block */ + setBlockUsed(volume, volume->volumeHeader->totalBlocks - 1, 0); + /* don't need to increment freeBlocks because we will allocate another alternate volume header later on */ + + /* "unallocate" the new blocks */ + for(i = ((volume->volumeHeader->totalBlocks / 8) + 1); i < newMapSize; i++) { + ASSERT(WRITE(volume->allocationFile, i, 1, &zero), "WRITE"); + } + + /* grow backing store size */ + ASSERT(WRITE(volume->image, newSize - 1, 1, &zero), "WRITE"); + + /* write new volume information */ + volume->volumeHeader->totalBlocks = newBlocks; + volume->volumeHeader->freeBlocks += blocksToGrow; + + /* reserve last block */ + setBlockUsed(volume, volume->volumeHeader->totalBlocks - 1, 1); + + updateVolume(volume); +} + +void removeAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName) { + CatalogRecordList* list; + CatalogRecordList* theList; + char fullName[1024]; + char* name; + char* pathComponent; + int pathLen; + char isRoot; + + HFSPlusCatalogFolder* folder; + theList = list = getFolderContents(folderID, volume); + + strcpy(fullName, parentName); + pathComponent = fullName + strlen(fullName); + + isRoot = FALSE; + if(strcmp(fullName, "/") == 0) { + isRoot = TRUE; + } + + while(list != NULL) { + name = unicodeToAscii(&list->name); + if(isRoot && (name[0] == '\0' || strncmp(name, ".HFS+ Private Directory Data", sizeof(".HFS+ Private Directory Data") - 1) == 0)) { + free(name); + list = list->next; + continue; + } + + strcpy(pathComponent, name); + pathLen = strlen(fullName); + + if(list->record->recordType == kHFSPlusFolderRecord) { + folder = (HFSPlusCatalogFolder*)list->record; + fullName[pathLen] = '/'; + fullName[pathLen + 1] = '\0'; + removeAllInFolder(folder->folderID, volume, fullName); + } else { + printf("%s\n", fullName); + removeFile(fullName, volume); + } + + free(name); + list = list->next; + } + + releaseCatalogRecordList(theList); + + if(!isRoot) { + *(pathComponent - 1) = '\0'; + printf("%s\n", fullName); + removeFile(fullName, volume); + } +} + + +void addAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName) { + CatalogRecordList* list; + CatalogRecordList* theList; + char cwd[1024]; + char fullName[1024]; + char testBuffer[1024]; + char* pathComponent; + int pathLen; + + char* name; + + DIR* dir; + DIR* tmp; + + HFSCatalogNodeID cnid; + + struct dirent* ent; + + AbstractFile* file; + HFSPlusCatalogFile* outFile; + + strcpy(fullName, parentName); + pathComponent = fullName + strlen(fullName); + + ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory"); + + theList = list = getFolderContents(folderID, volume); + + ASSERT((dir = opendir(cwd)) != NULL, "opendir"); + + while((ent = readdir(dir)) != NULL) { + if(ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) { + continue; + } + + strcpy(pathComponent, ent->d_name); + pathLen = strlen(fullName); + + cnid = 0; + list = theList; + while(list != NULL) { + name = unicodeToAscii(&list->name); + if(strcmp(name, ent->d_name) == 0) { + cnid = (list->record->recordType == kHFSPlusFolderRecord) ? (((HFSPlusCatalogFolder*)list->record)->folderID) + : (((HFSPlusCatalogFile*)list->record)->fileID); + free(name); + break; + } + free(name); + list = list->next; + } + + if((tmp = opendir(ent->d_name)) != NULL) { + closedir(tmp); + printf("folder: %s\n", fullName); fflush(stdout); + + if(cnid == 0) { + cnid = newFolder(fullName, volume); + } + + fullName[pathLen] = '/'; + fullName[pathLen + 1] = '\0'; + ASSERT(chdir(ent->d_name) == 0, "chdir"); + addAllInFolder(cnid, volume, fullName); + ASSERT(chdir(cwd) == 0, "chdir"); + } else { + printf("file: %s\n", fullName); fflush(stdout); + if(cnid == 0) { + cnid = newFile(fullName, volume); + } + file = createAbstractFileFromFile(fopen(ent->d_name, "rb")); + ASSERT(file != NULL, "fopen"); + outFile = (HFSPlusCatalogFile*)getRecordByCNID(cnid, volume); + writeToHFSFile(outFile, file, volume); + file->close(file); + free(outFile); + + if(strncmp(fullName, "/Applications/", sizeof("/Applications/") - 1) == 0) { + testBuffer[0] = '\0'; + strcpy(testBuffer, "/Applications/"); + strcat(testBuffer, ent->d_name); + strcat(testBuffer, ".app/"); + strcat(testBuffer, ent->d_name); + if(strcmp(testBuffer, fullName) == 0) { + if(strcmp(ent->d_name, "Installer") == 0 + || strcmp(ent->d_name, "BootNeuter") == 0 + ) { + printf("Giving setuid permissions to %s...\n", fullName); fflush(stdout); + chmodFile(fullName, 04755, volume); + } else { + printf("Giving permissions to %s\n", fullName); fflush(stdout); + chmodFile(fullName, 0755, volume); + } + } + } else if(strncmp(fullName, "/bin/", sizeof("/bin/") - 1) == 0 + || strncmp(fullName, "/Applications/BootNeuter.app/bin/", sizeof("/Applications/BootNeuter.app/bin/") - 1) == 0 + || strncmp(fullName, "/sbin/", sizeof("/sbin/") - 1) == 0 + || strncmp(fullName, "/usr/sbin/", sizeof("/usr/sbin/") - 1) == 0 + || strncmp(fullName, "/usr/bin/", sizeof("/usr/bin/") - 1) == 0 + || strncmp(fullName, "/usr/libexec/", sizeof("/usr/libexec/") - 1) == 0 + || strncmp(fullName, "/usr/local/bin/", sizeof("/usr/local/bin/") - 1) == 0 + || strncmp(fullName, "/usr/local/sbin/", sizeof("/usr/local/sbin/") - 1) == 0 + || strncmp(fullName, "/usr/local/libexec/", sizeof("/usr/local/libexec/") - 1) == 0 + ) { + chmodFile(fullName, 0755, volume); + printf("Giving permissions to %s\n", fullName); fflush(stdout); + } + } + } + + closedir(dir); + + releaseCatalogRecordList(theList); +} + +void extractAllInFolder(HFSCatalogNodeID folderID, Volume* volume) { + CatalogRecordList* list; + CatalogRecordList* theList; + char cwd[1024]; + char* name; + HFSPlusCatalogFolder* folder; + HFSPlusCatalogFile* file; + AbstractFile* outFile; + struct stat status; + + ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory"); + + theList = list = getFolderContents(folderID, volume); + + while(list != NULL) { + name = unicodeToAscii(&list->name); + if(strncmp(name, ".HFS+ Private Directory Data", sizeof(".HFS+ Private Directory Data") - 1) == 0 || name[0] == '\0') { + free(name); + list = list->next; + continue; + } + + if(list->record->recordType == kHFSPlusFolderRecord) { + folder = (HFSPlusCatalogFolder*)list->record; + printf("folder: %s\n", name); + if(stat(name, &status) != 0) { + ASSERT(mkdir(name, 0755) == 0, "mkdir"); + } + ASSERT(chdir(name) == 0, "chdir"); + extractAllInFolder(folder->folderID, volume); + ASSERT(chdir(cwd) == 0, "chdir"); + } else if(list->record->recordType == kHFSPlusFileRecord) { + printf("file: %s\n", name); + file = (HFSPlusCatalogFile*)list->record; + outFile = createAbstractFileFromFile(fopen(name, "wb")); + if(outFile != NULL) { + writeToFile(file, outFile, volume); + outFile->close(outFile); + } else { + printf("WARNING: cannot fopen %s\n", name); + } + } + + free(name); + list = list->next; + } + releaseCatalogRecordList(theList); +} + + +void addall_hfs(Volume* volume, const char* dirToMerge, const char* dest) { + HFSPlusCatalogRecord* record; + char* name; + char cwd[1024]; + char initPath[1024]; + int lastCharOfPath; + + ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory"); + + if(chdir(dirToMerge) != 0) { + printf("Cannot open that directory: %s\n", dirToMerge); + exit(0); + } + + record = getRecordFromPath(dest, volume, &name, NULL); + strcpy(initPath, dest); + lastCharOfPath = strlen(dest) - 1; + if(dest[lastCharOfPath] != '/') { + initPath[lastCharOfPath + 1] = '/'; + initPath[lastCharOfPath + 2] = '\0'; + } + + if(record != NULL) { + if(record->recordType == kHFSPlusFolderRecord) + addAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume, initPath); + else { + printf("Not a folder\n"); + exit(0); + } + } else { + printf("No such file or directory\n"); + exit(0); + } + + ASSERT(chdir(cwd) == 0, "chdir"); + free(record); + +} + +int copyAcrossVolumes(Volume* volume1, Volume* volume2, char* path1, char* path2) { + void* buffer; + size_t bufferSize; + AbstractFile* tmpFile; + int ret; + + buffer = malloc(1); + bufferSize = 0; + tmpFile = createAbstractFileFromMemoryFile((void**)&buffer, &bufferSize); + + if(!silence) + { + printf("retrieving... "); fflush(stdout); + } + + get_hfs(volume1, path1, tmpFile); + tmpFile->seek(tmpFile, 0); + + if(!silence) + { + printf("writing (%ld)... ", (long) tmpFile->getLength(tmpFile)); fflush(stdout); + } + + ret = add_hfs(volume2, tmpFile, path2); + + if(!silence) + { + printf("done\n"); + } + + free(buffer); + + return ret; +} + +void displayFolder(HFSCatalogNodeID folderID, Volume* volume) { + CatalogRecordList* list; + CatalogRecordList* theList; + HFSPlusCatalogFolder* folder; + HFSPlusCatalogFile* file; + time_t fileTime; + struct tm *date; + HFSPlusDecmpfs* compressData; + size_t attrSize; + + theList = list = getFolderContents(folderID, volume); + + while(list != NULL) { + if(list->record->recordType == kHFSPlusFolderRecord) { + folder = (HFSPlusCatalogFolder*)list->record; + printf("%06o ", folder->permissions.fileMode); + printf("%3d ", folder->permissions.ownerID); + printf("%3d ", folder->permissions.groupID); + printf("%12d ", folder->valence); + fileTime = APPLE_TO_UNIX_TIME(folder->contentModDate); + } else if(list->record->recordType == kHFSPlusFileRecord) { + file = (HFSPlusCatalogFile*)list->record; + printf("%06o ", file->permissions.fileMode); + printf("%3d ", file->permissions.ownerID); + printf("%3d ", file->permissions.groupID); + if(file->permissions.ownerFlags & UF_COMPRESSED) { + attrSize = getAttribute(volume, file->fileID, "com.apple.decmpfs", (uint8_t**)(&compressData)); + flipHFSPlusDecmpfs(compressData); + printf("%12" PRId64 " ", compressData->size); + free(compressData); + } else { + printf("%12" PRId64 " ", file->dataFork.logicalSize); + } + fileTime = APPLE_TO_UNIX_TIME(file->contentModDate); + } + + date = localtime(&fileTime); + if(date != NULL) { + printf("%2d/%2d/%4d %02d:%02d ", date->tm_mon, date->tm_mday, date->tm_year + 1900, date->tm_hour, date->tm_min); + } else { + printf(" "); + } + + printUnicode(&list->name); + printf("\n"); + + list = list->next; + } + + releaseCatalogRecordList(theList); +} + +void displayFileLSLine(Volume* volume, HFSPlusCatalogFile* file, const char* name) { + time_t fileTime; + struct tm *date; + HFSPlusDecmpfs* compressData; + + printf("%06o ", file->permissions.fileMode); + printf("%3d ", file->permissions.ownerID); + printf("%3d ", file->permissions.groupID); + + if(file->permissions.ownerFlags & UF_COMPRESSED) { + getAttribute(volume, file->fileID, "com.apple.decmpfs", (uint8_t**)(&compressData)); + flipHFSPlusDecmpfs(compressData); + printf("%12" PRId64 " ", compressData->size); + free(compressData); + } else { + printf("%12" PRId64 " ", file->dataFork.logicalSize); + } + + fileTime = APPLE_TO_UNIX_TIME(file->contentModDate); + date = localtime(&fileTime); + if(date != NULL) { + printf("%2d/%2d/%4d %2d:%02d ", date->tm_mon, date->tm_mday, date->tm_year + 1900, date->tm_hour, date->tm_min); + } else { + printf(" "); + } + printf("%s\n", name); + + XAttrList* next; + XAttrList* attrs = getAllExtendedAttributes(file->fileID, volume); + if(attrs != NULL) { + printf("Extended attributes\n"); + while(attrs != NULL) { + next = attrs->next; + printf("\t%s\n", attrs->name); + free(attrs->name); + free(attrs); + attrs = next; + } + } +} + +void hfs_ls(Volume* volume, const char* path) { + HFSPlusCatalogRecord* record; + char* name; + + record = getRecordFromPath(path, volume, &name, NULL); + + printf("%s: \n", name); + if(record != NULL) { + if(record->recordType == kHFSPlusFolderRecord) + displayFolder(((HFSPlusCatalogFolder*)record)->folderID, volume); + else + displayFileLSLine(volume, (HFSPlusCatalogFile*)record, name); + } else { + printf("No such file or directory\n"); + } + + printf("Total filesystem size: %d, free: %d\n", (volume->volumeHeader->totalBlocks - volume->volumeHeader->freeBlocks) * volume->volumeHeader->blockSize, volume->volumeHeader->freeBlocks * volume->volumeHeader->blockSize); + + free(record); +} + +void hfs_untar(Volume* volume, AbstractFile* tarFile) { + size_t tarSize = tarFile->getLength(tarFile); + size_t curRecord = 0; + char block[512]; + + while(curRecord < tarSize) { + tarFile->seek(tarFile, curRecord); + tarFile->read(tarFile, block, 512); + + uint32_t mode = 0; + char* fileName = NULL; + const char* target = NULL; + uint32_t type = 0; + uint32_t size; + uint32_t uid; + uint32_t gid; + + sscanf(&block[100], "%o", &mode); + fileName = &block[0]; + sscanf(&block[156], "%o", &type); + target = &block[157]; + sscanf(&block[124], "%o", &size); + sscanf(&block[108], "%o", &uid); + sscanf(&block[116], "%o", &gid); + + if(fileName[0] == '\0') + break; + + if(fileName[0] == '.' && fileName[1] == '/') { + fileName += 2; + } + + if(fileName[0] == '\0') + goto loop; + + if(fileName[strlen(fileName) - 1] == '/') + fileName[strlen(fileName) - 1] = '\0'; + + HFSPlusCatalogRecord* record = getRecordFromPath3(fileName, volume, NULL, NULL, TRUE, FALSE, kHFSRootFolderID); + if(record) { + if(record->recordType == kHFSPlusFolderRecord || type == 5) { + if(!silence) + printf("ignoring %s, type = %d\n", fileName, type); + free(record); + goto loop; + } else { + printf("replacing %s\n", fileName); + free(record); + removeFile(fileName, volume); + } + } + + if(type == 0) { + if(!silence) + printf("file: %s (%04o), size = %d\n", fileName, mode, size); + void* buffer = malloc(size); + tarFile->seek(tarFile, curRecord + 512); + tarFile->read(tarFile, buffer, size); + AbstractFile* inFile = createAbstractFileFromMemory(&buffer, size); + add_hfs(volume, inFile, fileName); + free(buffer); + } else if(type == 5) { + if(!silence) + printf("directory: %s (%04o)\n", fileName, mode); + newFolder(fileName, volume); + } else if(type == 2) { + if(!silence) + printf("symlink: %s (%04o) -> %s\n", fileName, mode, target); + makeSymlink(fileName, target, volume); + } + + chmodFile(fileName, mode, volume); + chownFile(fileName, uid, gid, volume); + +loop: + + curRecord = (curRecord + 512) + ((size + 511) / 512 * 512); + } + +} + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/rawfile.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/rawfile.c new file mode 100644 index 0000000..9023acf --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/rawfile.c @@ -0,0 +1,502 @@ +#include +#include +#include + +int writeExtents(RawFile* rawFile); + +int isBlockUsed(Volume* volume, uint32_t block) +{ + unsigned char byte; + + READ(volume->allocationFile, block / 8, 1, &byte); + return (byte & (1 << (7 - (block % 8)))) != 0; +} + +int setBlockUsed(Volume* volume, uint32_t block, int used) { + unsigned char byte; + + READ(volume->allocationFile, block / 8, 1, &byte); + if(used) { + byte |= (1 << (7 - (block % 8))); + } else { + byte &= ~(1 << (7 - (block % 8))); + } + ASSERT(WRITE(volume->allocationFile, block / 8, 1, &byte), "WRITE"); + + return TRUE; +} + +int allocate(RawFile* rawFile, off_t size) { + unsigned char* zeros; + Volume* volume; + HFSPlusForkData* forkData; + uint32_t blocksNeeded; + uint32_t blocksToAllocate; + Extent* extent; + Extent* lastExtent; + + uint32_t curBlock; + + volume = rawFile->volume; + forkData = rawFile->forkData; + extent = rawFile->extents; + + blocksNeeded = ((uint64_t)size / (uint64_t)volume->volumeHeader->blockSize) + (((size % volume->volumeHeader->blockSize) == 0) ? 0 : 1); + + if(blocksNeeded > forkData->totalBlocks) { + zeros = (unsigned char*) malloc(volume->volumeHeader->blockSize); + memset(zeros, 0, volume->volumeHeader->blockSize); + + blocksToAllocate = blocksNeeded - forkData->totalBlocks; + + if(blocksToAllocate > volume->volumeHeader->freeBlocks) { + return FALSE; + } + + lastExtent = NULL; + while(extent != NULL) { + lastExtent = extent; + extent = extent->next; + } + + if(lastExtent == NULL) { + rawFile->extents = (Extent*) malloc(sizeof(Extent)); + lastExtent = rawFile->extents; + lastExtent->blockCount = 0; + lastExtent->next = NULL; + curBlock = volume->volumeHeader->nextAllocation; + } else { + curBlock = lastExtent->startBlock + lastExtent->blockCount; + } + + while(blocksToAllocate > 0) { + if(isBlockUsed(volume, curBlock)) { + if(lastExtent->blockCount > 0) { + lastExtent->next = (Extent*) malloc(sizeof(Extent)); + lastExtent = lastExtent->next; + lastExtent->blockCount = 0; + lastExtent->next = NULL; + } + curBlock = volume->volumeHeader->nextAllocation; + volume->volumeHeader->nextAllocation++; + if(volume->volumeHeader->nextAllocation >= volume->volumeHeader->totalBlocks) { + volume->volumeHeader->nextAllocation = 0; + } + } else { + if(lastExtent->blockCount == 0) { + lastExtent->startBlock = curBlock; + } + + /* zero out allocated block */ + ASSERT(WRITE(volume->image, curBlock * volume->volumeHeader->blockSize, volume->volumeHeader->blockSize, zeros), "WRITE"); + + setBlockUsed(volume, curBlock, TRUE); + volume->volumeHeader->freeBlocks--; + blocksToAllocate--; + curBlock++; + lastExtent->blockCount++; + + if(curBlock >= volume->volumeHeader->totalBlocks) { + curBlock = volume->volumeHeader->nextAllocation; + } + } + } + + free(zeros); + } else if(blocksNeeded < forkData->totalBlocks) { + blocksToAllocate = blocksNeeded; + + lastExtent = NULL; + + while(blocksToAllocate > 0) { + if(blocksToAllocate > extent->blockCount) { + blocksToAllocate -= extent->blockCount; + lastExtent = extent; + extent = extent->next; + } else { + break; + } + } + + + if(blocksToAllocate == 0 && lastExtent != NULL) { + // snip the extent list here, since we don't need the rest + lastExtent->next = NULL; + } else if(blocksNeeded == 0) { + rawFile->extents = NULL; + } + + do { + for(curBlock = (extent->startBlock + blocksToAllocate); curBlock < (extent->startBlock + extent->blockCount); curBlock++) { + setBlockUsed(volume, curBlock, FALSE); + volume->volumeHeader->freeBlocks++; + } + lastExtent = extent; + extent = extent->next; + + if(blocksToAllocate == 0) + { + free(lastExtent); + } else { + lastExtent->next = NULL; + lastExtent->blockCount = blocksToAllocate; + } + + blocksToAllocate = 0; + } while(extent != NULL); + } + + writeExtents(rawFile); + + forkData->logicalSize = size; + forkData->totalBlocks = blocksNeeded; + + updateVolume(rawFile->volume); + + if(rawFile->catalogRecord != NULL) { + updateCatalog(rawFile->volume, rawFile->catalogRecord); + } + + return TRUE; +} + +static int rawFileRead(io_func* io,off_t location, size_t size, void *buffer) { + RawFile* rawFile; + Volume* volume; + Extent* extent; + + size_t blockSize; + off_t fileLoc; + off_t locationInBlock; + size_t possible; + + rawFile = (RawFile*) io->data; + volume = rawFile->volume; + blockSize = volume->volumeHeader->blockSize; + + if(!rawFile->extents) + return FALSE; + + extent = rawFile->extents; + fileLoc = 0; + + locationInBlock = location; + while(TRUE) { + fileLoc += extent->blockCount * blockSize; + if(fileLoc <= location) { + locationInBlock -= extent->blockCount * blockSize; + extent = extent->next; + if(extent == NULL) + break; + } else { + break; + } + } + + while(size > 0) { + if(extent == NULL) + return FALSE; + + possible = extent->blockCount * blockSize - locationInBlock; + + if(size > possible) { + ASSERT(READ(volume->image, extent->startBlock * blockSize + locationInBlock, possible, buffer), "READ"); + size -= possible; + buffer = (void*)(((size_t)buffer) + possible); + extent = extent->next; + } else { + ASSERT(READ(volume->image, extent->startBlock * blockSize + locationInBlock, size, buffer), "READ"); + break; + } + + locationInBlock = 0; + } + + return TRUE; +} + +static int rawFileWrite(io_func* io,off_t location, size_t size, void *buffer) { + RawFile* rawFile; + Volume* volume; + Extent* extent; + + size_t blockSize; + off_t fileLoc; + off_t locationInBlock; + size_t possible; + + rawFile = (RawFile*) io->data; + volume = rawFile->volume; + blockSize = volume->volumeHeader->blockSize; + + if(rawFile->forkData->logicalSize < (location + size)) { + ASSERT(allocate(rawFile, location + size), "allocate"); + } + + extent = rawFile->extents; + fileLoc = 0; + + locationInBlock = location; + while(TRUE) { + fileLoc += extent->blockCount * blockSize; + if(fileLoc <= location) { + locationInBlock -= extent->blockCount * blockSize; + extent = extent->next; + if(extent == NULL) + break; + } else { + break; + } + } + + while(size > 0) { + if(extent == NULL) + return FALSE; + + possible = extent->blockCount * blockSize - locationInBlock; + + if(size > possible) { + ASSERT(WRITE(volume->image, extent->startBlock * blockSize + locationInBlock, possible, buffer), "WRITE"); + size -= possible; + buffer = (void*)(((size_t)buffer) + possible); + extent = extent->next; + } else { + ASSERT(WRITE(volume->image, extent->startBlock * blockSize + locationInBlock, size, buffer), "WRITE"); + break; + } + + locationInBlock = 0; + } + + return TRUE; +} + +static void closeRawFile(io_func* io) { + RawFile* rawFile; + Extent* extent; + Extent* toRemove; + + rawFile = (RawFile*) io->data; + extent = rawFile->extents; + + while(extent != NULL) { + toRemove = extent; + extent = extent->next; + free(toRemove); + } + + free(rawFile); + free(io); +} + +int removeExtents(RawFile* rawFile) { + uint32_t blocksLeft; + HFSPlusForkData* forkData; + uint32_t currentBlock; + + uint32_t startBlock; + uint32_t blockCount; + + HFSPlusExtentDescriptor* descriptor; + int currentExtent; + HFSPlusExtentKey extentKey; + int exact; + + extentKey.keyLength = sizeof(HFSPlusExtentKey) - sizeof(extentKey.keyLength); + extentKey.forkType = 0; + extentKey.fileID = rawFile->id; + + forkData = rawFile->forkData; + blocksLeft = forkData->totalBlocks; + currentExtent = 0; + currentBlock = 0; + descriptor = (HFSPlusExtentDescriptor*) forkData->extents; + + while(blocksLeft > 0) { + if(currentExtent == 8) { + if(rawFile->volume->extentsTree == NULL) { + hfs_panic("no extents overflow file loaded yet!"); + return FALSE; + } + + if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) { + free(descriptor); + } + + extentKey.startBlock = currentBlock; + descriptor = (HFSPlusExtentDescriptor*) search(rawFile->volume->extentsTree, (BTKey*)(&extentKey), &exact, NULL, NULL); + if(descriptor == NULL || exact == FALSE) { + hfs_panic("inconsistent extents information!"); + return FALSE; + } else { + removeFromBTree(rawFile->volume->extentsTree, (BTKey*)(&extentKey)); + currentExtent = 0; + continue; + } + } + + startBlock = descriptor[currentExtent].startBlock; + blockCount = descriptor[currentExtent].blockCount; + + currentBlock += blockCount; + blocksLeft -= blockCount; + currentExtent++; + } + + if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) { + free(descriptor); + } + + return TRUE; +} + +int writeExtents(RawFile* rawFile) { + Extent* extent; + int currentExtent; + HFSPlusExtentKey extentKey; + HFSPlusExtentDescriptor descriptor[8]; + HFSPlusForkData* forkData; + + removeExtents(rawFile); + + forkData = rawFile->forkData; + currentExtent = 0; + extent = rawFile->extents; + + memset(forkData->extents, 0, sizeof(HFSPlusExtentRecord)); + while(extent != NULL && currentExtent < 8) { + ((HFSPlusExtentDescriptor*)forkData->extents)[currentExtent].startBlock = extent->startBlock; + ((HFSPlusExtentDescriptor*)forkData->extents)[currentExtent].blockCount = extent->blockCount; + extent = extent->next; + currentExtent++; + } + + if(extent != NULL) { + extentKey.keyLength = sizeof(HFSPlusExtentKey) - sizeof(extentKey.keyLength); + extentKey.forkType = 0; + extentKey.fileID = rawFile->id; + + currentExtent = 0; + + while(extent != NULL) { + if(currentExtent == 0) { + memset(descriptor, 0, sizeof(HFSPlusExtentRecord)); + } + + if(currentExtent == 8) { + extentKey.startBlock = descriptor[0].startBlock; + addToBTree(rawFile->volume->extentsTree, (BTKey*)(&extentKey), sizeof(HFSPlusExtentRecord), (unsigned char *)(&(descriptor[0]))); + currentExtent = 0; + } + + descriptor[currentExtent].startBlock = extent->startBlock; + descriptor[currentExtent].blockCount = extent->blockCount; + + currentExtent++; + extent = extent->next; + } + + extentKey.startBlock = descriptor[0].startBlock; + addToBTree(rawFile->volume->extentsTree, (BTKey*)(&extentKey), sizeof(HFSPlusExtentRecord), (unsigned char *)(&(descriptor[0]))); + } + + return TRUE; +} + +int readExtents(RawFile* rawFile) { + uint32_t blocksLeft; + HFSPlusForkData* forkData; + uint32_t currentBlock; + + Extent* extent; + Extent* lastExtent; + + HFSPlusExtentDescriptor* descriptor; + int currentExtent; + HFSPlusExtentKey extentKey; + int exact; + + extentKey.keyLength = sizeof(HFSPlusExtentKey) - sizeof(extentKey.keyLength); + extentKey.forkType = 0; + extentKey.fileID = rawFile->id; + + forkData = rawFile->forkData; + blocksLeft = forkData->totalBlocks; + currentExtent = 0; + currentBlock = 0; + descriptor = (HFSPlusExtentDescriptor*) forkData->extents; + + lastExtent = NULL; + + while(blocksLeft > 0) { + extent = (Extent*) malloc(sizeof(Extent)); + + if(currentExtent == 8) { + if(rawFile->volume->extentsTree == NULL) { + hfs_panic("no extents overflow file loaded yet!"); + return FALSE; + } + + if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) { + free(descriptor); + } + + extentKey.startBlock = currentBlock; + descriptor = (HFSPlusExtentDescriptor*) search(rawFile->volume->extentsTree, (BTKey*)(&extentKey), &exact, NULL, NULL); + if(descriptor == NULL || exact == FALSE) { + hfs_panic("inconsistent extents information!"); + return FALSE; + } else { + currentExtent = 0; + continue; + } + } + + extent->startBlock = descriptor[currentExtent].startBlock; + extent->blockCount = descriptor[currentExtent].blockCount; + extent->next = NULL; + + currentBlock += extent->blockCount; + blocksLeft -= extent->blockCount; + currentExtent++; + + if(lastExtent == NULL) { + rawFile->extents = extent; + } else { + lastExtent->next = extent; + } + + lastExtent = extent; + } + + if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) { + free(descriptor); + } + + return TRUE; +} + +io_func* openRawFile(HFSCatalogNodeID id, HFSPlusForkData* forkData, HFSPlusCatalogRecord* catalogRecord, Volume* volume) { + io_func* io; + RawFile* rawFile; + + io = (io_func*) malloc(sizeof(io_func)); + rawFile = (RawFile*) malloc(sizeof(RawFile)); + + rawFile->id = id; + rawFile->volume = volume; + rawFile->forkData = forkData; + rawFile->catalogRecord = catalogRecord; + rawFile->extents = NULL; + + io->data = rawFile; + io->read = &rawFileRead; + io->write = &rawFileWrite; + io->close = &closeRawFile; + + if(!readExtents(rawFile)) { + return NULL; + } + + return io; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/utility.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/utility.c new file mode 100644 index 0000000..01c3bb4 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/utility.c @@ -0,0 +1,30 @@ +#include +#include +#include + +void hfs_panic(const char* hfs_panicString) { + fprintf(stderr, "%s\n", hfs_panicString); + exit(1); +} + +void printUnicode(HFSUniStr255* str) { + int i; + + for(i = 0; i < str->length; i++) { + printf("%c", (char)(str->unicode[i] & 0xff)); + } +} + +char* unicodeToAscii(HFSUniStr255* str) { + int i; + char* toReturn; + + toReturn = (char*) malloc(sizeof(char) * (str->length + 1)); + + for(i = 0; i < str->length; i++) { + toReturn[i] = (char)(str->unicode[i] & 0xff); + } + toReturn[i] = '\0'; + + return toReturn; +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/volume.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/volume.c new file mode 100644 index 0000000..f157438 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/volume.c @@ -0,0 +1,176 @@ +#include +#include +#include + +void flipForkData(HFSPlusForkData* forkData) { + FLIPENDIAN(forkData->logicalSize); + FLIPENDIAN(forkData->clumpSize); + FLIPENDIAN(forkData->totalBlocks); + + flipExtentRecord(&forkData->extents); +} + +static HFSPlusVolumeHeader* readVolumeHeader(io_func* io, off_t offset) { + HFSPlusVolumeHeader* volumeHeader; + + volumeHeader = (HFSPlusVolumeHeader*) malloc(sizeof(HFSPlusVolumeHeader)); + + if(!(READ(io, offset, sizeof(HFSPlusVolumeHeader), volumeHeader))) + return NULL; + + FLIPENDIAN(volumeHeader->signature); + FLIPENDIAN(volumeHeader->version); + FLIPENDIAN(volumeHeader->attributes); + FLIPENDIAN(volumeHeader->lastMountedVersion); + FLIPENDIAN(volumeHeader->journalInfoBlock); + FLIPENDIAN(volumeHeader->createDate); + FLIPENDIAN(volumeHeader->modifyDate); + FLIPENDIAN(volumeHeader->backupDate); + FLIPENDIAN(volumeHeader->checkedDate); + FLIPENDIAN(volumeHeader->fileCount); + FLIPENDIAN(volumeHeader->folderCount); + FLIPENDIAN(volumeHeader->blockSize); + FLIPENDIAN(volumeHeader->totalBlocks); + FLIPENDIAN(volumeHeader->freeBlocks); + FLIPENDIAN(volumeHeader->nextAllocation); + FLIPENDIAN(volumeHeader->rsrcClumpSize); + FLIPENDIAN(volumeHeader->dataClumpSize); + FLIPENDIAN(volumeHeader->nextCatalogID); + FLIPENDIAN(volumeHeader->writeCount); + FLIPENDIAN(volumeHeader->encodingsBitmap); + + + flipForkData(&volumeHeader->allocationFile); + flipForkData(&volumeHeader->extentsFile); + flipForkData(&volumeHeader->catalogFile); + flipForkData(&volumeHeader->attributesFile); + flipForkData(&volumeHeader->startupFile); + + return volumeHeader; +} + +static int writeVolumeHeader(io_func* io, HFSPlusVolumeHeader* volumeHeaderToWrite, off_t offset) { + HFSPlusVolumeHeader* volumeHeader; + + volumeHeader = (HFSPlusVolumeHeader*) malloc(sizeof(HFSPlusVolumeHeader)); + memcpy(volumeHeader, volumeHeaderToWrite, sizeof(HFSPlusVolumeHeader)); + + FLIPENDIAN(volumeHeader->signature); + FLIPENDIAN(volumeHeader->version); + FLIPENDIAN(volumeHeader->attributes); + FLIPENDIAN(volumeHeader->lastMountedVersion); + FLIPENDIAN(volumeHeader->journalInfoBlock); + FLIPENDIAN(volumeHeader->createDate); + FLIPENDIAN(volumeHeader->modifyDate); + FLIPENDIAN(volumeHeader->backupDate); + FLIPENDIAN(volumeHeader->checkedDate); + FLIPENDIAN(volumeHeader->fileCount); + FLIPENDIAN(volumeHeader->folderCount); + FLIPENDIAN(volumeHeader->blockSize); + FLIPENDIAN(volumeHeader->totalBlocks); + FLIPENDIAN(volumeHeader->freeBlocks); + FLIPENDIAN(volumeHeader->nextAllocation); + FLIPENDIAN(volumeHeader->rsrcClumpSize); + FLIPENDIAN(volumeHeader->dataClumpSize); + FLIPENDIAN(volumeHeader->nextCatalogID); + FLIPENDIAN(volumeHeader->writeCount); + FLIPENDIAN(volumeHeader->encodingsBitmap); + + + flipForkData(&volumeHeader->allocationFile); + flipForkData(&volumeHeader->extentsFile); + flipForkData(&volumeHeader->catalogFile); + flipForkData(&volumeHeader->attributesFile); + flipForkData(&volumeHeader->startupFile); + + if(!(WRITE(io, offset, sizeof(HFSPlusVolumeHeader), volumeHeader))) + return FALSE; + + free(volumeHeader); + + return TRUE; +} + +int updateVolume(Volume* volume) { + ASSERT(writeVolumeHeader(volume->image, volume->volumeHeader, + ((off_t)volume->volumeHeader->totalBlocks * (off_t)volume->volumeHeader->blockSize) - 1024), "writeVolumeHeader"); + return writeVolumeHeader(volume->image, volume->volumeHeader, 1024); +} + +Volume* openVolume(io_func* io) { + Volume* volume; + io_func* file; + + volume = (Volume*) malloc(sizeof(Volume)); + volume->image = io; + volume->extentsTree = NULL; + + volume->volumeHeader = readVolumeHeader(io, 1024); + if(volume->volumeHeader == NULL) { + free(volume); + return NULL; + } + + file = openRawFile(kHFSExtentsFileID, &volume->volumeHeader->extentsFile, NULL, volume); + if(file == NULL) { + free(volume->volumeHeader); + free(volume); + return NULL; + } + + volume->extentsTree = openExtentsTree(file); + if(volume->extentsTree == NULL) { + free(volume->volumeHeader); + free(volume); + return NULL; + } + + file = openRawFile(kHFSCatalogFileID, &volume->volumeHeader->catalogFile, NULL, volume); + if(file == NULL) { + closeBTree(volume->extentsTree); + free(volume->volumeHeader); + free(volume); + return NULL; + } + + volume->catalogTree = openCatalogTree(file); + if(volume->catalogTree == NULL) { + closeBTree(volume->extentsTree); + free(volume->volumeHeader); + free(volume); + return NULL; + } + + volume->allocationFile = openRawFile(kHFSAllocationFileID, &volume->volumeHeader->allocationFile, NULL, volume); + if(volume->allocationFile == NULL) { + closeBTree(volume->catalogTree); + closeBTree(volume->extentsTree); + free(volume->volumeHeader); + free(volume); + return NULL; + } + + volume->attrTree = NULL; + file = openRawFile(kHFSAttributesFileID, &volume->volumeHeader->attributesFile, NULL, volume); + if(file != NULL) { + volume->attrTree = openAttributesTree(file); + if(!volume->attrTree) { + CLOSE(file); + } + } + + volume->metadataDir = getMetadataDirectoryID(volume); + + return volume; +} + +void closeVolume(Volume *volume) { + if(volume->attrTree) + closeBTree(volume->attrTree); + + CLOSE(volume->allocationFile); + closeBTree(volume->catalogTree); + closeBTree(volume->extentsTree); + free(volume->volumeHeader); + free(volume); +} diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/xattr.c b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/xattr.c new file mode 100644 index 0000000..e41026f --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/hfs/xattr.c @@ -0,0 +1,374 @@ +#include +#include +#include + +static inline void flipAttrData(HFSPlusAttrData* data) { + FLIPENDIAN(data->recordType); + FLIPENDIAN(data->size); +} + +static inline void flipAttrForkData(HFSPlusAttrForkData* data) { + FLIPENDIAN(data->recordType); + flipForkData(&data->theFork); +} + +static inline void flipAttrExtents(HFSPlusAttrExtents* data) { + FLIPENDIAN(data->recordType); + flipExtentRecord(&data->extents); +} + +static int attrCompare(BTKey* vLeft, BTKey* vRight) { + HFSPlusAttrKey* left; + HFSPlusAttrKey* right; + uint16_t i; + + uint16_t cLeft; + uint16_t cRight; + + left = (HFSPlusAttrKey*) vLeft; + right =(HFSPlusAttrKey*) vRight; + + if(left->fileID < right->fileID) { + return -1; + } else if(left->fileID > right->fileID) { + return 1; + } else { + for(i = 0; i < left->name.length; i++) { + if(i >= right->name.length) { + return 1; + } else { + cLeft = left->name.unicode[i]; + cRight = right->name.unicode[i]; + + if(cLeft < cRight) + return -1; + else if(cLeft > cRight) + return 1; + } + } + + if(i < right->name.length) { + return -1; + } else { + /* do a safety check on key length. Otherwise, bad things may happen later on when we try to add or remove with this key */ + /*if(left->keyLength == right->keyLength) { + return 0; + } else if(left->keyLength < right->keyLength) { + return -1; + } else { + return 1; + }*/ + return 0; + } + } +} + +#define UNICODE_START (sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t)) + +static BTKey* attrKeyRead(off_t offset, io_func* io) { + int i; + HFSPlusAttrKey* key; + + key = (HFSPlusAttrKey*) malloc(sizeof(HFSPlusAttrKey)); + + if(!READ(io, offset, UNICODE_START, key)) + return NULL; + + FLIPENDIAN(key->keyLength); + FLIPENDIAN(key->fileID); + FLIPENDIAN(key->startBlock); + FLIPENDIAN(key->name.length); + + if(key->name.length > 254) + { + printf("Invalid xattr key at offset %x\n", offset); + free(key); + return NULL; + } + + if(!READ(io, offset + UNICODE_START, key->name.length * sizeof(uint16_t), ((unsigned char *)key) + UNICODE_START)) + return NULL; + + for(i = 0; i < key->name.length; i++) { + FLIPENDIAN(key->name.unicode[i]); + } + + return (BTKey*)key; +} + +static int attrKeyWrite(off_t offset, BTKey* toWrite, io_func* io) { + HFSPlusAttrKey* key; + uint16_t keyLength; + uint16_t nodeNameLength; + int i; + + keyLength = toWrite->keyLength; + key = (HFSPlusAttrKey*) malloc(keyLength); + memcpy(key, toWrite, keyLength); + + nodeNameLength = key->name.length; + + FLIPENDIAN(key->keyLength); + FLIPENDIAN(key->fileID); + FLIPENDIAN(key->startBlock); + FLIPENDIAN(key->name.length); + + for(i = 0; i < nodeNameLength; i++) { + FLIPENDIAN(key->name.unicode[i]); + } + + if(!WRITE(io, offset, keyLength, key)) + return FALSE; + + free(key); + + return TRUE; +} + +static void attrKeyPrint(BTKey* toPrint) { + HFSPlusAttrKey* key; + + key = (HFSPlusAttrKey*)toPrint; + + printf("attribute%d:%d:", key->fileID, key->startBlock); + printUnicode(&key->name); +} + +static BTKey* attrDataRead(off_t offset, io_func* io) { + HFSPlusAttrRecord* record; + + record = (HFSPlusAttrRecord*) malloc(sizeof(HFSPlusAttrRecord)); + + if(!READ(io, offset, sizeof(uint32_t), record)) + return NULL; + + FLIPENDIAN(record->recordType); + switch(record->recordType) + { + case kHFSPlusAttrInlineData: + if(!READ(io, offset, sizeof(HFSPlusAttrData), record)) + return NULL; + + flipAttrData((HFSPlusAttrData*) record); + + record = realloc(record, sizeof(HFSPlusAttrData) + ((HFSPlusAttrData*) record)->size); + if(!READ(io, offset + sizeof(HFSPlusAttrData), ((HFSPlusAttrData*) record)->size, ((HFSPlusAttrData*) record)->data)) + return NULL; + + break; + + case kHFSPlusAttrForkData: + if(!READ(io, offset, sizeof(HFSPlusAttrForkData), record)) + return NULL; + + flipAttrForkData((HFSPlusAttrForkData*) record); + + break; + + case kHFSPlusAttrExtents: + if(!READ(io, offset, sizeof(HFSPlusAttrExtents), record)) + return NULL; + + flipAttrExtents((HFSPlusAttrExtents*) record); + + break; + } + + return (BTKey*)record; +} + +static int updateAttributes(Volume* volume, HFSPlusAttrKey* skey, HFSPlusAttrRecord* srecord) { + HFSPlusAttrKey key; + HFSPlusAttrRecord* record; + int ret, len; + + memcpy(&key, skey, skey->keyLength); + + switch(srecord->recordType) { + case kHFSPlusAttrInlineData: + len = srecord->attrData.size + sizeof(HFSPlusAttrData); + record = (HFSPlusAttrRecord*) malloc(len); + memcpy(record, srecord, len); + flipAttrData((HFSPlusAttrData*) record); + removeFromBTree(volume->attrTree, (BTKey*)(&key)); + ret = addToBTree(volume->attrTree, (BTKey*)(&key), len, (unsigned char *)record); + free(record); + break; + case kHFSPlusAttrForkData: + record = (HFSPlusAttrRecord*) malloc(sizeof(HFSPlusAttrForkData)); + memcpy(record, srecord, sizeof(HFSPlusAttrForkData)); + flipAttrForkData((HFSPlusAttrForkData*) record); + removeFromBTree(volume->attrTree, (BTKey*)(&key)); + ret = addToBTree(volume->attrTree, (BTKey*)(&key), sizeof(HFSPlusAttrForkData), (unsigned char *)record); + free(record); + break; + case kHFSPlusAttrExtents: + record = (HFSPlusAttrRecord*) malloc(sizeof(HFSPlusAttrExtents)); + memcpy(record, srecord, sizeof(HFSPlusAttrExtents)); + flipAttrExtents((HFSPlusAttrExtents*) record); + removeFromBTree(volume->attrTree, (BTKey*)(&key)); + ret = addToBTree(volume->attrTree, (BTKey*)(&key), sizeof(HFSPlusAttrExtents), (unsigned char *)record); + free(record); + break; + } + + return ret; +} + +size_t getAttribute(Volume* volume, uint32_t fileID, const char* name, uint8_t** data) { + HFSPlusAttrKey key; + HFSPlusAttrRecord* record; + size_t size; + int exact; + + if(!volume->attrTree) + return FALSE; + + memset(&key, 0 , sizeof(HFSPlusAttrKey)); + key.fileID = fileID; + key.startBlock = 0; + ASCIIToUnicode(name, &key.name); + key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); + + *data = NULL; + + record = (HFSPlusAttrRecord*) search(volume->attrTree, (BTKey*)(&key), &exact, NULL, NULL); + + if(exact == FALSE) { + if(record) + free(record); + + return 0; + } + + switch(record->recordType) + { + case kHFSPlusAttrInlineData: + size = record->attrData.size; + *data = (uint8_t*) malloc(size); + memcpy(*data, record->attrData.data, size); + free(record); + return size; + default: + fprintf(stderr, "unsupported attribute node format\n"); + return 0; + } +} + +int setAttribute(Volume* volume, uint32_t fileID, const char* name, uint8_t* data, size_t size) { + HFSPlusAttrKey key; + HFSPlusAttrData* record; + int ret, exact; + + if(!volume->attrTree) + return FALSE; + + memset(&key, 0 , sizeof(HFSPlusAttrKey)); + key.fileID = fileID; + key.startBlock = 0; + ASCIIToUnicode(name, &key.name); + key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); + + record = (HFSPlusAttrData*) malloc(sizeof(HFSPlusAttrData) + size); + memset(record, 0, sizeof(HFSPlusAttrData)); + + record->recordType = kHFSPlusAttrInlineData; + record->size = size; + memcpy(record->data, data, size); + + ret = updateAttributes(volume, &key, (HFSPlusAttrRecord*) record); + + free(record); + return ret; +} + +int unsetAttribute(Volume* volume, uint32_t fileID, const char* name) { + HFSPlusAttrKey key; + + if(!volume->attrTree) + return FALSE; + + memset(&key, 0 , sizeof(HFSPlusAttrKey)); + key.fileID = fileID; + key.startBlock = 0; + ASCIIToUnicode(name, &key.name); + key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); + return removeFromBTree(volume->attrTree, (BTKey*)(&key)); +} + +XAttrList* getAllExtendedAttributes(HFSCatalogNodeID CNID, Volume* volume) { + BTree* tree; + HFSPlusAttrKey key; + HFSPlusAttrRecord* record; + uint32_t nodeNumber; + int recordNumber; + BTNodeDescriptor* descriptor; + HFSPlusAttrKey* currentKey; + off_t recordOffset; + XAttrList* list = NULL; + XAttrList* lastItem = NULL; + XAttrList* item = NULL; + + if(!volume->attrTree) + return NULL; + + memset(&key, 0 , sizeof(HFSPlusAttrKey)); + key.fileID = CNID; + key.startBlock = 0; + key.name.length = 0; + key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); + + tree = volume->attrTree; + record = (HFSPlusAttrRecord*) search(tree, (BTKey*)(&key), NULL, &nodeNumber, &recordNumber); + if(record == NULL) + return NULL; + + free(record); + + while(nodeNumber != 0) { + descriptor = readBTNodeDescriptor(nodeNumber, tree); + + while(recordNumber < descriptor->numRecords) { + recordOffset = getRecordOffset(recordNumber, nodeNumber, tree); + currentKey = (HFSPlusAttrKey*) READ_KEY(tree, recordOffset, tree->io); + + if(currentKey->fileID == CNID) { + item = (XAttrList*) malloc(sizeof(XAttrList)); + item->name = (char*) malloc(currentKey->name.length + 1); + int i; + for(i = 0; i < currentKey->name.length; i++) { + item->name[i] = currentKey->name.unicode[i]; + } + item->name[currentKey->name.length] = '\0'; + item->next = NULL; + + if(lastItem != NULL) { + lastItem->next = item; + } else { + list = item; + } + + lastItem = item; + + free(currentKey); + } else { + free(currentKey); + free(descriptor); + return list; + } + + recordNumber++; + } + + nodeNumber = descriptor->fLink; + recordNumber = 0; + + free(descriptor); + } + return list; +} + +BTree* openAttributesTree(io_func* file) { + return openBTree(file, &attrCompare, &attrKeyRead, &attrKeyWrite, &attrKeyPrint, &attrDataRead); +} + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/abstractfile.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/abstractfile.h new file mode 100644 index 0000000..16bfd90 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/abstractfile.h @@ -0,0 +1,75 @@ +#ifndef ABSTRACTFILE_H +#define ABSTRACTFILE_H + +#include "common.h" +#include + +typedef struct AbstractFile AbstractFile; +typedef struct AbstractFile2 AbstractFile2; + +typedef size_t (*WriteFunc)(AbstractFile* file, const void* data, size_t len); +typedef size_t (*ReadFunc)(AbstractFile* file, void* data, size_t len); +typedef int (*SeekFunc)(AbstractFile* file, off_t offset); +typedef off_t (*TellFunc)(AbstractFile* file); +typedef void (*CloseFunc)(AbstractFile* file); +typedef off_t (*GetLengthFunc)(AbstractFile* file); +typedef void (*SetKeyFunc)(AbstractFile2* file, const unsigned int* key, const unsigned int* iv); + +typedef enum AbstractFileType { + AbstractFileTypeFile, + AbstractFileType8900, + AbstractFileTypeImg2, + AbstractFileTypeImg3, + AbstractFileTypeLZSS, + AbstractFileTypeIBootIM, + AbstractFileTypeMem, + AbstractFileTypeMemFile, + AbstractFileTypeDummy +} AbstractFileType; + +struct AbstractFile { + void* data; + WriteFunc write; + ReadFunc read; + SeekFunc seek; + TellFunc tell; + GetLengthFunc getLength; + CloseFunc close; + AbstractFileType type; +}; + +struct AbstractFile2 { + AbstractFile super; + SetKeyFunc setKey; +}; + + +typedef struct { + size_t offset; + void** buffer; + size_t bufferSize; +} MemWrapperInfo; + +typedef struct { + size_t offset; + void** buffer; + size_t* bufferSize; + size_t actualBufferSize; +} MemFileWrapperInfo; + +#ifdef __cplusplus +extern "C" { +#endif + AbstractFile* createAbstractFileFromFile(FILE* file); + AbstractFile* createAbstractFileFromDummy(); + AbstractFile* createAbstractFileFromMemory(void** buffer, size_t size); + AbstractFile* createAbstractFileFromMemoryFile(void** buffer, size_t* size); + AbstractFile* createAbstractFileFromMemoryFileBuffer(void** buffer, size_t* size, size_t actualBufferSize); + void abstractFilePrint(AbstractFile* file, const char* format, ...); + io_func* IOFuncFromAbstractFile(AbstractFile* file); +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/common.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/common.h new file mode 100644 index 0000000..ae5dbfc --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/common.h @@ -0,0 +1,108 @@ +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#define fseeko fseeko64 +#define ftello ftello64 +#define off_t off64_t +#define mkdir(x, y) mkdir(x) +#define PATH_SEPARATOR "\\" +#else +#define PATH_SEPARATOR "/" +#endif + +#define TRUE 1 +#define FALSE 0 + +#define FLIPENDIAN(x) flipEndian((unsigned char *)(&(x)), sizeof(x)) +#define FLIPENDIANLE(x) flipEndianLE((unsigned char *)(&(x)), sizeof(x)) + +#define IS_BIG_ENDIAN 0 +#define IS_LITTLE_ENDIAN 1 + +#define TIME_OFFSET_FROM_UNIX 2082844800L +#define APPLE_TO_UNIX_TIME(x) ((x) - TIME_OFFSET_FROM_UNIX) +#define UNIX_TO_APPLE_TIME(x) ((x) + TIME_OFFSET_FROM_UNIX) + +#define ASSERT(x, m) if(!(x)) { fflush(stdout); fprintf(stderr, "error: %s\n", m); perror("error"); fflush(stderr); exit(1); } + +extern char endianness; + +static inline void flipEndian(unsigned char* x, int length) { + int i; + unsigned char tmp; + + if(endianness == IS_BIG_ENDIAN) { + return; + } else { + for(i = 0; i < (length / 2); i++) { + tmp = x[i]; + x[i] = x[length - i - 1]; + x[length - i - 1] = tmp; + } + } +} + +static inline void flipEndianLE(unsigned char* x, int length) { + int i; + unsigned char tmp; + + if(endianness == IS_LITTLE_ENDIAN) { + return; + } else { + for(i = 0; i < (length / 2); i++) { + tmp = x[i]; + x[i] = x[length - i - 1]; + x[length - i - 1] = tmp; + } + } +} + +static inline void hexToBytes(const char* hex, uint8_t** buffer, size_t* bytes) { + *bytes = strlen(hex) / 2; + *buffer = (uint8_t*) malloc(*bytes); + size_t i; + for(i = 0; i < *bytes; i++) { + uint32_t byte; + sscanf(hex, "%2x", &byte); + (*buffer)[i] = byte; + hex += 2; + } +} + +static inline void hexToInts(const char* hex, unsigned int** buffer, size_t* bytes) { + *bytes = strlen(hex) / 2; + *buffer = (unsigned int*) malloc((*bytes) * sizeof(int)); + size_t i; + for(i = 0; i < *bytes; i++) { + sscanf(hex, "%2x", &((*buffer)[i])); + hex += 2; + } +} + +struct io_func_struct; + +typedef int (*readFunc)(struct io_func_struct* io, off_t location, size_t size, void *buffer); +typedef int (*writeFunc)(struct io_func_struct* io, off_t location, size_t size, void *buffer); +typedef void (*closeFunc)(struct io_func_struct* io); + +typedef struct io_func_struct { + void* data; + readFunc read; + writeFunc write; + closeFunc close; +} io_func; + +struct AbstractFile; + +unsigned char* decodeBase64(char* toDecode, size_t* dataLength); +void writeBase64(struct AbstractFile* file, unsigned char* data, size_t dataLength, int tabLength, int width); +char* convertBase64(unsigned char* data, size_t dataLength, int tabLength, int width); + +#endif diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmg.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmg.h new file mode 100644 index 0000000..b870004 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmg.h @@ -0,0 +1,342 @@ +#ifndef DMG_H +#define DMG_H + +#include +#include +#include + +#include +#include "abstractfile.h" + +#define CHECKSUM_CRC32 0x00000002 +#define CHECKSUM_MKBLOCK 0x0002 +#define CHECKSUM_NONE 0x0000 + +#define BLOCK_ZLIB 0x80000005 +#define BLOCK_RAW 0x00000001 +#define BLOCK_IGNORE 0x00000002 +#define BLOCK_COMMENT 0x7FFFFFFE +#define BLOCK_TERMINATOR 0xFFFFFFFF + +#define SECTOR_SIZE 512 + +#define DRIVER_DESCRIPTOR_SIGNATURE 0x4552 +#define APPLE_PARTITION_MAP_SIGNATURE 0x504D +#define UDIF_BLOCK_SIGNATURE 0x6D697368 +#define KOLY_SIGNATURE 0x6B6F6C79 +#define HFSX_SIGNATURE 0x4858 + +#define ATTRIBUTE_HDIUTIL 0x0050 + +#define HFSX_VOLUME_TYPE "Apple_HFSX" + +#define DDM_SIZE 0x1 +#define PARTITION_SIZE 0x3f +#define ATAPI_SIZE 0x8 +#define FREE_SIZE 0xa +#define EXTRA_SIZE (ATAPI_OFFSET + ATAPI_SIZE + FREE_SIZE) + +#define DDM_OFFSET 0x0 +#define PARTITION_OFFSET (DDM_SIZE) +#define ATAPI_OFFSET 64 +#define USER_OFFSET (ATAPI_OFFSET + ATAPI_SIZE) + +#define BOOTCODE_DMMY 0x444D4D59 +#define BOOTCODE_GOON 0x676F6F6E + +enum { + kUDIFFlagsFlattened = 1 +}; + +enum { + kUDIFDeviceImageType = 1, + kUDIFPartitionImageType = 2 +}; + +typedef struct { + uint32_t type; + uint32_t size; + uint32_t data[0x20]; +} __attribute__((__packed__)) UDIFChecksum; + +typedef struct { + uint32_t data1; /* smallest */ + uint32_t data2; + uint32_t data3; + uint32_t data4; /* largest */ +} __attribute__((__packed__)) UDIFID; + +typedef struct { + uint32_t fUDIFSignature; + uint32_t fUDIFVersion; + uint32_t fUDIFHeaderSize; + uint32_t fUDIFFlags; + + uint64_t fUDIFRunningDataForkOffset; + uint64_t fUDIFDataForkOffset; + uint64_t fUDIFDataForkLength; + uint64_t fUDIFRsrcForkOffset; + uint64_t fUDIFRsrcForkLength; + + uint32_t fUDIFSegmentNumber; + uint32_t fUDIFSegmentCount; + UDIFID fUDIFSegmentID; /* a 128-bit number like a GUID, but does not seem to be a OSF GUID, since it doesn't have the proper versioning byte */ + + UDIFChecksum fUDIFDataForkChecksum; + + uint64_t fUDIFXMLOffset; + uint64_t fUDIFXMLLength; + + uint8_t reserved1[0x78]; /* this is actually the perfect amount of space to store every thing in this struct until the checksum */ + + UDIFChecksum fUDIFMasterChecksum; + + uint32_t fUDIFImageVariant; + uint64_t fUDIFSectorCount; + + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + +} __attribute__((__packed__)) UDIFResourceFile; + +typedef struct { + uint32_t type; + uint32_t reserved; + uint64_t sectorStart; + uint64_t sectorCount; + uint64_t compOffset; + uint64_t compLength; +} __attribute__((__packed__)) BLKXRun; + +typedef struct { + uint16_t version; /* set to 5 */ + uint32_t isHFS; /* first dword of v53(ImageInfoRec): Set to 1 if it's a HFS or HFS+ partition -- duh. */ + uint32_t unknown1; /* second dword of v53: seems to be garbage if it's HFS+, stuff related to HFS embedded if it's that*/ + uint8_t dataLen; /* length of data that proceeds, comes right before the data in ImageInfoRec. Always set to 0 for HFS, HFS+ */ + uint8_t data[255]; /* other data from v53, dataLen + 1 bytes, the rest NULL filled... a string? Not set for HFS, HFS+ */ + uint32_t unknown2; /* 8 bytes before volumeModified in v53, seems to be always set to 0 for HFS, HFS+ */ + uint32_t unknown3; /* 4 bytes before volumeModified in v53, seems to be always set to 0 for HFS, HFS+ */ + uint32_t volumeModified; /* offset 272 in v53 */ + uint32_t unknown4; /* always seems to be 0 for UDIF */ + uint16_t volumeSignature; /* HX in our case */ + uint16_t sizePresent; /* always set to 1 */ +} __attribute__((__packed__)) SizeResource; + +typedef struct { + uint16_t version; /* set to 1 */ + uint32_t type; /* set to 0x2 for MKBlockChecksum */ + uint32_t checksum; +} __attribute__((__packed__)) CSumResource; + +typedef struct NSizResource { + char isVolume; + unsigned char* sha1Digest; + uint32_t blockChecksum2; + uint32_t bytes; + uint32_t modifyDate; + uint32_t partitionNumber; + uint32_t version; + uint32_t volumeSignature; + struct NSizResource* next; +} NSizResource; + +#define DDM_DESCRIPTOR 0xFFFFFFFF +#define ENTIRE_DEVICE_DESCRIPTOR 0xFFFFFFFE + +typedef struct { + uint32_t fUDIFBlocksSignature; + uint32_t infoVersion; + uint64_t firstSectorNumber; + uint64_t sectorCount; + + uint64_t dataStart; + uint32_t decompressBufferRequested; + uint32_t blocksDescriptor; + + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + uint32_t reserved6; + + UDIFChecksum checksum; + + uint32_t blocksRunCount; + BLKXRun runs[0]; +} __attribute__((__packed__)) BLKXTable; + +typedef struct { + uint32_t ddBlock; + uint16_t ddSize; + uint16_t ddType; +} __attribute__((__packed__)) DriverDescriptor; + +typedef struct { + uint16_t pmSig; + uint16_t pmSigPad; + uint32_t pmMapBlkCnt; + uint32_t pmPyPartStart; + uint32_t pmPartBlkCnt; + unsigned char pmPartName[32]; + unsigned char pmParType[32]; + uint32_t pmLgDataStart; + uint32_t pmDataCnt; + uint32_t pmPartStatus; + uint32_t pmLgBootStart; + uint32_t pmBootSize; + uint32_t pmBootAddr; + uint32_t pmBootAddr2; + uint32_t pmBootEntry; + uint32_t pmBootEntry2; + uint32_t pmBootCksum; + unsigned char pmProcessor[16]; + uint32_t bootCode; + uint16_t pmPad[186]; +} __attribute__((__packed__)) Partition; + +typedef struct { + uint16_t sbSig; + uint16_t sbBlkSize; + uint32_t sbBlkCount; + uint16_t sbDevType; + uint16_t sbDevId; + uint32_t sbData; + uint16_t sbDrvrCount; + uint32_t ddBlock; + uint16_t ddSize; + uint16_t ddType; + DriverDescriptor ddPad[0]; +} __attribute__((__packed__)) DriverDescriptorRecord; + +typedef struct ResourceData { + uint32_t attributes; + unsigned char* data; + size_t dataLength; + int id; + char* name; + struct ResourceData* next; +} ResourceData; + +typedef void (*FlipDataFunc)(unsigned char* data, char out); +typedef void (*ChecksumFunc)(void* ckSum, const unsigned char* data, size_t len); + +typedef struct ResourceKey { + unsigned char* key; + ResourceData* data; + struct ResourceKey* next; + FlipDataFunc flipData; +} ResourceKey; + +#define SHA1_DIGEST_SIZE 20 + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA1_CTX; + +typedef struct { + uint32_t block; + uint32_t crc; + SHA1_CTX sha1; +} ChecksumToken; + +static inline uint32_t readUInt32(AbstractFile* file) { + uint32_t data; + + ASSERT(file->read(file, &data, sizeof(data)) == sizeof(data), "fread"); + FLIPENDIAN(data); + + return data; +} + +static inline void writeUInt32(AbstractFile* file, uint32_t data) { + FLIPENDIAN(data); + ASSERT(file->write(file, &data, sizeof(data)) == sizeof(data), "fwrite"); +} + +static inline uint32_t readUInt64(AbstractFile* file) { + uint64_t data; + + ASSERT(file->read(file, &data, sizeof(data)) == sizeof(data), "fread"); + FLIPENDIAN(data); + + return data; +} + +static inline void writeUInt64(AbstractFile* file, uint64_t data) { + FLIPENDIAN(data); + ASSERT(file->write(file, &data, sizeof(data)) == sizeof(data), "fwrite"); +} + +#ifdef __cplusplus +extern "C" { +#endif + void outResources(AbstractFile* file, AbstractFile* out); + + uint32_t CRC32Checksum(uint32_t* crc, const unsigned char *buf, size_t len); + uint32_t MKBlockChecksum(uint32_t* ckSum, const unsigned char* data, size_t len); + + void BlockSHA1CRC(void* token, const unsigned char* data, size_t len); + void BlockCRC(void* token, const unsigned char* data, size_t len); + void CRCProxy(void* token, const unsigned char* data, size_t len); + + void SHA1Init(SHA1_CTX* context); + void SHA1Update(SHA1_CTX* context, const uint8_t* data, const size_t len); + void SHA1Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX* context); + + void flipUDIFChecksum(UDIFChecksum* o, char out); + void readUDIFChecksum(AbstractFile* file, UDIFChecksum* o); + void writeUDIFChecksum(AbstractFile* file, UDIFChecksum* o); + void readUDIFID(AbstractFile* file, UDIFID* o); + void writeUDIFID(AbstractFile* file, UDIFID* o); + void readUDIFResourceFile(AbstractFile* file, UDIFResourceFile* o); + void writeUDIFResourceFile(AbstractFile* file, UDIFResourceFile* o); + + ResourceKey* readResources(AbstractFile* file, UDIFResourceFile* resourceFile); + void writeResources(AbstractFile* file, ResourceKey* resources); + void releaseResources(ResourceKey* resources); + + NSizResource* readNSiz(ResourceKey* resources); + ResourceKey* writeNSiz(NSizResource* nSiz); + void releaseNSiz(NSizResource* nSiz); + + extern const char* plistHeader; + extern const char* plistFooter; + + ResourceKey* getResourceByKey(ResourceKey* resources, const char* key); + ResourceData* getDataByID(ResourceKey* resource, int id); + ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const char* name, const char* data, size_t dataLength, uint32_t attributes); + ResourceKey* makePlst(); + ResourceKey* makeSize(HFSPlusVolumeHeader* volumeHeader); + + void flipDriverDescriptorRecord(DriverDescriptorRecord* record, char out); + void flipPartition(Partition* partition, char out, unsigned int BlockSize); + void flipPartitionMultiple(Partition* partition, char multiple, char out, unsigned int BlockSize); + + void readDriverDescriptorMap(AbstractFile* file, ResourceKey* resources); + DriverDescriptorRecord* createDriverDescriptorMap(uint32_t numSectors, unsigned int BlockSize); + int writeDriverDescriptorMap(int pNum, AbstractFile* file, DriverDescriptorRecord* DDM, unsigned int BlockSize, ChecksumFunc dataForkChecksum, void* dataForkToken, ResourceKey **resources); + void readApplePartitionMap(AbstractFile* file, ResourceKey* resources, unsigned int BlockSize); + Partition* createApplePartitionMap(uint32_t numSectors, const char* volumeType, unsigned int BlockSize); + int writeApplePartitionMap(int pNum, AbstractFile* file, Partition* partitions, unsigned int BlockSize, ChecksumFunc dataForkChecksum, void* dataForkToken, ResourceKey **resources, NSizResource** nsizIn); + int writeATAPI(int pNum, AbstractFile* file, unsigned int BlockSize, ChecksumFunc dataForkChecksum, void* dataForkToken, ResourceKey **resources, NSizResource** nsizIn); + int writeFreePartition(int pNum, AbstractFile* outFile, uint32_t offset, uint32_t numSectors, ResourceKey** resources); + + void extractBLKX(AbstractFile* in, AbstractFile* out, BLKXTable* blkx); + BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorNumber, uint32_t numSectors, uint32_t blocksDescriptor, + uint32_t checksumType, ChecksumFunc uncompressedChk, void* uncompressedChkToken, ChecksumFunc compressedChk, + void* compressedChkToken, Volume* volume, int addComment); + + + int extractDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, int partNum); + int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize); + int convertToISO(AbstractFile* abstractIn, AbstractFile* abstractOut); + int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmgfile.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmgfile.h new file mode 100644 index 0000000..ed78dc6 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmgfile.h @@ -0,0 +1,21 @@ +/* + * dmgfile.h + * libdmg-hfsplus + * + */ + +#include + +io_func* openDmgFile(AbstractFile* dmg); +io_func* openDmgFilePartition(AbstractFile* dmg, int partition); + +typedef struct DMG { + AbstractFile* dmg; + ResourceKey* resources; + uint32_t numBLKX; + BLKXTable** blkx; + void* runData; + uint64_t runStart; + uint64_t runEnd; + uint64_t offset; +} DMG; diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmglib.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmglib.h new file mode 100644 index 0000000..b1d3f7a --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/dmglib.h @@ -0,0 +1,19 @@ +#ifndef DMGLIB_H +#define DMGLIB_H + +#include +#include "abstractfile.h" + +#ifdef __cplusplus +extern "C" { +#endif + int extractDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, int partNum); + int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize); + + int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut); + int convertToISO(AbstractFile* abstractIn, AbstractFile* abstractOut); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/filevault.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/filevault.h new file mode 100644 index 0000000..42cd0f4 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/dmg/filevault.h @@ -0,0 +1,98 @@ +#ifndef FILEVAULT_H +#define FILEVAULT_H + +#include +#include "dmg.h" + +#ifdef HAVE_CRYPT + +#include +#include + +#define FILEVAULT_CIPHER_KEY_LENGTH 16 +#define FILEVAULT_CIPHER_BLOCKSIZE 16 +#define FILEVAULT_CHUNK_SIZE 4096 +#define FILEVAULT_PBKDF2_ITER_COUNT 1000 +#define FILEVAULT_MSGDGST_LENGTH 20 + +/* + * Information about the FileVault format was yoinked from vfdecrypt, which was written by Ralf-Philipp Weinmann , + * Jacob Appelbaum , and Christian Fromme + */ + +#define FILEVAULT_V2_SIGNATURE 0x656e637263647361ULL + +typedef struct FileVaultV1Header { + uint8_t padding1[48]; + uint32_t kdfIterationCount; + uint32_t kdfSaltLen; + uint8_t kdfSalt[48]; + uint8_t unwrapIV[0x20]; + uint32_t wrappedAESKeyLen; + uint8_t wrappedAESKey[296]; + uint32_t wrappedHMACSHA1KeyLen; + uint8_t wrappedHMACSHA1Key[300]; + uint32_t integrityKeyLen; + uint8_t integrityKey[48]; + uint8_t padding2[484]; +} __attribute__((__packed__)) FileVaultV1Header; + +typedef struct FileVaultV2Header { + uint64_t signature; + uint32_t version; + uint32_t encIVSize; + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + uint32_t unk4; + uint32_t unk5; + UDIFID uuid; + uint32_t blockSize; + uint64_t dataSize; + uint64_t dataOffset; + uint8_t padding[0x260]; + uint32_t kdfAlgorithm; + uint32_t kdfPRNGAlgorithm; + uint32_t kdfIterationCount; + uint32_t kdfSaltLen; + uint8_t kdfSalt[0x20]; + uint32_t blobEncIVSize; + uint8_t blobEncIV[0x20]; + uint32_t blobEncKeyBits; + uint32_t blobEncAlgorithm; + uint32_t blobEncPadding; + uint32_t blobEncMode; + uint32_t encryptedKeyblobSize; + uint8_t encryptedKeyblob[0x30]; +} __attribute__((__packed__)) FileVaultV2Header; + +typedef struct FileVaultInfo { + union { + FileVaultV1Header v1; + FileVaultV2Header v2; + } header; + + uint8_t version; + uint64_t dataOffset; + uint64_t dataSize; + uint32_t blockSize; + + AbstractFile* file; + + HMAC_CTX hmacCTX; + AES_KEY aesKey; + AES_KEY aesEncKey; + + off_t offset; + + uint32_t curChunk; + unsigned char chunk[FILEVAULT_CHUNK_SIZE]; + + char dirty; + char headerDirty; +} FileVaultInfo; +#endif + +AbstractFile* createAbstractFileFromFileVault(AbstractFile* file, const char* key); + +#endif diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfscompress.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfscompress.h new file mode 100644 index 0000000..d4437b1 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfscompress.h @@ -0,0 +1,71 @@ +#ifndef HFSCOMPRESS_H +#define HFSCOMPRESS_H + +#include +#include "common.h" + +#define CMPFS_MAGIC 0x636D7066 + +typedef struct HFSPlusDecmpfs { + uint32_t magic; + uint32_t flags; + uint64_t size; + uint8_t data[0]; +} __attribute__ ((packed)) HFSPlusDecmpfs; + +typedef struct HFSPlusCmpfRsrcHead { + uint32_t headerSize; + uint32_t totalSize; + uint32_t dataSize; + uint32_t flags; +} __attribute__ ((packed)) HFSPlusCmpfRsrcHead; + +typedef struct HFSPlusCmpfRsrcBlock { + uint32_t offset; + uint32_t size; +} __attribute__ ((packed)) HFSPlusCmpfRsrcBlock; + +typedef struct HFSPlusCmpfRsrcBlockHead { + uint32_t dataSize; + uint32_t numBlocks; + HFSPlusCmpfRsrcBlock blocks[0]; +} __attribute__ ((packed)) HFSPlusCmpfRsrcBlockHead; + +typedef struct HFSPlusCmpfEnd { + uint32_t pad[6]; + uint16_t unk1; + uint16_t unk2; + uint16_t unk3; + uint32_t magic; + uint32_t flags; + uint64_t size; + uint32_t unk4; +} __attribute__ ((packed)) HFSPlusCmpfEnd; + +typedef struct HFSPlusCompressed { + Volume* volume; + HFSPlusCatalogFile* file; + io_func* io; + size_t decmpfsSize; + HFSPlusDecmpfs* decmpfs; + + HFSPlusCmpfRsrcHead rsrcHead; + HFSPlusCmpfRsrcBlockHead* blocks; + + int dirty; + + uint8_t* cached; + uint32_t cachedStart; + uint32_t cachedEnd; +} HFSPlusCompressed; + +#ifdef __cplusplus +extern "C" { +#endif + void flipHFSPlusDecmpfs(HFSPlusDecmpfs* compressData); + io_func* openHFSPlusCompressed(Volume* volume, HFSPlusCatalogFile* file); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfslib.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfslib.h new file mode 100644 index 0000000..31da55e --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfslib.h @@ -0,0 +1,25 @@ +#include "common.h" +#include "hfsplus.h" +#include "abstractfile.h" + +#ifdef __cplusplus +extern "C" { +#endif + void writeToFile(HFSPlusCatalogFile* file, AbstractFile* output, Volume* volume); + void writeToHFSFile(HFSPlusCatalogFile* file, AbstractFile* input, Volume* volume); + void get_hfs(Volume* volume, const char* inFileName, AbstractFile* output); + int add_hfs(Volume* volume, AbstractFile* inFile, const char* outFileName); + void grow_hfs(Volume* volume, uint64_t newSize); + void removeAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName); + void addAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName); + void addall_hfs(Volume* volume, const char* dirToMerge, const char* dest); + void extractAllInFolder(HFSCatalogNodeID folderID, Volume* volume); + int copyAcrossVolumes(Volume* volume1, Volume* volume2, char* path1, char* path2); + + void hfs_untar(Volume* volume, AbstractFile* tarFile); + void hfs_ls(Volume* volume, const char* path); + void hfs_setsilence(int s); +#ifdef __cplusplus +} +#endif + diff --git a/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfsplus.h b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfsplus.h new file mode 100644 index 0000000..28cd353 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/emf_decrypter/includes/hfs/hfsplus.h @@ -0,0 +1,586 @@ +#ifndef HFSPLUS_H +#define HFSPLUS_H + +#include +#include +#include + + +#include "common.h" + +#define READ(a, b, c, d) ((*((a)->read))(a, b, c, d)) +#define WRITE(a, b, c, d) ((*((a)->write))(a, b, c, d)) +#define CLOSE(a) ((*((a)->close))(a)) +#define COMPARE(a, b, c) ((*((a)->compare))(b, c)) +#define READ_KEY(a, b, c) ((*((a)->keyRead))(b, c)) +#define WRITE_KEY(a, b, c, d) ((*((a)->keyWrite))(b, c, d)) +#define READ_DATA(a, b, c) ((*((a)->dataRead))(b, c)) + +struct BTKey { + uint16_t keyLength; + unsigned char data[0]; +} __attribute__((__packed__)); + +typedef struct BTKey BTKey; + +typedef BTKey* (*dataReadFunc)(off_t offset, struct io_func_struct* io); +typedef void (*keyPrintFunc)(BTKey* toPrint); +typedef int (*keyWriteFunc)(off_t offset, BTKey* toWrite, struct io_func_struct* io); +typedef int (*compareFunc)(BTKey* left, BTKey* right); + +#define STR_SIZE(str) (sizeof(uint16_t) + (sizeof(uint16_t) * (str).length)) + +#ifndef __HFS_FORMAT__ + +typedef uint32_t HFSCatalogNodeID; + +enum { + kHFSRootParentID = 1, + kHFSRootFolderID = 2, + kHFSExtentsFileID = 3, + kHFSCatalogFileID = 4, + kHFSBadBlockFileID = 5, + kHFSAllocationFileID = 6, + kHFSStartupFileID = 7, + kHFSAttributesFileID = 8, + kHFSRepairCatalogFileID = 14, + kHFSBogusExtentFileID = 15, + kHFSFirstUserCatalogNodeID = 16 +}; + +struct HFSUniStr255 { + uint16_t length; + uint16_t unicode[255]; +} __attribute__((__packed__)); +typedef struct HFSUniStr255 HFSUniStr255; +typedef const HFSUniStr255 *ConstHFSUniStr255Param; + +struct HFSPlusExtentDescriptor { + uint32_t startBlock; + uint32_t blockCount; +} __attribute__((__packed__)); +typedef struct HFSPlusExtentDescriptor HFSPlusExtentDescriptor; + +typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[8]; + +struct HFSPlusForkData { + uint64_t logicalSize; + uint32_t clumpSize; + uint32_t totalBlocks; + HFSPlusExtentRecord extents; +} __attribute__((__packed__)); +typedef struct HFSPlusForkData HFSPlusForkData; + +struct HFSPlusVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t lastMountedVersion; + uint32_t journalInfoBlock; + + uint32_t createDate; + uint32_t modifyDate; + uint32_t backupDate; + uint32_t checkedDate; + + uint32_t fileCount; + uint32_t folderCount; + + uint32_t blockSize; + uint32_t totalBlocks; + uint32_t freeBlocks; + + uint32_t nextAllocation; + uint32_t rsrcClumpSize; + uint32_t dataClumpSize; + HFSCatalogNodeID nextCatalogID; + + uint32_t writeCount; + uint64_t encodingsBitmap; + + uint32_t finderInfo[8]; + + HFSPlusForkData allocationFile; + HFSPlusForkData extentsFile; + HFSPlusForkData catalogFile; + HFSPlusForkData attributesFile; + HFSPlusForkData startupFile; +} __attribute__((__packed__)); +typedef struct HFSPlusVolumeHeader HFSPlusVolumeHeader; + +enum { + kBTLeafNode = -1, + kBTIndexNode = 0, + kBTHeaderNode = 1, + kBTMapNode = 2 +}; + +struct BTNodeDescriptor { + uint32_t fLink; + uint32_t bLink; + int8_t kind; + uint8_t height; + uint16_t numRecords; + uint16_t reserved; +} __attribute__((__packed__)); +typedef struct BTNodeDescriptor BTNodeDescriptor; + +#define kHFSCaseFolding 0xCF +#define kHFSBinaryCompare 0xBC + +struct BTHeaderRec { + uint16_t treeDepth; + uint32_t rootNode; + uint32_t leafRecords; + uint32_t firstLeafNode; + uint32_t lastLeafNode; + uint16_t nodeSize; + uint16_t maxKeyLength; + uint32_t totalNodes; + uint32_t freeNodes; + uint16_t reserved1; + uint32_t clumpSize; // misaligned + uint8_t btreeType; + uint8_t keyCompareType; + uint32_t attributes; // long aligned again + uint32_t reserved3[16]; +} __attribute__((__packed__)); +typedef struct BTHeaderRec BTHeaderRec; + +struct HFSPlusExtentKey { + uint16_t keyLength; + uint8_t forkType; + uint8_t pad; + HFSCatalogNodeID fileID; + uint32_t startBlock; +} __attribute__((__packed__)); +typedef struct HFSPlusExtentKey HFSPlusExtentKey; + +struct HFSPlusCatalogKey { + uint16_t keyLength; + HFSCatalogNodeID parentID; + HFSUniStr255 nodeName; +} __attribute__((__packed__)); +typedef struct HFSPlusCatalogKey HFSPlusCatalogKey; + +#ifndef __MACTYPES__ +struct Point { + int16_t v; + int16_t h; +} __attribute__((__packed__)); +typedef struct Point Point; + +struct Rect { + int16_t top; + int16_t left; + int16_t bottom; + int16_t right; +} __attribute__((__packed__)); +typedef struct Rect Rect; + +/* OSType is a 32-bit value made by packing four 1-byte characters + together. */ +typedef uint32_t FourCharCode; +typedef FourCharCode OSType; + +#endif + +/* Finder flags (finderFlags, fdFlags and frFlags) */ +enum { + kIsOnDesk = 0x0001, /* Files and folders (System 6) */ + kColor = 0x000E, /* Files and folders */ + kIsShared = 0x0040, /* Files only (Applications only) If */ + /* clear, the application needs */ + /* to write to its resource fork, */ + /* and therefore cannot be shared */ + /* on a server */ + kHasNoINITs = 0x0080, /* Files only (Extensions/Control */ + /* Panels only) */ + /* This file contains no INIT resource */ + kHasBeenInited = 0x0100, /* Files only. Clear if the file */ + /* contains desktop database resources */ + /* ('BNDL', 'FREF', 'open', 'kind'...) */ + /* that have not been added yet. Set */ + /* only by the Finder. */ + /* Reserved for folders */ + kHasCustomIcon = 0x0400, /* Files and folders */ + kIsStationery = 0x0800, /* Files only */ + kNameLocked = 0x1000, /* Files and folders */ + kHasBundle = 0x2000, /* Files only */ + kIsInvisible = 0x4000, /* Files and folders */ + kIsAlias = 0x8000 /* Files only */ +}; + +/* Extended flags (extendedFinderFlags, fdXFlags and frXFlags) */ +enum { + kExtendedFlagsAreInvalid = 0x8000, /* The other extended flags */ + /* should be ignored */ + kExtendedFlagHasCustomBadge = 0x0100, /* The file or folder has a */ + /* badge resource */ + kExtendedFlagHasRoutingInfo = 0x0004 /* The file contains routing */ + /* info resource */ +}; + +enum { + kSymLinkFileType = 0x736C6E6B, /* 'slnk' */ + kSymLinkCreator = 0x72686170 /* 'rhap' */ +}; + +struct FileInfo { + OSType fileType; /* The type of the file */ + OSType fileCreator; /* The file's creator */ + uint16_t finderFlags; + Point location; /* File's location in the folder. */ + uint16_t reservedField; +} __attribute__((__packed__)); +typedef struct FileInfo FileInfo; + +struct ExtendedFileInfo { + int16_t reserved1[4]; + uint16_t extendedFinderFlags; + int16_t reserved2; + int32_t putAwayFolderID; +} __attribute__((__packed__)); +typedef struct ExtendedFileInfo ExtendedFileInfo; + +struct FolderInfo { + Rect windowBounds; /* The position and dimension of the */ + /* folder's window */ + uint16_t finderFlags; + Point location; /* Folder's location in the parent */ + /* folder. If set to {0, 0}, the Finder */ + /* will place the item automatically */ + uint16_t reservedField; +} __attribute__((__packed__)); +typedef struct FolderInfo FolderInfo; + +struct ExtendedFolderInfo { + Point scrollPosition; /* Scroll position (for icon views) */ + int32_t reserved1; + uint16_t extendedFinderFlags; + int16_t reserved2; + int32_t putAwayFolderID; +} __attribute__((__packed__)); +typedef struct ExtendedFolderInfo ExtendedFolderInfo; + +#ifndef _STAT_H_ +#ifndef _SYS_STAT_H +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ +#define S_ISTXT 0001000 /* sticky bit */ + +#define S_IRWXU 0000700 /* RWX mask for owner */ +#define S_IRUSR 0000400 /* R for owner */ +#define S_IWUSR 0000200 /* W for owner */ +#define S_IXUSR 0000100 /* X for owner */ + +#define S_IRWXG 0000070 /* RWX mask for group */ +#define S_IRGRP 0000040 /* R for group */ +#define S_IWGRP 0000020 /* W for group */ +#define S_IXGRP 0000010 /* X for group */ + +#define S_IRWXO 0000007 /* RWX mask for other */ +#define S_IROTH 0000004 /* R for other */ +#define S_IWOTH 0000002 /* W for other */ +#define S_IXOTH 0000001 /* X for other */ + +#define S_IFMT 0170000 /* type of file mask */ +#define S_IFIFO 0010000 /* named pipe (fifo) */ +#define S_IFCHR 0020000 /* character special */ +#define S_IFDIR 0040000 /* directory */ +#define S_IFBLK 0060000 /* block special */ +#define S_IFREG 0100000 /* regular */ +#define S_IFLNK 0120000 /* symbolic link */ +#define S_IFSOCK 0140000 /* socket */ +#define S_IFWHT 0160000 /* whiteout */ +#endif +#endif + +#define UF_COMPRESSED 040 + +struct HFSPlusBSDInfo { + uint32_t ownerID; + uint32_t groupID; + uint8_t adminFlags; + uint8_t ownerFlags; + uint16_t fileMode; + union { + uint32_t iNodeNum; + uint32_t linkCount; + uint32_t rawDevice; + } special; +} __attribute__((__packed__)); +typedef struct HFSPlusBSDInfo HFSPlusBSDInfo; + +enum { + kHFSPlusFolderRecord = 0x0001, + kHFSPlusFileRecord = 0x0002, + kHFSPlusFolderThreadRecord = 0x0003, + kHFSPlusFileThreadRecord = 0x0004 +}; + +enum { + kHFSFileLockedBit = 0x0000, /* file is locked and cannot be written to */ + kHFSFileLockedMask = 0x0001, + + kHFSThreadExistsBit = 0x0001, /* a file thread record exists for this file */ + kHFSThreadExistsMask = 0x0002, + + kHFSHasAttributesBit = 0x0002, /* object has extended attributes */ + kHFSHasAttributesMask = 0x0004, + + kHFSHasSecurityBit = 0x0003, /* object has security data (ACLs) */ + kHFSHasSecurityMask = 0x0008, + + kHFSHasFolderCountBit = 0x0004, /* only for HFSX, folder maintains a separate sub-folder count */ + kHFSHasFolderCountMask = 0x0010, /* (sum of folder records and directory hard links) */ + + kHFSHasLinkChainBit = 0x0005, /* has hardlink chain (inode or link) */ + kHFSHasLinkChainMask = 0x0020, + + kHFSHasChildLinkBit = 0x0006, /* folder has a child that's a dir link */ + kHFSHasChildLinkMask = 0x0040 +}; + +struct HFSPlusCatalogFolder { + int16_t recordType; + uint16_t flags; + uint32_t valence; + HFSCatalogNodeID folderID; + uint32_t createDate; + uint32_t contentModDate; + uint32_t attributeModDate; + uint32_t accessDate; + uint32_t backupDate; + HFSPlusBSDInfo permissions; + FolderInfo userInfo; + ExtendedFolderInfo finderInfo; + uint32_t textEncoding; + uint32_t folderCount; +} __attribute__((__packed__)); +typedef struct HFSPlusCatalogFolder HFSPlusCatalogFolder; + +struct HFSPlusCatalogFile { + int16_t recordType; + uint16_t flags; + uint32_t reserved1; + HFSCatalogNodeID fileID; + uint32_t createDate; + uint32_t contentModDate; + uint32_t attributeModDate; + uint32_t accessDate; + uint32_t backupDate; + HFSPlusBSDInfo permissions; + FileInfo userInfo; + ExtendedFileInfo finderInfo; + uint32_t textEncoding; + uint32_t reserved2; + + HFSPlusForkData dataFork; + HFSPlusForkData resourceFork; +} __attribute__((__packed__)); +typedef struct HFSPlusCatalogFile HFSPlusCatalogFile; + +struct HFSPlusCatalogThread { + int16_t recordType; + int16_t reserved; + HFSCatalogNodeID parentID; + HFSUniStr255 nodeName; +} __attribute__((__packed__)); +typedef struct HFSPlusCatalogThread HFSPlusCatalogThread; + +enum { + kHFSPlusAttrInlineData = 0x10, + kHFSPlusAttrForkData = 0x20, + kHFSPlusAttrExtents = 0x30 +}; + +struct HFSPlusAttrForkData { + uint32_t recordType; + uint32_t reserved; + HFSPlusForkData theFork; +} __attribute__((__packed__)); +typedef struct HFSPlusAttrForkData HFSPlusAttrForkData; + +struct HFSPlusAttrExtents { + uint32_t recordType; + uint32_t reserved; + HFSPlusExtentRecord extents; +}; +typedef struct HFSPlusAttrExtents HFSPlusAttrExtents; + +struct HFSPlusAttrData { + uint32_t recordType; + uint32_t reserved[2]; + uint32_t size; + uint8_t data[0]; +} __attribute__((__packed__)); +typedef struct HFSPlusAttrData HFSPlusAttrData; + +union HFSPlusAttrRecord { + uint32_t recordType; + HFSPlusAttrData attrData; + HFSPlusAttrForkData forkData; + HFSPlusAttrExtents overflowExtents; +}; +typedef union HFSPlusAttrRecord HFSPlusAttrRecord; + +struct HFSPlusAttrKey { + uint16_t keyLength; + uint16_t pad; + uint32_t fileID; + uint32_t startBlock; + HFSUniStr255 name; +} __attribute__((__packed__)); +typedef struct HFSPlusAttrKey HFSPlusAttrKey; + +enum { + kHardLinkFileType = 0x686C6E6B, /* 'hlnk' */ + kHFSPlusCreator = 0x6866732B /* 'hfs+' */ +}; + +#endif + +struct HFSPlusCatalogRecord { + int16_t recordType; + unsigned char data[0]; +} __attribute__((__packed__)); +typedef struct HFSPlusCatalogRecord HFSPlusCatalogRecord; + +struct CatalogRecordList { + HFSUniStr255 name; + HFSPlusCatalogRecord* record; + struct CatalogRecordList* next; +}; +typedef struct CatalogRecordList CatalogRecordList; + +struct XAttrList { + char* name; + struct XAttrList* next; +}; +typedef struct XAttrList XAttrList; + +struct Extent { + uint32_t startBlock; + uint32_t blockCount; + struct Extent* next; +}; + +typedef struct Extent Extent; + +typedef struct { + io_func* io; + BTHeaderRec *headerRec; + compareFunc compare; + dataReadFunc keyRead; + keyWriteFunc keyWrite; + keyPrintFunc keyPrint; + dataReadFunc dataRead; +} BTree; + +typedef struct { + io_func* image; + HFSPlusVolumeHeader* volumeHeader; + + BTree* extentsTree; + BTree* catalogTree; + BTree* attrTree; + io_func* allocationFile; + HFSCatalogNodeID metadataDir; +} Volume; + + +typedef struct { + HFSCatalogNodeID id; + HFSPlusCatalogRecord* catalogRecord; + Volume* volume; + HFSPlusForkData* forkData; + Extent* extents; +} RawFile; + +#ifdef __cplusplus +extern "C" { +#endif + void hfs_panic(const char* panicString); + + void printUnicode(HFSUniStr255* str); + char* unicodeToAscii(HFSUniStr255* str); + + BTNodeDescriptor* readBTNodeDescriptor(uint32_t num, BTree* tree); + + BTHeaderRec* readBTHeaderRec(io_func* io); + + BTree* openBTree(io_func* io, compareFunc compare, dataReadFunc keyRead, keyWriteFunc keyWrite, keyPrintFunc keyPrint, dataReadFunc dataRead); + + void closeBTree(BTree* tree); + + off_t getRecordOffset(int num, uint32_t nodeNum, BTree* tree); + + off_t getNodeNumberFromPointerRecord(off_t offset, io_func* io); + + void* search(BTree* tree, BTKey* searchKey, int *exact, uint32_t *nodeNumber, int *recordNumber); + + io_func* openFlatFile(const char* fileName); + io_func* openFlatFileRO(const char* fileName); + + io_func* openRawFile(HFSCatalogNodeID id, HFSPlusForkData* forkData, HFSPlusCatalogRecord* catalogRecord, Volume* volume); + + BTree* openAttributesTree(io_func* file); + size_t getAttribute(Volume* volume, uint32_t fileID, const char* name, uint8_t** data); + int setAttribute(Volume* volume, uint32_t fileID, const char* name, uint8_t* data, size_t size); + int unsetAttribute(Volume* volume, uint32_t fileID, const char* name); + XAttrList* getAllExtendedAttributes(HFSCatalogNodeID CNID, Volume* volume); + + void flipExtentRecord(HFSPlusExtentRecord* extentRecord); + + BTree* openExtentsTree(io_func* file); + + void ASCIIToUnicode(const char* ascii, HFSUniStr255* unistr); + + void flipCatalogFolder(HFSPlusCatalogFolder* record); + void flipCatalogFile(HFSPlusCatalogFile* record); + void flipCatalogThread(HFSPlusCatalogThread* record, int out); + + BTree* openCatalogTree(io_func* file); + int updateCatalog(Volume* volume, HFSPlusCatalogRecord* catalogRecord); + int move(const char* source, const char* dest, Volume* volume); + int removeFile(const char* fileName, Volume* volume); + HFSCatalogNodeID newFolder(const char* pathName, Volume* volume); + HFSCatalogNodeID newFile(const char* pathName, Volume* volume); + int chmodFile(const char* pathName, int mode, Volume* volume); + int chownFile(const char* pathName, uint32_t owner, uint32_t group, Volume* volume); + int makeSymlink(const char* pathName, const char* target, Volume* volume); + + HFSCatalogNodeID getMetadataDirectoryID(Volume* volume); + HFSPlusCatalogRecord* getRecordByCNID(HFSCatalogNodeID CNID, Volume* volume); + HFSPlusCatalogRecord* getLinkTarget(HFSPlusCatalogRecord* record, HFSCatalogNodeID parentID, HFSPlusCatalogKey *key, Volume* volume); + CatalogRecordList* getFolderContents(HFSCatalogNodeID CNID, Volume* volume); + HFSPlusCatalogRecord* getRecordFromPath(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey); + HFSPlusCatalogRecord* getRecordFromPath2(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey, char traverse); + HFSPlusCatalogRecord* getRecordFromPath3(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey, char traverse, char returnLink, HFSCatalogNodeID parentID); + void releaseCatalogRecordList(CatalogRecordList* list); + + int isBlockUsed(Volume* volume, uint32_t block); + int setBlockUsed(Volume* volume, uint32_t block, int used); + int allocate(RawFile* rawFile, off_t size); + + void flipForkData(HFSPlusForkData* forkData); + + Volume* openVolume(io_func* io); + void closeVolume(Volume *volume); + int updateVolume(Volume* volume); + + int debugBTree(BTree* tree, int displayTree); + + int addToBTree(BTree* tree, BTKey* searchKey, size_t length, unsigned char* content); + + int removeFromBTree(BTree* tree, BTKey* searchKey); + + int32_t FastUnicodeCompare ( register uint16_t str1[], register uint16_t length1, + register uint16_t str2[], register uint16_t length2); +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/dump-imessages/iphone-dataprotection/img3fs/Makefile b/dump-imessages/iphone-dataprotection/img3fs/Makefile new file mode 100644 index 0000000..80ab715 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/img3fs/Makefile @@ -0,0 +1,5 @@ +img3fs: img3fs.c + gcc -o $@ $^ -Wall -lfuse_ino64 -lcrypto -I/usr/local/include/osxfuse || gcc -o $@ $^ -Wall -losxfuse_i64 -lcrypto -I/usr/local/include/osxfuse + +clean: + rm img3fs diff --git a/dump-imessages/iphone-dataprotection/img3fs/README b/dump-imessages/iphone-dataprotection/img3fs/README new file mode 100644 index 0000000..e3870ea --- /dev/null +++ b/dump-imessages/iphone-dataprotection/img3fs/README @@ -0,0 +1,12 @@ +FUSE img3 filesystem +read/write/encryption support + +Usage example: + +mkdir /tmp/img3 +img3fs /tmp/img3 038-0032-002.dmg -iv 9b20ae16bebf4cf1b9101374c3ab0095 -key 06849aead2e9a6ca8a82c3929bad5c2368942e3681a3d5751720d2aacf0694c0 +hdiutil attach /tmp/img3/DATA.dmg +rm -rf /Volumes/ramdisk/usr/local/standalone/firmware/* +echo "Hello World!" > /Volumes/ramdisk/hello.txt +hdiutil eject /Volumes/ramdisk +umount /tmp/img3 \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/img3fs/img3fs.c b/dump-imessages/iphone-dataprotection/img3fs/img3fs.c new file mode 100644 index 0000000..beb69bd --- /dev/null +++ b/dump-imessages/iphone-dataprotection/img3fs/img3fs.c @@ -0,0 +1,466 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _FILE_OFFSET_BITS 64 +#define FUSE_USE_VERSION 26 +#include + +#define MAX_IMG3_ELEMENTS 15 + +struct img3_config +{ + char *img3filename; + char *iv; + char *key; +}; + +#define MYFS_OPT(t, p, v) { t, offsetof(struct img3_config, p), v } + +static struct fuse_opt img3_opts[] = { + MYFS_OPT("-iv %s", iv, 0), + MYFS_OPT("-key %s", key, 0), + FUSE_OPT_END +}; + +typedef struct IMG3Header +{ + uint32_t magic; + uint32_t fullSize; + uint32_t sizeNoPack; + uint32_t sigCheckArea; + uint32_t iden; +} __attribute__((packed)) IMG3Header; + +typedef struct IMG3Element +{ + uint32_t magic; + uint32_t total_length; + uint32_t data_length; + uint32_t offset; + char name[10]; +} __attribute__((packed)) IMG3Element; + +typedef struct KBAG +{ + uint32_t cryptState;// 1 if the key and IV in the KBAG are encrypted with the GID-key + // 2 is used with a second KBAG for the S5L8920, use is unknown. + uint32_t aesType; // 0x80 = aes-128, 0xc0 = aes-192, 0x100 = aes256 + char EncIV[16]; + union + { + char EncKey128[16]; + char EncKey192[24]; + char EncKey256[32]; + } key; +} KBAG; + +typedef struct IMG3 +{ + int fd; + uint8_t* mmap; + uint32_t aesType;//0=no aes, 0x80 = aes-128, 0xc0 = aes-192, 0x100 = aes256 + const EVP_CIPHER* cipherType; + uint8_t iv[16]; + uint8_t key[32]; + uint8_t* decrypted_data; + int data_was_modified; + + uint32_t size; + struct IMG3Header header; + uint32_t num_elements; + struct IMG3Element* data_element; + struct IMG3Element elements[MAX_IMG3_ELEMENTS]; +} IMG3; + +void hexToBytes(const char* hex, uint8_t* buffer, size_t bufferLen) { + size_t i; + for(i = 0; i < bufferLen && *hex != '\0'; i++) { + uint32_t byte; + sscanf(hex, "%02x", &byte); + buffer[i] = byte; + hex += 2; + } +} + +/** +Check for magic constants/strings in decrypted data to get an idea if the IV/key were ok +**/ +char* img3_check_decrypted_data(const uint8_t* buffer, uint32_t len) +{ + if (len > 16 && !strncmp("complzss", buffer, 8)) + { + return "kernelcache"; + } + if (len > 0x800 && !strncmp("H+", &buffer[0x400], 2)) + { + return "ramdisk"; + } + if (len > 0x300 && !strncmp("iBoot", &buffer[0x280], 5)) + { + return "bootloader"; + } + //TODO devicetree, logos + return NULL; +} + +IMG3* img3_init(struct img3_config* config) +{ + IMG3* img3 = NULL; + struct stat st; + uint32_t len,offset,i,keylen; + + if(stat(config->img3filename, &st) == -1) + { + perror("stat"); + return NULL; + } + len = st.st_size; + + int fd = open(config->img3filename, O_RDWR); + if (fd == -1) + { + perror("open"); + return NULL; + } + + img3 = malloc(sizeof(IMG3)); + + if (img3 == NULL) + { + perror("malloc"); + return NULL; + } + + img3->fd = fd; + img3->size = len; + img3->num_elements = 0; + img3->aesType = 0; + img3->data_was_modified = 0; + img3->cipherType = NULL; + img3->decrypted_data = NULL; + img3->data_element = NULL; + img3->mmap = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (img3->mmap == (void*) -1) + { + perror("mmap"); + free(img3); + return NULL; + } + + keylen = 0; + if (config->iv != NULL && config->key != NULL) + { + if(strlen(config->iv) != 32) + { + printf("IV must be 16 bytes\n"); + free(img3); + return NULL; + } + keylen = strlen(config->key); + if (keylen != 32 && keylen != 64 && keylen != 48) + { + printf("Key must be 16,24 or 32 bytes\n"); + free(img3); + return NULL; + } + hexToBytes(config->iv, img3->iv, 16); + hexToBytes(config->key, img3->key, 32); + } + + if(read(fd, &img3->header, sizeof(IMG3Header)) != sizeof(IMG3Header)) + { + perror("read IMG3 header"); + free(img3); + return NULL; + } + if(img3->header.magic != 'Img3' || img3->header.fullSize != len) + { + printf("bad magic or len\n"); + free(img3); + return NULL; + } + + for(offset=sizeof(IMG3Header),i=0; offset < len && i < MAX_IMG3_ELEMENTS; i++) + { + if(lseek(fd, offset, SEEK_SET) == -1) + break; + if(read(fd, &img3->elements[i], 12) != 12) + break; + if(img3->elements[i].total_length < 12) + break; + if(img3->elements[i].data_length > img3->elements[i].total_length) + break; + if(offset + img3->elements[i].data_length < offset) + break; + if(offset + img3->elements[i].total_length < offset) + break; + if(offset + img3->elements[i].total_length > len) + break; + img3->elements[i].offset = offset + 12; + img3->elements[i].name[0] = (img3->elements[i].magic & 0xff000000) >> 24; + img3->elements[i].name[1] = (img3->elements[i].magic & 0xff0000) >> 16; + img3->elements[i].name[2] = (img3->elements[i].magic & 0xff00) >> 8; + img3->elements[i].name[3] = (img3->elements[i].magic & 0xff); + img3->elements[i].name[4] = 0; + + printf("TAG: %s OFFSET %x data_length:%x\n", img3->elements[i].name, offset, img3->elements[i].data_length); + + if (img3->elements[i].magic == 'KBAG') + { + KBAG* kbag = (KBAG*) &(img3->mmap[offset+12]); + if(kbag->cryptState == 1) + { + if( kbag->aesType != 0x80 && kbag->aesType != 0xC0 && kbag->aesType != 0x100) + { + printf("Unknown aesType %x\n", kbag->aesType); + } + else if (keylen*4 != kbag->aesType) + { + printf("Wrong length for key parameter, got %d, aesType is %x\n", keylen, kbag->aesType); + free(img3); + return NULL; + } + else + { + printf("KBAG cryptState=%x aesType=%x\n", kbag->cryptState, kbag->aesType); + img3->aesType = kbag->aesType; + } + } + } + else if (img3->elements[i].magic == 'DATA') + { + img3->data_element = &img3->elements[i]; + + if (img3->header.iden == 'rdsk') + { + //XXX hdiutil fails if extension is not .dmg + strcpy(img3->elements[i].name, "DATA.dmg"); + } + } + offset += img3->elements[i].total_length; + } + img3->num_elements = i; + + if(img3->data_element != NULL && img3->aesType != 0) + { + img3->decrypted_data = malloc(img3->data_element->data_length); + if (img3->decrypted_data == NULL) + { + perror("FAIL: malloc(img3->data_element->data_length)"); + free(img3); + return NULL; + } + switch(img3->aesType) + { + case 0x80: + img3->cipherType = EVP_aes_128_cbc(); + break; + case 0xC0: + img3->cipherType = EVP_aes_192_cbc(); + break; + case 0x100: + img3->cipherType = EVP_aes_256_cbc(); + break; + default: + return img3; //should not reach + } + EVP_CIPHER_CTX ctx; + uint32_t decryptedLength = (img3->data_element->total_length - 12) & ~0xf; + printf("Decrypting DATA section\n"); + + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit_ex(&ctx, img3->cipherType, NULL, img3->key, img3->iv); + EVP_DecryptUpdate(&ctx, img3->decrypted_data, &decryptedLength, + &img3->mmap[img3->data_element->offset], decryptedLength); + + char* info = img3_check_decrypted_data(img3->decrypted_data, decryptedLength); + if(info != NULL) + { + printf("Decrypted data seems OK : %s\n", info); + } + else + { + printf("Unknown decrypted data, key/iv might be wrong\n"); + } + } + return img3; +} +static IMG3* img3 = NULL; + +static void img3_destroy() +{ + if (img3->aesType != 0 && img3->decrypted_data != NULL && img3->data_was_modified) + { + EVP_CIPHER_CTX ctx; + uint32_t encryptedLength = img3->data_element->total_length - 12; + //printf("Encrypting DATA section\n"); + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit_ex(&ctx, img3->cipherType, NULL, img3->key, img3->iv); + EVP_EncryptUpdate(&ctx, &img3->mmap[img3->data_element->offset], &encryptedLength, + img3->decrypted_data, encryptedLength); + } +} + +static int +img3_getattr(const char *path, struct stat *stbuf) +{ + int i; + memset(stbuf, 0, sizeof(struct stat)); + + if(!strcmp(path, "/")) + { + stbuf->st_mode = S_IFDIR | 0777; + stbuf->st_nlink = 3; + return 0; + } + for(i=0; i < img3->num_elements; i++) + { + if(!strcmp(path+1, img3->elements[i].name)) + { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = img3->elements[i].data_length; + return 0; + } + } + return -ENOENT; +} + +static int +img3_open(const char *path, struct fuse_file_info *fi) +{ + int i; + for(i=0; i < img3->num_elements; i++) + { + if(!strcmp(path+1, img3->elements[i].name)) + { + fi->fh = i; + return 0; + } + } + return -ENOENT; +} + +static int +img3_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + int i; + if(strcmp(path, "/")) + { + return -ENOENT; + } + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + + for(i=0; i < img3->num_elements; i++) + { + filler(buf, img3->elements[i].name, NULL, 0); + } + return 0; +} + +static int +img3_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + IMG3Element* element = &img3->elements[fi->fh]; + + if (offset >= element->data_length) { + return 0; + } + + if (offset + size > element->data_length) { /* Trim the read to the file size. */ + size = element->data_length - offset; + } + + if (img3->aesType != 0 && element == img3->data_element) + { + memcpy(buf, img3->decrypted_data + offset, size); + return size; + } + lseek(img3->fd, element->offset + offset, SEEK_SET); + return read(img3->fd, buf, size); +} + +static int +img3_write(const char *path, const char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + IMG3Element* element = &img3->elements[fi->fh]; + + if (offset >= element->data_length) { + return 0; + } + + if (offset + size > element->data_length) { /* Trim the write to the file size. */ + size = element->data_length - offset; + } + + if (img3->aesType != 0 && element == img3->data_element) + { + img3->data_was_modified = 1; + memcpy(img3->decrypted_data + offset, buf, size); + return size; + } + + lseek(img3->fd, element->offset + offset, SEEK_SET); + return write(img3->fd, buf, size); +} + + +static struct fuse_operations img3_filesystem_operations = { + .getattr = img3_getattr, + .open = img3_open, + .read = img3_read, + .write = img3_write, + .readdir = img3_readdir, + .destroy = img3_destroy +}; + + +static int img3_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) +{ + static int i = -1; + struct img3_config* config = (struct img3_config*) data; + + i++; + if (key == FUSE_OPT_KEY_NONOPT && i == 1) + { + config->img3filename = strdup(arg); + return 0; + } + return 1; +} + +int main(int argc, char **argv) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct img3_config commandline_conf; + memset(&commandline_conf, 0, sizeof(commandline_conf)); + + fuse_opt_parse(&args, &commandline_conf, img3_opts, img3_opt_proc); + + if (commandline_conf.img3filename == NULL) + { + printf("Usage %s mountpoint img3filename [-key KEY -iv IV]\n", argv[0]); + return -1; + } + img3 = img3_init(&commandline_conf); + if (img3 != NULL) + { + return fuse_main(args.argc, args.argv, &img3_filesystem_operations, img3_opt_proc); + } + return 0; +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/README.txt b/dump-imessages/iphone-dataprotection/python_scripts/README.txt new file mode 100644 index 0000000..b54e296 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/README.txt @@ -0,0 +1,3 @@ +Dependencies + pycrypto (https://www.dlitz.net/software/pycrypto/) + construct (http://construct.wikispaces.com/) \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/backup_tool.py b/dump-imessages/iphone-dataprotection/python_scripts/backup_tool.py new file mode 100644 index 0000000..a21f302 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/backup_tool.py @@ -0,0 +1,78 @@ +from backups.backup3 import decrypt_backup3 +from backups.backup4 import MBDB +from keystore.keybag import Keybag +from util import readPlist, makedirs +import os +import sys +import plistlib + +showinfo = ["Device Name", "Display Name", "Last Backup Date", "IMEI", + "Serial Number", "Product Type", "Product Version", "iTunes Version"] + +def extract_backup(backup_path, output_path, password=""): + if not os.path.exists(backup_path + "/Manifest.plist"): + print "Manifest.plist not found" + return + manifest = readPlist(backup_path + "/Manifest.plist") + + info = readPlist( backup_path + "/Info.plist") + for i in showinfo: + print i + " : " + unicode(info.get(i, "missing")) + + print "Extract backup to %s ? (y/n)" % output_path + if raw_input() == "n": + return + + print "Backup is %sencrypted" % (int(not manifest["IsEncrypted"]) * "not ") + + if manifest["IsEncrypted"] and password == "": + print "Enter backup password : " + password = raw_input() + + if not manifest.has_key("BackupKeyBag"): + print "No BackupKeyBag in manifest, assuming iOS 3.x backup" + decrypt_backup3(backup_path, output_path, password) + else: + mbdb = MBDB(backup_path) + + kb = Keybag.createWithBackupManifest(manifest, password) + if not kb: + return + manifest["password"] = password + makedirs(output_path) + plistlib.writePlist(manifest, output_path + "/Manifest.plist") + + mbdb.keybag = kb + mbdb.extract_backup(output_path) + + print "You can decrypt the keychain using the following command : " + print "python keychain_tool.py -d \"%s\" \"%s\"" % (output_path + "/KeychainDomain/keychain-backup.plist", output_path + "/Manifest.plist") + +def extract_all(): + if sys.platform == "win32": + mobilesync = os.environ["APPDATA"] + "/Apple Computer/MobileSync/Backup/" + elif sys.platform == "darwin": + mobilesync = os.environ["HOME"] + "/Library/Application Support/MobileSync/Backup/" + else: + print "Unsupported operating system" + return + print "-" * 60 + print "Searching for iTunes backups" + print "-" * 60 + + for udid in os.listdir(mobilesync): + extract_backup(mobilesync + "/" + udid, udid + "_extract") + +def main(): + if len(sys.argv) < 2: + print "Usage: %s [output path]" % sys.argv[0] + return + backup_path = sys.argv[1] + output_path = os.path.dirname(backup_path) + "_extract" + if len(sys.argv) >= 3: + output_path = sys.argv[2] + + extract_backup(backup_path, output_path) + +if __name__ == "__main__": + main() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/backups/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/backups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dump-imessages/iphone-dataprotection/python_scripts/backups/backup3.py b/dump-imessages/iphone-dataprotection/python_scripts/backups/backup3.py new file mode 100644 index 0000000..e3d2d1d --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/backups/backup3.py @@ -0,0 +1,67 @@ +from crypto.PBKDF2 import PBKDF2 +from crypto.aes import AESdecryptCBC +from util import read_file, write_file, makedirs, readPlist +from util.bplist import BPlistReader +import hashlib +import struct +import glob +import sys +import os + +""" +decrypt iOS 3 backup blob (metadata and file contents) +""" + +def decrypt_blob(blob, auth_key): + len = struct.unpack(">H", blob[0:2])[0] + if len != 66: + print "blob len != 66" + magic = struct.unpack(">H", blob[2:4])[0] + if magic != 0x0100: + print "magic != 0x0100" + iv = blob[4:20] + + blob_key = AESdecryptCBC(blob[20:68], auth_key, iv)[:32] + + return AESdecryptCBC(blob[68:], blob_key, iv, padding=True) + +def decrypt_backup3(backupfolder, outputfolder, passphrase): + auth_key = None + manifest = readPlist(backupfolder + "/Manifest.plist") + + if manifest["IsEncrypted"]: + manifest_data = manifest["Data"].data + + authdata = manifest["AuthData"].data + + pkbdf_salt = authdata[:8] + iv = authdata[8:24] + key = PBKDF2(passphrase,pkbdf_salt,iterations=2000).read(32) + + data = AESdecryptCBC(authdata[24:], key, iv) + auth_key = data[:32] + + if hashlib.sha1(auth_key).digest() != data[32:52]: + print "wrong auth key (hash mismatch) => wrong passphrase" + return + + print "Passphrase seems OK" + + for mdinfo_name in glob.glob(backupfolder + "/*.mdinfo"): + + mddata_name = mdinfo_name[:-7] + ".mddata" + mdinfo = readPlist(mdinfo_name) + metadata = mdinfo["Metadata"].data + if mdinfo["IsEncrypted"]: + metadata = decrypt_blob(metadata, auth_key) + metadata = BPlistReader.plistWithString(metadata) + + print metadata["Path"] + + filedata = read_file(mddata_name) + if mdinfo["IsEncrypted"]: + filedata = decrypt_blob(filedata, auth_key) + + filename = metadata["Path"] + makedirs(outputfolder + "/" + os.path.dirname(filename)) + write_file(outputfolder + "/" + filename, filedata) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/backups/backup4.py b/dump-imessages/iphone-dataprotection/python_scripts/backups/backup4.py new file mode 100644 index 0000000..cc81c38 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/backups/backup4.py @@ -0,0 +1,174 @@ +from Crypto.Cipher import AES +from hashlib import sha1 +from struct import unpack +import os + +MBDB_SIGNATURE = 'mbdb\x05\x00' + +MASK_SYMBOLIC_LINK = 0xa000 +MASK_REGULAR_FILE = 0x8000 +MASK_DIRECTORY = 0x4000 + +def warn(msg): + print "WARNING: %s" % msg + +class MBFileRecord(object): + def __init__(self, mbdb): + self.domain = self._decode_string(mbdb) + if self.domain is None: + warn("Domain name missing from record") + + self.path = self._decode_string(mbdb) + if self.path is None: + warn("Relative path missing from record") + + self.target= self._decode_string(mbdb) # for symbolic links + + self.digest = self._decode_string(mbdb) + self.encryption_key = self._decode_data(mbdb) + + data = mbdb.read(40) # metadata, fixed size + + self.mode, = unpack('>H', data[0:2]) + if not(self.is_regular_file() or self.is_symbolic_link() or self.is_directory()): + print self.mode + warn("File type mising from record mode") + + if self.is_symbolic_link() and self.target is None: + warn("Target required for symblolic links") + + self.inode_number = unpack('>Q', data[2:10]) + self.user_id, = unpack('>I', data[10:14]) + self.group_id = unpack('>I', data[14:18]) + self.last_modification_time, = unpack('>i', data[18:22]) + self.last_status_change_time, = unpack('>i', data[22:26]) + self.birth_time, = unpack('>i', data[26:30]) + self.size, = unpack('>q', data[30:38]) + + if self.size != 0 and not self.is_regular_file(): + warn("Non-zero size for a record which is not a regular file") + + self.protection_class = ord(data[38]) + + num_attributes = ord(data[39]) + if num_attributes == 0: + self.extended_attributes = None + else: + self.extended_attributes = {} + for i in xrange(num_attributes): + k = self._decode_string(mbdb) + v = self._decode_data(mbdb) + self.extended_attributes[k] = v + + def _decode_string(self, s): + s_len, = unpack('>H', s.read(2)) + if s_len == 0xffff: + return None + return s.read(s_len) + + def _decode_data(self, s): + return self._decode_string(s) + + def type(self): + return self.mode & 0xf000 + + def is_symbolic_link(self): + return self.type() == MASK_SYMBOLIC_LINK + + def is_regular_file(self): + return self.type() == MASK_REGULAR_FILE + + def is_directory(self): + return self.type() == MASK_DIRECTORY + +class MBDB(object): + def __init__(self, path): + self.files = {} + self.backup_path = path + self.keybag = None + # open the database + mbdb = file(path + '/Manifest.mbdb', 'rb') + + # skip signature + signature = mbdb.read(len(MBDB_SIGNATURE)) + if signature != MBDB_SIGNATURE: + raise Exception("Bad mbdb signature") + try: + while True: + rec = MBFileRecord(mbdb) + fn = rec.domain + "-" + rec.path + sb = sha1(fn).digest().encode('hex') + if len(sb) % 2 == 1: + sb = '0'+sb + self.files[sb] = rec + except: + mbdb.close() + + def get_file_by_name(self, filename): + for (k, v) in self.files.iteritems(): + if v.path == filename: + return (k, v) + return None + + def extract_backup(self, output_path): + for record in self.files.values(): + # create directories if they do not exist + # makedirs throw an exception, my code is ugly =) + if record.is_directory(): + try: + os.makedirs(os.path.join(output_path, record.domain, record.path)) + except: + pass + + for (filename, record) in self.files.items(): + # skip directories + if record.is_directory(): + continue + self.extract_file(filename, record, output_path) + + def extract_file(self, filename, record, output_path): + # adjust output file name + if record.is_symbolic_link(): + out_file = record.target + else: + out_file = record.path + + # read backup file + try: + f1 = file(os.path.join(self.backup_path, filename), 'rb') + except(IOError): + warn("File %s (%s) has not been found" % (filename, record.path)) + return + + # write output file + output_path = os.path.join(output_path, record.domain, out_file) + print("Writing %s" % output_path) + f2 = file(output_path, 'wb') + + aes = None + + if record.encryption_key is not None and self.keybag: # file is encrypted! + key = self.keybag.unwrapKeyForClass(record.protection_class, record.encryption_key[4:]) + if not key: + warn("Cannot unwrap key") + return + aes = AES.new(key, AES.MODE_CBC, "\x00"*16) + + while True: + data = f1.read(8192) + if not data: + break + if aes: + data2 = data = aes.decrypt(data) + f2.write(data) + + f1.close() + if aes: + c = data2[-1] + i = ord(c) + if i < 17 and data2.endswith(c*i): + f2.truncate(f2.tell() - i) + else: + warn("Bad padding, last byte = 0x%x !" % i) + + f2.close() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/crypto/PBKDF2.py b/dump-imessages/iphone-dataprotection/python_scripts/crypto/PBKDF2.py new file mode 100644 index 0000000..2ca11d3 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/crypto/PBKDF2.py @@ -0,0 +1,354 @@ +#!/usr/bin/python +# -*- coding: ascii -*- +########################################################################### +# PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation +# +# Copyright (C) 2007, 2008 Dwayne C. Litzenberger +# All rights reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation. +# +# THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Country of origin: Canada +# +########################################################################### +# Sample PBKDF2 usage: +# from Crypto.Cipher import AES +# from PBKDF2 import PBKDF2 +# import os +# +# salt = os.urandom(8) # 64-bit salt +# key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key +# iv = os.urandom(16) # 128-bit IV +# cipher = AES.new(key, AES.MODE_CBC, iv) +# ... +# +# Sample crypt() usage: +# from PBKDF2 import crypt +# pwhash = crypt("secret") +# alleged_pw = raw_input("Enter password: ") +# if pwhash == crypt(alleged_pw, pwhash): +# print "Password good" +# else: +# print "Invalid password" +# +########################################################################### +# History: +# +# 2007-07-27 Dwayne C. Litzenberger +# - Initial Release (v1.0) +# +# 2007-07-31 Dwayne C. Litzenberger +# - Bugfix release (v1.1) +# - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor +# function in the previous release) silently truncates all keys to 64 +# bytes. The way it was used in the previous release, this would only be +# problem if the pseudorandom function that returned values larger than +# 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like +# anything that silently reduces the security margin from what is +# expected. +# +# 2008-06-17 Dwayne C. Litzenberger +# - Compatibility release (v1.2) +# - Add support for older versions of Python (2.2 and 2.3). +# +########################################################################### + +__version__ = "1.2" + +from struct import pack +from binascii import b2a_hex +from random import randint +import string + +try: + # Use PyCrypto (if available) + from Crypto.Hash import HMAC, SHA as SHA1 + +except ImportError: + # PyCrypto not available. Use the Python standard library. + import hmac as HMAC + import sha as SHA1 + +def strxor(a, b): + return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)]) + +def b64encode(data, chars="+/"): + tt = string.maketrans("+/", chars) + return data.encode('base64').replace("\n", "").translate(tt) + +class PBKDF2(object): + """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation + + This implementation takes a passphrase and a salt (and optionally an + iteration count, a digest module, and a MAC module) and provides a + file-like object from which an arbitrarily-sized key can be read. + + If the passphrase and/or salt are unicode objects, they are encoded as + UTF-8 before they are processed. + + The idea behind PBKDF2 is to derive a cryptographic key from a + passphrase and a salt. + + PBKDF2 may also be used as a strong salted password hash. The + 'crypt' function is provided for that purpose. + + Remember: Keys generated using PBKDF2 are only as strong as the + passphrases they are derived from. + """ + + def __init__(self, passphrase, salt, iterations=1000, + digestmodule=SHA1, macmodule=HMAC): + self.__macmodule = macmodule + self.__digestmodule = digestmodule + self._setup(passphrase, salt, iterations, self._pseudorandom) + + def _pseudorandom(self, key, msg): + """Pseudorandom function. e.g. HMAC-SHA1""" + return self.__macmodule.new(key=key, msg=msg, + digestmod=self.__digestmodule).digest() + + def read(self, bytes): + """Read the specified number of key bytes.""" + if self.closed: + raise ValueError("file-like object is closed") + + size = len(self.__buf) + blocks = [self.__buf] + i = self.__blockNum + while size < bytes: + i += 1 + if i > 0xffffffffL or i < 1: + # We could return "" here, but + raise OverflowError("derived key too long") + block = self.__f(i) + blocks.append(block) + size += len(block) + buf = "".join(blocks) + retval = buf[:bytes] + self.__buf = buf[bytes:] + self.__blockNum = i + return retval + + def __f(self, i): + # i must fit within 32 bits + assert 1 <= i <= 0xffffffffL + U = self.__prf(self.__passphrase, self.__salt + pack("!L", i)) + result = U + for j in xrange(2, 1+self.__iterations): + U = self.__prf(self.__passphrase, U) + result = strxor(result, U) + return result + + def hexread(self, octets): + """Read the specified number of octets. Return them as hexadecimal. + + Note that len(obj.hexread(n)) == 2*n. + """ + return b2a_hex(self.read(octets)) + + def _setup(self, passphrase, salt, iterations, prf): + # Sanity checks: + + # passphrase and salt must be str or unicode (in the latter + # case, we convert to UTF-8) + if isinstance(passphrase, unicode): + passphrase = passphrase.encode("UTF-8") + if not isinstance(passphrase, str): + raise TypeError("passphrase must be str or unicode") + if isinstance(salt, unicode): + salt = salt.encode("UTF-8") + if not isinstance(salt, str): + raise TypeError("salt must be str or unicode") + + # iterations must be an integer >= 1 + if not isinstance(iterations, (int, long)): + raise TypeError("iterations must be an integer") + if iterations < 1: + raise ValueError("iterations must be at least 1") + + # prf must be callable + if not callable(prf): + raise TypeError("prf must be callable") + + self.__passphrase = passphrase + self.__salt = salt + self.__iterations = iterations + self.__prf = prf + self.__blockNum = 0 + self.__buf = "" + self.closed = False + + def close(self): + """Close the stream.""" + if not self.closed: + del self.__passphrase + del self.__salt + del self.__iterations + del self.__prf + del self.__blockNum + del self.__buf + self.closed = True + +def crypt(word, salt=None, iterations=None): + """PBKDF2-based unix crypt(3) replacement. + + The number of iterations specified in the salt overrides the 'iterations' + parameter. + + The effective hash length is 192 bits. + """ + + # Generate a (pseudo-)random salt if the user hasn't provided one. + if salt is None: + salt = _makesalt() + + # salt must be a string or the us-ascii subset of unicode + if isinstance(salt, unicode): + salt = salt.encode("us-ascii") + if not isinstance(salt, str): + raise TypeError("salt must be a string") + + # word must be a string or unicode (in the latter case, we convert to UTF-8) + if isinstance(word, unicode): + word = word.encode("UTF-8") + if not isinstance(word, str): + raise TypeError("word must be a string or unicode") + + # Try to extract the real salt and iteration count from the salt + if salt.startswith("$p5k2$"): + (iterations, salt, dummy) = salt.split("$")[2:5] + if iterations == "": + iterations = 400 + else: + converted = int(iterations, 16) + if iterations != "%x" % converted: # lowercase hex, minimum digits + raise ValueError("Invalid salt") + iterations = converted + if not (iterations >= 1): + raise ValueError("Invalid salt") + + # Make sure the salt matches the allowed character set + allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" + for ch in salt: + if ch not in allowed: + raise ValueError("Illegal character %r in salt" % (ch,)) + + if iterations is None or iterations == 400: + iterations = 400 + salt = "$p5k2$$" + salt + else: + salt = "$p5k2$%x$%s" % (iterations, salt) + rawhash = PBKDF2(word, salt, iterations).read(24) + return salt + "$" + b64encode(rawhash, "./") + +# Add crypt as a static method of the PBKDF2 class +# This makes it easier to do "from PBKDF2 import PBKDF2" and still use +# crypt. +PBKDF2.crypt = staticmethod(crypt) + +def _makesalt(): + """Return a 48-bit pseudorandom salt for crypt(). + + This function is not suitable for generating cryptographic secrets. + """ + binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)]) + return b64encode(binarysalt, "./") + +def test_pbkdf2(): + """Module self-test""" + from binascii import a2b_hex + + # + # Test vectors from RFC 3962 + # + + # Test 1 + result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16) + expected = a2b_hex("cdedb5281bb2f801565a1122b2563515") + if result != expected: + raise RuntimeError("self-test failed") + + # Test 2 + result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32) + expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b" + "a7e52ddbc5e5142f708a31e2e62b1e13") + if result != expected: + raise RuntimeError("self-test failed") + + # Test 3 + result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32) + expected = ("139c30c0966bc32ba55fdbf212530ac9" + "c5ec59f1a452f5cc9ad940fea0598ed1") + if result != expected: + raise RuntimeError("self-test failed") + + # Test 4 + result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32) + expected = ("9ccad6d468770cd51b10e6a68721be61" + "1a8b4d282601db3b36be9246915ec82a") + if result != expected: + raise RuntimeError("self-test failed") + + # + # Other test vectors + # + + # Chunked read + f = PBKDF2("kickstart", "workbench", 256) + result = f.read(17) + result += f.read(17) + result += f.read(1) + result += f.read(2) + result += f.read(3) + expected = PBKDF2("kickstart", "workbench", 256).read(40) + if result != expected: + raise RuntimeError("self-test failed") + + # + # crypt() test vectors + # + + # crypt 1 + result = crypt("cloadm", "exec") + expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql' + if result != expected: + raise RuntimeError("self-test failed") + + # crypt 2 + result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....') + expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g' + if result != expected: + raise RuntimeError("self-test failed") + + # crypt 3 + result = crypt("dcl", "tUsch7fU", iterations=13) + expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL" + if result != expected: + raise RuntimeError("self-test failed") + + # crypt 4 (unicode) + result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2', + '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ') + expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ' + if result != expected: + raise RuntimeError("self-test failed") + +if __name__ == '__main__': + test_pbkdf2() + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/dump-imessages/iphone-dataprotection/python_scripts/crypto/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/crypto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dump-imessages/iphone-dataprotection/python_scripts/crypto/aes.py b/dump-imessages/iphone-dataprotection/python_scripts/crypto/aes.py new file mode 100644 index 0000000..93c4074 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/crypto/aes.py @@ -0,0 +1,26 @@ +from Crypto.Cipher import AES + +ZEROIV = "\x00"*16 +def removePadding(blocksize, s): + 'Remove rfc 1423 padding from string.' + n = ord(s[-1]) # last byte contains number of padding bytes + if n > blocksize or n > len(s): + raise Exception('invalid padding') + return s[:-n] + + +def AESdecryptCBC(data, key, iv=ZEROIV, padding=False): + if len(data) % 16: + print "AESdecryptCBC: data length not /16, truncating" + data = data[0:(len(data)/16) * 16] + data = AES.new(key, AES.MODE_CBC, iv).decrypt(data) + if padding: + return removePadding(16, data) + return data + +def AESencryptCBC(data, key, iv=ZEROIV, padding=False): + if len(data) % 16: + print "AESdecryptCBC: data length not /16, truncating" + data = data[0:(len(data)/16) * 16] + data = AES.new(key, AES.MODE_CBC, iv).encrypt(data) + return data diff --git a/dump-imessages/iphone-dataprotection/python_scripts/crypto/aeswrap.py b/dump-imessages/iphone-dataprotection/python_scripts/crypto/aeswrap.py new file mode 100644 index 0000000..75dbb84 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/crypto/aeswrap.py @@ -0,0 +1,70 @@ +import struct +from Crypto.Cipher import AES + +""" + http://www.ietf.org/rfc/rfc3394.txt + quick'n'dirty AES wrap implementation + used by iOS 4 KeyStore kernel extension for wrapping/unwrapping encryption keys +""" +def unpack64bit(s): + return struct.unpack(">Q",s)[0] +def pack64bit(s): + return struct.pack(">Q",s) + +def AESUnwrap(kek, wrapped): + C = [] + for i in xrange(len(wrapped)/8): + C.append(unpack64bit(wrapped[i*8:i*8+8])) + n = len(C) - 1 + R = [0] * (n+1) + A = C[0] + + for i in xrange(1,n+1): + R[i] = C[i] + + for j in reversed(xrange(0,6)): + for i in reversed(xrange(1,n+1)): + todec = pack64bit(A ^ (n*j+i)) + todec += pack64bit(R[i]) + B = AES.new(kek).decrypt(todec) + A = unpack64bit(B[:8]) + R[i] = unpack64bit(B[8:]) + + #assert A == 0xa6a6a6a6a6a6a6a6, "AESUnwrap: integrity check FAIL, wrong kek ?" + if A != 0xa6a6a6a6a6a6a6a6: + #print "AESUnwrap: integrity check FAIL, wrong kek ?" + return None + res = "".join(map(pack64bit, R[1:])) + return res + +def AESwrap(kek, data): + A = 0xa6a6a6a6a6a6a6a6 + R = [0] + for i in xrange(len(data)/8): + R.append(unpack64bit(data[i*8:i*8+8])) + n = len(R) - 1 + + for j in xrange(0,6): + for i in xrange(1,n+1): + B = AES.new(kek).encrypt(pack64bit(A) + pack64bit(R[i])) + A = unpack64bit(B[:8]) ^ (n*j+i) + R[i] = unpack64bit(B[8:]) + + res = pack64bit(A) + "".join(map(pack64bit, R[1:])) + return res + +if __name__ == "__main__": + #format (kek, data, expected_ciphertext) + test_vectors = [ + ("000102030405060708090A0B0C0D0E0F", "00112233445566778899AABBCCDDEEFF", "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5"), + ("000102030405060708090A0B0C0D0E0F1011121314151617", "00112233445566778899AABBCCDDEEFF", "96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D"), + ("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "00112233445566778899AABBCCDDEEFF", "64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7"), + ("000102030405060708090A0B0C0D0E0F1011121314151617", "00112233445566778899AABBCCDDEEFF0001020304050607", "031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2"), + ("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "00112233445566778899AABBCCDDEEFF0001020304050607", "A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1"), + ("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F", "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21") + ] + for kek, data, expected in test_vectors: + ciphertext = AESwrap(kek.decode("hex"), data.decode("hex")) + assert ciphertext == expected.decode("hex") + assert AESUnwrap(kek.decode("hex"), ciphertext) == data.decode("hex") + print "All tests OK !" diff --git a/dump-imessages/iphone-dataprotection/python_scripts/crypto/curve25519.py b/dump-imessages/iphone-dataprotection/python_scripts/crypto/curve25519.py new file mode 100644 index 0000000..00a04e0 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/crypto/curve25519.py @@ -0,0 +1,74 @@ +from Crypto.Util import number + +CURVE_P = (2**255 - 19) +CURVE_A = 121665 + +def curve25519_monty(x1, z1, x2, z2, qmqp): + a = (x1 + z1) * (x2 - z2) % CURVE_P + b = (x1 - z1) * (x2 + z2) % CURVE_P + x4 = (a + b) * (a + b) % CURVE_P + + e = (a - b) * (a - b) % CURVE_P + z4 = e * qmqp % CURVE_P + + a = (x1 + z1) * (x1 + z1) % CURVE_P + b = (x1 - z1) * (x1 - z1) % CURVE_P + x3 = a * b % CURVE_P + + g = (a - b) % CURVE_P + h = (a + CURVE_A * g) % CURVE_P + z3 = (g * h) % CURVE_P + + return x3, z3, x4, z4 + +def curve25519_mult(n, q): + nqpqx, nqpqz = q, 1 + nqx, nqz = 1, 0 + + for i in range(255, -1, -1): + if (n >> i) & 1: + nqpqx,nqpqz,nqx,nqz = curve25519_monty(nqpqx, nqpqz, nqx, nqz, q) + else: + nqx,nqz,nqpqx,nqpqz = curve25519_monty(nqx, nqz, nqpqx, nqpqz, q) + return nqx, nqz + +def curve25519(secret, basepoint): + a = ord(secret[0]) + a &= 248 + b = ord(secret[31]) + b &= 127 + b |= 64 + s = chr(a) + secret[1:-1] + chr(b) + + s = number.bytes_to_long(s[::-1]) + basepoint = number.bytes_to_long(basepoint[::-1]) + + x, z = curve25519_mult(s, basepoint) + zmone = number.inverse(z, CURVE_P) + z = x * zmone % CURVE_P + return number.long_to_bytes(z)[::-1] + + +if __name__ == "__main__": + from crypto.aeswrap import AESUnwrap + from Crypto.Hash import SHA256 + + z="04000000080000000200000048000000000000000000000000000000000000000000000002917dc2542198edeb1078c4d1ebab74d9ca87890657ba02b9825dadf20a002f44360c6f87743fac0236df1f9eedbea801e31677aef3a09adfb4e10a37ae27facf419ab3ea3f39f4".decode("hex") + + mysecret = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14".decode("hex") + mypublic = "b1c652786697a5feef36a56f36fde524a21193f4e563627977ab515f600fdb3a".decode("hex") + hispublic = z[36:36+32] + + #c4d9fe462a2ebbf0745195ce7dc5e8b49947bbd5b42da74175d5f8125b44582b + shared = curve25519(mysecret, hispublic) + print shared.encode("hex") + + h = SHA256.new() + h.update('\x00\x00\x00\x01') + h.update(shared) + h.update(hispublic) + h.update(mypublic) + md = h.digest() + + #e442c81b91ea876d3cf42d3aea75f4b0c3f90f9fd045e1f5784b91260f3bdc9c + print AESUnwrap(md, z[32+36:]).encode("hex") diff --git a/dump-imessages/iphone-dataprotection/python_scripts/crypto/gcm.py b/dump-imessages/iphone-dataprotection/python_scripts/crypto/gcm.py new file mode 100644 index 0000000..0322ea9 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/crypto/gcm.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +from Crypto.Cipher import AES +from Crypto.Util import strxor +from struct import pack, unpack + +def gcm_rightshift(vec): + for x in range(15, 0, -1): + c = vec[x] >> 1 + c |= (vec[x-1] << 7) & 0x80 + vec[x] = c + vec[0] >>= 1 + return vec + +def gcm_gf_mult(a, b): + mask = [ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ] + poly = [ 0x00, 0xe1 ] + + Z = [0] * 16 + V = [c for c in a] + + for x in range(128): + if b[x >> 3] & mask[x & 7]: + Z = [V[y] ^ Z[y] for y in range(16)] + bit = V[15] & 1 + V = gcm_rightshift(V) + V[0] ^= poly[bit] + return Z + +def ghash(h, auth_data, data): + u = (16 - len(data)) % 16 + v = (16 - len(auth_data)) % 16 + + x = auth_data + chr(0) * v + data + chr(0) * u + x += pack('>QQ', len(auth_data) * 8, len(data) * 8) + + y = [0] * 16 + vec_h = [ord(c) for c in h] + + for i in range(0, len(x), 16): + block = [ord(c) for c in x[i:i+16]] + y = [y[j] ^ block[j] for j in range(16)] + y = gcm_gf_mult(y, vec_h) + + return ''.join(chr(c) for c in y) + +def inc32(block): + counter, = unpack('>L', block[12:]) + counter += 1 + return block[:12] + pack('>L', counter) + +def gctr(k, icb, plaintext): + y = '' + if len(plaintext) == 0: + return y + + aes = AES.new(k) + cb = icb + + for i in range(0, len(plaintext), aes.block_size): + cb = inc32(cb) + encrypted = aes.encrypt(cb) + plaintext_block = plaintext[i:i+aes.block_size] + y += strxor.strxor(plaintext_block, encrypted[:len(plaintext_block)]) + + return y + +def gcm_decrypt(k, iv, encrypted, auth_data, tag): + aes = AES.new(k) + h = aes.encrypt(chr(0) * aes.block_size) + + if len(iv) == 12: + y0 = iv + "\x00\x00\x00\x01" + else: + y0 = ghash(h, '', iv) + + decrypted = gctr(k, y0, encrypted) + s = ghash(h, auth_data, encrypted) + + t = aes.encrypt(y0) + T = strxor.strxor(s, t) + if T != tag: + raise ValueError('Decrypted data is invalid') + else: + return decrypted + +def gcm_encrypt(k, iv, plaintext, auth_data): + aes = AES.new(k) + h = aes.encrypt(chr(0) * aes.block_size) + + if len(iv) == 12: + y0 = iv + "\x00\x00\x00\x01" + else: + y0 = ghash(h, '', iv) + + encrypted = gctr(k, y0, plaintext) + s = ghash(h, auth_data, encrypted) + + t = aes.encrypt(y0) + T = strxor.strxor(s, t) + return (encrypted, T) + +def main(): + #http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf + k = 'AD7A2BD03EAC835A6F620FDCB506B345'.decode("hex") + p = '' + a = 'D609B1F056637A0D46DF998D88E5222AB2C2846512153524C0895E8108000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001'.decode("hex") + iv = '12153524C0895E81B2C28465'.decode("hex") + c, t = gcm_encrypt(k, iv, '', a) + assert c == "" + assert t == "f09478a9b09007d06f46e9b6a1da25dd".decode("hex") + + k = 'AD7A2BD03EAC835A6F620FDCB506B345'.decode("hex") + p = '08000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A0002'.decode("hex") + a = 'D609B1F056637A0D46DF998D88E52E00B2C2846512153524C0895E81'.decode("hex") + iv = '12153524C0895E81B2C28465'.decode("hex") + c, t = gcm_encrypt(k, iv, p, a) + assert c == '701AFA1CC039C0D765128A665DAB69243899BF7318CCDC81C9931DA17FBE8EDD7D17CB8B4C26FC81E3284F2B7FBA713D'.decode("hex") + assert t == '4F8D55E7D3F06FD5A13C0C29B9D5B880'.decode("hex") + + key = "91bfb6cbcff07b93a4c68bbfe99ac63b713f0627025c0fb1ffc5b0812dc284f8".decode("hex") + data = "020000000B00000028000000DE44D22E96B1966BAEF4CBEA8675871D40BA669401BD4EBB52AF9C025134187E70549012058456BF0EC0FA1F8FF9F822AC4312AB2141FA712E6D1482358EAC1421A1BFFA81EF38BD0BF2E52675D665EFE3C534E188F575774FAA92E74345575E370B9982661FAE8BD9243B7AD7D2105B275424C0CA1145B9D43AFF04F2747E40D62EC60563960D62A894BE66F267B14D75C0572BE60CC9B339D440FCB418D4F729BBF15C14E0D3A43E4A8B44523D8B3B0F3E7DF85AA67A707EE19CB893277D2392234D7DBC17DA4A0BD7F166189FC54C16C20D287E20FD2FB11BD2CE09ADBDABB95124CD4BFE219E34D3C80E69570A5A506555D7094916C5D75E0065F1796F556EDF0DAA1AA758E0C85AE3951BD363F26B1D43F6CBAEE12D97AD3B60CFA89C1C76BB29F2B54BE31B6CE166F4860C5E5DA92588EF53AA946DF159E60E6F05009D12FB1E37".decode("hex") + ciphertext = data[12+40:-16] + tag = data[-16:] + print repr(gcm_decrypt(key, '', ciphertext, '', tag)) + + +if __name__ == '__main__': + main() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/demo_backup_keychain.py b/dump-imessages/iphone-dataprotection/python_scripts/demo_backup_keychain.py new file mode 100644 index 0000000..abccac7 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/demo_backup_keychain.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python + +import sys, os +from PyQt4 import QtGui, QtCore +from backups.backup4 import MBDB +from keychain.keychain4 import Keychain4 +from util.bplist import BPlistReader +from keystore.keybag import Keybag +from util import readPlist + +class KeychainTreeWidget(QtGui.QTreeWidget): + def __init__(self, parent=None): + QtGui.QTreeWidget.__init__(self, parent) + + self.setGeometry(10, 10, 780, 380) + self.header().hide() + self.setColumnCount(2) + +class KeychainTreeWidgetItem(QtGui.QTreeWidgetItem): + def __init__(self, title): + QtGui.QTreeWidgetItem.__init__(self, [title]) + + fnt = self.font(0) + fnt.setBold(True) + self.setFont(0, fnt) + self.setColors() + + def setText(self, column, title): + QtGui.QTreeWidgetItem.setText(self, column, title) + + def setColors(self): + self.setForeground(0, QtGui.QBrush(QtGui.QColor(80, 80, 80))) + self.setBackground(0, QtGui.QBrush(QtGui.QColor(230, 230, 230))) + self.setBackground(1, QtGui.QBrush(QtGui.QColor(230, 230, 230))) + +class LockedKeychainTreeWidgetItem(KeychainTreeWidgetItem): + def setColors(self): + self.setForeground(0, QtGui.QBrush(QtGui.QColor(255, 80, 80))) + self.setBackground(0, QtGui.QBrush(QtGui.QColor(255, 230, 230))) + self.setBackground(1, QtGui.QBrush(QtGui.QColor(255, 230, 230))) + +class KeychainWindow(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + + self.setGeometry(100, 100, 800, 400) + self.setWindowTitle('Keychain Explorer') + + self.passwordTree = KeychainTreeWidget(parent=self) + + def setGenericPasswords(self, pwds): + self.genericPasswords = pwds + + self.passwordItems = KeychainTreeWidgetItem('Generic Passwords') + + for pwd in self.genericPasswords: + if not pwd.has_key('acct'): + continue + if len(pwd['acct']) > 0: + item_title = '%s (%s)' % (pwd['svce'], pwd['acct']) + else: + item_title = pwd['svce'] + + if pwd['data'] is None: + item = LockedKeychainTreeWidgetItem(item_title) + else: + item = KeychainTreeWidgetItem(item_title) + + item.addChild(QtGui.QTreeWidgetItem(['Service', pwd['svce']])) + item.addChild(QtGui.QTreeWidgetItem(['Account', pwd['acct']])) + if pwd['data'] is not None: + item.addChild(QtGui.QTreeWidgetItem(['Data', pwd['data']])) + else: + item.addChild(QtGui.QTreeWidgetItem(['Data', 'N/A'])) + item.addChild(QtGui.QTreeWidgetItem(['Access Group', pwd['agrp']])) + + self.passwordItems.addChild(item) + + self.passwordTree.addTopLevelItem(self.passwordItems) + + self.passwordTree.expandAll() + self.passwordTree.resizeColumnToContents(0) + + def setInternetPasswords(self, pwds): + self.internetPasswords = pwds + + self.internetPasswordItems = KeychainTreeWidgetItem('Internet Passwords') + + for pwd in pwds: + item_title = '%s (%s)' % (pwd['srvr'], pwd['acct']) + + item = KeychainTreeWidgetItem(item_title) + + item.addChild(QtGui.QTreeWidgetItem(['Server', pwd['srvr']])) + item.addChild(QtGui.QTreeWidgetItem(['Account', pwd['acct']])) + if pwd['data'] is not None: + item.addChild(QtGui.QTreeWidgetItem(['Data', pwd['data']])) + else: + item.addChild(QtGui.QTreeWidgetItem(['Data', 'N/A'])) + + item.addChild(QtGui.QTreeWidgetItem(['Port', str(pwd['port'])])) + item.addChild(QtGui.QTreeWidgetItem(['Access Group', pwd['agrp']])) + + self.internetPasswordItems.addChild(item) + + self.passwordTree.addTopLevelItem(self.internetPasswordItems) + + self.passwordTree.expandAll() + self.passwordTree.resizeColumnToContents(0) + +def warn(msg): + print "WARNING: %s" % msg + +def getBackupKeyBag(backupfolder, passphrase): + manifest = readPlist(backupfolder + "/Manifest.plist") + + kb = Keybag(manifest["BackupKeyBag"].data) + + if kb.unlockBackupKeybagWithPasscode(passphrase): + print "BackupKeyBag unlock OK" + return kb + else: + return None + +def main(): + app = QtGui.QApplication(sys.argv) + init_path = "{0:s}/Apple Computer/MobileSync/Backup".format(os.getenv('APPDATA')) + dirname = QtGui.QFileDialog.getExistingDirectory(None, "Select iTunes backup directory", init_path) + kb = getBackupKeyBag(dirname, 'pouet') #XXX: hardcoded password for demo + if not kb: + warn("Backup keybag unlock fail : wrong passcode?") + return + db = MBDB(dirname) + db.keybag = kb + filename, record = db.get_file_by_name("keychain-backup.plist") + keychain_data = db.read_file(filename, record) + + f = file('keychain.tmp', 'wb') + f.write(keychain_data) + f.close() + + kc = Keychain4('keychain.tmp', kb) + + pwds = kc.get_passwords() + inet_pwds = kc.get_inet_passwords() + + qb = KeychainWindow() + qb.setGenericPasswords(pwds) + qb.setInternetPasswords(inet_pwds) + qb.show() + + sys.exit(app.exec_()) + pass + +if __name__ == '__main__': + main() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/demo_bruteforce.py b/dump-imessages/iphone-dataprotection/python_scripts/demo_bruteforce.py new file mode 100644 index 0000000..19568cb --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/demo_bruteforce.py @@ -0,0 +1,88 @@ +import plistlib +import os +from keystore.keybag import Keybag +from keychain.keychain4 import Keychain4 +from keychain.managedconfiguration import bruteforce_old_pass +from util.ramdiskclient import RamdiskToolClient +from util import write_file + +def bf_system(): + curdir = os.path.dirname(os.path.abspath(__file__)) + client = RamdiskToolClient() + di = client.getDeviceInfos() + devicedir = di["udid"] + if os.getcwd().find(devicedir) == -1: + try: + os.mkdir(devicedir) + except: + pass + os.chdir(devicedir) + key835 = di.get("key835").decode("hex") + + systembag = client.getSystemKeyBag() + kbkeys = systembag["KeyBagKeys"].data + kb = Keybag.createWithDataSignBlob(kbkeys, key835) + keybags = di.setdefault("keybags", {}) + kbuuid = kb.uuid.encode("hex") + print "Keybag UUID :", kbuuid + if True and keybags.has_key(kbuuid) and keybags[kbuuid].has_key("passcodeKey"): + print "We've already seen this keybag" + passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") + print kb.unlockWithPasscodeKey(passcodeKey) + kb.printClassKeys() + else: + keybags[kbuuid] = {"KeyBagKeys": systembag["KeyBagKeys"]} + di["KeyBagKeys"] = systembag["KeyBagKeys"] + di.save() + print "Enter passcode or leave blank for bruteforce:" + z = raw_input() + res = client.getPasscodeKey(systembag["KeyBagKeys"].data, z) + if kb.unlockWithPasscodeKey(res.get("passcodeKey").decode("hex")): + print "Passcode \"%s\" OK" % z + di.update(res) + keybags[kbuuid].update(res) + di.save() + keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") + write_file("keychain-2.db", keychain_blob) + print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" + return + if z != "": + print "Wrong passcode, trying to bruteforce !" + if kb.passcodeComplexity == 0: + print "Trying all 4-digits passcodes..." + bf = client.bruteforceKeyBag(systembag["KeyBagKeys"].data) + if bf: + di.update(bf) + keybags[kbuuid].update(bf) + print bf + print kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")) + kb.printClassKeys() + di["classKeys"] = kb.getClearClassKeysDict() + di.save() + else: + print "Complex passcode used, trying dictionary attack ..." + dictfile = os.path.join(curdir, 'wordlist.dict') + try: + wordlist = open(dictfile, 'r').readlines() + except (OSError, IOError), e: + exit(e) + for line in wordlist: + res = client.getPasscodeKey(systembag["KeyBagKeys"].data, line.rstrip('\n')) + if kb.unlockWithPasscodeKey(res.get("passcodeKey").decode("hex")): + print "Passcode \"%s\" OK" % line.rstrip('\n') + di.update(res) + keybags[kbuuid].update(res) + di.save() + keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") + write_file("keychain-2.db", keychain_blob) + print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" + return + print "Passcode not found!" + return + + #keychain_blob = client.downloadFile("/private/var/Keychains/keychain-2.db") + keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") + write_file("keychain-2.db", keychain_blob) + print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" + +bf_system() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/demo_escrow.py b/dump-imessages/iphone-dataprotection/python_scripts/demo_escrow.py new file mode 100644 index 0000000..a951af9 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/demo_escrow.py @@ -0,0 +1,38 @@ +import os +import plistlib +from keystore.keybag import Keybag +from util.ramdiskclient import RamdiskToolClient + +""" +this wont work on iOS 5 unless the passcode was already bruteforced +""" +def escrow(): + client = RamdiskToolClient() + di = client.getDeviceInfos() + key835 = di.get("key835").decode("hex") + + plist = os.environ["ALLUSERSPROFILE"] + "/Apple/Lockdown/%s.plist" % di["udid"] + lockdown = plistlib.readPlist(plist) + kb = Keybag.createWithDataSignBlob(lockdown["EscrowBag"].data, key835) + + keybags = di.setdefault("keybags", {}) + kbuuid = kb.uuid.encode("hex") + if not keybags.has_key(kbuuid): + print lockdown["HostID"] + res = client.getEscrowRecord(lockdown["HostID"]) + bagkey = res.get("BagKey") + print "Bag key" + bagkey.data.encode("hex") + res = client.getPasscodeKey(lockdown["EscrowBag"].data, bagkey) + print res + passcodeKey = res["passcodeKey"].decode("hex") + keybags[kbuuid] = {"KeyBagKeys": lockdown["EscrowBag"], + "passcode": bagkey, + "passcodeKey": passcodeKey.encode("hex")} + pl.update(keybags[kbuuid]) + else: + passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") + + print kb.unlockWithPasscodeKey(passcodeKey) + kb.printClassKeys() + +escrow() \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/emf_decrypter.py b/dump-imessages/iphone-dataprotection/python_scripts/emf_decrypter.py new file mode 100644 index 0000000..dd9d1b0 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/emf_decrypter.py @@ -0,0 +1,34 @@ +from optparse import OptionParser +from hfs.emf import EMFVolume +from util.bdev import FileBlockDevice +import plistlib + +def main(): + parser = OptionParser(usage="emf_decrypter.py disk_image.bin") + parser.add_option("-w", "--nowrite", dest="write", action="store_false", default=True, + help="disable modifications of input file, for testing") + (options, args) = parser.parse_args() + if len(args) < 1: + parser.print_help() + return + device_infos = None + if len(args) >= 2: device_infos = plistlib.readPlist(args[1]) + + p = FileBlockDevice(args[0], 0, options.write) + v = EMFVolume(p, device_infos) + if not v.keybag.unlocked: + print "Keybag locked, protected files won't be decrypted, continue anyway ?" + if raw_input() == "n": + return + if options.write: + print "WARNING ! This tool will modify the hfs image and possibly wreck it if something goes wrong !" + print "Make sure to backup the image before proceeding" + print "You can use the --nowrite option to do a dry run instead" + else: + print "Test mode : the input file will not be modified" + print "Press a key to continue or CTRL-C to abort" + raw_input() + v.decryptAllFiles() + +if __name__ == "__main__": + main() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/emf_undelete.py b/dump-imessages/iphone-dataprotection/python_scripts/emf_undelete.py new file mode 100644 index 0000000..40cc2bb --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/emf_undelete.py @@ -0,0 +1,25 @@ +import os +import sys +from hfs.emf import EMFVolume +from hfs.journal import do_emf_carving +from util.bdev import FileBlockDevice + +if __name__ == "__main__": + if len(sys.argv) < 2: + print "Usage: emf_undelete.py disk_image.bin" + sys.exit(0) + filename = sys.argv[1] + volume = EMFVolume(FileBlockDevice(filename), None) + dirname = os.path.dirname(filename) + if dirname == "": + dirname = "." + outdir = dirname + "/" + volume.volumeID().encode("hex") + "_" + os.path.basename(filename) + carveokdir = outdir + "/undelete/" + carvenokdir = outdir + "/junk/" + try: + os.makedirs(carveokdir) + os.makedirs(carvenokdir) + except: + pass + + do_emf_carving(volume, carveokdir, carvenokdir) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/firmware/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/firmware/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dump-imessages/iphone-dataprotection/python_scripts/firmware/img2.py b/dump-imessages/iphone-dataprotection/python_scripts/firmware/img2.py new file mode 100644 index 0000000..6908687 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/firmware/img2.py @@ -0,0 +1,12 @@ +from construct.core import Struct +from construct.macros import * + +IMG2 = Struct("IMG2", + String("magic",4), + ULInt32("block_size"), + ULInt32("images_offset"), + ULInt32("images_block"), + ULInt32("images_length"), + Padding(0x1C), + ULInt32("crc32"), + ) \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/firmware/img3.py b/dump-imessages/iphone-dataprotection/python_scripts/firmware/img3.py new file mode 100644 index 0000000..d6f8bd4 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/firmware/img3.py @@ -0,0 +1,292 @@ +from crypto.aes import AESdecryptCBC +from util import read_file, write_file +from util.ramdiskclient import RamdiskToolClient +import M2Crypto +import struct +import hashlib +import os +import sys + +def decryptGID(data): + try: + client = RamdiskToolClient.get() + except: + return None + r = client.aesGID(data) + if r and r.has_key("data"): + return r.data.data + return None + +def decryptPseudoGID(data): + pseudogid = "5F650295E1FFFC97CE77ABD49DD955B3".decode("hex") + return AESdecryptCBC(data, pseudogid, padding=False) + +def dword(s,i): + return struct.unpack(" 16 and data.startswith("complzss"): + return "kernel" + if len(data) > 0x800 and data[0x400:0x400+2] == "H+": + return "ramdisk" + if len(data) > 0x300 and data[0x280:0x285] == "iBoot": + return "bootloader"; + if data.find("serial-number") != -1: + return "devicetree" + if data.startswith("iBootIm"): + return "bootlogo" + + def getRawData(self): + return self.sections["DATA"][:self.datalen] + + def decryptData(self, key=None, iv=None): + if not self.sections.has_key("KBAG"): + return self.getRawData() + + if not key or not iv: + if not self.decryptKBAG(): + return + key = self.key + iv = self.iv + + data = AESdecryptCBC(self.sections["DATA"], key, iv) + x = self.isValidDecryptedData(data) + if not x: + print >> sys.stderr, "%s : Decrypted data seems invalid" % self.shortname + print >> sys.stderr, data[:50].encode("hex") + return False + print "%s : decrypted OK (%s)" % (self.shortname, x) + return data[:self.datalen] + +if __name__ == "__main__": + img3 = Img3(sys.argv[1]) + img3.sigcheck() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/firmware/scfg.py b/dump-imessages/iphone-dataprotection/python_scripts/firmware/scfg.py new file mode 100644 index 0000000..efd27c3 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/firmware/scfg.py @@ -0,0 +1,27 @@ +from construct.core import Struct +from construct.macros import * +from construct import RepeatUntil, OneOf +from util import hexdump + +SCFGItem = Struct("SCFGItem", + String("tag", 4), + String("data", 16, padchar="\x00") + ) + +SCFG = Struct("SCFG", + OneOf(String("magic", 4), ["gfCS"]), + ULInt32("length"), + ULInt32("unk1"), + ULInt32("unk2"), + ULInt32("unk3"), + ULInt32("unk4") + ) + +def parse_SCFG(data): + res = {} + scfg = SCFG.parse(data) + assert scfg.length > 0x18 + for i in Array((scfg.length - 0x18) / 20, SCFGItem).parse(data[0x18:scfg.length]): + if i.tag != "\xFF\xFF\xFF\xFF": + res[str(i.tag)[::-1]] = str(i.data) + return res diff --git a/dump-imessages/iphone-dataprotection/python_scripts/hfs/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/hfs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dump-imessages/iphone-dataprotection/python_scripts/hfs/btree.py b/dump-imessages/iphone-dataprotection/python_scripts/hfs/btree.py new file mode 100644 index 0000000..94d9778 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/hfs/btree.py @@ -0,0 +1,268 @@ +from structs import * + +""" +Probably buggy +HAX, only works on case SENSITIVE +""" + +class BTree(object): + def __init__(self, file, keyStruct, dataStruct): + self.file = file + self.keyStruct = keyStruct + self.dataStruct = dataStruct + block0 = self.file.readBlock(0) + btnode = BTNodeDescriptor.parse(block0) + assert btnode.kind == kBTHeaderNode + self.header = BTHeaderRec.parse(block0[BTNodeDescriptor.sizeof():]) + assert self.header.keyCompareType == 0 or self.header.keyCompareType == 0 or kHFSBinaryCompare + #TODO: do more testing when nodeSize != blockSize + self.nodeSize = self.header.nodeSize + self.nodesInBlock = file.blockSize / self.header.nodeSize + self.blocksForNode = self.header.nodeSize / file.blockSize + #print file.blockSize , self.header.nodeSize + self.lastRecordNumber = 0 + type, (hdr, maprec) = self.readBtreeNode(0) + assert len(maprec) == self.nodeSize - 256 + if self.header.totalNodes / 8 > len(maprec): + pass #TODO: handle map records + self.maprec = maprec + + def isNodeInUse(self, nodeNumber): + thisByte = ord(self.maprec[nodeNumber / 8]) + return (thisByte & (1 << (7 - (nodeNumber % 8)))) != 0 + + def readEmptySpace(self): + res = "" + z = 0 + for i in xrange(self.header.totalNodes): + if not self.isNodeInUse(i): + z += 1 + res += self.readNode(i) + assert z == self.header.freeNodes + return res + + #convert construct structure to tuple + def getComparableKey(self, k): + raise Exception("implement in subclass") + + def compareKeys(self, k1, k2): + k2 = self.getComparableKey(k2) + if k1 == k2: + return 0 + return -1 if k1 < k2 else 1 + + def printLeaf(self, key, data): + print key, data + + def readNode(self, nodeNumber): + node = "" + for i in xrange(self.blocksForNode): + node += self.file.readBlock(nodeNumber * self.blocksForNode + i) + return node + + def readBtreeNode(self, nodeNumber): + self.lastnodeNumber = nodeNumber + node = self.readNode(nodeNumber) + self.lastbtnode = btnode = BTNodeDescriptor.parse(node) + + if btnode.kind == kBTHeaderNode: + assert btnode.numRecords == 3 + end = self.nodeSize - 8 #2*4 + offsets = Array(btnode.numRecords+1, UBInt16("off")).parse(node[end:]) + assert offsets[-4] == end + hdr = BTHeaderRec.parse(node[BTNodeDescriptor.sizeof():]) + maprec = node[offsets[-3]:end] + return kBTHeaderNode, [hdr, maprec] + elif btnode.kind == kBTIndexNode: + recs = [] + offsets = Array(btnode.numRecords, UBInt16("off")).parse(node[-2*btnode.numRecords:]) + for i in xrange(btnode.numRecords): + off = offsets[btnode.numRecords-i-1] + k = self.keyStruct.parse(node[off:]) + off += 2 + k.keyLength + k.childNode = UBInt32("nodeNumber").parse(node[off:off+4]) + recs.append(k) + return kBTIndexNode, recs + elif btnode.kind == kBTLeafNode: + recs = [] + offsets = Array(btnode.numRecords, UBInt16("off")).parse(node[-2*btnode.numRecords:]) + for i in xrange(btnode.numRecords): + off = offsets[btnode.numRecords-i-1] + k = self.keyStruct.parse(node[off:]) + off += 2 + k.keyLength + d = self.dataStruct.parse(node[off:]) + recs.append((k,d)) + return kBTLeafNode, recs + else: + raise Exception("Invalid node type " + str(btnode)) + + def search(self, searchKey, node=None): + if node == None: + node = self.header.rootNode + + type, stuff = self.readBtreeNode(node) + if len(stuff) == 0: + return None, None + + if type == kBTIndexNode: + for i in xrange(len(stuff)): + if self.compareKeys(searchKey, stuff[i]) < 0: + if i > 0: + i = i - 1 + return self.search(searchKey, stuff[i].childNode) + return self.search(searchKey, stuff[len(stuff)-1].childNode) + elif type == kBTLeafNode: + self.lastRecordNumber = 0 + for k,v in stuff: + res = self.compareKeys(searchKey, k) + if res == 0: + return k, v + if res < 0: + return None, None + self.lastRecordNumber += 1 + return None, None + + def traverse(self, node=None, count=0, callback=None): + if node == None: + node = self.header.rootNode + + type, stuff = self.readBtreeNode(node) + + if type == kBTIndexNode: + for i in xrange(len(stuff)): + count += self.traverse(stuff[i].childNode, callback=callback) + elif type == kBTLeafNode: + for k,v in stuff: + if callback: + callback(k,v) + else: + self.printLeaf(k, v) + count += 1 + return count + + def traverseLeafNodes(self, callback=None): + nodeNumber = self.header.firstLeafNode + count = 0 + while nodeNumber != 0: + _, stuff = self.readBtreeNode(nodeNumber) + count += len(stuff) + for k,v in stuff: + if callback: + callback(k,v) + else: + self.printLeaf(k, v) + nodeNumber = self.lastbtnode.fLink + return count + + #XXX + def searchMultiple(self, searchKey, filterKeyFunction=lambda x:False): + self.search(searchKey) + nodeNumber = self.lastnodeNumber + recordNumber = self.lastRecordNumber + kv = [] + while nodeNumber != 0: + _, stuff = self.readBtreeNode(nodeNumber) + for k,v in stuff[recordNumber:]: + if filterKeyFunction(k): + kv.append((k,v)) + else: + return kv + nodeNumber = self.lastbtnode.fLink + recordNumber = 0 + return kv + + def getLBAsHax(self): + nodes = [self.lastnodeNumber] + n = self.lastbtnode + for i in xrange(2): + nodes.append(self.lastbtnode.bLink) + self.readBtreeNode(self.lastbtnode.bLink) + self.lastbtnode = n + for i in xrange(2): + nodes.append(self.lastbtnode.fLink) + self.readBtreeNode(self.lastbtnode.fLink) + res = [] + for n in nodes: + res.append(self.file.getLBAforBlock(n * self.blocksForNode)) + return res + +class CatalogTree(BTree): + def __init__(self, file, volume): + super(CatalogTree,self).__init__(file, HFSPlusCatalogKey, HFSPlusCatalogData) + self.volume = volume + + def printLeaf(self, k, d): + if d.recordType == kHFSPlusFolderRecord or d.recordType == kHFSPlusFileRecord: + print getString(k) + + def getComparableKey(self, k2): + #XXX http://dubeiko.com/development/FileSystems/HFSPLUS/tn1150.html#StringComparisonAlgorithm + return (k2.parentID, getString(k2)) + + def searchByCNID(self, cnid): + threadk, threadd = self.search((cnid, "")) + return self.search((threadd.data.parentID, getString(threadd.data))) if threadd else (None, None) + + def getFolderContents(self, cnid): + return self.searchMultiple((cnid, ""), lambda k:k.parentID == cnid) + + def getRecordFromPath(self, path): + if not path.startswith("/"): + return None, None + if path == "/": + return self.searchByCNID(kHFSRootFolderID) + parentId=kHFSRootFolderID + i = 1 + k, v = None, None + for p in path.split("/")[1:]: + if p == "": + break + k,v = self.search((parentId, p)) + if (k,v) == (None, None): + return None, None + + if v.recordType == kHFSPlusFolderRecord: + parentId = v.data.folderID + elif v.recordType == kHFSPlusFileRecord and is_symlink(v.data): + linkdata = self.volume.readFileByRecord(v) + print "symlink %s => %s" % (p, linkdata) + if not linkdata: + return None, None + t = path.split("/") + t[i] = linkdata + newpath = "/".join(t) + return self.getRecordFromPath(newpath) + else: + break + i += 1 + return k,v + +class ExtentsOverflowTree(BTree): + def __init__(self, file): + super(ExtentsOverflowTree,self).__init__(file, HFSPlusExtentKey, HFSPlusExtentRecord) + + def getComparableKey(self, k2): + return (k2.fileID, k2.forkType, k2.startBlock) + + def searchExtents(self, fileID, forkType, startBlock): + return self.search((fileID, forkType, startBlock)) + +class AttributesTree(BTree): + def __init__(self, file): + super(AttributesTree,self).__init__(file, HFSPlusAttrKey, HFSPlusAttrData) + + def printLeaf(self, k, d): + print k.fileID, getString(k), d.data.encode("hex") + + def getComparableKey(self, k2): + return (k2.fileID, getString(k2)) + + def searchXattr(self, fileID, name): + k,v = self.search((fileID, name)) + return v.data if v else None + + def getAllXattrs(self, fileID): + res = {} + for k,v in self.searchMultiple((fileID, ""), lambda k:k.fileID == fileID): + res[getString(k)] = v.data + return res diff --git a/dump-imessages/iphone-dataprotection/python_scripts/hfs/emf.py b/dump-imessages/iphone-dataprotection/python_scripts/hfs/emf.py new file mode 100644 index 0000000..5eb6561 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/hfs/emf.py @@ -0,0 +1,220 @@ +from construct import Struct, ULInt16, ULInt32, String +from construct.macros import ULInt64, Padding, If +from crypto.aes import AESencryptCBC, AESdecryptCBC +from hfs import HFSVolume, HFSFile +from keystore.keybag import Keybag +from structs import HFSPlusVolumeHeader, kHFSPlusFileRecord, getString, \ + kHFSRootParentID +from util import search_plist +from util.bruteforce import loadKeybagFromVolume +import hashlib +import os +import plistlib +import struct + +""" +iOS >= 4 raw images +http://opensource.apple.com/source/xnu/xnu-1699.22.73/bsd/hfs/hfs_cprotect.c +http://opensource.apple.com/source/xnu/xnu-1699.22.73/bsd/sys/cprotect.h +""" + +cp_root_xattr = Struct("cp_root_xattr", + ULInt16("major_version"), + ULInt16("minor_version"), + ULInt64("flags"), + ULInt32("reserved1"), + ULInt32("reserved2"), + ULInt32("reserved3"), + ULInt32("reserved4") +) + +cprotect_xattr = Struct("cprotect_xattr", + ULInt16("xattr_major_version"), + ULInt16("xattr_minor_version"), + ULInt32("flags"), + ULInt32("persistent_class"), + ULInt32("key_size"), + If(lambda ctx: ctx["xattr_major_version"] >= 4, Padding(20)), + String("persistent_key", length=lambda ctx: ctx["key_size"]) +) +NSProtectionNone = 4 + +PROTECTION_CLASSES={ + 1:"NSFileProtectionComplete", + 2:"NSFileProtectionCompleteUnlessOpen", + 3:"NSFileProtectionCompleteUntilFirstUserAuthentication", + 4:"NSFileProtectionNone", + 5:"NSFileProtectionRecovery?" +} + +#HAX: flags set in finderInfo[3] to tell if the image was already decrypted +FLAG_DECRYPTING = 0x454d4664 #EMFd big endian +FLAG_DECRYPTED = 0x454d4644 #EMFD big endian + +class EMFFile(HFSFile): + def __init__(self, volume, hfsplusfork, fileID, filekey, deleted=False): + super(EMFFile,self).__init__(volume, hfsplusfork, fileID, deleted) + self.filekey = filekey + self.ivkey = None + self.decrypt_offset = 0 + if volume.cp_major_version == 4: + self.ivkey = hashlib.sha1(filekey).digest()[:16] + + def processBlock(self, block, lba): + iv = self.volume.ivForLBA(lba) + ciphertext = AESencryptCBC(block, self.volume.emfkey, iv) + if not self.ivkey: + clear = AESdecryptCBC(ciphertext, self.filekey, iv) + else: + clear = "" + for i in xrange(len(block)/0x1000): + iv = self.volume.ivForLBA(self.decrypt_offset, False) + iv = AESencryptCBC(iv, self.ivkey) + clear += AESdecryptCBC(ciphertext[i*0x1000:(i+1)*0x1000], self.filekey,iv) + self.decrypt_offset += 0x1000 + return clear + + def decryptFile(self): + self.decrypt_offset = 0 + bs = self.volume.blockSize + for extent in self.extents: + for i in xrange(extent.blockCount): + lba = extent.startBlock+i + data = self.volume.readBlock(lba) + if len(data) == bs: + clear = self.processBlock(data, lba) + self.volume.writeBlock(lba, clear) + + +class EMFVolume(HFSVolume): + def __init__(self, bdev, device_infos, **kwargs): + super(EMFVolume,self).__init__(bdev, **kwargs) + volumeid = self.volumeID().encode("hex") + + if not device_infos: + dirname = os.path.dirname(bdev.filename) + device_infos = search_plist(dirname, {"dataVolumeUUID":volumeid}) + if not device_infos: + raise Exception("Missing keyfile") + try: + self.emfkey = None + if device_infos.has_key("EMF"): + self.emfkey = device_infos["EMF"].decode("hex") + self.lbaoffset = device_infos["dataVolumeOffset"] + self.keybag = Keybag.createWithPlist(device_infos) + except: + raise #Exception("Invalid keyfile") + + rootxattr = self.getXattr(kHFSRootParentID, "com.apple.system.cprotect") + self.decrypted = (self.header.finderInfo[3] == FLAG_DECRYPTED) + self.cp_major_version = None + self.cp_root = None + if rootxattr == None: + print "(No root com.apple.system.cprotect xattr)" + else: + self.cp_root = cp_root_xattr.parse(rootxattr) + ver = self.cp_root.major_version + print "cprotect version : %d (iOS %d)" % (ver, 4 + int(ver != 2)) + assert self.cp_root.major_version == 2 or self.cp_root.major_version == 4 + self.cp_major_version = self.cp_root.major_version + self.keybag = loadKeybagFromVolume(self, device_infos) + + def ivForLBA(self, lba, add=True): + iv = "" + if add: + lba = lba + self.lbaoffset + lba &= 0xffffffff + for _ in xrange(4): + if (lba & 1): + lba = 0x80000061 ^ (lba >> 1); + else: + lba = lba >> 1; + iv += struct.pack(" self.logicalSize: + return "BLOCK OUT OF BOUNDS" + "\xFF" * (bs - len("BLOCK OUT OF BOUNDS")) + bc = 0 + for extent in self.extents: + bc += extent.blockCount + if n < bc: + lba = extent.startBlock+(n-(bc-extent.blockCount)) + if not self.deleted and self.fileID != kHFSAllocationFileID and not self.volume.isBlockInUse(lba): + print "FAIL, block %x not marked as used" % n + return self.processBlock(self.volume.readBlock(lba), lba) + return "" + + def getLBAforBlock(self, n): + bc = 0 + for extent in self.extents: + bc += extent.blockCount + if n < bc: + return extent.startBlock+(n-(bc-extent.blockCount)) + + def writeBlock(self, n, data): + bs = self.volume.blockSize + if n*bs > self.logicalSize: + raise Exception("writeBlock, out of bounds %d" % n) + bc = 0 + for extent in self.extents: + bc += extent.blockCount + if n < bc: + lba = extent.startBlock+(n-(bc-extent.blockCount)) + self.volume.writeBlock(lba, data) + return + + +class HFSCompressedResourceFork(HFSFile): + def __init__(self, volume, hfsplusfork, fileID): + super(HFSCompressedResourceFork,self).__init__(volume, hfsplusfork, fileID) + block0 = self.readBlock(0) + self.header = HFSPlusCmpfRsrcHead.parse(block0) + print self.header + self.blocks = HFSPlusCmpfRsrcBlockHead.parse(block0[self.header.headerSize:]) + print "HFSCompressedResourceFork numBlocks:", self.blocks.numBlocks + + #HAX, readblock not implemented + def readAllBuffer(self): + buff = super(HFSCompressedResourceFork, self).readAllBuffer() + r = "" + base = self.header.headerSize + 4 + for b in self.blocks.HFSPlusCmpfRsrcBlock: + r += zlib.decompress(buff[base+b.offset:base+b.offset+b.size]) + return r + +class HFSVolume(object): + def __init__(self, bdev): + self.bdev = bdev + + try: + data = self.bdev.readBlock(0) + self.header = HFSPlusVolumeHeader.parse(data[0x400:0x800]) + assert self.header.signature == 0x4858 or self.header.signature == 0x482B + except: + raise + #raise Exception("Not an HFS+ image") + + self.blockSize = self.header.blockSize + self.bdev.setBlockSize(self.blockSize) + + #if os.path.getsize(filename) < self.header.totalBlocks * self.blockSize: + # print "WARNING: HFS image appears to be truncated" + + self.allocationFile = HFSFile(self, self.header.allocationFile, kHFSAllocationFileID) + self.allocationBitmap = self.allocationFile.readAllBuffer() + self.extentsFile = HFSFile(self, self.header.extentsFile, kHFSExtentsFileID) + self.extentsTree = ExtentsOverflowTree(self.extentsFile) + self.catalogFile = HFSFile(self, self.header.catalogFile, kHFSCatalogFileID) + self.xattrFile = HFSFile(self, self.header.attributesFile, kHFSAttributesFileID) + self.catalogTree = CatalogTree(self.catalogFile, self) + self.xattrTree = AttributesTree(self.xattrFile) + + self.hasJournal = self.header.attributes & (1 << kHFSVolumeJournaledBit) + + def readBlock(self, b): + return self.bdev.readBlock(b) + + def writeBlock(self, lba, data): + return self.bdev.writeBlock(lba, data) + + def volumeID(self): + return struct.pack(">LL", self.header.finderInfo[6], self.header.finderInfo[7]) + + def isBlockInUse(self, block): + thisByte = ord(self.allocationBitmap[block / 8]) + return (thisByte & (1 << (7 - (block % 8)))) != 0 + + def unallocatedBlocks(self): + for i in xrange(self.header.totalBlocks): + if not self.isBlockInUse(i): + yield i, self.read(i*self.blockSize, self.blockSize) + + def getExtentsOverflowForFile(self, fileID, startBlock, forkType=kForkTypeData): + return self.extentsTree.searchExtents(fileID, forkType, startBlock) + + def getXattr(self, fileID, name): + return self.xattrTree.searchXattr(fileID, name) + + def getFileByPath(self, path): + return self.catalogTree.getRecordFromPath(path) + + def getFileIDByPath(self, path): + key, record = self.catalogTree.getRecordFromPath(path) + if not record: + return + if record.recordType == kHFSPlusFolderRecord: + return record.data.folderID + return record.data.fileID + + def listFolderContents(self, path): + k,v = self.catalogTree.getRecordFromPath(path) + if not k or v.recordType != kHFSPlusFolderRecord: + return + for k,v in self.catalogTree.getFolderContents(v.data.folderID): + if v.recordType == kHFSPlusFolderRecord: + #.HFS+ Private Directory Data\r + print v.data.folderID, getString(k).replace("\r","") + "/" + elif v.recordType == kHFSPlusFileRecord: + print v.data.fileID, getString(k) + + def ls(self, path): + k,v = self.catalogTree.getRecordFromPath(path) + return self._ls(k, v) + + def _ls(self, k, v): + res = {} + + if not k or v.recordType != kHFSPlusFolderRecord: + return None + for k,v in self.catalogTree.getFolderContents(v.data.folderID): + if v.recordType == kHFSPlusFolderRecord: + #.HFS+ Private Directory Data\r + res[getString(k).replace("\r","") + "/"] = v.data + elif v.recordType == kHFSPlusFileRecord: + res[getString(k)] = v.data + return res + + def listXattrs(self, path): + k,v = self.catalogTree.getRecordFromPath(path) + if k and v.recordType == kHFSPlusFileRecord: + return self.xattrTree.getAllXattrs(v.data.fileID) + elif k and v.recordType == kHFSPlusFolderThreadRecord: + return self.xattrTree.getAllXattrs(v.data.folderID) + + def readFileByRecord(self, record): + assert record.recordType == kHFSPlusFileRecord + xattr = self.getXattr(record.data.fileID, "com.apple.decmpfs") + data = None + if xattr: + decmpfs = HFSPlusDecmpfs.parse(xattr) + if decmpfs.compression_type == 1: + return xattr[16:] + elif decmpfs.compression_type == 3: + if decmpfs.uncompressed_size == len(xattr) - 16: + return xattr[16:] + return zlib.decompress(xattr[16:]) + elif decmpfs.compression_type == 4: + f = HFSCompressedResourceFork(self, record.data.resourceFork, record.data.fileID) + data = f.readAllBuffer() + return data + + f = HFSFile(self, record.data.dataFork, record.data.fileID) + return f.readAllBuffer() + + #TODO: returnString compress + def readFile(self, path, outFolder="./", returnString=False): + k,v = self.catalogTree.getRecordFromPath(path) + if not v: + print "File %s not found" % path + return + assert v.recordType == kHFSPlusFileRecord + xattr = self.getXattr(v.data.fileID, "com.apple.decmpfs") + if xattr: + decmpfs = HFSPlusDecmpfs.parse(xattr) + + if decmpfs.compression_type == 1: + return xattr[16:] + elif decmpfs.compression_type == 3: + if decmpfs.uncompressed_size == len(xattr) - 16: + z = xattr[16:] + else: + z = zlib.decompress(xattr[16:]) + open(outFolder + os.path.basename(path), "wb").write(z) + return + elif decmpfs.compression_type == 4: + f = HFSCompressedResourceFork(self, v.data.resourceFork, v.data.fileID) + z = f.readAllBuffer() + open(outFolder + os.path.basename(path), "wb").write(z) + return z + + f = HFSFile(self, v.data.dataFork, v.data.fileID) + if returnString: + return f.readAllBuffer() + else: + f.readAll(outFolder + os.path.basename(path)) + + def readJournal(self): + #jb = self.read(self.header.journalInfoBlock * self.blockSize, self.blockSize) + #jib = JournalInfoBlock.parse(jb) + #return self.read(jib.offset,jib.size) + return self.readFile("/.journal", returnString=True) + + def listAllFileIds(self): + self.fileids={} + self.catalogTree.traverseLeafNodes(callback=self.grabFileId) + return self.fileids + + def grabFileId(self, k,v): + if v.recordType == kHFSPlusFileRecord: + self.fileids[v.data.fileID] = True + + def getFileRecordForFileID(self, fileID): + k,v = self.catalogTree.searchByCNID(fileID) + return v + + def getFullPath(self, fileID): + k,v = self.catalogTree.search((fileID, "")) + if not k: + print "File ID %d not found" % fileID + return "" + p = getString(v.data) + while k: + k,v = self.catalogTree.search((v.data.parentID, "")) + if k.parentID == kHFSRootFolderID: + break + p = getString(v.data) + "/" + p + + return "/" + p + + def getFileRecordForPath(self, path): + k,v = self.catalogTree.getRecordFromPath(path) + if not k: + return + return v.data + + def getAllExtents(self, hfsplusfork, fileID): + b = 0 + extents = [] + for extent in hfsplusfork.HFSPlusExtentDescriptor: + extents.append(extent) + b += extent.blockCount + while b != hfsplusfork.totalBlocks: + k,v = self.getExtentsOverflowForFile(fileID, b) + if not v: + print "extents overflow missing, startblock=%d" % b + break + for extent in v: + extents.append(extent) + b += extent.blockCount + return extents + + def dohashFiles(self, k,v): + if v.recordType == kHFSPlusFileRecord: + filename = getString(k) + f = HFSFile(self, v.data.dataFork, v.data.fileID) + print filename, hashlib.sha1(f.readAllBuffer()).hexdigest() + + def hashFiles(self): + self.catalogTree.traverseLeafNodes(callback=self.dohashFiles) + +if __name__ == "__main__": + v = HFSVolume("myramdisk.dmg",offset=0x40) + v.listFolderContents("/") + print v.readFile("/usr/local/share/restore/imeisv_svn.plist") + print v.listXattrs("/usr/local/share/restore/imeisv_svn.plist") diff --git a/dump-imessages/iphone-dataprotection/python_scripts/hfs/journal.py b/dump-imessages/iphone-dataprotection/python_scripts/hfs/journal.py new file mode 100644 index 0000000..164e094 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/hfs/journal.py @@ -0,0 +1,152 @@ +from crypto.aes import AESencryptCBC, AESdecryptCBC +from emf import cprotect_xattr, EMFFile +from structs import * +from util import write_file, sizeof_fmt +import hashlib + +""" +Implementation of the following paper : +Using the HFS+ Journal For Deleted File Recovery. Aaron Burghardt, Adam Feldman. DFRWS 2008 +http://www.dfrws.org/2008/proceedings/p76-burghardt.pdf +http://www.dfrws.org/2008/proceedings/p76-burghardt_pres.pdf +""" + +def carveBtreeNode(node, kClass, dClass): + try: + btnode = BTNodeDescriptor.parse(node) + + if btnode.kind == kBTLeafNode: + off = BTNodeDescriptor.sizeof() + recs = [] + offsets = Array(btnode.numRecords, UBInt16("off")).parse(node[-2*btnode.numRecords:]) + for i in xrange(btnode.numRecords): + off = offsets[btnode.numRecords-i-1] + k = kClass.parse(node[off:]) + off += 2 + k.keyLength + d = dClass.parse(node[off:]) + recs.append((k,d)) + return recs + return [] + except: + return [] + +""" +for standard HFS volumes +""" +def carveHFSVolumeJournal(volume): + journal = volume.readJournal() + hdr = journal_header.parse(journal) + sector_size = hdr.jhdr_size + nodeSize = volume.catalogTree.nodeSize + + f={} + for i in xrange(0,len(journal), sector_size): + for k,v in carveBtreeNode(journal[i:i+nodeSize],HFSPlusCatalogKey, HFSPlusCatalogData): + if v.recordType == kHFSPlusFileRecord: + name = getString(k) + h = hashlib.sha1(HFSPlusCatalogKey.build(k)).digest() + if f.has_key(h): + continue + if volume.catalogTree.searchByCNID(v.data.fileID) == (None, None): + if volume.isBlockInUse(v.data.dataFork.HFSPlusExtentDescriptor[0].startBlock) == False: + print "deleted file", v.data.fileID, name + fileid = v.data.fileID + f[h]=(name, v) + return f.values() + + +magics=["SQLite", "bplist", " 3 and not image.device_infos.has_key("passcode"): + # print "No passcode found in plist file, bruteforce required to access protected data" + + self.carver = None + + def set_partition(self, name, vol): + self.volume = vol + self.do_cd("/") + self.volname = name + self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, self.curdir) + + def do_info(self, p): + pprint(self.device_infos) + + def do_save(self, p): + print "Save device information plist to [%s]:" % self.savepath, + path2 = raw_input() + if path2: self.savepath = path2 + if os.path.exists(self.savepath): + print "File already exists, overwrite ? [y/n]:", + if raw_input() != "y": + return + plistlib.writePlist(self.device_infos, self.savepath) + + def do_system(self, p): + self.set_partition("system", self.system) + + def do_data(self, p): + self.set_partition("data", self.data) + + def do_pix(self, p): + self.do_data("") + self.do_cd("/mobile/Media/DCIM/100APPLE") + + def do_keychain(self, p): + self.data.readFile("/Keychains/keychain-2.db") + keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex")) + keychain.print_all(False) + + def do_keychain_cert(self, p): + t = p.split() + id = int(t[0]) + if len(t) == 2: filename = t[1] + else: filename = "" + keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex")) + keychain.cert(id, filename) + + def do_keychain_key(self, p): + t = p.split() + id = int(t[0]) + if len(t) == 2: filename = t[1] + else: filename = "" + keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex")) + keychain.key(id, filename) + + def do_exit(self, p): + return True + + def do_quit(self, p): + return self.do_exit(p) + + def do_reboot(self, p): + if not self.rdisk: + self.rdisk = RamdiskToolClient.get() + self.rdisk.reboot() + return self.do_exit(p) + + def do_pwd(self, p): + print self.curdir + + def do_cd(self, p): + if len(p) == 0: p = "/" + if not p.startswith("/"): + new = self.curdir + p + else: + new = p + if not p.endswith("/"): new = new + "/" + d = self.volume.ls(new) + if d != None: + self.curdir = new + self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, new) + else: + print "%s not found/is not a directory" % new + + def get_path(self, p): + path = p + if not path.startswith("/"): + path = self.curdir + path + return path + + def _complete(self, text, line, begidx, endidx): + filename = text.split("/")[-1] + dirname = "/".join(text.split("/")[:-1]) + if text.startswith("/"): + contents = self.volume.ls(dirname) + else: + contents = self.volume.ls(self.curdir + dirname) + if not contents: + return [] + if dirname != "" and not dirname.endswith("/"): + dirname += "/" + res = [dirname + x for x in contents.keys() if x.startswith(filename)] + return res + + #TODO if size==0 check if compressed + def do_ls(self, p): + dirDict = self.volume.ls((self.curdir + "/" + p).replace("//","/")) + if not dirDict: + return + for name in sorted(dirDict.keys()): + size = "" + protection_class = "" + record = dirDict[name] + if hasattr(record, "fileID"): + size = sizeof_fmt(record.dataFork.logicalSize) + cprotect = self.volume.getXattr(record.fileID, "com.apple.system.cprotect") + if cprotect: + protection_class = PROTECTION_CLASSES[struct.unpack(" %s" % (cp.persistent_class, PROTECTION_CLASSES.get(cp.persistent_class)) + if not cp.persistent_key: + return + fk = self.volume.getFileKeyForCprotect(cprotect) + if fk: + print "Unwrapped file key : %s" % fk.encode("hex") + else: + print "Cannot decrypt file key" + + + def do_open(self, p): + path = self.get_path(p) + if self.volume.readFile(path): + os.startfile(os.path.basename(path)) + + def do_xxd(self, p): + t = p.split() + path = self.get_path(t[0]) + data = self.volume.readFile(path, returnString=True) + if not data: + return + if len(t) > 1: + hexdump(data[:int(t[1])]) + else: + hexdump(data) + + def do_effaceable(self, p): + print "Effaceable Lockers" + for k,v in self.image.lockers.lockers.items(): + print "%s: %s" % (k, v.encode("hex")) + + def do_BAG1(self, p): + print "BAG1 locker from effaceable storage" + bag1 = self.image.lockers.get("BAG1") + hexdump(bag1) + print "IV:", bag1[4:20].encode("hex") + print "Key:", bag1[20:].encode("hex") + + def do_keybag(self, p): + self.data.keybag.printClassKeys() + + def do_plist(self, p): + d = None + data = self.volume.readFile(self.get_path(p), returnString=True) + if data: + d = parsePlist(data) + pprint(d) + else: + try: + d = readPlist(p) + if d: pprint(d) + except: + pass + if d and d.has_key("_MKBIV"): + print "=>System keybag file" + print "_MKBPAYLOAD: encrypted" + print "_MKBIV: %s" % d["_MKBIV"].data.encode("hex") + print "_MKBWIPEID: 0x%x (%s)" % (d["_MKBWIPEID"], ("%x"%(d["_MKBWIPEID"])).decode("hex")) + + def do_bruteforce(self, p): + if bruteforcePasscode(self.image.device_infos, self.data): + print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) * "un") + self.do_save("") + + def do_ptable(self, p): + pt = self.image.getPartitionTable() + print "Block device partition table" + print "".join(map(lambda x:x.ljust(12), ["Index", "Name", "Start LBA", "End LBA", "Size"])) + for i in xrange(len(pt)): + p = pt[i] + print "".join(map(lambda x:str(x).ljust(12), [i, p.name, p.first_lba, p.last_lba, sizeof_fmt((p.last_lba - p.first_lba)*self.image.pageSize)])) + + def do_nand_dump(self, p): + if len(p)==0: + print "Usage: nand_dump my_nand.bin" + return + self.image.dump(p) + + def do_dd(self, p): + if len(p)==0: + print "Usage: dd output_file.dmg" + return + self.volume.bdev.dumpToFile(p.split()[0]) + + def do_img3(self, p): + self.image.extract_img3s() + + def do_shsh(self, p): + self.image.extract_shsh() + + def do_debug(self,p): + from IPython.Shell import IPShellEmbed + ipshell = IPShellEmbed() + ipshell(local_ns=locals()) + +def main(): + parser = OptionParser(usage="%prog [options] nand_image.bin device_infos.plist") + (options, args) = parser.parse_args() + + if len(args) >= 2: + plistname = args[1] + nandimagename = args[0] + device_infos = plistlib.readPlist(plistname) + print "Loading device information from %s" % plistname + else: + nandimagename ="remote" + client = RamdiskToolClient.get() + device_infos = client.device_infos + print_device_infos(device_infos) + image = NAND(nandimagename, device_infos) + + ExaminerShell(image).cmdloop("") + +if __name__ == "__main__": + main() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/kernel_patcher.py b/dump-imessages/iphone-dataprotection/python_scripts/kernel_patcher.py new file mode 100755 index 0000000..ca1d875 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/kernel_patcher.py @@ -0,0 +1,224 @@ +#!/usr/bin/python + +import plistlib +import zipfile +import struct +import sys +from optparse import OptionParser +from Crypto.Cipher import AES +from util.lzss import decompress_lzss + +devices = {"n82ap": "iPhone1,2", + "n88ap": "iPhone2,1", + "n90ap": "iPhone3,1", + "n90bap": "iPhone3,2", + "n92ap": "iPhone3,3", + "n18ap": "iPod3,1", + "n81ap": "iPod4,1", + "k48ap": "iPad1,1", + "n72ap": "iPod2,1", + } + +h=lambda x:x.replace(" ","").decode("hex") + +#thx to 0x56 +patchs_ios6 = { + "IOAESAccelerator enable UID" : (h("B0 F5 FA 6F 00 F0 92 80"), h("B0 F5 FA 6F 00 20 00 20")), + "_PE_i_can_has_debugger" : (h("80 B1 43 F2 BE 01 C0 F2"), h("01 20 70 47 BE 01 C0 F2")), +} + +#https://github.com/comex/datautils0/blob/master/make_kernel_patchfile.c +patchs_ios5 = { + "CSED" : (h("df f8 88 33 1d ee 90 0f a2 6a 1b 68"), h("df f8 88 33 1d ee 90 0f a2 6a 01 23")), + "AMFI" : (h("D0 47 01 21 40 B1 13 35"), h("00 20 01 21 40 B1 13 35")), + "_PE_i_can_has_debugger" : (h("38 B1 05 49 09 68 00 29"), h("01 20 70 47 09 68 00 29")), + "task_for_pid_0" : (h("00 21 02 91 ba f1 00 0f 01 91 06 d1 02 a8"), h("00 21 02 91 ba f1 00 0f 01 91 06 e0 02 a8")), + "IOAESAccelerator enable UID" : (h("67 D0 40 F6"), h("00 20 40 F6")), + #not stritcly required, useful for testing + "getxattr system": ("com.apple.system.\x00", "com.apple.aaaaaa.\x00"), + "IOAES gid": (h("40 46 D4 F8 54 43 A0 47"), h("40 46 D4 F8 43 A0 00 20")), + #HAX to fit into the 40 char boot-args (redsn0w 0.9.10) + "nand-disable-driver": ("nand-disable-driver\x00", "nand-disable\x00\x00\x00\x00\x00\x00\x00\x00") +} + +patchs_ios4 = { + "NAND_epoch" : ("\x90\x47\x83\x45", "\x90\x47\x00\x20"), + "CSED" : ("\x00\x00\x00\x00\x01\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00", "\x01\x00\x00\x00\x01\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00"), + "AMFI" : ("\x01\xD1\x01\x30\x04\xE0\x02\xDB", "\x00\x20\x01\x30\x04\xE0\x02\xDB"), + "_PE_i_can_has_debugger" : (h("48 B1 06 4A 13 68 13 B9"), h("01 20 70 47 13 68 13 B9")), + "IOAESAccelerator enable UID" : ("\x56\xD0\x40\xF6", "\x00\x00\x40\xF6"), + "getxattr system": ("com.apple.system.\x00", "com.apple.aaaaaa.\x00"), +} + +patchs_armv6 = { + "NAND_epoch" : (h("00 00 5B E1 0E 00 00 0A"), h("00 00 5B E1 0E 00 00 EA")), + "CSED" : (h("00 00 00 00 01 00 00 00 80 00 00 00 00 00 00 00"), h("01 00 00 00 01 00 00 00 80 00 00 00 00 00 00 00")), + "AMFI" : (h("00 00 00 0A 00 40 A0 E3 04 00 A0 E1 90 80 BD E8"), h("00 00 00 0A 00 40 A0 E3 01 00 A0 E3 90 80 BD E8")), + "_PE_i_can_has_debugger" : (h("00 28 0B D0 07 4A 13 68 00 2B 02 D1 03 60 10 68"), h("01 20 70 47 07 4A 13 68 00 2B 02 D1 03 60 10 68")), + "IOAESAccelerator enable UID" : (h("5D D0 36 4B 9A 42"), h("00 20 36 4B 9A 42")), + "IOAES gid": (h("FA 23 9B 00 9A 42 05 D1"), h("00 20 00 20 9A 42 05 D1")), + "nand-disable-driver": ("nand-disable-driver\x00", "nand-disable\x00\x00\x00\x00\x00\x00\x00\x00"), +} +patchs_ios4_fixnand = { + "Please reboot => jump to prepare signature": (h("B0 47 DF F8 E8 04 F3 E1"), h("B0 47 DF F8 E8 04 1D E0")), + "prepare signature => jump to write signature": (h("10 43 18 60 DF F8 AC 04"), h("10 43 18 60 05 E1 00 20")), + "check write ok => infinite loop" : (h("A3 48 B0 47 01 24"), h("A3 48 B0 47 FE E7")) +} + +#grab keys from redsn0w Keys.plist +class IPSWkeys(object): + def __init__(self, manifest): + self.keys = {} + buildi = manifest["BuildIdentities"][0] + dc = buildi["Info"]["DeviceClass"] + build = "%s_%s_%s" % (devices.get(dc,dc), manifest["ProductVersion"], manifest["ProductBuildVersion"]) + try: + rs = plistlib.readPlist("Keys.plist") + except: + raise Exception("Get Keys.plist from redsn0w and place it in the current directory") + for k in rs["Keys"]: + if k["Build"] == build: + self.keys = k + break + + def getKeyIV(self, filename): + if not self.keys.has_key(filename): + return None, None + k = self.keys[filename] + return k.get("Key",""), k.get("IV","") + +def decryptImg3(blob, key, iv): + assert blob[:4] == "3gmI", "Img3 magic tag" + data = "" + for i in xrange(20, len(blob)): + tag = blob[i:i+4] + size, real_size = struct.unpack("= real_size, "Img3 length check" + data = blob[i+12:i+size] + break + i += size + return AES.new(key, AES.MODE_CBC, iv).decrypt(data)[:real_size] + + +def main(ipswname, options): + ipsw = zipfile.ZipFile(ipswname) + manifest = plistlib.readPlistFromString(ipsw.read("BuildManifest.plist")) + kernelname = manifest["BuildIdentities"][0]["Manifest"]["KernelCache"]["Info"]["Path"] + devclass = manifest["BuildIdentities"][0]["Info"]["DeviceClass"] + kernel = ipsw.read(kernelname) + keys = IPSWkeys(manifest) + + key,iv = keys.getKeyIV(kernelname) + + if key == None: + print "No keys found for kernel" + return + + print "Decrypting %s" % kernelname + kernel = decryptImg3(kernel, key.decode("hex"), iv.decode("hex")) + assert kernel.startswith("complzss"), "Decrypted kernelcache does not start with \"complzss\" => bad key/iv ?" + + print "Unpacking ..." + kernel = decompress_lzss(kernel) + assert kernel.startswith("\xCE\xFA\xED\xFE"), "Decompressed kernelcache does not start with 0xFEEDFACE" + + patchs = patchs_ios5 + if devclass in ["n82ap", "n72ap"]: + print "Using ARMv6 kernel patches" + patchs = patchs_armv6 + elif manifest["ProductVersion"].startswith("4."): + print "Using iOS 4 kernel patches" + patchs = patchs_ios4 + elif manifest["ProductVersion"].startswith("6."): + print "Using iOS 6 kernel patches" + patchs = patchs_ios6 + + if options.fixnand: + if patchs != patchs_ios4: + print "FAIL : use --fixnand with iOS 4.x IPSW" + return + patchs.update(patchs_ios4_fixnand) + kernelname = "fix_nand_" + kernelname + print "WARNING : only use this kernel to fix NAND epoch brick" + + for p in patchs: + print "Doing %s patch" % p + s, r = patchs[p] + c = kernel.count(s) + if c != 1: + print "=> FAIL, count=%d, do not boot that kernel it wont work" % c + else: + kernel = kernel.replace(s,r) + + outkernel = "%s.patched" % kernelname + open(outkernel, "wb").write(kernel) + print "Patched kernel written to %s" % outkernel + + ramdiskname = manifest["BuildIdentities"][0]["Manifest"]["RestoreRamDisk"]["Info"]["Path"] + key,iv = keys.getKeyIV("Ramdisk") + ramdisk = ipsw.read(ramdiskname) + + print "Decrypting %s" % ramdiskname + ramdisk = decryptImg3(ramdisk, key.decode("hex"), iv.decode("hex")) + + assert ramdisk[0x400:0x402] == "H+", "H+ magic not found in decrypted ramdisk => bad key/iv ?" + + customramdisk = "myramdisk_%s.dmg" % devclass + f = open(customramdisk, "wb") + f.write(ramdisk) + f.close() + + if manifest["ProductVersion"].startswith("6."): + print "Run ./build_ramdisk_ios6.sh %s" % customramdisk + print "Then redsn0w -i %s -r %s -k %s -a \"-v rd=md0 amfi=0xff cs_enforcement_disable=1\"" % (ipswname, customramdisk, outkernel) + return + + build_cmd = "./build_ramdisk.sh %s %s %s %s %s" % (ipswname, ramdiskname, key, iv, customramdisk) + rs_cmd = "redsn0w -i %s -r %s -k %s" % (ipswname, customramdisk, outkernel) + rdisk_script="""#!/bin/sh + +for VER in 4.2 4.3 5.0 5.1 6.0 +do + if [ -f "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$VER.sdk/System/Library/Frameworks/IOKit.framework/IOKit" ]; + then + SDKVER=$VER + echo "Found iOS SDK $SDKVER" + break + fi +done +if [ "$SDKVER" == "" ]; then + echo "iOS SDK not found" + exit +fi +SDKVER=$SDKVER make -C ramdisk_tools + +%s + +if [ "$?" == "0" ] +then + echo "You can boot the ramdisk using the following command (fix paths)" + echo "%s" + echo "Add -a \\"-v rd=md0 nand-disable=1\\" for nand dump/read only access" +fi +""" % (build_cmd, rs_cmd) + + scriptname="make_ramdisk_%s.sh" % devclass + f=open(scriptname, "wb") + f.write(rdisk_script) + f.close() + + print "Created script %s, you can use it to (re)build the ramdisk"% scriptname + + +if __name__ == "__main__": + parser = OptionParser(usage="%prog [options] IPSW") + parser.add_option("-f", "--fixnand", + action="store_true", dest="fixnand", default=False, + help="Apply NAND epoch fix kernel patches") + + (options, args) = parser.parse_args() + if len(args) < 1: + parser.print_help() + else: + main(args[0], options) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/keychain/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/keychain/__init__.py new file mode 100644 index 0000000..d87aaf8 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/keychain/__init__.py @@ -0,0 +1,12 @@ +import sqlite3 +from keychain3 import Keychain3 +from keychain4 import Keychain4 + +def keychain_load(filename, keybag, key835): + version = sqlite3.connect(filename).execute("SELECT version FROM tversion").fetchone()[0] + #print "Keychain version : %d" % version + if version == 3: + return Keychain3(filename, key835) + elif version >= 4: + return Keychain4(filename, keybag) + raise Exception("Unknown keychain version %d" % version) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain.py b/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain.py new file mode 100644 index 0000000..2da0761 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain.py @@ -0,0 +1,237 @@ +from store import PlistKeychain, SQLiteKeychain +from util import write_file +from util.asciitables import print_table +from util.bplist import BPlistReader +from util.cert import RSA_KEY_DER_to_PEM, CERT_DER_to_PEM +import M2Crypto +import hashlib +import plistlib +import sqlite3 +import string +import struct + +KSECATTRACCESSIBLE = { + 6: "kSecAttrAccessibleWhenUnlocked", + 7: "kSecAttrAccessibleAfterFirstUnlock", + 8: "kSecAttrAccessibleAlways", + 9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly", + 10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly", + 11: "kSecAttrAccessibleAlwaysThisDeviceOnly" +} +printset = set(string.printable) + +def render_password(p): + data = p["data"] + if data != None and data.startswith("bplist") and data.find("\x00") != -1: + pl = BPlistReader.plistWithString(p["data"]) + filename = "%s_%s_%d.plist" % (p["svce"],p["acct"],p["rowid"]) + plistlib.writePlist(pl, filename) + #write_file("bin_"+filename, p["data"]) + data = filename + + if p.has_key("srvr"): + return "%s:%d;%s;%s" % (p["srvr"],p["port"],p["acct"],data) + else: + return "%s;%s;%s" % (p["svce"],p["acct"],data) + +class Keychain(object): + def __init__(self, filename): + magic = open(filename, "rb").read(16) + if magic.startswith("SQLite"): + self.store = SQLiteKeychain(filename) + elif magic.startswith("bplist"): + self.store = PlistKeychain(filename) + else: + raise Exception("Unknown keychain format for %s" % filename) + self.bsanitize = True + self.items = {"genp": None, "inet": None, "cert": None, "keys": None} + + def decrypt_data(self, data): + return data #override this method + + def decrypt_item(self, res): + res["data"] = self.decrypt_data(res["data"]) + if not res["data"]: + return {} + return res + + def get_items(self, table): + if self.items[table]: + return self.items[table] + self.items[table] = filter(lambda x:x!={}, map(self.decrypt_item, self.store.get_items(table))) + return self.items[table] + + def get_passwords(self): + return self.get_items("genp") + + def get_inet_passwords(self): + return self.get_items("inet") + + def get_keys(self): + return self.get_items("keys") + + def get_cert(self): + return self.get_items("cert") + + def get_certs(self): + certs = {} + pkeys = {} + keys = self.get_keys() + for row in self.get_cert(): + cert = M2Crypto.X509.load_cert_der_string(row["data"]) + subject = cert.get_subject().as_text() + common_name = cert.get_subject().get_entries_by_nid(M2Crypto.X509.X509_Name.nid['CN']) + if len(common_name): + subject = str(common_name[0].get_data()) + else: + subject = "cn_unknown_%d" % row["rowid"] + certs[subject+ "_%s" % row["agrp"]] = cert + + #print subject + #print "Access :\t" + KSECATTRACCESSIBLE.get(row["clas"]) + + for k in keys: + if k["agrp"] == row["agrp"] and k["klbl"] == row["pkhh"]: + pkey_der = k["data"] + pkey_der = RSA_KEY_DER_to_PEM(pkey_der) + pkeys[subject + "_%s" % row["agrp"]] = pkey_der + break + + return certs, pkeys + + + def save_passwords(self): + passwords = "\n".join(map(render_password, self.get_passwords())) + inetpasswords = "\n".join(map(render_password, self.get_inet_passwords())) + print "Writing passwords to keychain.csv" + write_file("keychain.csv", "Passwords;;\n"+passwords+"\nInternet passwords;;\n"+ inetpasswords) + + def save_certs_keys(self): + certs, pkeys = self.get_certs() + for c in certs: + filename = c + ".crt" + print "Saving certificate %s" % filename + certs[c].save_pem(filename) + for k in pkeys: + filename = k + ".key" + print "Saving key %s" % filename + write_file(filename, pkeys[k]) + + def sanitize(self, pw): + if pw.startswith("bplist"): + return "" + elif not set(pw).issubset(printset): + pw = ">"+ pw.encode("hex") + #pw = " : " + pw.encode("hex") + if self.bsanitize: + return pw[:2] + ("*" * (len(pw) - 2)) + return pw + + def print_all(self, sanitize=True): + self.bsanitize = sanitize + headers = ["Service", "Account", "Data", "Access group", "Protection class"] + rows = [] + for p in self.get_passwords(): + row = [p.get("svce","?"), + str(p.get("acct","?"))[:40], + self.sanitize(p.get("data","?"))[:20], + p.get("agrp","?"), + KSECATTRACCESSIBLE.get(p["clas"])[18:]] + rows.append(row) + + print_table("Passwords", headers, rows) + + headers = ["Server", "Account", "Data", "Access group", "Protection class"] + rows = [] + + for p in self.get_inet_passwords(): + addr = "?" + if p.has_key("srvr"): + addr = p["srvr"] + ":" + str(p["port"]) + row = [addr, + str(p.get("acct","?")), + self.sanitize(p.get("data","?"))[:20], + p.get("agrp","?"), + KSECATTRACCESSIBLE.get(p["clas"])[18:]] + rows.append(row) + + print_table("Internet Passwords", headers, rows) + + headers = ["Id", "Common Name", "Access group", "Protection class"] + rows = [] + c = {} + + for row in self.get_cert(): + subject = "?" + if row.has_key("data"): + cert = M2Crypto.X509.load_cert_der_string(row["data"]) + subject = cert.get_subject().as_text() + common_name = cert.get_subject().get_entries_by_nid(M2Crypto.X509.X509_Name.nid['CN']) + if len(common_name): + subject = str(common_name[0].get_data()) + else: + subject = "cn_unknown_%d" % row["rowid"] + c[hashlib.sha1(str(row["pkhh"])).hexdigest() + row["agrp"]] = subject + row = [str(row["rowid"]), + subject[:81], + row.get("agrp","?")[:31], + KSECATTRACCESSIBLE.get(row["clas"])[18:] + ] + rows.append(row) + + print_table("Certificates", headers, rows) + + headers = ["Id", "Label", "Common Name", "Access group", "Protection class"] + rows = [] + for row in self.get_keys(): + subject = "" + if row.has_key("klbl"): + subject = c.get(hashlib.sha1(str(row["klbl"])).hexdigest() + row["agrp"], "") + row = [str(row["rowid"]), row.get("labl", "?")[:30], subject[:39], row.get("agrp","?")[:31], + KSECATTRACCESSIBLE.get(row["clas"])[18:]] + rows.append(row) + print_table("Keys", headers, rows) + + def get_push_token(self): + for p in self.get_passwords(): + if p["svce"] == "push.apple.com": + return p["data"] + + def get_managed_configuration(self): + for p in self.get_passwords(): + if p["acct"] == "Private" and p["svce"] == "com.apple.managedconfiguration" and p["agrp"] == "apple": + return BPlistReader.plistWithString(p["data"]) + + def _diff(self, older, res, func, key): + res.setdefault(key, []) + current = func(self) + for p in func(older): + if not p in current and not p in res[key]: + res[key].append(p) + + def diff(self, older, res): + self._diff(older, res, Keychain.get_passwords, "genp") + self._diff(older, res, Keychain.get_inet_passwords, "inet") + self._diff(older, res, Keychain.get_cert, "cert") + self._diff(older, res, Keychain.get_keys, "keys") + + def cert(self, rowid, filename=""): + for row in self.get_cert(): + if row["rowid"] == rowid: + blob = CERT_DER_to_PEM(row["data"]) + if filename: + write_file(filename, blob) + cert = M2Crypto.X509.load_cert_der_string(row["data"]) + print cert.as_text() + return + + def key(self, rowid, filename=""): + for row in self.get_keys(): + if row["rowid"] == rowid: + blob = RSA_KEY_DER_to_PEM(row["data"]) + if filename: + write_file(filename, blob) + #k = M2Crypto.RSA.load_key_string(blob) + print blob + return + \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain3.py b/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain3.py new file mode 100644 index 0000000..6e1dbbb --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain3.py @@ -0,0 +1,44 @@ +from keychain import Keychain +from crypto.aes import AESdecryptCBC, AESencryptCBC +import hashlib + +class Keychain3(Keychain): + def __init__(self, filename, key835=None): + Keychain.__init__(self, filename) + self.key835 = key835 + + def decrypt_data(self, data): + if data == None: + return "" + data = str(data) + + if not self.key835: + print "Key 835 not availaible" + return "" + + data = AESdecryptCBC(data[16:], self.key835, data[:16], padding=True) + + #data_column = iv + AES128_K835(iv, data + sha1(data)) + if hashlib.sha1(data[:-20]).digest() != data[-20:]: + print "data field hash mismatch : bad key ?" + return "ERROR decrypting data : bad key ?" + + return data[:-20] + + def change_key835(self, newkey): + tables = {"genp": "SELECT rowid, data FROM genp", + "inet": "SELECT rowid, data FROM inet", + "cert": "SELECT rowid, data FROM cert", + "keys": "SELECT rowid, data FROM keys"} + + for t in tables.keys(): + for row in self.conn.execute(tables[t]): + rowid = row["rowid"] + data = str(row["data"]) + iv = data[:16] + data = AESdecryptCBC(data[16:], self.key835, iv) + data = AESencryptCBC(data, newkey, iv) + data = iv + data + data = buffer(data) + self.conn.execute("UPDATE %s SET data=? WHERE rowid=?" % t, (data, rowid)) + self.conn.commit() \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain4.py b/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain4.py new file mode 100644 index 0000000..475b1c8 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain4.py @@ -0,0 +1,92 @@ +from crypto.aes import AESdecryptCBC +import struct + +""" + iOS 4 keychain-2.db data column format + + version 0x00000000 + key class 0x00000008 + kSecAttrAccessibleWhenUnlocked 6 + kSecAttrAccessibleAfterFirstUnlock 7 + kSecAttrAccessibleAlways 8 + kSecAttrAccessibleWhenUnlockedThisDeviceOnly 9 + kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly 10 + kSecAttrAccessibleAlwaysThisDeviceOnly 11 + wrapped AES256 key 0x28 bytes (passed to kAppleKeyStoreKeyUnwrap) + encrypted data (AES 256 CBC zero IV) +""" +from keychain import Keychain +from crypto.gcm import gcm_decrypt +from util.bplist import BPlistReader + +KSECATTRACCESSIBLE = { + 6: "kSecAttrAccessibleWhenUnlocked", + 7: "kSecAttrAccessibleAfterFirstUnlock", + 8: "kSecAttrAccessibleAlways", + 9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly", + 10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly", + 11: "kSecAttrAccessibleAlwaysThisDeviceOnly" +} + +class Keychain4(Keychain): + def __init__(self, filename, keybag): + if not keybag.unlocked: + print "Keychain object created with locked keybag, some items won't be decrypted" + Keychain.__init__(self, filename) + self.keybag = keybag + + def decrypt_item(self, row): + version, clas = struct.unpack("= 9 and not self.keybag.deviceKey: + return {} + if version >= 2: + dict = self.decrypt_blob(row["data"]) + if not dict: + return {"clas": clas, "rowid": row["rowid"]} + if dict.has_key("v_Data"): + dict["data"] = dict["v_Data"].data + else: + dict["data"] = "" + dict["rowid"] = row["rowid"] + dict["clas"] = clas + return dict + row["clas"] = clas + return Keychain.decrypt_item(self, row) + + def decrypt_data(self, data): + data = self.decrypt_blob(data) + if type(data) == dict: + return data["v_Data"].data + return data + + def decrypt_blob(self, blob): + if blob == None: + return "" + + if len(blob) < 48: + print "keychain blob length must be >= 48" + return + + version, clas = struct.unpack("= iPad 3) +FLAG_UNKNOWN = 0x80000000 + +WRAP_DEVICE = 1 +WRAP_PASSCODE = 2 + +KEY_TYPES = ["AES", "Curve25519"] +PROTECTION_CLASSES={ + 1:"NSFileProtectionComplete", + 2:"NSFileProtectionCompleteUnlessOpen", + 3:"NSFileProtectionCompleteUntilFirstUserAuthentication", + 4:"NSFileProtectionNone", + 5:"NSFileProtectionRecovery?", + + 6: "kSecAttrAccessibleWhenUnlocked", + 7: "kSecAttrAccessibleAfterFirstUnlock", + 8: "kSecAttrAccessibleAlways", + 9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly", + 10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly", + 11: "kSecAttrAccessibleAlwaysThisDeviceOnly" +} + +""" + device key : key 0x835 +""" +class Keybag(object): + def __init__(self, data): + self.type = None + self.uuid = None + self.wrap = None + self.deviceKey = None + self.unlocked = False + self.passcodeComplexity = 0 + self.attrs = {} + self.classKeys = {} + self.KeyBagKeys = None #DATASIGN blob + self.parseBinaryBlob(data) + + @staticmethod + def getSystemkbfileWipeID(filename): + mkb = BPlistReader.plistWithFile(filename) + return mkb["_MKBWIPEID"] + + @staticmethod + def createWithPlist(pldict): + k835 = pldict.key835.decode("hex") + data = "" + if pldict.has_key("KeyBagKeys"): + data = pldict["KeyBagKeys"].data + else: + data = "" + keybag = Keybag.createWithDataSignBlob(data, k835) + + if pldict.has_key("passcodeKey"): + if keybag.unlockWithPasscodeKey(pldict["passcodeKey"].decode("hex")): + print "Keybag unlocked with passcode key" + else: + print "FAILed to unlock keybag with passcode key" + #HAX: inject DKey + keybag.setDKey(pldict) + return keybag + + def setDKey(self, device_infos): + self.classKeys[4] = {"CLAS": 4, "KEY": device_infos["DKey"].decode("hex")} + + @staticmethod + def createWithSystemkbfile(filename, bag1key, deviceKey=None): + if filename.startswith("bplist"): #HAX + mkb = BPlistReader.plistWithString(filename) + else: + mkb = BPlistReader.plistWithFile(filename) + try: + decryptedPlist = AESdecryptCBC(mkb["_MKBPAYLOAD"].data, bag1key, mkb["_MKBIV"].data, padding=True) + except: + print "FAIL: AESdecryptCBC _MKBPAYLOAD => wrong BAG1 key ?" + return None + if not decryptedPlist.startswith("bplist"): + print "FAIL: decrypted _MKBPAYLOAD is not bplist" + return None + decryptedPlist = BPlistReader.plistWithString(decryptedPlist) + blob = decryptedPlist["KeyBagKeys"].data + kb = Keybag.createWithDataSignBlob(blob, deviceKey) + if decryptedPlist.has_key("OpaqueStuff"): + OpaqueStuff = BPlistReader.plistWithString(decryptedPlist["OpaqueStuff"].data) + kb.passcodeComplexity = OpaqueStuff.get("keyboardType") + return kb + + + @staticmethod + def createWithDataSignBlob(blob, deviceKey=None): + keybag = tlvToDict(blob) + + kb = Keybag(keybag.get("DATA", "")) + kb.deviceKey = deviceKey + kb.KeyBagKeys = blob + kb.unlockAlwaysAccessible() + + if len(keybag.get("SIGN", "")): + hmackey = AESUnwrap(deviceKey, kb.attrs["HMCK"]) + #hmac key and data are swapped (on purpose or by mistake ?) + sigcheck = hmac.new(key=keybag["DATA"], msg=hmackey, digestmod=sha1).digest() + #fixed in ios 7 + if kb.attrs["VERS"] >= 4: + sigcheck = hmac.new(key=hmackey, msg=keybag["DATA"], digestmod=sha1).digest() + if sigcheck != keybag.get("SIGN", ""): + print "Keybag: SIGN check FAIL" + return kb + + @staticmethod + def createWithBackupManifest(manifest, password, deviceKey=None): + kb = Keybag(manifest["BackupKeyBag"].data) + kb.deviceKey = deviceKey + if not kb.unlockBackupKeybagWithPasscode(password): + print "Cannot decrypt backup keybag. Wrong password ?" + return + return kb + + def isBackupKeybag(self): + return self.type == BACKUP_KEYBAG + + def parseBinaryBlob(self, data): + currentClassKey = None + + for tag, data in loopTLVBlocks(data): + if len(data) == 4: + data = struct.unpack(">L", data)[0] + if tag == "TYPE": + self.type = data & 0x3FFFFFFF #ignore the flags + if self.type > 3: + print "FAIL: keybag type > 3 : %d" % self.type + elif tag == "UUID" and self.uuid is None: + self.uuid = data + elif tag == "WRAP" and self.wrap is None: + self.wrap = data + elif tag == "UUID": + if currentClassKey: + self.classKeys[currentClassKey["CLAS"]] = currentClassKey + currentClassKey = {"UUID": data} + elif tag in CLASSKEY_TAGS: + currentClassKey[tag] = data + else: + self.attrs[tag] = data + if currentClassKey: + self.classKeys[currentClassKey["CLAS"]] = currentClassKey + + def getPasscodekeyFromPasscode(self, passcode): + if self.type == BACKUP_KEYBAG or self.type == OTA_KEYBAG: + return PBKDF2(passcode, self.attrs["SALT"], iterations=self.attrs["ITER"]).read(32) + else: + #Warning, need to run derivation on device with this result + return PBKDF2(passcode, self.attrs["SALT"], iterations=1).read(32) + + def unlockBackupKeybagWithPasscode(self, passcode): + if self.type != BACKUP_KEYBAG and self.type != OTA_KEYBAG: + print "unlockBackupKeybagWithPasscode: not a backup keybag" + return False + return self.unlockWithPasscodeKey(self.getPasscodekeyFromPasscode(passcode)) + + def unlockAlwaysAccessible(self): + for classkey in self.classKeys.values(): + k = classkey["WPKY"] + if classkey["WRAP"] == WRAP_DEVICE: + if not self.deviceKey: + continue + k = AESdecryptCBC(k, self.deviceKey) + classkey["KEY"] = k + return True + + def unlockWithPasscodeKey(self, passcodekey): + if self.type != BACKUP_KEYBAG and self.type != OTA_KEYBAG: + if not self.deviceKey: + print "ERROR, need device key to unlock keybag" + return False + + for classkey in self.classKeys.values(): + if not classkey.has_key("WPKY"): + continue + k = classkey["WPKY"] + if classkey["WRAP"] & WRAP_PASSCODE: + k = AESUnwrap(passcodekey, classkey["WPKY"]) + if not k: + return False + if classkey["WRAP"] & WRAP_DEVICE: + if not self.deviceKey: + continue + k = AESdecryptCBC(k, self.deviceKey) + classkey["KEY"] = k + self.unlocked = True + return True + + def unwrapCurve25519(self, persistent_class, persistent_key): + assert len(persistent_key) == 0x48 + #assert persistent_class == 2 #NSFileProtectionCompleteUnlessOpen + mysecret = self.classKeys[persistent_class]["KEY"] + mypublic = self.classKeys[persistent_class]["PBKY"] + hispublic = persistent_key[:32] + shared = curve25519(mysecret, hispublic) + md = sha256('\x00\x00\x00\x01' + shared + hispublic + mypublic).digest() + return AESUnwrap(md, persistent_key[32:]) + + def unwrapKeyForClass(self, clas, persistent_key, printError=True): + if not self.classKeys.has_key(clas) or not self.classKeys[clas].has_key("KEY"): + if printError: print "Keybag key %d missing or locked" % clas + return "" + ck = self.classKeys[clas]["KEY"] + #if self.attrs.get("VERS", 2) >= 3 and clas == 2: + if self.attrs.get("VERS", 2) >= 3 and self.classKeys[clas].get("KTYP", 0) == 1: + return self.unwrapCurve25519(clas, persistent_key) + if len(persistent_key) == 0x28: + return AESUnwrap(ck, persistent_key) + return + + def wrapKeyForClass(self, clas, persistent_key): + if not self.classKeys.has_key(clas) or not self.classKeys[clas].has_key("KEY"): + print "Keybag key %d missing or locked" % clas + return "" + ck = self.classKeys[clas]["KEY"] + return AESwrap(ck, persistent_key) + + def printClassKeys(self): + print "Keybag type : %s keybag (%d)" % (KEYBAG_TYPES[self.type], self.type) + print "Keybag version : %d" % self.attrs["VERS"] + print "Keybag UUID : %s" % self.uuid.encode("hex") + print "-"*128 + print "".join(["Class".ljust(53), + "WRAP".ljust(5), + "Type".ljust(11), + "Key".ljust(65), + "Public key"]) + print "-"*128 + for k, ck in self.classKeys.items(): + if k == 6: print "" + print "".join([PROTECTION_CLASSES.get(k).ljust(53), + str(ck.get("WRAP","")).ljust(5), + KEY_TYPES[ck.get("KTYP",0)].ljust(11), + ck.get("KEY", "").encode("hex").ljust(65), + ck.get("PBKY", "").encode("hex")]) + print "" + + def getClearClassKeysDict(self): + if self.unlocked: + d = {} + for ck in self.classKeys.values(): + d["%d" % ck["CLAS"]] = ck.get("KEY","").encode("hex") + return d diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/carver.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/carver.py new file mode 100644 index 0000000..f79e1e7 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/nand/carver.py @@ -0,0 +1,381 @@ +from crypto.aes import AESdecryptCBC, AESencryptCBC +from hfs.emf import cprotect_xattr, EMFVolume +from hfs.hfs import HFSVolume, hfs_date, HFSFile +from hfs.journal import carveBtreeNode, isDecryptedCorrectly +from hfs.structs import * +from util import sizeof_fmt, makedirs, hexdump +import hashlib +import os +import struct + +class NANDCarver(object): + def __init__(self, volume, image, outputdir=None): + self.volume = volume + self.image = image + self.nand = image + self.ftlhax = False + self.userblocks = None + self.lpnToVpn = None + self.files = {} + self.keys = {} + self.encrypted = image.encrypted and hasattr(volume, "emfkey") + self.encrypted = hasattr(volume, "cp_root") and volume.cp_root != None + if outputdir == None: + if image.filename != "remote": outputdir = os.path.join(os.path.dirname(image.filename), "undelete") + else: outputdir = os.path.join(".", "undelete") + print "Carver output %s" % outputdir + self.outputdir = outputdir + self.okfiles = 0 + self.first_lba = self.volume.bdev.lbaoffset + self.pageSize = image.pageSize + self.blankPage = "\xDE\xAD\xBE\xEF" * (self.pageSize/4) + self.emfkey = None + self.fileIds = None + self.fastMode = False + if hasattr(volume, "emfkey"): + self.emfkey = volume.emfkey + + def carveFile(self, hfsfile, callback, lbas=None, filter_=None): + for e in hfsfile.extents: + if e.blockCount == 0: + break + for i in xrange(e.startBlock, e.startBlock+e.blockCount): + if lbas and not i in lbas: + continue + + if self.fastMode: + for vpn in self.ftlhax.get(self.first_lba+i, []): + usn = 0 + s,d = self.nand.ftl.YAFTL_readPage(vpn, self.emfkey, self.first_lba+i) + callback(d, usn, filter_) + else: + # s,d = self.nand.ftl.YAFTL_readPage(vpn, self.emfkey, self.first_lba+i) + # callback(d, 0) + usnsForLbn = self.ftlhax.get(self.first_lba+i, []) + for usn in sorted(usnsForLbn.keys())[:-1]: + for vpn in usnsForLbn[usn]: + s,d = self.nand.ftl.YAFTL_readPage(vpn, self.emfkey, self.first_lba+i) + callback(d, usn, filter_) + + def _catalogFileCallback(self, data, usn, filter_): + for k,v in carveBtreeNode(data,HFSPlusCatalogKey, HFSPlusCatalogData): + if v.recordType != kHFSPlusFileRecord: + continue + if filter_ and not filter_(k,v): + continue + name = getString(k) + #if not self.filterFileName(name): + # continue + h = hashlib.sha1(HFSPlusCatalogKey.build(k)).digest() + if self.files.has_key(h): + continue + if not self.fileIds.has_key(v.data.fileID): + print "Found deleted file record", v.data.fileID, name.encode("utf-8"), "created", hfs_date(v.data.createDate) + self.files[h] = (name,v, usn) + + def _attributesFileCallback(self, data, usn, filter_): + for k,v in carveBtreeNode(data,HFSPlusAttrKey, HFSPlusAttrData): + if getString(k) != "com.apple.system.cprotect": + continue + if self.fileIds.has_key(k.fileID): + continue + filekeys = self.keys.setdefault(k.fileID, []) + try: + cprotect = cprotect_xattr.parse(v.data) + except: + continue + if cprotect.key_size == 0: + continue + filekey = self.volume.keybag.unwrapKeyForClass(cprotect.persistent_class, cprotect.persistent_key, False) + if filekey and not filekey in filekeys: + #print "Found key for file ID ", k.fileID + filekeys.append(filekey) + + def carveCatalog(self, lbas=None, filter_=None): + return self.carveFile(self.volume.catalogFile, self._catalogFileCallback, lbas, filter_) + + def carveKeys(self, lbas=None): + return self.carveFile(self.volume.xattrFile, self._attributesFileCallback, lbas) + + def pagesForLBN(self, lbn): + return self.ftlhax.get(self.first_lba + lbn, {}) + + def decryptFileBlock(self, pn, filekey, lbn, decrypt_offset): + s, ciphertext = self.nand.ftl.YAFTL_readPage(pn, None, lbn) + if not self.encrypted: + return ciphertext + if not self.image.isIOS5(): + return AESdecryptCBC(ciphertext, filekey, self.volume.ivForLBA(lbn)) + clear = "" + ivkey = hashlib.sha1(filekey).digest()[:16] + for i in xrange(len(ciphertext)/0x1000): + iv = self.volume.ivForLBA(decrypt_offset, False) + iv = AESencryptCBC(iv, ivkey) + clear += AESdecryptCBC(ciphertext[i*0x1000:(i+1)*0x1000], filekey, iv) + decrypt_offset += 0x1000 + return clear + + def writeUndeletedFile(self, filename, data): + knownExtensions = (".m4a", ".plist",".sqlite",".sqlitedb", ".jpeg", ".jpg", ".png", ".db",".json",".xml",".sql") + #windows invalid chars \/:*?"<>| + filename = str(filename.encode("utf-8")).translate(None, "\\/:*?\"<>|,") + folder = self.outputdir + if self.outputdir == "./": + folder = folder + "/undelete" + elif filename.lower().endswith(knownExtensions): + ext = filename[filename.rfind(".")+1:] + folder = folder + "/" + ext.lower() + makedirs(folder) + open(folder + "/" + filename, "wb").write(data) + + def getFileAtUSN(self, filename, filerecord, filekey, usn, previousVersion=None, exactSize=True): + missing_pages = 0 + decrypt_offset = 0 + file_pages = [] + logicalSize = filerecord.dataFork.logicalSize + for extent in self.volume.getAllExtents(filerecord.dataFork, filerecord.fileID): + for bn in xrange(extent.startBlock, extent.startBlock + extent.blockCount): + pn = self.pagesForLBN(bn).get(usn) #fail + if pn: + clear = self.decryptFileBlock(pn[-1], filekey, bn, decrypt_offset) + file_pages.append(clear) + elif previousVersion: + file_pages.append(previousVersion[len(file_pages)]) + else: + file_pages.append(self.blankPage) + missing_pages += 1 + decrypt_offset += self.pageSize + + print "Recovered %d:%s %d missing pages, size %s, created %s, contentModDate %s" % \ + (filerecord.fileID, filename.encode("utf-8"), missing_pages, sizeof_fmt(logicalSize), hfs_date(filerecord.createDate), hfs_date(filerecord.contentModDate)) + filename = "%d_%d_%s" % (filerecord.fileID, usn, filename) + if missing_pages == 0: + filename = "OK_" + filename + self.okfiles += 1 + data = "".join(file_pages) + if exactSize: + data = data[:logicalSize] + self.writeUndeletedFile(filename, data) + return file_pages + + #test for SQLite + def rollbackExistingFile(self, filename): + filerecord = self.volume.getFileRecordForPath(filename) + filekey = self.volume.getFileKeyForFileId(filerecord.fileID) + print "filekey", filekey.encode("hex") + z = None + for extent in filerecord.dataFork.HFSPlusExtentDescriptor: + for bn in xrange(extent.startBlock, extent.startBlock + extent.blockCount): + pages = self.pagesForLBN(bn) + print pages + for usn in sorted(pages.keys()): + d = self.decryptFileBlock(pages[usn][-1], filekey, bn, 0) + if d.startswith("SQL") or True: + filechangecounter = struct.unpack(">L", d[24:28]) + print usn, "OK", filechangecounter + z = self.getFileAtUSN(os.path.basename(filename), filerecord, filekey, usn, z) + else: + print usn, "FAIL" + return + + def filterFileName(self, filename): + return filename.lower().endswith(".jpg") + + def getExistingFileIDs(self): + print "Collecting existing file ids" + self.fileIds = self.volume.listAllFileIds() + print "%d file IDs" % len(self.fileIds.keys()) + + def carveDeletedFiles_fast(self, catalogLBAs=None, filter_=None): + self.fastMode = True + if not self.ftlhax: + hax, userblocks = self.nand.ftl.YAFTL_lookup1() + self.ftlhax = hax + self.userblocks = userblocks + + self.files = {} + if not self.fileIds: + self.getExistingFileIDs() + print "Carving catalog file" + #catalogLBAs = None + self.carveCatalog(catalogLBAs, filter_) + + keysLbas = [] + for name, vv, usn in self.files.values(): + for i in xrange(vv.data.fileID, vv.data.fileID + 100): + if self.volume.xattrTree.search((i, "com.apple.system.cprotect")): + keysLbas.extend(self.volume.xattrTree.getLBAsHax()) + break + + #print "keysLbas", keysLbas + if self.encrypted and len(self.keys) == 0: + print "Carving attribute file for file keys" + #self.carveKeys(keysLbas) + self.carveKeys() + + self.okfiles = 0 + total = 0 + print "%d files, %d keys" % (len(self.files), len(self.keys)) + for name, vv, usn in self.files.values(): + if not self.keys.has_key(vv.data.fileID): + print "No file key for %s" % name.encode("utf-8") + keys = set(self.keys.get(vv.data.fileID, [self.emfkey])) + print "%s" % (name.encode("utf-8")) + if self.readFileHax(name, vv.data, keys): + total += 1 + + print "Carving done, recovered %d deleted files, %d are most likely OK" % (total, self.okfiles) + + def carveDeleteFiles_slow(self, catalogLBAs=None, filter_=None): + self.fastMode = False + self.files = {} + if not self.ftlhax: + self.ftlhax = self.nand.ftl.YAFTL_hax2() + if not self.fileIds: + self.getExistingFileIDs() + if self.encrypted and len(self.keys) == 0: + print "Carving attribute file for file keys" + self.carveKeys() + print "Carving catalog file" + self.carveCatalog(catalogLBAs, filter_) + + self.okfiles = 0 + total = 0 + print "%d files" % len(self.files) + for name, vv, usn in self.files.values(): + keys = set(self.keys.get(vv.data.fileID, [self.emfkey])) + print "%s num keys = %d" % (name, len(keys)) + good_usn = 0 + for filekey in keys: + if good_usn: + break + first_block = vv.data.dataFork.HFSPlusExtentDescriptor[0].startBlock + for usn, pn in self.pagesForLBN(first_block).items(): + d = self.decryptFileBlock(pn[-1], filekey, self.first_lba+first_block, 0) + if isDecryptedCorrectly(d): + #print "USN for first block : ", usn + good_usn = usn + break + if good_usn == 0: + continue + self.getFileAtUSN(name, vv.data, filekey, good_usn) + + def getBBTOC(self, block): + btoc = self.nand.ftl.readBTOCPages(block, self.nand.ftl.totalPages) + if not btoc: + return self.nand.ftl.block_lpn_to_vpn(block) + bbtoc = {} + for i in xrange(len(btoc)): + bbtoc[btoc[i]] = block*self.nand.ftl.vfl.pages_per_sublk + i + return bbtoc + + def readFileHax(self, filename, filerecord, filekeys): + lba0 = self.first_lba + filerecord.dataFork.HFSPlusExtentDescriptor[0].startBlock + filekey = None + good_usn = None + first_vpn = 0 + first_usn = 0 + hax = self.ftlhax + print "%d versions for first lba" % len(hax.get(lba0, [])) + for k in filekeys: + for vpn in hax.get(lba0, []): + s, ciphertext = self.nand.ftl.YAFTL_readPage(vpn, key=None, lpn=None) + if not ciphertext: + continue + d = self.decryptFileBlock2(ciphertext, k, lba0, 0) + #hexdump(d[:16]) + if isDecryptedCorrectly(d): + filekey = k + first_vpn = vpn + first_usn = good_usn = s.usn + block = vpn / self.nand.ftl.vfl.pages_per_sublk + break + if not filekey: + return False + logicalSize = filerecord.dataFork.logicalSize + missing_pages = 0 + file_pages = [] + lbns = [] + for extent in self.volume.getAllExtents(filerecord.dataFork, filerecord.fileID): + for bn in xrange(extent.startBlock, extent.startBlock + extent.blockCount): + lbns.append(self.first_lba + bn) + datas = {} + usnblocksToLookAT = sorted(filter(lambda x: x >= good_usn, self.userblocks.keys()))[:5] + print usnblocksToLookAT + usnblocksToLookAT.insert(0, 0) + first_block = True + done = False + for usn in usnblocksToLookAT: + if first_block: + bbtoc = self.getBBTOC(block) + first_block = False + else: + bbtoc = self.getBBTOC(self.userblocks[usn]) + for lbn in bbtoc.keys(): + if not lbn in lbns: + continue + idx = lbns.index(lbn) + s, ciphertext = self.nand.ftl.YAFTL_readPage(bbtoc[lbn], key=None, lpn=None) + if not ciphertext: + continue + ciphertext = self.decryptFileBlock2(ciphertext, filekey, lbn, idx*self.pageSize) + if idx == 0: + if not isDecryptedCorrectly(ciphertext): + continue + datas[idx*self.pageSize] = (ciphertext, lbn - self.first_lba) + #if idx == len(lbns): + if len(datas) == len(lbns): + done=True + break + + if done: + break + cleartext = "" + decrypt_offset = 0 + for i in xrange(0,logicalSize, self.pageSize): + if datas.has_key(i): + ciphertext, lbn = datas[i] + cleartext += ciphertext + else: + cleartext += self.blankPage + missing_pages += 1 + decrypt_offset += self.pageSize + + print "Recovered %d:%s %d missing pages, size %s, created %s, contentModDate %s" % \ + (filerecord.fileID, filename.encode("utf-8"), missing_pages, sizeof_fmt(logicalSize), hfs_date(filerecord.createDate), hfs_date(filerecord.contentModDate)) + filename = "%d_%d_%s" % (filerecord.fileID, first_usn, filename) + if missing_pages == 0: + filename = "OK_" + filename + self.okfiles += 1 + if True:#exactSize: + cleartext = cleartext[:logicalSize] + self.writeUndeletedFile(filename, cleartext) + return True + + def decryptFileBlock2(self, ciphertext, filekey, lbn, decrypt_offset): + if not self.encrypted: + return ciphertext + if not self.image.isIOS5(): + return AESdecryptCBC(ciphertext, filekey, self.volume.ivForLBA(lbn, add=False)) + clear = "" + ivkey = hashlib.sha1(filekey).digest()[:16] + for i in xrange(len(ciphertext)/0x1000): + iv = self.volume.ivForLBA(decrypt_offset, False) + iv = AESencryptCBC(iv, ivkey) + clear += AESdecryptCBC(ciphertext[i*0x1000:(i+1)*0x1000], filekey, iv) + decrypt_offset += 0x1000 + return clear + + def getFileRanges(self, hfsfile): + res = [] + for e in hfsfile.extents: + if e.blockCount == 0: + break + res.append(xrange(e.startBlock, e.startBlock+e.blockCount)) + return res + + def readFSPage(self, vpn, lba): + s,d = self.nand.ftl.YAFTL_readPage(vpn, self.emfkey, self.first_lba+lba) + if s: + return d + diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/image.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/image.py new file mode 100644 index 0000000..c87d514 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/nand/image.py @@ -0,0 +1,86 @@ +import os +import struct +import sys + +""" +row-by-row dump +page = data + spare metadata + iokit return code + iokit return code 2 +""" +class NANDImageFlat(object): + def __init__(self, filename, geometry): + flags = os.O_RDONLY + if sys.platform == "win32": + flags |= os.O_BINARY + self.fd = os.open(filename, flags) + self.nCEs = geometry["#ce"] + self.pageSize = geometry["#page-bytes"] + self.metaSize = geometry.get("meta-per-logical-page", 12) + self.dumpedPageSize = geometry.get("dumpedPageSize", self.pageSize + self.metaSize + 8) + self.hasIOKitStatus = True + if self.dumpedPageSize == self.pageSize + geometry["#spare-bytes"] or self.dumpedPageSize == self.pageSize + self.metaSize: + self.hasIOKitStatus = False + self.blankPage = "\xFF" * self.pageSize + self.blankSpare = "\xFF" * self.metaSize + self.imageSize = os.path.getsize(filename) + expectedSize = geometry["#ce"] * geometry["#ce-blocks"] * geometry["#block-pages"] * self.dumpedPageSize + if self.imageSize < expectedSize: + raise Exception("Error: image appears to be truncated, expected size=%d" % expectedSize) + print "Image size matches expected size, looks ok" + + def _readPage(self, ce, page): + i = page * self.nCEs + ce + off = i * self.dumpedPageSize + os.lseek(self.fd, off, os.SEEK_SET) + return os.read(self.fd, self.dumpedPageSize) + + def readPage(self, ce, page): + d = self._readPage(ce, page) + if not d or len(d) != self.dumpedPageSize: + return None,None + if self.hasIOKitStatus: + r1,r2 = struct.unpack("= FTL_CTX_TYPE and s.type <= FTL_CTX_TYPE_MAX: + if s.usn < minUsnDec: + ftlCtrlBlock = vb + minUsnDec = s.usn + + print ftlCtrlBlock + self.ftlCtrlBlock = ftlCtrlBlock + for p in xrange(self.vfl.pages_per_sublk-1,1, -1): + s, d = self.vfl.read_single_page(ftlCtrlBlock * self.vfl.pages_per_sublk + p) + if not s: + continue + #print s + #print p + if s.type == FTL_CTX_TYPE: + print s.usn + ctx = FTLCxt.parse(d) + if ctx.versionLower == 0x46560001: + print ctx + assert ctx.FTLCtrlPage == (ftlCtrlBlock * self.vfl.pages_per_sublk + p) + break + else: + print "Unclean shutdown, last type 0x%x" % s.type + return False + self.ctx = ctx + print "FTL_open OK !" + return True + + def determine_block_type(self, block): + maxUSN = 0 + isSequential = True + for page in xrange(self.vfl.pages_per_sublk-1,1, -1): + s, _ = self.vfl.read_single_page(block * self.vfl.pages_per_sublk + page) + if not s: + continue + if s.usn > maxUSN: + maxUSN = s.usn + if s.lpn % self.vfl.pages_per_sublk != page: + isSequential = False + return isSequential, maxUSN + return isSequential, maxUSN + + def FTL_restore(self): + self.pLogs = self.vfl.nand.loadCachedData("pLogs") + self.pawMapTable = self.vfl.nand.loadCachedData("pawMapTable") + if self.pLogs and self.pawMapTable: + print "Found cached FTL restore information" + return + self.pawMapTable = {} + self.pLogs = {} + ctx = None + for p in xrange(self.vfl.pages_per_sublk-1,1, -1): + s, d = self.vfl.read_single_page(self.ftlCtrlBlock * self.vfl.pages_per_sublk + p) + if not s: + continue + if s.type == FTL_CTX_TYPE: + print s.usn + ctx = FTLCxt.parse(d) + if ctx.versionLower == 0x46560001: + print ctx + assert ctx.FTLCtrlPage == (self.ftlCtrlBlock * self.vfl.pages_per_sublk + p) + print "Found most recent ctx" + break + if not ctx: + print "FTL_restore fail did not find ctx" + raise + blockMap = {} + self.nonSequential = {} + print "FTL_restore in progress ..." + for sblock in xrange(self.vfl.userSuBlksTotal + 23): + for page in xrange(self.vfl.pages_per_sublk): + s, d = self.vfl.read_single_page(sblock * self.vfl.pages_per_sublk + page) + if not s: + continue + if s.type >= FTL_CTX_TYPE and s.type <= FTL_CTX_TYPE_MAX: + break + if s.type != USER_TYPE and s.type != USER_LAST_TYPE: + print "Weird page type %x at %x %x" % (s.type, sblock, page) + continue + if s.lpn % self.vfl.pages_per_sublk != page: + print "Block %d non sequential" % sblock + self.nonSequential[sblock] = 1 + blockMap[sblock] = (s.lpn / self.vfl.pages_per_sublk, s.usn) + break + + z = dict([(i, [(a, blockMap[a][1]) for a in blockMap.keys() if blockMap[a][0] ==i]) for i in xrange(self.vfl.userSuBlksTotal)]) + for k,v in z.items(): + if len(v) == 2: + print k, v + vbA, usnA = v[0] + vbB, usnB = v[1] + if usnA > usnB: #smallest USN is map block, highest log block + self.pawMapTable[k] = vbB + self.restoreLogBlock(k, vbA) + else: + self.pawMapTable[k] = vbA + self.restoreLogBlock(k, vbB) + elif len(v) > 2: + raise Exception("fufu", k, v) + else: + self.pawMapTable[k] = v[0][0] + self.vfl.nand.cacheData("pLogs", self.pLogs) + self.vfl.nand.cacheData("pawMapTable", self.pawMapTable) + + def restoreLogBlock(self, lbn, vbn): + log = {"wVbn": vbn, "wPageOffsets": {}} + for page in xrange(self.vfl.pages_per_sublk): + s, d = self.vfl.read_single_page(vbn * self.vfl.pages_per_sublk + page) + if not s: + break + log["wPageOffsets"][s.lpn % self.vfl.pages_per_sublk] = page + self.pLogs[lbn] = log + + def mapPage(self, lbn, offset): + if self.pLogs.has_key(lbn): + if self.pLogs[lbn]["wPageOffsets"].has_key(offset): + offset = self.pLogs[lbn]["wPageOffsets"][offset] + #print "mapPage got log %d %d" % (lbn, offset) + return self.pLogs[lbn]["wVbn"] * self.vfl.pages_per_sublk + offset + if not self.pawMapTable.has_key(lbn): + return 0xFFFFFFFF + return self.pawMapTable[lbn] * self.vfl.pages_per_sublk + offset + + def readLPN(self, lpn, key=None): + lbn = lpn / self.vfl.pages_per_sublk + offset = lpn % self.vfl.pages_per_sublk + vpn = self.mapPage(lbn, offset) + if vpn == 0xFFFFFFFF: + print "lbn not found %d" % lbn + return "\xFF" * self.nand.pageSize + s,d = self.vfl.read_single_page(vpn, key, lpn) + if not s: + return None + if s.lpn != lpn: + raise Exception("FTL translation FAIL spare lpn=%d vs expected %d" % (s.lpn, lpn)) + return d + diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/nand.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/nand.py new file mode 100644 index 0000000..15cacaa --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/nand/nand.py @@ -0,0 +1,425 @@ +from crypto.aes import AESdecryptCBC +from firmware.img2 import IMG2 +from firmware.img3 import Img3, extract_img3s +from firmware.scfg import parse_SCFG +from hfs.emf import EMFVolume +from hfs.hfs import HFSVolume +from image import NANDImageSplitCEs, NANDImageFlat +from keystore.effaceable import check_effaceable_header, EffaceableLockers +from legacyftl import FTL +from partition_tables import GPT_partitions, parse_lwvm, parse_mbr, parse_gpt, \ + APPLE_ENCRYPTED +from progressbar import ProgressBar +from remote import NANDRemote, IOFlashStorageKitClient +from structs import * +from util import sizeof_fmt, write_file, load_pickle, save_pickle, hexdump, \ + makedirs +from util.bdev import FTLBlockDevice +from vfl import VFL +from vsvfl import VSVFL +from yaftl import YAFTL +import math +import os +import plistlib +import struct + +def ivForPage(page): + iv = "" + for _ in xrange(4): + if (page & 1): + page = 0x80000061 ^ (page >> 1); + else: + page = page >> 1; + iv += struct.pack(" has a NOR)" + + vfltype = '1' #use VSVFL by default + if not nandsig: + nandsig = sp0.get("NANDDRIVERSIGN") + if not nandsig: + print "NANDDRIVERSIGN not found, assuming metadata withening = %d" % self.metadata_whitening + else: + nSig, flags = struct.unpack(" self.nCEs or page > self.pagesPerCE: + #hax physical banking + pass#raise Exception("CE %d Page %d out of bounds" % (ce, page)) + if self.filename != "remote": #undo banking hax + bank = (page & ~((1 << self.bank_mask) - 1)) >> self.bank_mask + page2 = (page & ((1 << self.bank_mask) - 1)) + page2 = bank * (self.blocks_per_bank) * self.pagesPerBlock + page2 + spare, data = self.image.readPage(ce, page2) + else: + spare, data = self.image.readPage(ce, page) + if not data: + return None,None + if self.metadata_whitening and spare != "\x00"*12 and len(spare) == 12: + spare = self.unwhitenMetadata(spare, page) + spare = spareType.parse(spare) + if key and self.encrypted: + if lpn != None: pageNum = spare.lpn #XXX + else: pageNum = page + return spare, self.decryptPage(data, key, pageNum) + return spare, data + + def decryptPage(self, data, key, pageNum): + return AESdecryptCBC(data, key, ivForPage(pageNum)) + + def unpackSpecialPage(self, data): + l = struct.unpack(" gpt.current_lba + check = gpt.crc + gpt.crc = 0 + actual = crc32(GPT_header.build(gpt)) + if actual != check: + print "GPT crc check fail %d vs %d" % (actual, check) + return None + return gpt + +def parse_lwvm(data, pageSize): + try: + hdr = LWVM_header.parse(data) + if hdr.type != LWVM_MAGIC: + print "LwVM magic mismatch" + return + tocheck = data[:44] + "\x00\x00\x00\x00" + data[48:0x1000] + check = crc32(tocheck) & 0xffffffff + if check != hdr.crc32: + return None + print "LwVM header CRC OK" + partitions = hdr.LWVM_partitionRecord[:hdr.numPartitions] + deviceSize=0 + #XXX: HAAAAAAAX + for s in [8, 16, 32, 64, 128]: + if hdr.mediaSize < (s* 1024*1024*1024): + deviceSize = s + break + for i in xrange(len(hdr.chunks)): + if hdr.chunks[i] == 0x0: + lba0 = (i * deviceSize*1024*1024) / pageSize + partitions[0].first_lba = lba0 + partitions[0].last_lba = lba0 + (partitions[0].end - partitions[0].begin) / pageSize + elif hdr.chunks[i] == 0x1000: + lbad = (i * deviceSize*1024*1024) / pageSize + partitions[1].first_lba = lbad + partitions[1].last_lba = lbad + (partitions[1].end - partitions[1].begin) / pageSize + return partitions + except: + return None + diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/remote.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/remote.py new file mode 100644 index 0000000..985cf66 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/nand/remote.py @@ -0,0 +1,99 @@ +from progressbar import ProgressBar +from usbmux import usbmux +from util import hexdump, sizeof_fmt +import datetime +import hashlib +import struct +import os + +CMD_DUMP = 0 +CMD_PROXY = 1 +kIOFlashStorageOptionRawPageIO = 0x002 +kIOFlashStorageOptionBootPageIO = 0x100 + +class IOFlashStorageKitClient(object): + def __init__(self, udid=None, host="localhost", port=2000): + self.host = host + self.port = port + self.connect(udid) + + def connect(self, udid=None): + mux = usbmux.USBMux() + mux.process(1.0) + if not mux.devices: + print "Waiting for iOS device" + while not mux.devices: + mux.process(1.0) + if not mux.devices: + print "No device found" + return + dev = mux.devices[0] + try: + self.s = mux.connect(dev, self.port) + except: + raise Exception("Connexion to device %s port %d failed" % (dev.serial, self.port)) + + def send_command(self, cmd): + return self.s.send(struct.pack(" 0 and s.foo.meta.usnDec <= minUsn: + minUsn = s.foo.meta.usnDec; + MostRecentVFLCxtBlock = b + if MostRecentVFLCxtBlock == -1: + print "MostRecentVFLCxtBlock == -1" + return + last = None + for pageNum in xrange(0, self.pages_per_block, 1): + s,d = nand.readMetaPage(ce, MostRecentVFLCxtBlock, pageNum, _vfl_vsvfl_spare_data) + if not d: + break + vflctx = _vfl_vfl_context.parse(d) + if vfl_check_checksum(vflctx, _vfl_vfl_context): + last = vflctx + if not last: + raise Exception("VFL open FAIL 1") + self.vflContexts.append(last) + if last.version == 1 and last.usn_inc >= self.current_version: + self.current_version = last.usn_inc + self.context = last + if not self.context: + raise Exception("VFL open FAIL") + + print "VFL context open OK" + + def VFL_get_FTLCtrlBlock(self): + for ctx in self.vflContexts: + if ctx.usn_inc == self.current_version: + return ctx.control_block + + def vfl_is_good_block(self, bbt, block): + if block > self.blocks_per_ce: + raise Exception("vfl_is_good_block block %d out of bounds" % block) + index = block/8 + return ((bbt[index / 8] >> (7 - (index % 8))) & 0x1) == 0x1 + + def virtual_block_to_physical_block(self, ce, pBlock): + if self.vfl_is_good_block(self.vflContexts[ce].bad_block_table, pBlock): + return pBlock + ctx = self.vflContexts[ce] + for pwDesPbn in xrange(0, ctx.num_reserved_blocks): + if ctx.reserved_block_pool_map[pwDesPbn] == pBlock: + if pwDesPbn > self.blocks_per_ce: + raise Exception("Destination physical block for remapping is greater than number of blocks per bank!") + return ctx.reserved_block_pool_start + pwDesPbn + print "Bad block %d not remapped" % pBlock + return pBlock + + def virtual_page_number_to_virtual_address(self, vpn): + vbank = vpn % self.num_ce + vblock = vpn / self.pages_per_sublk + vpage = (vpn / self.num_ce) % self.pages_per_block + return vbank, vblock, vpage + + def read_single_page(self, vpn, key=None, lpn=None): + vpn += self.pages_per_sublk * self.FTLData_field_4 + vbank, vblock, vpage = self.virtual_page_number_to_virtual_address(vpn) + pblock = self.virtual_block_to_physical_block(vbank, vblock) + #print "VFL read_single_page %d => %d, %d" % (vpn,ce,pPage) + return self.nand.readPage(vbank, pblock*self.nand.pagesPerBlock + vpage, key, lpn) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/vsvfl.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/vsvfl.py new file mode 100644 index 0000000..40aca32 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/nand/vsvfl.py @@ -0,0 +1,193 @@ +from construct import * +from structs import next_power_of_two, PAGETYPE_VFL, CEIL_DIVIDE +from vfl import vfl_check_checksum, _vfl_vsvfl_spare_data + +""" +https://github.com/iDroid-Project/openiBoot/blob/master/vfl-vsvfl/vsvfl.c +https://github.com/iDroid-Project/openiBoot/blob/master/vfl-vsvfl/includes/vfl/vsvfl.h +""" + +_vfl_vsvfl_context = Struct("_vfl_vsvfl_context", + ULInt32("usn_inc"), + ULInt32("usn_dec"), + ULInt32("ftl_type"), + ULInt16("usn_block"), + ULInt16("usn_page"), + ULInt16("active_context_block"), + ULInt16("write_failure_count"), + ULInt16("bad_block_count"), + Array(4, ULInt8("replaced_block_count")), + ULInt16("num_reserved_blocks"), + ULInt16("field_1C"), + ULInt16("total_reserved_blocks"), + Array(6, ULInt8("field_20")), + Array(820, ULInt16("reserved_block_pool_map")), + Array(4, ULInt16("vfl_context_block")), + ULInt16("usable_blocks_per_bank"), + ULInt16("reserved_block_pool_start"), + Array(3, ULInt16("control_block")), + ULInt16("scrub_list_length"), + Array(20, ULInt16("scrub_list")), + Array(4, ULInt32("field_6CA")), + ULInt32("vendor_type"), + Array(204, ULInt8("field_6DE")), + ULInt16("remapping_schedule_start"), + Array(0x48, ULInt8("unk3")), + ULInt32("version"), + ULInt32("checksum1"), + ULInt32("checksum2") +) + + +class VSVFL(object): + def __init__(self, nand): + self.nand = nand + self.banks_per_ce_vfl = 1 + if self.nand.vendorType in [0x100010, 0x100014, 0x120014, 0x150011]: + self.banks_per_ce_vfl = 2 + self.banks_total = nand.nCEs * self.banks_per_ce_vfl + self.num_ce = nand.nCEs + self.banks_per_ce = nand.banks_per_ce_physical + self.blocks_per_ce = nand.blocksPerCE + self.pages_per_block = nand.pagesPerBlock + self.pages_per_block_2 = next_power_of_two(self.pages_per_block) + self.pages_per_sublk = self.pages_per_block * self.banks_per_ce_vfl * self.num_ce + self.blocks_per_bank = self.blocks_per_ce / self.banks_per_ce + self.blocks_per_bank_vfl = self.blocks_per_ce / self.banks_per_ce_vfl + self.vendorType = nand.vendorType + if self.vendorType == 0x10001: + self.virtual_to_physical = self.virtual_to_physical_10001 + elif self.vendorType == 0x150011: + self.virtual_to_physical = self.virtual_to_physical_100014 + elif self.vendorType in [0x100010, 0x100014, 0x120014]: + self.virtual_to_physical = self.virtual_to_physical_150011 + else: + raise Exception("VSVFL: unsupported vendor 0x%x" % self.vendorType) + self.bank_address_space = nand.bank_address_space + self.vflContexts = [] + self.bbt = [] + self.current_version = 0 + reserved_blocks = 0 + if self.nand.bfn: + reserved_blocks = 16 + fs_start_block = reserved_blocks+16 #XXX + for ce in xrange(self.num_ce): + vflctx = None + for b in xrange(reserved_blocks, fs_start_block): + s, d = nand.readMetaPage(ce, b, 0, _vfl_vsvfl_spare_data) + if not d: + continue + vflctx = _vfl_vsvfl_context.parse(d) + if not vfl_check_checksum(vflctx, _vfl_vsvfl_context): + vflctx = None + continue + break + if not vflctx: + raise Exception("Unable to find VSVFL context for CE %d" % ce) + MostRecentVFLCxtBlock = -1 + minUsn = 0xFFFFFFFF + for b in vflctx.vfl_context_block: + s, d = nand.readMetaPage(ce, b, 0, _vfl_vsvfl_spare_data) + if not d or s.type1 != PAGETYPE_VFL: + continue + if s.foo.meta.usnDec > 0 and s.foo.meta.usnDec <= minUsn: + minUsn = s.foo.meta.usnDec; + MostRecentVFLCxtBlock = b + if MostRecentVFLCxtBlock == -1: + print "MostRecentVFLCxtBlock == -1" + return + last = None + for pageNum in xrange(0, self.pages_per_block, 1): + s,d = nand.readMetaPage(ce, MostRecentVFLCxtBlock, pageNum, _vfl_vsvfl_spare_data) + if not d or s.type1 != PAGETYPE_VFL: + break + last = d + vflctx = _vfl_vsvfl_context.parse(last) + if not vfl_check_checksum(vflctx, _vfl_vsvfl_context): + print "VSVFL checksum FAIL" + self.vflContexts.append(vflctx) + if vflctx.version == 2 and vflctx.usn_inc >= self.current_version: + self.current_version = vflctx.usn_inc + self.context = vflctx + if not self.context: + raise Exception("VSVFL open FAIL") + + num_reserved = self.vflContexts[0].reserved_block_pool_start + num_non_reserved = self.blocks_per_bank_vfl - num_reserved + for ce in xrange(self.num_ce): + bbt = [0xFF] * (CEIL_DIVIDE(self.blocks_per_ce, 8)) + ctx = self.vflContexts[ce] + for bank in xrange(0, self.banks_per_ce_vfl): + for i in xrange(0, num_non_reserved): + mapEntry = ctx.reserved_block_pool_map[bank*num_non_reserved + i] + if mapEntry == 0xFFF0: + continue + if mapEntry < self.blocks_per_ce: + pBlock = mapEntry + elif mapEntry > 0xFFF0: + pBlock = self.virtual_block_to_physical_block(ce + bank * self.num_ce, num_reserved + i) + else: + print "VSVFL: bad map table" + bbt[pBlock / 8] &= ~(1 << (pBlock % 8)) + self.bbt.append(bbt) + print "VSVFL context open OK" + + def VFL_get_FTLCtrlBlock(self): + for ctx in self.vflContexts: + if ctx.usn_inc == self.current_version: + return ctx.control_block + + def virtual_to_physical_10001(self, vBank, vPage): + return vBank, vPage + + def virtual_to_physical_100014(self, vBank, vPage): + pBank = vBank / self.num_ce; + pPage = ((self.pages_per_block - 1) & vPage) | (2 * (~(self.pages_per_block - 1) & vPage)) + if (pBank & 1): + pPage |= self.pages_per_block + return vBank % self.num_ce, pPage + + def virtual_to_physical_150011(self, vBank, vPage): + pBlock = 2 * (vPage / self.pages_per_block) + if(vBank % (2 * self.num_ce) >= self.num_ce): + pBlock += 1 + return vBank % self.num_ce, self.pages_per_block * pBlock | (vPage % 128) + + def virtual_block_to_physical_block(self, vBank, vBlock): + ce, pPage = self.virtual_to_physical(vBank, self.pages_per_block * vBlock) + return pPage / self.pages_per_block + + def vfl_is_good_block(self, bbt, block): + if block > self.blocks_per_ce: + raise Exception("vfl_is_good_block block %d out of bounds" % block) + return (bbt[block / 8] & (1 << (block % 8))) != 0 + + def remap_block(self, ce, pBlock): + if self.vfl_is_good_block(self.bbt[ce], pBlock): + return pBlock + ctx = self.vflContexts[ce] + for pwDesPbn in xrange(0, self.blocks_per_ce - ctx.reserved_block_pool_start * self.banks_per_ce_vfl): + if ctx.reserved_block_pool_map[pwDesPbn] == pBlock: + vBank = ce + self.num_ce * (pwDesPbn / (self.blocks_per_bank_vfl - ctx.reserved_block_pool_start)) + vBlock = ctx.reserved_block_pool_start + (pwDesPbn % (self.blocks_per_bank_vfl - ctx.reserved_block_pool_start)) + z = self.virtual_block_to_physical_block(vBank, vBlock) + #print "remapped block %d => %d" % (pBlock, z) + return z + print "Bad block %d not remapped" % pBlock + return pBlock + + def virtual_page_number_to_physical(self, vpn): + vBank = vpn % self.banks_total + ce = vBank % self.nand.nCEs + + pBlock = self.virtual_block_to_physical_block(vBank, vpn / self.pages_per_sublk) + pBlock = self.remap_block(ce, pBlock) + bank_offset = self.bank_address_space * (pBlock / self.blocks_per_bank) + page = self.pages_per_block_2 * (bank_offset + (pBlock % self.blocks_per_bank)) \ + + ((vpn % self.pages_per_sublk) / self.banks_total) + return ce, page + + def read_single_page(self, vpn, key=None, lpn=None): + ce, pPage = self.virtual_page_number_to_physical(vpn) + #print "VFL read_single_page %d => %d, %d" % (vpn,ce,pPage) + return self.nand.readPage(ce, pPage, key, lpn) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/nand/yaftl.py b/dump-imessages/iphone-dataprotection/python_scripts/nand/yaftl.py new file mode 100644 index 0000000..7b0c7ce --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/nand/yaftl.py @@ -0,0 +1,357 @@ +from array import array +from construct.core import Struct, Union +from construct.macros import * +from progressbar import ProgressBar +from structs import * +import struct + + +#https://github.com/iDroid-Project/openiBoot/blob/master/openiboot/ftl-yaftl/yaftl.c +YAFTL_CXT = Struct("YAFTL_CXT", + String("version", 4), + ULInt32("unknCalculatedValue0"), + ULInt32("totalPages"), + ULInt32("latestUserBlock"), + ULInt32("cxt_unkn0_usn"), + ULInt32("latestIndexBlock"), + ULInt32("maxIndexUsn"), + ULInt32("blockStatsField4"), + ULInt32("blockStatsField10"), + ULInt32("numAllocatedBlocks"), + ULInt32("numIAllocatedBlocks"), + ULInt32("unk184_0xA"), + Array(10, ULInt32("cxt_unkn1")), + ULInt32("field_58"), + ULInt16("tocArrayLength"), + ULInt16("tocPagesPerBlock"), + ULInt16("tocEntriesPerPage"), + ULInt16("unkn_0x2A"), + ULInt16("userPagesPerBlock"), + ULInt16("unk64"), + Array(11, ULInt32("cxt_unkn2")), + ULInt8("unk188_0x63"), +) + +TOCStruct = Struct("TOCStruct", + ULInt32("indexPage"), + ULInt16("cacheNum"), + ULInt16("TOCUnkMember2"), +) + +BlockStats = Struct("BlockStats", + ULInt32("numAllocated"), + ULInt32("field_4"), + ULInt32("numValidDPages"), + ULInt32("numIAllocated"), + ULInt32("field_10"), + ULInt32("numValidIPages"), + ULInt32("numFree"), + ULInt32("field_1C"), +) + + +class YAFTL(object): + def __init__(self, vfl, usn=0): + self.vfl = vfl + self.lpnToVpn = None + bytesPerPage = vfl.nand.pageSize + numBlocks = vfl.context.usable_blocks_per_bank + self.blankPage = bytesPerPage * "\x00" + self.numBlocks = numBlocks + self.tocPagesPerBlock = vfl.pages_per_sublk * 4 / bytesPerPage + if vfl.pages_per_sublk * 4 % bytesPerPage: + self.tocPagesPerBlock += 1 + self.tocEntriesPerPage = bytesPerPage / 4 + self.tocArrayLength = CEIL_DIVIDE(vfl.pages_per_sublk * numBlocks * 4, bytesPerPage) + self.nPagesTocPageIndices = CEIL_DIVIDE(self.tocArrayLength * 4, bytesPerPage) + self.nPagesBlockStatuses = CEIL_DIVIDE(numBlocks * 1, bytesPerPage) + self.nPagesBlockReadCounts = CEIL_DIVIDE(numBlocks * 2, bytesPerPage) + self.nPagesBlockEraseCounts = CEIL_DIVIDE(numBlocks * 4, bytesPerPage) + self.nPagesBlockValidPagesDNumbers = self.nPagesBlockReadCounts + self.nPagesBlockValidPagesINumbers = self.nPagesBlockReadCounts + self.ctrlBlockPageOffset = self.nPagesTocPageIndices \ + + self.nPagesBlockStatuses \ + + self.nPagesBlockReadCounts \ + + self.nPagesBlockEraseCounts \ + + self.nPagesBlockValidPagesDNumbers \ + + self.nPagesBlockValidPagesINumbers \ + + 2 * self.tocPagesPerBlock \ + + 2 + self.totalPages = (self.numBlocks - 8) * (self.vfl.pages_per_sublk - self.tocPagesPerBlock)# - unknCalculatedValue0 + self.userPagesPerBlock = self.vfl.pages_per_sublk - self.tocPagesPerBlock + maxUsn = 0 + ftlCtrlBlock = -1 + for b in self.vfl.VFL_get_FTLCtrlBlock(): + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk) + if not d: + continue + if usn and s.usn > usn: + break + if s.usn > maxUsn: + maxUsn = s.usn + ftlCtrlBlock = b + if ftlCtrlBlock == -1 or not maxUsn: + print "ftlCtrlBlock not found, restore needed" + self.YAFTL_restore() + return + i = 0 + maxUsn = 0 + while i < self.vfl.pages_per_sublk - self.ctrlBlockPageOffset: + s,d = self.YAFTL_readPage(ftlCtrlBlock*self.vfl.pages_per_sublk + i + self.ctrlBlockPageOffset) + if not d: + if self.YAFTL_readCxtInfo(ftlCtrlBlock*self.vfl.pages_per_sublk + i): + return + print "YaFTL_readCxtInfo FAIL, restore needed maxUsn=%d" % maxUsn + self.YAFTL_restore() + return + if s and s.usn > maxUsn: + maxUsn = s.usn + i += self.ctrlBlockPageOffset + 1 + print "YaFTL open fail" + self.YAFTL_restore() + + def readBTOCPages(self, block, maxVal): + data = "" + for i in xrange(self.tocPagesPerBlock): + s,d = self.YAFTL_readPage((block+1) * self.vfl.pages_per_sublk - self.tocPagesPerBlock + i) + if not s: + return None + data += d + btoc = array("I",data) + for i in xrange(len(btoc)): + if btoc[i] > maxVal: + btoc[i] = 0xFFFFFFFF + return btoc + + def YAFTL_restore(self): + self.lpnToVpn = self.vfl.nand.loadCachedData("yaftlrestore") + if self.lpnToVpn: + print "Found cached FTL restore information" + return + userBlocks = {} + indexBlocks = {} + print "FTL restore in progress" + pbar = ProgressBar(self.numBlocks) + pbar.start() + for b in xrange(0, self.numBlocks): + pbar.update(b) + #read fist page in block, if empty then block is empty + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + 0) + if not s: + continue + if s.type == PAGETYPE_INDEX: + indexBlocks[s.usn] = b + elif s.type == PAGETYPE_LBN: + if userBlocks.has_key(s.usn): + print "Two blocks with same USN, something is weird" + userBlocks[s.usn] = b + elif s.type == PAGETYPE_FTL_CLEAN: + pass + pbar.finish() + lpnToVpn = {} + for usn in sorted(userBlocks.keys(), reverse=True): + b = userBlocks[usn] + btoc = self.readBTOCPages(b, self.totalPages) + if btoc: + for i in xrange(self.userPagesPerBlock-1,-1, -1): + if not lpnToVpn.has_key(btoc[i]): + lpnToVpn[btoc[i]] = b * self.vfl.pages_per_sublk + i + else: + print "BTOC not found for block %d (usn %d), scanning all pages" % (b, usn) + i = 0 + for p in xrange(self.vfl.pages_per_sublk - self.tocPagesPerBlock -1, -1, -1): + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + p) + if s: + i+= 1 + if s and not lpnToVpn.has_key(s.lpn): + lpnToVpn[s.lpn] = b * self.vfl.pages_per_sublk + p + print "%d used pages in block" % i + self.vfl.nand.cacheData("yaftlrestore", lpnToVpn) + self.lpnToVpn = lpnToVpn + return lpnToVpn + + def YAFTL_readCxtInfo(self, page): + s,d = self.YAFTL_readPage(page) + if not s or s.type != PAGETYPE_FTL_CLEAN: + return False + ctx = YAFTL_CXT.parse(d) + ctx.spareUsn = s.usn + if ctx.version != "CX01": + print "Wrong FTL version %s" % ctx.version + return False + self.usn = s.usn + pageToRead = page + 1; + userTOCBuffer = self.YAFTL_read_n_Page(pageToRead, self.tocPagesPerBlock) + if not userTOCBuffer: + raise(Exception("userTOCBuffer")) + pageToRead += self.tocPagesPerBlock + indexTOCBuffer = self.YAFTL_read_n_Page(pageToRead, self.tocPagesPerBlock) + pageToRead += self.tocPagesPerBlock + 1 + tocArrayIndexPages = self.YAFTL_read_n_Page(pageToRead, self.nPagesTocPageIndices) + self.tocArrayIndexPages = array("I", tocArrayIndexPages) + assert self.tocArrayIndexPages.itemsize == 4 + self.indexCache = {} + pageToRead += self.nPagesTocPageIndices + + if False: #we don't care, we just want to read + blockStatuses = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockStatuses) + pageToRead += self.nPagesBlockStatuses + blockReadCounts = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockReadCounts) + pageToRead += self.nPagesBlockReadCounts + blockEraseCounts = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockEraseCounts) + pageToRead += self.nPagesBlockEraseCounts + validPagesINo = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockValidPagesINumbers) + pageToRead += self.nPagesBlockValidPagesINumbers + validPagesDNo = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockValidPagesDNumbers) + + print "YaFTL context OK, version=%s maxIndexUsn=%d context usn=%d" % (ctx.version, ctx.maxIndexUsn, self.usn) + return True + + def YAFTL_read_n_Page(self, page, n, failIfBlank=False): + r = "" + for i in xrange(0, n): + s,d = self.YAFTL_readPage(page +i) + if not d: + if failIfBlank: + return + return r + r += d + return r + + def YAFTL_readPage(self, page, key=META_KEY, lpn=None): + return self.vfl.read_single_page(page, key, lpn) + + def build_lpn_to_vpn(self): + lpnToVpn = {} + for p in xrange(self.totalPages): + x = self.translateLPNtoVPN(p) + if x != 0xFFFFFFFF: + lpnToVpn[p] = x + self.vfl.nand.cacheData("currentftl", lpnToVpn) + return lpnToVpn + + def translateLPNtoVPN(self, lpn): + if self.lpnToVpn: + return self.lpnToVpn.get(lpn, 0xFFFFFFFF) + tocPageNum = (lpn) / self.tocEntriesPerPage + indexPage = self.tocArrayIndexPages[tocPageNum] + if indexPage == 0xffffffff: + return 0xffffffff + #print "indexPage %x" % indexPage + if self.indexCache.has_key(indexPage): + tocPageBuffer = self.indexCache[indexPage] + else: + s,tocPageBuffer = self.YAFTL_readPage(indexPage) + if not tocPageBuffer: + print "tocPageBuffer fail" + return 0xffffffff + assert s.type == PAGETYPE_INDEX + tocPageBuffer = array("I", tocPageBuffer) + self.indexCache[indexPage] = tocPageBuffer + + tocEntry = tocPageBuffer[lpn % self.tocEntriesPerPage] + return tocEntry + + def readLPN(self, lpn, key=None):#, nPages): + vpn = self.translateLPNtoVPN(lpn) + if vpn == 0xffffffff: + return self.blankPage + #print "tocEntry %d" % tocEntry + #print "FTL %d => %d" % (lpn, vpn) + s,d = self.YAFTL_readPage(vpn, key, lpn) + if d == None: + return self.blankPage + if s.lpn != lpn: + raise Exception("YAFTL translation FAIL spare lpn=%d vs expected %d" % (s.lpn, lpn)) + return d + + def YAFTL_lookup1(self): + hax = self.vfl.nand.loadCachedData("YAFTL_lookup1") + if hax: + print "Found cached FTL lookup table" + return hax + userBlocks = {} + indexBlocks = {} + print "Building FTL lookup table v1" + pbar = ProgressBar(self.numBlocks) + pbar.start() + for b in xrange(0, self.numBlocks): + pbar.update(b) + #read fist page in block, if empty then block is empty + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + 0) + if not s: + continue + if s.type == PAGETYPE_INDEX: + indexBlocks[s.usn] = b + elif s.type == PAGETYPE_LBN: + if userBlocks.has_key(s.usn): + print "Two blocks with same USN, something is weird" + userBlocks[s.usn] = b + elif s.type == PAGETYPE_FTL_CLEAN: + pass#print b, "ftl block" + pbar.finish() + lpnToVpn = {} + for usn in sorted(userBlocks.keys(), reverse=False): + b = userBlocks[usn] + btoc = self.readBTOCPages(b, self.totalPages) + #print usn, b + if btoc: + for i in xrange(self.userPagesPerBlock-1,-1, -1): + lpnToVpn.setdefault(btoc[i], []).append(b * self.vfl.pages_per_sublk + i) + else: + #print "btoc not found for block %d (usn %d), scanning all pages" % (b, usn) + i = 0 + usn = -1 + for p in xrange(self.vfl.pages_per_sublk - self.tocPagesPerBlock -1, -1, -1): + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + p) + if not s: + break + i+= 1 + if usn == -1: + usn = s.usn + if usn != s.usn: + #print "Two usns in same block %d %d" % (usn, s.usn) + usn = s.usn + lpnToVpn.setdefault(s.lpn, []).append(b * self.vfl.pages_per_sublk + p) + #print "%d used pages in block" % i + #self.vfl.nand.cacheData("YAFTL_lookup1", (lpnToVpn, userBlocks)) + return lpnToVpn, userBlocks + + def YAFTL_hax2(self): + hax = self.vfl.nand.loadCachedData("YAFTL_hax2") + if hax: + print "Found cached FTL HAX2 information" + return hax + + print "FTL hax2 in progress" + pbar = ProgressBar(self.numBlocks) + pbar.start() + lpnToVpn = {} + for b in xrange(0, self.numBlocks): + pbar.update(b) + #read fist page in block, if empty then block is empty (right?) + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + 0) + if not s: + continue + if s.type == PAGETYPE_LBN: + i = 0 + usn = -1 + for p in xrange(0, self.vfl.pages_per_sublk - self.tocPagesPerBlock): + s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + p) + if not s: + break + lpnToVpn.setdefault(s.lpn, {}).setdefault(s.usn, []).append(b * self.vfl.pages_per_sublk + p) + i+= 1 + + pbar.finish() + self.vfl.nand.cacheData("YAFTL_hax2", lpnToVpn) + return lpnToVpn + + def block_lpn_to_vpn(self, block): + res = {} + for p in xrange(0, self.vfl.pages_per_sublk - self.tocPagesPerBlock): + s,d = self.YAFTL_readPage(block * self.vfl.pages_per_sublk + p) + if not s: + break + res[s.lpn] = block * self.vfl.pages_per_sublk + p + return res diff --git a/dump-imessages/iphone-dataprotection/python_scripts/usbmux/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/usbmux/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dump-imessages/iphone-dataprotection/python_scripts/usbmux/usbmux.py b/dump-imessages/iphone-dataprotection/python_scripts/usbmux/usbmux.py new file mode 100644 index 0000000..79ec26a --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/usbmux/usbmux.py @@ -0,0 +1,246 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# usbmux.py - usbmux client library for Python +# +# Copyright (C) 2009 Hector Martin "marcan" +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 or version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import socket, struct, select, sys + +try: + import plistlib + haveplist = True +except: + haveplist = False + +class MuxError(Exception): + pass + +class MuxVersionError(MuxError): + pass + +class SafeStreamSocket: + def __init__(self, address, family): + self.sock = socket.socket(family, socket.SOCK_STREAM) + self.sock.connect(address) + def send(self, msg): + totalsent = 0 + while totalsent < len(msg): + sent = self.sock.send(msg[totalsent:]) + if sent == 0: + raise MuxError("socket connection broken") + totalsent = totalsent + sent + def recv(self, size): + msg = '' + while len(msg) < size: + chunk = self.sock.recv(size-len(msg)) + if chunk == '': + raise MuxError("socket connection broken") + msg = msg + chunk + return msg + +class MuxDevice(object): + def __init__(self, devid, usbprod, serial, location): + self.devid = devid + self.usbprod = usbprod + self.serial = serial + self.location = location + def __str__(self): + return ""%(self.devid, self.usbprod, self.serial, self.location) + +class BinaryProtocol(object): + TYPE_RESULT = 1 + TYPE_CONNECT = 2 + TYPE_LISTEN = 3 + TYPE_DEVICE_ADD = 4 + TYPE_DEVICE_REMOVE = 5 + VERSION = 0 + def __init__(self, socket): + self.socket = socket + self.connected = False + + def _pack(self, req, payload): + if req == self.TYPE_CONNECT: + return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00" + elif req == self.TYPE_LISTEN: + return "" + else: + raise ValueError("Invalid outgoing request type %d"%req) + + def _unpack(self, resp, payload): + if resp == self.TYPE_RESULT: + return {'Number':struct.unpack("I", payload)[0]} + elif resp == self.TYPE_DEVICE_ADD: + devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload) + serial = serial.split("\0")[0] + return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}} + elif resp == self.TYPE_DEVICE_REMOVE: + devid = struct.unpack("I", payload)[0] + return {'DeviceID': devid} + else: + raise MuxError("Invalid incoming request type %d"%req) + + def sendpacket(self, req, tag, payload={}): + payload = self._pack(req, payload) + if self.connected: + raise MuxError("Mux is connected, cannot issue control packets") + length = 16 + len(payload) + data = struct.pack("IIII", length, self.VERSION, req, tag) + payload + self.socket.send(data) + def getpacket(self): + if self.connected: + raise MuxError("Mux is connected, cannot issue control packets") + dlen = self.socket.recv(4) + dlen = struct.unpack("I", dlen)[0] + body = self.socket.recv(dlen - 4) + version, resp, tag = struct.unpack("III",body[:0xc]) + if version != self.VERSION: + raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version)) + payload = self._unpack(resp, body[0xc:]) + return (resp, tag, payload) + +class PlistProtocol(BinaryProtocol): + TYPE_RESULT = "Result" + TYPE_CONNECT = "Connect" + TYPE_LISTEN = "Listen" + TYPE_DEVICE_ADD = "Attached" + TYPE_DEVICE_REMOVE = "Detached" #??? + TYPE_PLIST = 8 + VERSION = 1 + def __init__(self, socket): + if not haveplist: + raise Exception("You need the plistlib module") + BinaryProtocol.__init__(self, socket) + + def _pack(self, req, payload): + return payload + + def _unpack(self, resp, payload): + return payload + + def sendpacket(self, req, tag, payload={}): + payload['ClientVersionString'] = 'usbmux.py by marcan' + if isinstance(req, int): + req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2] + payload['MessageType'] = req + payload['ProgName'] = 'tcprelay' + BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload)) + def getpacket(self): + resp, tag, payload = BinaryProtocol.getpacket(self) + if resp != self.TYPE_PLIST: + raise MuxError("Received non-plist type %d"%resp) + payload = plistlib.readPlistFromString(payload) + return payload['MessageType'], tag, payload + +class MuxConnection(object): + def __init__(self, socketpath, protoclass): + self.socketpath = socketpath + if sys.platform in ['win32', 'cygwin']: + family = socket.AF_INET + address = ('127.0.0.1', 27015) + else: + family = socket.AF_UNIX + address = self.socketpath + self.socket = SafeStreamSocket(address, family) + self.proto = protoclass(self.socket) + self.pkttag = 1 + self.devices = [] + + def _getreply(self): + while True: + resp, tag, data = self.proto.getpacket() + if resp == self.proto.TYPE_RESULT: + return tag, data + else: + raise MuxError("Invalid packet type received: %d"%resp) + def _processpacket(self): + resp, tag, data = self.proto.getpacket() + if resp == self.proto.TYPE_DEVICE_ADD: + self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID'])) + elif resp == self.proto.TYPE_DEVICE_REMOVE: + for dev in self.devices: + if dev.devid == data['DeviceID']: + self.devices.remove(dev) + elif resp == self.proto.TYPE_RESULT: + raise MuxError("Unexpected result: %d"%resp) + else: + raise MuxError("Invalid packet type received: %d"%resp) + def _exchange(self, req, payload={}): + mytag = self.pkttag + self.pkttag += 1 + self.proto.sendpacket(req, mytag, payload) + recvtag, data = self._getreply() + if recvtag != mytag: + raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag)) + return data['Number'] + + def listen(self): + ret = self._exchange(self.proto.TYPE_LISTEN) + if ret != 0: + raise MuxError("Listen failed: error %d"%ret) + def process(self, timeout=None): + if self.proto.connected: + raise MuxError("Socket is connected, cannot process listener events") + rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout) + if xlo: + self.socket.sock.close() + raise MuxError("Exception in listener socket") + if rlo: + self._processpacket() + def connect(self, device, port): + ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)}) + if ret != 0: + raise MuxError("Connect failed: error %d"%ret) + self.proto.connected = True + return self.socket.sock + def close(self): + self.socket.sock.close() + +class USBMux(object): + def __init__(self, socketpath=None): + if socketpath is None: + if sys.platform == 'darwin': + socketpath = "/var/run/usbmuxd" + else: + socketpath = "/var/run/usbmuxd" + self.socketpath = socketpath + self.listener = MuxConnection(socketpath, BinaryProtocol) + try: + self.listener.listen() + self.version = 0 + self.protoclass = BinaryProtocol + except MuxVersionError: + self.listener = MuxConnection(socketpath, PlistProtocol) + self.listener.listen() + self.protoclass = PlistProtocol + self.version = 1 + self.devices = self.listener.devices + def process(self, timeout=None): + self.listener.process(timeout) + def connect(self, device, port): + connector = MuxConnection(self.socketpath, self.protoclass) + return connector.connect(device, port) + +if __name__ == "__main__": + mux = USBMux() + print "Waiting for devices..." + if not mux.devices: + mux.process(0.1) + while True: + print "Devices:" + for dev in mux.devices: + print dev + mux.process() diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/__init__.py b/dump-imessages/iphone-dataprotection/python_scripts/util/__init__.py new file mode 100644 index 0000000..a7be10d --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/__init__.py @@ -0,0 +1,123 @@ +import glob +import plistlib +import os +from bplist import BPlistReader +import cPickle +import gzip + +def read_file(filename): + f = open(filename, "rb") + data = f.read() + f.close() + return data + +def write_file(filename,data): + f = open(filename, "wb") + f.write(data) + f.close() + +def makedirs(dirs): + try: + os.makedirs(dirs) + except: + pass + +def getHomePath(foldername, filename): + home = os.path.expanduser('~') + folderpath = os.path.join(home, foldername) + if not os.path.exists(folderpath): + makedirs(folderpath) + return os.path.join(folderpath, filename) + +def readHomeFile(foldername, filename): + path = getHomePath(foldername, filename) + if not os.path.exists(path): + return None + return read_file(path) + +#return path to HOME+foldername+filename +def writeHomeFile(foldername, filename, data): + filepath = getHomePath(foldername, filename) + write_file(filepath, data) + return filepath + +def readPlist(filename): + f = open(filename,"rb") + d = f.read(16) + f.close() + if d.startswith("bplist"): + return BPlistReader.plistWithFile(filename) + else: + return plistlib.readPlist(filename) + +def parsePlist(s): + if s.startswith("bplist"): + return BPlistReader.plistWithString(s) + else: + return plistlib.readPlistFromString(s) + +#http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size +def sizeof_fmt(num): + for x in ['bytes','KB','MB','GB','TB']: + if num < 1024.0: + return "%d%s" % (num, x) + num /= 1024.0 + +#http://www.5dollarwhitebox.org/drupal/node/84 +def convert_bytes(bytes): + bytes = float(bytes) + if bytes >= 1099511627776: + terabytes = bytes / 1099511627776 + size = '%.2fT' % terabytes + elif bytes >= 1073741824: + gigabytes = bytes / 1073741824 + size = '%.2fG' % gigabytes + elif bytes >= 1048576: + megabytes = bytes / 1048576 + size = '%.2fM' % megabytes + elif bytes >= 1024: + kilobytes = bytes / 1024 + size = '%.2fK' % kilobytes + else: + size = '%.2fb' % bytes + return size + +def xor_strings(a,b): + r="" + for i in xrange(len(a)): + r+= chr(ord(a[i])^ord(b[i])) + return r + +hex = lambda data: " ".join("%02X" % ord(i) for i in data) +ascii = lambda data: "".join(c if 31 < ord(c) < 127 else "." for c in data) + +def hexdump(d): + for i in xrange(0,len(d),16): + data = d[i:i+16] + print "%08X | %s | %s" % (i, hex(data).ljust(47), ascii(data)) + +def search_plist(directory, matchDict): + for p in map(os.path.normpath, glob.glob(directory + "/*.plist")): + try: + d = plistlib.readPlist(p) + ok = True + for k,v in matchDict.items(): + if d.get(k) != v: + ok = False + break + if ok: + print "Using plist file %s" % p + return d + except: + continue + +def save_pickle(filename,data): + f = gzip.open(filename,"wb") + cPickle.dump(data, f, cPickle.HIGHEST_PROTOCOL) + f.close() + +def load_pickle(filename): + f = gzip.open(filename,"rb") + data = cPickle.load(f) + f.close() + return data diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/asciitables.py b/dump-imessages/iphone-dataprotection/python_scripts/util/asciitables.py new file mode 100644 index 0000000..cbcd98d --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/asciitables.py @@ -0,0 +1,29 @@ + +def print_table(title, headers, rows): + widths = [] + + for i in xrange(len(headers)): + z = map(len, [str(row[i]) for row in rows]) + z.append(len(headers[i])) + widths.append(max(z)) + + width = sum(widths) + len(headers) + 1 + print "-"* width + print "|" + title.center(width-2) + "|" + print "-"* width + hline = "|" + for i in xrange(len(headers)): + hline += headers[i].ljust(widths[i]) + "|" + print hline + + print "-"* width + for row in rows: + line = "|" + for i in xrange(len(row)): + line += str(row[i]).ljust(widths[i]) + "|" + print line + + if len(rows) == 0: + print "|" + "No entries".center(width-2) + "|" + print "-"* width + print "" diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/bdev.py b/dump-imessages/iphone-dataprotection/python_scripts/util/bdev.py new file mode 100644 index 0000000..bf379d6 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/bdev.py @@ -0,0 +1,139 @@ +import os +import sys +from util import sizeof_fmt, hexdump +from progressbar import ProgressBar +from crypto.aes import AESdecryptCBC, AESencryptCBC + +class FileBlockDevice(object): + def __init__(self, filename, offset=0, write=False): + flag = os.O_RDONLY if not write else os.O_RDWR + if sys.platform == 'win32': + flag = flag | os.O_BINARY + self.filename = filename + self.fd = os.open(filename, flag) + self.offset = offset + self.writeFlag = write + self.size = os.path.getsize(filename) + self.setBlockSize(8192) + + def setBlockSize(self, bs): + self.blockSize = bs + self.nBlocks = self.size / bs + + def readBlock(self, blockNum): + os.lseek(self.fd, self.offset + self.blockSize * blockNum, os.SEEK_SET) + return os.read(self.fd, self.blockSize) + + def write(self, offset, data): + if self.writeFlag: #fail silently for testing + os.lseek(self.fd, self.offset + offset, os.SEEK_SET) + return os.write(self.fd, data) + + def writeBlock(self, lba, block): + return self.write(lba*self.blockSize, block) + +class FTLBlockDevice(object): + def __init__(self, nand, first_lba, last_lba, defaultKey=None): + self.nand = nand + self.pageSize = nand.pageSize + self.blockSize = 0 #not used + self.key = defaultKey + self.lbaoffset = first_lba + self.last_lba = last_lba + self.setBlockSize(self.pageSize) + + def setBlockSize(self, bs): + self.blockSize = bs + self.lbasPerPage = self.pageSize / bs + self.lbaToLpnFactor = bs / (self.pageSize+0.0) + self.pagesPerLBA = bs / self.pageSize + if bs > self.pageSize: + pass#raise Exception("FTLBlockDevice lba-size > pageSize not handled") + + def readBlock(self, blockNum): + #if (self.lbaoffset + blockNum / self.lbasPerPage) > self.last_lba: + # print "readBlock past last lba", blockNum + # print "readBlock past last lba", blockNum + # return "\x00" * self.blockSize + lpn = int(self.lbaoffset + blockNum * self.lbaToLpnFactor) + d = self.nand.readLPN(lpn, self.key) + for i in xrange(1, self.pagesPerLBA): + d += self.nand.readLPN(lpn + i, self.key) + if self.lbasPerPage: + zz = blockNum % self.lbasPerPage + return d[zz*self.blockSize:(zz+1)*self.blockSize] + return d + + def write(self, offset, data): + raise Exception("FTLBlockDevice write method not implemented") + + def writeBlock(self, lba, block): + raise Exception("FTLBlockDevice writeBlock method not implemented") + + def dumpToFile(self, outputfilename): + hs = sizeof_fmt((self.last_lba - self.lbaoffset) * self.pageSize) + print "Dumping partition to %s (%s)" % (outputfilename, hs) + flags = os.O_CREAT | os.O_RDWR + if sys.platform == "win32": + flags |= os.O_BINARY + fd=os.open(outputfilename, flags) + + pbar = ProgressBar(self.last_lba - self.lbaoffset - 1) + pbar.start() + for i in xrange(self.lbaoffset, self.last_lba): + pbar.update(i-self.lbaoffset) + d = self.nand.readLPN(i, self.key) + if i == self.lbaoffset and d[0x400:0x402] != "HX": + print "FAIL? Not HFS partition or wrong key" + os.write(fd, d) + pbar.finish() + os.close(fd) + +class IMG3BlockDevice(object): + def __init__(self, filename, key, iv, write=False): + flag = os.O_RDONLY if not write else os.O_RDWR + if sys.platform == 'win32': + flag = flag | os.O_BINARY + self.filename = filename + self.fd = os.open(filename, flag) + self.writeFlag = write + d = os.read(self.fd, 8192) + if d[:4] != "3gmI": + raise Exception("IMG3BlockDevice bad magic %s" % d[:4]) + if d[0x34:0x38] != "ATAD": + raise Exception("Fu") + self.encrypted = True + self.key = key + self.iv0 = iv + self.offset = 0x40 + self.size = os.path.getsize(filename) + self.setBlockSize(8192) + + def setBlockSize(self, bs): + self.blockSize = bs + self.nBlocks = self.size / bs + self.ivs = {0: self.iv0} + + def getIVforBlock(self, blockNum): + #read last 16 bytes of previous block to get IV + if not self.ivs.has_key(blockNum): + os.lseek(self.fd, self.offset + self.blockSize * blockNum - 16, os.SEEK_SET) + self.ivs[blockNum] = os.read(self.fd, 16) + return self.ivs[blockNum] + + def readBlock(self, blockNum): + os.lseek(self.fd, self.offset + self.blockSize * blockNum, os.SEEK_SET) + data = os.read(self.fd, self.blockSize) + if self.encrypted: + data = AESdecryptCBC(data, self.key, self.getIVforBlock(blockNum)) + return data + + def _write(self, offset, data): + if self.writeFlag: #fail silently for testing + os.lseek(self.fd, self.offset + offset, os.SEEK_SET) + return os.write(self.fd, data) + + def writeBlock(self, lba, data): + if self.encrypted: + data = AESencryptCBC(data, self.key, self.getIVforBlock(lba)) + return self._write(lba*self.blockSize, data) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/bplist.py b/dump-imessages/iphone-dataprotection/python_scripts/util/bplist.py new file mode 100644 index 0000000..2190d06 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/bplist.py @@ -0,0 +1,251 @@ +""" +http://github.com/farcaller/bplist-python/blob/master/bplist.py +""" +import struct +import plistlib +from datetime import datetime, timedelta + +class BPListWriter(object): + def __init__(self, objects): + self.bplist = "" + self.objects = objects + + def binary(self): + '''binary -> string + + Generates bplist + ''' + self.data = 'bplist00' + + # TODO: flatten objects and count max length size + + # TODO: write objects and save offsets + + # TODO: write offsets + + # TODO: write metadata + + return self.data + + def write(self, filename): + ''' + + Writes bplist to file + ''' + if self.bplist != "": + pass + # TODO: save self.bplist to file + else: + raise Exception('BPlist not yet generated') + +class BPlistReader(object): + def __init__(self, s): + self.data = s + self.objects = [] + self.resolved = {} + + def __unpackIntStruct(self, sz, s): + '''__unpackIntStruct(size, string) -> int + + Unpacks the integer of given size (1, 2 or 4 bytes) from string + ''' + if sz == 1: + ot = '!B' + elif sz == 2: + ot = '!H' + elif sz == 4: + ot = '!I' + elif sz == 8: + ot = '!Q' + else: + raise Exception('int unpack size '+str(sz)+' unsupported') + return struct.unpack(ot, s)[0] + + def __unpackInt(self, offset): + '''__unpackInt(offset) -> int + + Unpacks int field from plist at given offset + ''' + return self.__unpackIntMeta(offset)[1] + + def __unpackIntMeta(self, offset): + '''__unpackIntMeta(offset) -> (size, int) + + Unpacks int field from plist at given offset and returns its size and value + ''' + obj_header = struct.unpack('!B', self.data[offset])[0] + obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F) + int_sz = 2**obj_info + return int_sz, self.__unpackIntStruct(int_sz, self.data[offset+1:offset+1+int_sz]) + + def __resolveIntSize(self, obj_info, offset): + '''__resolveIntSize(obj_info, offset) -> (count, offset) + + Calculates count of objref* array entries and returns count and offset to first element + ''' + if obj_info == 0x0F: + ofs, obj_count = self.__unpackIntMeta(offset+1) + objref = offset+2+ofs + else: + obj_count = obj_info + objref = offset+1 + return obj_count, objref + + def __unpackFloatStruct(self, sz, s): + '''__unpackFloatStruct(size, string) -> float + + Unpacks the float of given size (4 or 8 bytes) from string + ''' + if sz == 4: + ot = '!f' + elif sz == 8: + ot = '!d' + else: + raise Exception('float unpack size '+str(sz)+' unsupported') + return struct.unpack(ot, s)[0] + + def __unpackFloat(self, offset): + '''__unpackFloat(offset) -> float + + Unpacks float field from plist at given offset + ''' + obj_header = struct.unpack('!B', self.data[offset])[0] + obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F) + int_sz = 2**obj_info + return int_sz, self.__unpackFloatStruct(int_sz, self.data[offset+1:offset+1+int_sz]) + + def __unpackDate(self, offset): + td = int(struct.unpack(">d", self.data[offset+1:offset+9])[0]) + return datetime(year=2001,month=1,day=1) + timedelta(seconds=td) + + def __unpackItem(self, offset): + '''__unpackItem(offset) + + Unpacks and returns an item from plist + ''' + obj_header = struct.unpack('!B', self.data[offset])[0] + obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F) + if obj_type == 0x00: + if obj_info == 0x00: # null 0000 0000 + return None + elif obj_info == 0x08: # bool 0000 1000 // false + return False + elif obj_info == 0x09: # bool 0000 1001 // true + return True + elif obj_info == 0x0F: # fill 0000 1111 // fill byte + raise Exception("0x0F Not Implemented") # this is really pad byte, FIXME + else: + raise Exception('unpack item type '+str(obj_header)+' at '+str(offset)+ 'failed') + elif obj_type == 0x10: # int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes + return self.__unpackInt(offset) + elif obj_type == 0x20: # real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes + return self.__unpackFloat(offset) + elif obj_type == 0x30: # date 0011 0011 ... // 8 byte float follows, big-endian bytes + return self.__unpackDate(offset) + elif obj_type == 0x40: # data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes + obj_count, objref = self.__resolveIntSize(obj_info, offset) + return plistlib.Data(self.data[objref:objref+obj_count]) # XXX: we return data as str + elif obj_type == 0x50: # string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes + obj_count, objref = self.__resolveIntSize(obj_info, offset) + return self.data[objref:objref+obj_count] + elif obj_type == 0x60: # string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t + obj_count, objref = self.__resolveIntSize(obj_info, offset) + return self.data[objref:objref+obj_count*2].decode('utf-16be') + elif obj_type == 0x80: # uid 1000 nnnn ... // nnnn+1 is # of bytes + # FIXME: Accept as a string for now + obj_count, objref = self.__resolveIntSize(obj_info, offset) + return plistlib.Data(self.data[objref:objref+obj_count]) + elif obj_type == 0xA0: # array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows + obj_count, objref = self.__resolveIntSize(obj_info, offset) + arr = [] + for i in range(obj_count): + arr.append(self.__unpackIntStruct(self.object_ref_size, self.data[objref+i*self.object_ref_size:objref+i*self.object_ref_size+self.object_ref_size])) + return arr + elif obj_type == 0xC0: # set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows + # XXX: not serializable via apple implementation + raise Exception("0xC0 Not Implemented") # FIXME: implement + elif obj_type == 0xD0: # dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows + obj_count, objref = self.__resolveIntSize(obj_info, offset) + keys = [] + for i in range(obj_count): + keys.append(self.__unpackIntStruct(self.object_ref_size, self.data[objref+i*self.object_ref_size:objref+i*self.object_ref_size+self.object_ref_size])) + values = [] + objref += obj_count*self.object_ref_size + for i in range(obj_count): + values.append(self.__unpackIntStruct(self.object_ref_size, self.data[objref+i*self.object_ref_size:objref+i*self.object_ref_size+self.object_ref_size])) + dic = {} + for i in range(obj_count): + dic[keys[i]] = values[i] + return dic + else: + raise Exception('don\'t know how to unpack obj type '+hex(obj_type)+' at '+str(offset)) + + def __resolveObject(self, idx): + try: + return self.resolved[idx] + except KeyError: + obj = self.objects[idx] + if type(obj) == list: + newArr = [] + for i in obj: + newArr.append(self.__resolveObject(i)) + self.resolved[idx] = newArr + return newArr + if type(obj) == dict: + newDic = {} + for k,v in obj.iteritems(): + rk = self.__resolveObject(k) + rv = self.__resolveObject(v) + newDic[rk] = rv + self.resolved[idx] = newDic + return newDic + else: + self.resolved[idx] = obj + return obj + + def parse(self): + # read header + if self.data[:8] != 'bplist00': + raise Exception('Bad magic') + + # read trailer + self.offset_size, self.object_ref_size, self.number_of_objects, self.top_object, self.table_offset = struct.unpack('!6xBB4xI4xI4xI', self.data[-32:]) + #print "** plist offset_size:",self.offset_size,"objref_size:",self.object_ref_size,"num_objs:",self.number_of_objects,"top:",self.top_object,"table_ofs:",self.table_offset + + # read offset table + self.offset_table = self.data[self.table_offset:-32] + self.offsets = [] + ot = self.offset_table + for i in xrange(self.number_of_objects): + offset_entry = ot[:self.offset_size] + ot = ot[self.offset_size:] + self.offsets.append(self.__unpackIntStruct(self.offset_size, offset_entry)) + #print "** plist offsets:",self.offsets + + # read object table + self.objects = [] + k = 0 + for i in self.offsets: + obj = self.__unpackItem(i) + #print "** plist unpacked",k,type(obj),obj,"at",i + k += 1 + self.objects.append(obj) + + # rebuild object tree + #for i in range(len(self.objects)): + # self.__resolveObject(i) + + # return root object + return self.__resolveObject(self.top_object) + + @classmethod + def plistWithString(cls, s): + parser = cls(s) + return parser.parse() + + @classmethod + def plistWithFile(cls, f): + file = open(f,"rb") + parser = cls(file.read()) + file.close() + return parser.parse() \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/bruteforce.py b/dump-imessages/iphone-dataprotection/python_scripts/util/bruteforce.py new file mode 100644 index 0000000..c08cfbb --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/bruteforce.py @@ -0,0 +1,71 @@ +from keystore.keybag import Keybag +from keystore.effaceable import EffaceableLockers +from util.ramdiskclient import RamdiskToolClient +import plistlib + +COMPLEXITY={ + 0: "4 digits", + 1: "n digits", + 2: "n alphanum" + } + +def checkPasscodeComplexity(data_volume): + pl = data_volume.readFile("/mobile/Library/ConfigurationProfiles/UserSettings.plist", returnString=True) + if not pl: + print "Failed to read UserSettings.plist, assuming simple passcode" + return 0 + pl = plistlib.readPlistFromString(pl) + #print "passcodeKeyboardComplexity :", pl["restrictedValue"]["passcodeKeyboardComplexity"] + value = pl["restrictedValue"]["passcodeKeyboardComplexity"]["value"] + print "passcodeKeyboardComplexity %d => %s" % (value, COMPLEXITY.get(value)) + return pl["restrictedValue"]["passcodeKeyboardComplexity"]["value"] + +def loadKeybagFromVolume(volume, device_infos): + systembag = volume.readFile("/keybags/systembag.kb", returnString=True) + if not systembag or not systembag.startswith("bplist"): + print "FAIL: could not read /keybags/systembag.kb from data partition" + return False + lockers = EffaceableLockers(device_infos["lockers"].data) + bag1key = lockers.get("BAG1")[-32:] + keybag = Keybag.createWithSystemkbfile(systembag, bag1key, device_infos.get("key835", "").decode("hex")) + keybag.setDKey(device_infos) + if device_infos.has_key("passcodeKey"): + keybag.unlockWithPasscodeKey(device_infos.get("passcodeKey").decode("hex")) + return keybag + +def bruteforcePasscode(device_infos, data_volume): + if device_infos.has_key("passcode"): + print "Passcode already found, no bruteforce required" + return False + kb = data_volume.keybag + if not kb: + return False + + rd = RamdiskToolClient.get() + if rd.device_infos.udid != device_infos.udid: + print "Wrong device connected" + return + + print "Passcode comlexity (from OpaqueStuff) : %s" % COMPLEXITY.get(kb.passcodeComplexity) + print "Enter passcode or leave blank for bruteforce:" + z = raw_input() + bf = rd.getPasscodeKey(kb.KeyBagKeys, z) + if kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")): + print "Passcode \"%s\" OK" % z + else: + if z != "": + print "Wrong passcode, trying to bruteforce !" + if kb.passcodeComplexity != 0: + print "Complex passcode used, not bruteforcing" + return False + + bf = rd.bruteforceKeyBag(kb.KeyBagKeys) + if bf and kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")): + print "Bruteforce successful, passcode : %s" % bf["passcode"] + print "Passcode key : %s" % bf.get("passcodeKey") + if kb.unlocked: + device_infos.update(bf) + device_infos["classKeys"] = kb.getClearClassKeysDict() + device_infos["KeyBagKeys"] = plistlib.Data(kb.KeyBagKeys) + return True + return False diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/cert.py b/dump-imessages/iphone-dataprotection/python_scripts/util/cert.py new file mode 100644 index 0000000..6f88c7a --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/cert.py @@ -0,0 +1,16 @@ +import base64 + +def chunks(l, n): + return (l[i:i+n] for i in xrange(0, len(l), n)) + +def RSA_KEY_DER_to_PEM(data): + a = ["-----BEGIN RSA PRIVATE KEY-----"] + a.extend(chunks(base64.b64encode(data),64)) + a.append("-----END RSA PRIVATE KEY-----") + return "\n".join(a) + +def CERT_DER_to_PEM(data): + a = ["-----BEGIN CERTIFICATE-----"] + a.extend(chunks(base64.b64encode(data),64)) + a.append("-----END CERTIFICATE-----") + return "\n".join(a) \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/lzss.py b/dump-imessages/iphone-dataprotection/python_scripts/util/lzss.py new file mode 100644 index 0000000..556c893 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/lzss.py @@ -0,0 +1,71 @@ +""" +/************************************************************** + LZSS.C -- A Data Compression Program +*************************************************************** + 4/6/1989 Haruhiko Okumura + Use, distribute, and modify this program freely. + Please send me your improved versions. + PC-VAN SCIENCE + NIFTY-Serve PAF01022 + CompuServe 74050,1022 + +**************************************************************/ +/* + * lzss.c - Package for decompressing lzss compressed objects + * + * Copyright (c) 2003 Apple Computer, Inc. + * + * DRI: Josh de Cesare + */ +""" +from array import array +import struct + +N = 4096 +F = 18 +THRESHOLD = 2 +NIL = N + +def decompress_lzss(str): + if str[:8] !="complzss": + print "decompress_lzss: complzss magic missing" + return + decompsize = struct.unpack(">L", str[12:16])[0] + text_buf = array("B", " "*(N + F - 1)) + src = array("B", str[0x180:]) + srclen = len(src) + dst = array("B", " "*decompsize) + r = N - F + srcidx, dstidx, flags, c = 0, 0, 0, 0 + + while True: + flags >>= 1 + if ((flags & 0x100) == 0): + if (srcidx >= srclen): + break + c = src[srcidx]; srcidx += 1 + flags = c | 0xFF00; + + if (flags & 1): + if (srcidx >= srclen): + break + c = src[srcidx]; srcidx += 1 + dst[dstidx] = c; dstidx += 1 + text_buf[r] = c; r += 1 + r &= (N - 1); + else: + if (srcidx >= srclen): + break + i = src[srcidx]; srcidx += 1 + if (srcidx >= srclen): + break + j = src[srcidx]; srcidx += 1 + i |= ((j & 0xF0) << 4) + j = (j & 0x0F) + THRESHOLD + for k in xrange(j+1): + c = text_buf[(i + k) & (N - 1)] + dst[dstidx] = c; dstidx += 1 + text_buf[r] = c; r += 1 + r &= (N - 1) + return dst.tostring() + diff --git a/dump-imessages/iphone-dataprotection/python_scripts/util/ramdiskclient.py b/dump-imessages/iphone-dataprotection/python_scripts/util/ramdiskclient.py new file mode 100644 index 0000000..eb069e5 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/util/ramdiskclient.py @@ -0,0 +1,172 @@ +import plistlib +import struct +import socket +from datetime import datetime +from progressbar import ProgressBar, Percentage, Bar, SimpleProgress, ETA +from usbmux import usbmux +from util import sizeof_fmt + +kIOAESAcceleratorEncrypt = 0 +kIOAESAcceleratorDecrypt = 1 + +kIOAESAcceleratorGIDMask = 0x3E8 +kIOAESAcceleratorUIDMask = 0x7D0 + + +class DeviceInfo(dict): + @staticmethod + def create(dict): + try: + assert dict.has_key("dataVolumeUUID") + filename = "%s.plist" % dict.get("dataVolumeUUID") + return DeviceInfo(plistlib.readPlist(filename)) + except: + return DeviceInfo(dict) + + def save(self): + filename = "%s.plist" % self.get("dataVolumeUUID", "unk") + plistlib.writePlist(self, filename) + + #stop doing magic stuff + #def __del__(self): + # self.save() + +class RamdiskToolClient(object): + instance = None + @staticmethod + def get(): + if not RamdiskToolClient.instance: + RamdiskToolClient.instance = RamdiskToolClient() + return RamdiskToolClient.instance + + def __init__(self, udid=None, host="localhost", port=1999): + self.host = host + self.port = port + self.device_infos = {} + self.s = None + self.connect(udid) + self.getDeviceInfos() + + def close(self): + if self.s: + self.s.close() + self.s = None + + def connect(self, udid=None): + mux = usbmux.USBMux() + mux.process(1.0) + if not mux.devices: + print "Waiting for iOS device" + while not mux.devices: + mux.process(1.0) + if not mux.devices: + print "No device found" + return + dev = mux.devices[0] + print "Connecting to device : " + dev.serial + try: + self.s = mux.connect(dev, self.port) + except: + raise Exception("Connexion to device port %d failed" % self.port) + + def getDeviceInfos(self): + self.device_infos = self.send_req({"Request":"DeviceInfo"}) + keys = self.grabDeviceKeys() + if keys: + self.device_infos.update(keys) + return DeviceInfo.create(self.device_infos) + + def downloadFile(self, path): + res = self.send_req({"Request": "DownloadFile", + "Path": path}) + if type(res) == plistlib._InternalDict and res.has_key("Data"): + return res["Data"].data + + def getSystemKeyBag(self): + return self.send_req({"Request":"GetSystemKeyBag"}) + + def bruteforceKeyBag(self, KeyBagKeys): + return self.send_req({"Request":"BruteforceSystemKeyBag", + "KeyBagKeys": plistlib.Data(KeyBagKeys)}) + + def getEscrowRecord(self, hostID): + return self.send_req({"Request":"GetEscrowRecord", + "HostID": hostID}) + + def getPasscodeKey(self, keybagkeys, passcode): + return self.send_req({"Request":"KeyBagGetPasscodeKey", + "KeyBagKeys": plistlib.Data(keybagkeys), + "passcode": passcode}) + + def send_msg(self, dict): + plist = plistlib.writePlistToString(dict) + data = struct.pack("L",blob[i+4:i+8])[0] + data = blob[i+8:i+8+length] + yield (tag,data) + i += 8 + length \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/python_scripts/windows_redsn0w_keys.py b/dump-imessages/iphone-dataprotection/python_scripts/windows_redsn0w_keys.py new file mode 100644 index 0000000..6139859 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/windows_redsn0w_keys.py @@ -0,0 +1,12 @@ +#HAX + +d=open("redsn0w_win_0.9.9b4/redsn0w.exe", "rb").read() + +i = d.find("IV") +i = d.rfind("", i) + +assert i != -1 +assert j != -1 + +open("Keys.plist", "wb").write(d[i:j+8]) diff --git a/dump-imessages/iphone-dataprotection/python_scripts/wordlist.dict b/dump-imessages/iphone-dataprotection/python_scripts/wordlist.dict new file mode 100644 index 0000000..e65040e --- /dev/null +++ b/dump-imessages/iphone-dataprotection/python_scripts/wordlist.dict @@ -0,0 +1,48 @@ +12345 +123456 +123456 +1234567 +12345678 +123456789 +1234567890 +09876 +098765 +0987654 +09876543 +098765432 +0987654321 +abcd +abcd1 +abcde +abcde1 +abcdef +abcdef1 +abcdefg +abcdefg1 +qwer +qwer1 +qwert +qwert1 +qwertz +qwertz1 +asdf +asdfg +asdfg1 +asdfgh +asdfgh1 +yxcv +yxcv1 +yxcvb +yxcvb1 +yxcvbn +yxcvbn1 +yxcvbnm +yxcvbnm1 +mnbv +mnbv1 +mnbvc +mnbvc1 +mnbvcx +mnbvcx1 +mnbvcxy +mnbvcxy1 \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleEffaceableStorage.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleEffaceableStorage.c new file mode 100644 index 0000000..83cd1f7 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleEffaceableStorage.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include "AppleEffaceableStorage.h" +#include "IOKit.h" + +int AppleEffaceableStorage__getLocker(uint32_t lockerId, uint8_t* buffer, size_t len) { + uint64_t outScalar = 0; + uint32_t one = 1; + uint64_t inScalar = lockerId; + + return IOKit_call("AppleEffaceableStorage", + kAppleEffaceableStorageGetLocker, + &inScalar, + 1, + NULL, + 0, + &outScalar, + &one, + buffer, + &len); +} + +int AppleEffaceableStorage__getBytes(uint8_t* buffer, size_t len) +{ + const uint64_t offset = 0; + + return IOKit_call("AppleEffaceableStorage", + kAppleEffaceableStorageGetBytes, + &offset, + 1, + NULL, + 0, + NULL, + NULL, + buffer, + &len); +} + + +int AppleEffaceableStorage__getLockerFromBytes(uint32_t tag, uint8_t* lockers, size_t lockers_len, uint8_t* buffer, size_t len) +{ + struct EffaceableLocker* p = (struct EffaceableLocker*) lockers; + unsigned int i=0; + + while (i < lockers_len) + { + //printf("p->magic=%x\n", p->magic); + if (p->magic != 'Lk') //0x4c6B + break; + if (p->len == 0 || ((i+8+p->len) > lockers_len)) + break; + //printf("p->tag=%x\n", p->tag); + if ((p->tag & ~0x80000000) == tag) + { + len = len < p->len ? len : p->len; + memcpy(buffer, p->data, len); + return 0; + } + i = i + 8 + p->len; + p = (struct EffaceableLocker*) (&lockers[i]); + } + return -1; + +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleEffaceableStorage.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleEffaceableStorage.h new file mode 100644 index 0000000..5dfdfee --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleEffaceableStorage.h @@ -0,0 +1,40 @@ +/* +AppleEffaceableStorage +0 : getCapacity +1 : getBytes (kernel debug) +2 : setBytes (kernel debug) +3 : isFormatted +4 : format +5 : getLocker +6 : setLocker +7 : effaceLocker +8 : lockerSpace +*/ +#define kAppleEffaceableStorageGetBytes 1 +#define kAppleEffaceableStorageGetLocker 5 + + +#define LOCKER_DKEY 0x446B6579 +#define LOCKER_EMF 0x454D4621 +#define LOCKER_BAG1 0x42414731 +#define LOCKER_LWVM 0x4C77564d + +struct EffaceableLocker +{ + unsigned short magic; //0x4c6B = "kL" + unsigned short len; + unsigned int tag; //BAG1, EMF, Dkey, DONE + unsigned char data[1]; +}; + +struct BAG1Locker +{ + unsigned int magic;//'BAG1'; + unsigned char iv[16]; + unsigned char key[32]; +}; + +int AppleEffaceableStorage__getLocker(uint32_t lockerId, uint8_t* buffer, size_t len); +int AppleEffaceableStorage__getBytes(uint8_t* buffer, size_t len); +int AppleEffaceableStorage__getLockerFromBytes(uint32_t tag, uint8_t* lockers, size_t lockers_len, uint8_t* buffer, size_t len); + diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore.c new file mode 100644 index 0000000..67bde6d --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore.c @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include "IOKit.h" +#include "IOAESAccelerator.h" +#include "AppleEffaceableStorage.h" +#include "AppleKeyStore.h" +#include "util.h" +#include "bsdcrypto/rijndael.h" +#include "bsdcrypto/key_wrap.h" + +CFDictionaryRef AppleKeyStore_loadKeyBag(const char* folder, const char* filename) +{ + char keybagPath[100]; + struct BAG1Locker bag1_locker={0}; + //unsigned char buffer_bag1[52] = {0}; + + snprintf(keybagPath, 99, "%s/%s.kb", folder, filename); + + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, + (const UInt8*)keybagPath, + strlen(keybagPath), + 0); + + if (url == NULL) + return NULL; + + CFReadStreamRef stream = CFReadStreamCreateWithFile (kCFAllocatorDefault, url); + + if (stream == NULL) + return NULL; + + if (CFReadStreamOpen(stream) != TRUE) + return NULL; + + CFPropertyListRef plist = CFPropertyListCreateWithStream (kCFAllocatorDefault, + stream, + 0, + kCFPropertyListImmutable, + NULL, + NULL + ); + if (plist == NULL) + return NULL; + + CFDataRef data = CFDictionaryGetValue(plist, CFSTR("_MKBPAYLOAD")); + + if (data == NULL) + return NULL; + + uint8_t* mkbpayload = valloc(CFDataGetLength(data)); + CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), mkbpayload); + int length = CFDataGetLength(data); + + if (length < 16) + { + free(mkbpayload); + return NULL; + } + + if (AppleEffaceableStorage__getLocker(LOCKER_BAG1, (uint8_t*) &bag1_locker, sizeof(struct BAG1Locker))) + { + free(mkbpayload); + return NULL; + } + + if (bag1_locker.magic != 'BAG1') + fprintf(stderr, "AppleKeyStore_loadKeyBag: bad BAG1 magic\n"); + + size_t decryptedSize = 0; + + CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, + kCCAlgorithmAES128, + kCCOptionPKCS7Padding, + bag1_locker.key, + kCCKeySizeAES256, + bag1_locker.iv, + mkbpayload, + length, + mkbpayload, + length, + &decryptedSize); + + if (cryptStatus != kCCSuccess) + { + fprintf(stderr, "AppleKeyStore_loadKeyBag CCCrypt kCCDecrypt with BAG1 key failed, return code=%x\n", cryptStatus); + free(mkbpayload); + return NULL; + } + + CFDataRef data2 = CFDataCreate(kCFAllocatorDefault, + mkbpayload, + decryptedSize + ); + + if (data2 == NULL) + { + free(mkbpayload); + return NULL; + } + + CFErrorRef e=NULL; + CFPropertyListRef plist2 = CFPropertyListCreateWithData(kCFAllocatorDefault, data2, kCFPropertyListImmutable, NULL, &e); + if (plist2 == NULL) + { + fprintf(stderr, "AppleKeyStore_loadKeyBag failed to create plist, AES fail ? decryptedSize=%zx\n", decryptedSize); + + CFShow(e); + } + free(mkbpayload); + CFRelease(data2); + return plist2; +} + +int AppleKeyStoreKeyBagInit() +{ + uint64_t out = 0; + uint32_t one = 1; + return IOKit_call("AppleKeyStore", + kAppleKeyStoreInitUserClient, + NULL, + 0, + NULL, + 0, + &out, + &one, + NULL, + NULL); +} + +int AppleKeyStoreKeyBagCreateWithData(CFDataRef data, uint64_t* keybagId) +{ + uint32_t outCnt = 1; + aes_key_wrap_ctx ctx; + uint8_t hmckkey[32] = {0}; + + int retcode = IOKit_call("AppleKeyStore", + kAppleKeyStoreKeyBagCreateWithData, + NULL, + 0, + CFDataGetBytePtr(data), + CFDataGetLength(data), + keybagId, + &outCnt, + NULL, + NULL + ); + if (retcode != 0xE00002C9) + return retcode; + //HAX to load new iOS 7 keybags on previous iOS kernels + uint32_t* kbdata = (uint32_t*) CFDataGetBytePtr(data); + if ((kbdata[2] == 'SREV') && (kbdata[4] == 0x04000000)) + { + printf("Patching iOS 7 keybag VERS 4 signature for older kernels\n"); + + aes_key_wrap_set_key(&ctx, IOAES_key835(), 16); + assert(kbdata[0x38/4] == 'KCMH'); + assert(!aes_key_unwrap(&ctx, (const uint8_t*) &kbdata[0x38/4 + 2], hmckkey, 4)); + + assert(kbdata[CFSwapInt32BigToHost(kbdata[1])/4 + 2] == 'NGIS'); + + CCHmac(kCCHmacAlgSHA1, + (const void *) &kbdata[2], + CFSwapInt32BigToHost(kbdata[1]), + hmckkey, + 32, + &kbdata[CFSwapInt32BigToHost(kbdata[1])/4 + 2 + 2]); + } + outCnt = 1; + return IOKit_call("AppleKeyStore", + kAppleKeyStoreKeyBagCreateWithData, + NULL, + 0, + CFDataGetBytePtr(data), + CFDataGetLength(data), + keybagId, + &outCnt, + NULL, + NULL + ); +} + +int AppleKeyStoreKeyBagSetSystem(uint64_t keybagId) +{ + return IOKit_call("AppleKeyStore", + kAppleKeyStoreKeyBagSetSystem, + &keybagId, + 1, + NULL, + 0, + NULL, + NULL, + NULL, + NULL); +} + +int AppleKeyStoreUnlockDevice(io_connect_t conn, CFDataRef passcode) +{ + return IOConnectCallMethod(conn, + kAppleKeyStoreUnlockDevice, + NULL, + 0, + CFDataGetBytePtr(passcode), + CFDataGetLength(passcode), + NULL, + NULL, + NULL, + NULL); +} + +KeyBag* AppleKeyStore_parseBinaryKeyBag(CFDataRef kb) +{ + const uint8_t* ptr = CFDataGetBytePtr(kb); + unsigned int len = CFDataGetLength(kb); + struct KeyBagBlobItem* p = (struct KeyBagBlobItem*) ptr; + const uint8_t* end; + + if (p->tag != 'ATAD') { + printf("Keybag does not start with DATA\n"); + return NULL; + } + if (8 + CFSwapInt32BigToHost(p->len) > len) { + return NULL; + } + + KeyBag* keybag = malloc(sizeof(KeyBag)); + if (keybag == NULL) + return NULL; + + memset(keybag, 0, sizeof(KeyBag)); + + end = ptr + 8 + CFSwapInt32BigToHost(p->len); + p = (struct KeyBagBlobItem*) p->data.bytes; + int kbuuid=0; + int i = -1; + + while ((uint8_t*)p < end) { + //printf("%x\n", p->tag); + len = CFSwapInt32BigToHost(p->len); + + if (p->tag == 'SREV') { + keybag->version = CFSwapInt32BigToHost(p->data.intvalue); + } + else if (p->tag == 'TLAS') { + memcpy(keybag->salt, p->data.bytes, 20); + } + else if (p->tag == 'RETI') { + keybag->iter = CFSwapInt32BigToHost(p->data.intvalue); + } + else if (p->tag == 'DIUU') { + if (!kbuuid) + { + memcpy(keybag->uuid, p->data.bytes, 16); + kbuuid = 1; + } + else + { + i++; + if (i >= MAX_CLASS_KEYS) + break; + memcpy(keybag->keys[i].uuid, p->data.bytes, 16); + } + } + else if (p->tag == 'SALC') + { + keybag->keys[i].clas = CFSwapInt32BigToHost(p->data.intvalue); + } + else if (p->tag == 'PARW' && kbuuid) + { + keybag->keys[i].wrap = CFSwapInt32BigToHost(p->data.intvalue); + } + else if (p->tag == 'YKPW') + { + memcpy(keybag->keys[i].wpky, p->data.bytes, (len > 40) ? 40 : len); + } + p = (struct KeyBagBlobItem*) &p->data.bytes[len]; + } + keybag->numKeys = i + 1; + + return keybag; +} + +void AppleKeyStore_printKeyBag(KeyBag* kb) +{ + int i; + printf("Keybag version : %d\n", kb->version); + printf("Keybag keys : %d\n", kb->numKeys); + printf("Class\tWrap\tKey\n"); + for (i=0; i < kb->numKeys; i++) + { + printf("%d\t%d\t", kb->keys[i].clas, kb->keys[i].wrap); + printBytesToHex(kb->keys[i].wpky, kb->keys[i].wrap & 2 ? 40 : 32); + printf("\n"); + } + printf("\n"); +} + +CFMutableDictionaryRef AppleKeyStore_getClassKeys(KeyBag* kb) +{ + int i; + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, kb->numKeys, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef key; + + for (i=0; i < kb->numKeys; i++) + { + if(kb->keys[i].wrap == 0) + { + key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kb->keys[i].clas); + addHexaString(dict, key, kb->keys[i].wpky, 32); + CFRelease(key); + } + } + return dict; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore.h new file mode 100644 index 0000000..c229618 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore.h @@ -0,0 +1,83 @@ +#include + +/* + AppleKeyStore + 0 : initUserClient scalarOutSize=1 + 1 : + 2 : AppleKeyStoreKeyBagCreate + 3 : AppleKeyStoreKeyBagCopyData inscalars=id structOutSize=0x8000 + 4 : keybagrelease inscalars":[0]} + 5 : AppleKeyStoreKeyBagSetSystem + 6 : AppleKeyStoreKeyBagCreateWithData + 7 : getlockstate "inscalars":[0], "scalarOutSize":1} + 8 : AppleKeyStoreLockDevice + 9 : AppleKeyStoreUnlockDevice instruct + 10: AppleKeyStoreKeyWrap + 11: AppleKeyStoreKeyUnwrap + 12: AppleKeyStoreKeyBagUnlock + 13: AppleKeyStoreKeyBagLock + 14: AppleKeyStoreKeyBagGetSystem scalarOutSize=1 + 15: AppleKeyStoreKeyBagChangeSecret + 17: AppleKeyStoreGetDeviceLockState scalarOutSize=1 + 18: AppleKeyStoreRecoverWithEscrowBag + 19: AppleKeyStoreOblitClassD + */ +#define kAppleKeyStoreInitUserClient 0 +#define kAppleKeyStoreKeyBagSetSystem 5 +#define kAppleKeyStoreKeyBagCreateWithData 6 +#define kAppleKeyStoreUnlockDevice 9 + +#define MAX_CLASS_KEYS 20 + +struct KeyBagBlobItem +{ + unsigned int tag; + unsigned int len; + union + { + unsigned int intvalue; + unsigned char bytes[1]; + } data; +}; + +typedef struct ClassKey +{ + unsigned char uuid[16]; + unsigned int clas; + unsigned int wrap; + unsigned char wpky[40]; +} ClassKey; + +typedef struct KeyBag +{ + unsigned int version; + unsigned int type; + unsigned char uuid[16]; + unsigned char hmck[40]; + unsigned char salt[20]; + unsigned int iter; + + unsigned int numKeys; + + struct ClassKey keys[MAX_CLASS_KEYS]; +} KeyBag; + + +int AppleKeyStoreKeyBagInit(); +CFDictionaryRef AppleKeyStore_loadKeyBag(const char* folder, const char* filename); +int AppleKeyStoreKeyBagCreateWithData(CFDataRef data, uint64_t* keybagId); +int AppleKeyStoreKeyBagSetSystem(uint64_t keybagId); +int AppleKeyStoreUnlockDevice(io_connect_t conn, CFDataRef passcode); + +KeyBag* AppleKeyStore_parseBinaryKeyBag(CFDataRef kb); +void AppleKeyStore_printKeyBag(KeyBag* kb); + +int AppleKeyStore_getPasscodeKey(KeyBag* keybag, + const char* passcode, + size_t passcodeLen, + uint8_t* passcodeKey); + +int AppleKeyStore_unlockKeybagFromUserland(KeyBag* kb, + const char* passcode, + size_t passcodeLen, + uint8_t* key835); diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore_kdf.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore_kdf.c new file mode 100644 index 0000000..c0ab47f --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/AppleKeyStore_kdf.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include "IOKit.h" +#include "IOAESAccelerator.h" +#include "AppleEffaceableStorage.h" +#include "AppleKeyStore.h" +#include "bsdcrypto/pbkdf2.h" +#include "bsdcrypto/rijndael.h" +#include "bsdcrypto/key_wrap.h" + +int AppleKeyStore_derivation(void* data, uint32_t dataLength, uint32_t iter, uint32_t vers); +uint32_t AppleKeyStore_xorExpand(uint32_t* dst, uint32_t dstLen, uint32_t* input, uint32_t inLen, uint32_t xorKey); +void AppleKeyStore_xorCompress(uint32_t* input, uint32_t inputLen, uint32_t* output, uint32_t outputLen); + +#define DERIVATION_BUFFER_SIZE 4096 +uint8_t buf1[DERIVATION_BUFFER_SIZE]; +uint8_t buf2[DERIVATION_BUFFER_SIZE]; + +IOByteCount IOAESStructSize1 = sizeof(IOAESStruct); +IOAESStruct in ={buf1,buf2,DERIVATION_BUFFER_SIZE,{0},0,128,{0},kIOAESAcceleratorUIDMask,0}; +IOAESStruct out = {0}; + +int AppleKeyStore_getPasscodeKey(KeyBag* keybag, + const char* passcode, + size_t passcodeLen, + uint8_t* passcodeKey) +{ + //One PBKDF2 iter, hardcoded salt length + pkcs5_pbkdf2(passcode, passcodeLen, keybag->salt, 20, passcodeKey, 32, 1); + + return AppleKeyStore_derivation(passcodeKey, 32, keybag->iter, keybag->version); +} + +int AppleKeyStore_derivation(void* data, uint32_t dataLength, uint32_t iter, uint32_t vers) +{ + IOReturn ret; + io_connect_t conn = IOAESAccelerator_getIOconnect(); + memset(in.iv, 0, 16); + + uint32_t r4; + uint32_t nBlocks = DERIVATION_BUFFER_SIZE / dataLength; //4096/32=128 + uint32_t xorkey = 1; + + uint32_t* buffer2 = data; + if (vers >= 2) + { + buffer2 = malloc(dataLength); + memcpy(buffer2, data, dataLength); + } + while (iter > 0) + { + //version=1 xorKey alawys=1, buffer2 changes at each iter + //version=2 xorKey changes at each iter, buffer2 is always the input (pbkdf2(passcode)) + r4 = AppleKeyStore_xorExpand((uint32_t*)buf1, DERIVATION_BUFFER_SIZE, buffer2, dataLength, xorkey); + if (vers >= 2) + xorkey = r4; + + if((ret = IOConnectCallStructMethod(conn, kIOAESAcceleratorTask, &in, IOAESStructSize1, &out, &IOAESStructSize1)) != kIOReturnSuccess) + { + fprintf(stderr, "IOConnectCallStructMethod fail : %x\n", ret); + return -1; + } + memcpy(in.iv, out.iv, 16); + + r4 = nBlocks; + if (r4 >= iter) + { + r4 = iter; + } + AppleKeyStore_xorCompress((uint32_t*) buf2, r4 * dataLength, data, dataLength); + iter -= r4; + } + if (vers >= 2) + { + free(buffer2); + } + return 0; +} + +/* +uint32_t paddedLen = (inLen + 3) & (~3);//aligne sur 4 octets +if (dstLen % paddedLen) + return; +uint32_t localBuf[inLen/4]; + +memcpy(localBuf, input, inLen); +memset(&localBuf[inLen], 0, paddedLen - inLen);*/ +uint32_t AppleKeyStore_xorExpand(uint32_t* dst, uint32_t dstLen, uint32_t* input, uint32_t inLen, uint32_t xorKey) +{ + uint32_t* dstEnd = &dst[dstLen/4]; + uint32_t i = 0; + + while (dst < dstEnd) + { + i = 0; + while (i < inLen/4) + { + *dst = input[i] ^ xorKey; + dst++; + i++; + } + xorKey++; + } + return xorKey; +} + +void AppleKeyStore_xorCompress(uint32_t* input, uint32_t inputLen, uint32_t* output, uint32_t outputLen) +{ + uint32_t i; + + for (i=0; i < (inputLen/4); i++) + { + output[i%(outputLen/4)] ^= input[i]; + } +} + +int AppleKeyStore_unlockKeybagFromUserland(KeyBag* kb, const char* passcode, size_t passcodeLen, uint8_t* key835) +{ + u_int8_t passcodeKey[32]={0}; + u_int8_t unwrappedKey[40]={0}; + aes_key_wrap_ctx ctx; + int i; + + AppleKeyStore_getPasscodeKey(kb, passcode, passcodeLen, passcodeKey); + aes_key_wrap_set_key(&ctx, passcodeKey, 32); + + for (i=0; i < kb->numKeys; i++) + { + if (kb->keys[i].wrap & 2) + { + if(aes_key_unwrap(&ctx, kb->keys[i].wpky, unwrappedKey, 4)) + return 0; + memcpy(kb->keys[i].wpky, unwrappedKey, 32); + kb->keys[i].wrap &= ~2; + } + if (kb->keys[i].wrap & 1) + { + doAES(kb->keys[i].wpky, kb->keys[i].wpky, 32, kIOAESAcceleratorCustomMask, key835, NULL, kIOAESAcceleratorDecrypt, 128); + kb->keys[i].wrap &= ~1; + } + } + return 1; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/IOAESAccelerator.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOAESAccelerator.c new file mode 100644 index 0000000..7461c08 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOAESAccelerator.c @@ -0,0 +1,160 @@ +/** +https://github.com/planetbeing/xpwn/blob/master/crypto/aes.c +**/ +#include +#include +#include +#include +#include +#include +#include "IOAESAccelerator.h" +#include "IOKit.h" + +io_connect_t conn = 0; +IOByteCount IOAESStructSize = sizeof(IOAESStruct); +pthread_once_t once_control = PTHREAD_ONCE_INIT; + +//see com.apple.driver.AppleCDMA +typedef struct +{ + uint32_t key_id; + uint32_t hw_key_id; + uint8_t nonce_to_encrypt_with_hw_key[16]; + uint8_t* value; +} device_key_descriptor; + +#define NUM_DEVICE_KEYS 4 +device_key_descriptor ios_device_keys[NUM_DEVICE_KEYS]= { + {0x835, kIOAESAcceleratorUIDMask, {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, NULL}, + {0x899, kIOAESAcceleratorUIDMask, {0xD1, 0xE8, 0xFC, 0xB5, 0x39, 0x37, 0xBF, 0x8D, 0xEF, 0xC7, 0x4C, 0xD1, 0xD0, 0xF1, 0xD4, 0xB0}, NULL}, + {0x89B, kIOAESAcceleratorUIDMask, {0x18, 0x3E, 0x99, 0x67, 0x6B, 0xB0, 0x3C, 0x54, 0x6F, 0xA4, 0x68, 0xF5, 0x1C, 0x0C, 0xBD, 0x49}, NULL}, + {0x89A, kIOAESAcceleratorUIDMask, {0xDB, 0x1F, 0x5B, 0x33, 0x60, 0x6C, 0x5F, 0x1C, 0x19, 0x34, 0xAA, 0x66, 0x58, 0x9C, 0x06, 0x61}, NULL}, +}; + +void aes_init() +{ + conn = IOKit_getConnect("IOAESAccelerator"); +} + +io_connect_t IOAESAccelerator_getIOconnect() +{ + pthread_once(&once_control, aes_init); + return conn; +} + + +int doAES(void* inbuf, void *outbuf, uint32_t size, uint32_t keyMask, void* key, void* iv, int mode, int bits) { + IOReturn ret; + IOAESStruct in; + + pthread_once(&once_control, aes_init); + + in.mode = mode; + in.bits = bits; + in.inbuf = inbuf; + in.outbuf = outbuf; + in.size = size; + in.mask = keyMask; + + memset(in.keybuf, 0, sizeof(in.keybuf)); + + if(key) + memcpy(in.keybuf, key, in.bits / 8); + + if(iv) + memcpy(in.iv, iv, 16); + else + memset(in.iv, 0, 16); + + ret = IOConnectCallStructMethod(conn, kIOAESAcceleratorTask, &in, IOAESStructSize, &in, &IOAESStructSize); + if(ret == kIOReturnBadArgument) { + IOAESStructSize = IOAESStruct_sizeold; + ret = IOConnectCallStructMethod(conn, kIOAESAcceleratorTask, &in, IOAESStructSize, &in, &IOAESStructSize); + } + + if(iv) + memcpy(iv, in.iv, 16); + + return ret; +} + +IOReturn doAES_wrapper(void* thisxxx, int mode, void* iv, void* outbuf, void *inbuf, uint32_t size, uint32_t keyMask) +{ + int x = doAES(inbuf, outbuf, size, keyMask, NULL, iv, mode, 128); + return !x; +} + +int patch_IOAESAccelerator(); + +int AES_UID_Encrypt(void* input2, void* output, size_t len) +{ + IOAESStruct in; + IOReturn ret; + static int triedToPatchKernelAlready = 0; + unsigned char* input = valloc(16); + + memcpy(input, input2, 16); + + pthread_once(&once_control, aes_init); + + in.mode = kIOAESAcceleratorEncrypt; + in.mask = kIOAESAcceleratorUIDMask; + in.bits = 128; + in.inbuf = input; + in.outbuf = output; + in.size = len; + + memset(in.keybuf, 0, sizeof(in.keybuf)); + memset(in.iv, 0, 16); + + ret = IOConnectCallStructMethod(conn, kIOAESAcceleratorTask, &in, IOAESStructSize, &in, &IOAESStructSize); + if(ret == kIOReturnBadArgument) { + IOAESStructSize = IOAESStruct_sizeold; + ret = IOConnectCallStructMethod(conn, kIOAESAcceleratorTask, &in, IOAESStructSize, &in, &IOAESStructSize); + } + + if(ret == kIOReturnNotPrivileged && !triedToPatchKernelAlready) { + triedToPatchKernelAlready = 1; + fprintf(stderr, "Trying to patch IOAESAccelerator kernel extension to allow UID key usage\n"); + patch_IOAESAccelerator(); + ret = AES_UID_Encrypt(input2, output, len); + } + if(ret != kIOReturnSuccess) { + fprintf(stderr, "IOAESAccelerator returned: %x\n", ret); + } + return ret; +} + +uint8_t* IOAES_get_device_key(uint32_t id) +{ + static uint8_t nullkey[16] = {0}; + int i; + for(i=0; i < NUM_DEVICE_KEYS; i++) + { + if (ios_device_keys[i].key_id != id) + continue; + if (ios_device_keys[i].value != NULL) + return ios_device_keys[i].value; + + ios_device_keys[i].value = (uint8_t*) valloc(16); //on ARMv6 devices stuff needs to be aligned + memcpy(ios_device_keys[i].value, ios_device_keys[i].nonce_to_encrypt_with_hw_key, 16); + AES_UID_Encrypt(ios_device_keys[i].value, ios_device_keys[i].value, 16); + return ios_device_keys[i].value; + } + return nullkey; + +} +uint8_t* IOAES_key835() +{ + return IOAES_get_device_key(0x835); +} + +uint8_t* IOAES_key89B() +{ + return IOAES_get_device_key(0x89B); +} + +uint8_t* IOAES_key89A() +{ + return IOAES_get_device_key(0x89A); +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/IOAESAccelerator.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOAESAccelerator.h new file mode 100644 index 0000000..216eceb --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOAESAccelerator.h @@ -0,0 +1,36 @@ +#define kIOAESAcceleratorInfo 0 +#define kIOAESAcceleratorTask 1 +#define kIOAESAcceleratorTest 2 + +#define kIOAESAcceleratorEncrypt 0 +#define kIOAESAcceleratorDecrypt 1 + +#define kIOAESAcceleratorGIDMask 0x3E8 +#define kIOAESAcceleratorUIDMask 0x7D0 +#define kIOAESAcceleratorCustomMask 0 + +typedef struct +{ + void* inbuf; + void* outbuf; + uint32_t size; + uint8_t iv[16]; + uint32_t mode; + uint32_t bits; + uint8_t keybuf[32]; + uint32_t mask; + uint32_t zero; //ios 4.2.1 +} IOAESStruct; + +#define IOAESStruct_size41 (sizeof(IOAESStruct)) +#define IOAESStruct_sizeold (sizeof(IOAESStruct) - 4) + +void aes_init(); +io_connect_t IOAESAccelerator_getIOconnect(); +int doAES(void* inbuf, void *outbuf, uint32_t size, uint32_t keyMask, void* key, void* iv, int mode, int bits); +int AES_UID_Encrypt(void* input, void* output, size_t len); + +uint8_t* IOAES_key835(); +uint8_t* IOAES_key89A(); +uint8_t* IOAES_key89B(); + diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/IOKit.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOKit.c new file mode 100644 index 0000000..beebbbf --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOKit.c @@ -0,0 +1,90 @@ +#include +#include +#include "IOKit.h" + +struct ioconnectCache { + const char* serviceName; + io_connect_t conn; +}; + +struct ioconnectCache cache[10]={{NULL, 0}}; + +void __attribute__((destructor)) IOKit_destruct() +{ + int i; + for (i=0; i < 10 && cache[i].conn != 0; i++) { + //printf("Closing %s\n", cache[i].serviceName); + IOServiceClose(cache[i].conn); + } +} + +io_connect_t IOKit_getConnect(const char* serviceName) +{ + IOReturn ret; + io_connect_t conn = 0; + int i; + + for (i=0; i < 10 && cache[i].serviceName != NULL; i++) { + if (!strcmp(serviceName, cache[i].serviceName)) + { + //printf("got cache for %s\n", serviceName); + return cache[i].conn; + } + } + + CFMutableDictionaryRef dict = IOServiceMatching(serviceName); + io_service_t dev = IOServiceGetMatchingService(kIOMasterPortDefault, dict); + + if(!dev) { + fprintf(stderr, "FAIL: Could not get %s service\n", serviceName); + return -1; + } + + ret = IOServiceOpen(dev, mach_task_self(), 0, &conn); + + IOObjectRelease(dev); + if(ret != kIOReturnSuccess) { + fprintf(stderr, "FAIL: Cannot open service %s\n", serviceName); + return -1; + } + + if (i < 10) { + cache[i].serviceName = serviceName; + cache[i].conn = conn; + } + + return conn; +} + +IOReturn IOKit_call(const char* serviceName, + uint32_t selector, + const uint64_t *input, + uint32_t inputCnt, + const void *inputStruct, + size_t inputStructCnt, + uint64_t *output, + uint32_t *outputCnt, + void *outputStruct, + size_t *outputStructCnt) +{ + IOReturn ret; + io_connect_t conn = IOKit_getConnect(serviceName); + + ret = IOConnectCallMethod(conn, + selector, + input, + inputCnt, + inputStruct, + inputStructCnt, + output, + outputCnt, + outputStruct, + outputStructCnt); + + if (ret != kIOReturnSuccess) + { + fprintf(stderr, "IOConnectCallMethod on %s selector %d returned %x\n", serviceName, selector, ret); + } + + return ret; +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/IOKit.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOKit.h new file mode 100644 index 0000000..a4c1429 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOKit.h @@ -0,0 +1,14 @@ +#include + +io_connect_t IOKit_getConnect(const char* serviceName); + +IOReturn IOKit_call(const char* serviceName, + uint32_t selector, + const uint64_t *input, + uint32_t inputCnt, + const void *inputStruct, + size_t inputStructCnt, + uint64_t *output, + uint32_t *outputCnt, + void *outputStruct, + size_t *outputStructCnt); \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/IOUSBDeviceControllerLib.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOUSBDeviceControllerLib.h new file mode 100644 index 0000000..d5c2b0c --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/IOUSBDeviceControllerLib.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * IOUSBDeviceControllerLib.h + * IOUSBDeviceFamily + * + * Created by Paul Chinn on 11/6/07. + * Copyright 2007 Apple Inc. All rights reserved. + * + */ + +#ifndef _IOKIT_IOUSBDEVICECONTROLLERLIB_H_ +#define _IOKIT_IOUSBDEVICECONTROLLERLIB_H_ + +#include +#include +#include + +/*! + @header IOUSBDeviceControllerLib + IOUSBDeviceControllerLib provides some API to access devicce-mode-usb controllers. + */ + +__BEGIN_DECLS + + +/*! @typedef IOUSBDeviceControllerRef + @abstract This is the type of a reference to the IOUSBDeviceController. + */ +typedef struct __IOUSBDeviceController* IOUSBDeviceControllerRef; + +/*! @typedef IOUSBDeviceDescriptionRef + @abstract Object that describes the device, configurations and interfaces of a IOUSBDeviceController. + */ +typedef struct __IOUSBDeviceDescription* IOUSBDeviceDescriptionRef; + +/*! @typedef IOUSBDeviceArrivalCallback + @abstract Function callback for notification of asynchronous arrival of an IOUSBDeviceController . + */ +typedef void (*IOUSBDeviceArrivalCallback) ( + void * context, + IOUSBDeviceControllerRef device); +/*! + @function IOUSBDeviceControllerGetTypeID + @abstract Returns the type identifier of all IOUSBDeviceController instances. + */ +CF_EXPORT +CFTypeID IOUSBDeviceControllerGetTypeID(void) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function IOUSBDeviceDescriptionGetTypeID + @abstract Returns the type identifier of all IOUSBDeviceDescription instances. + */ +CF_EXPORT +CFTypeID IOUSBDeviceDescriptionGetTypeID(void) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function IOUSBDeviceControllerCreate + @abstract Creates an IOUSBDeviceController object. + @discussion Creates a CF object that provides access to the kernel's IOUSBDeviceController IOKit object. + @param allocator Allocator to be used during creation. + @param deviceRef The newly created object. Only valid if the call succeeds. + @result The status of the call. The call will fail if no IOUSBDeviceController exists in the kernel. + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerCreate( + CFAllocatorRef allocator, + IOUSBDeviceControllerRef* deviceRef + ) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function IOUSBDeviceControllerGoOffAndOnBus + @abstract Cause the controller to drop off bus and return. + @discussion The controller will drop off USB appearing to the host as if it has been unlugged. After the given msecDelay + has elapsed, it will come back on bus. + @param deviceRef The controller object + @param msecDelay The time in milliseconds to stay off-bus. + @result The status of the call. + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerGoOffAndOnBus(IOUSBDeviceControllerRef device, uint32_t msecDelay) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function IOUSBDeviceControllerForceOffBus + @abstract Cause the controller to stay off. + @discussion The controller will drop off USB appearing to the host as if it has been unlugged. + @param deviceRef The controller object + @param enable If true the controller is dropped off the bus and kept off. When false the controller will no longer be forced off. + @result The status of the call. + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerForceOffBus(IOUSBDeviceControllerRef device, int enable) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! @function IOUSBDeviceControllerRegisterArrivalCallback + @abstract Schedules async controller arrival with a run loop + @discussion Establishs a callback to be invoked when an IOUSBDeviceController becomes available in-kernel. + @param callback The function invoked when the controller arrives. It receives a IOUSBDeviceControllerRef annd the caller-provided context. + @param context A caller-specified pointer that is provided when the callback is invoked. + @param runLoop RunLoop to be used when scheduling any asynchronous activity. + @param runLoopMode Run loop mode to be used when scheduling any asynchronous activity. + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerRegisterArrivalCallback(IOUSBDeviceArrivalCallback callback, void *context, CFRunLoopRef runLoop, CFStringRef runLoopMode) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +void IOUSBDeviceControllerRemoveArrivalCallback() +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! @function IOUSBDeviceControllerSetDescription + @abstract Provide the information required to configure the IOUSBDeviceController in kernel + @param device The controller instance to receive the description + @param description The description to use. + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerSetDescription(IOUSBDeviceControllerRef device, IOUSBDeviceDescriptionRef description) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! @function IOUSBDeviceControllerSendCommand + @abstract Issue a command to the in-kernel usb-device stack + @discussion This sends a command string and optional parameter object into the kernel. Commands are passed to the controller-driver, the +"device", then to the individual interface drivers, until one of those handles it. + @param device The controller instance to receive the command + @param command A string command. Valid commands are determined by the various in-kernel drivers comprising the usb-device stack + @param param An optional, arbitrary object that is appropriate for the given command + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerSendCommand(IOUSBDeviceControllerRef device, CFStringRef command, CFTypeRef param) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! @function IOUSBDeviceControllerSetPreferredConfiguration + @abstract Sets the preferred configuration number to gain desired functionality on the host + @param device The controller instance to receive the description + @param config Preferred configuration number that will be sent to the host. + */ +CF_EXPORT +IOReturn IOUSBDeviceControllerSetPreferredConfiguration(IOUSBDeviceControllerRef device, int config) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +CF_EXPORT +IOUSBDeviceDescriptionRef IOUSBDeviceDescriptionCreate(CFAllocatorRef allocator) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! @function IOUSBDeviceDescriptionCreateFromController + @abstract Retrieve the current description from the IOUSBDeviceController + @discussion This retrieves the currently set description from the kernel's IOUSBDeviceController. It represents the full description of the device as + it is currently presented on the USB. The call can fail if the controller exists but has not et received a description. + @param allocator The CF allocator to use when creating the description + @param device The controller instance from which to receive the description + */ +CF_EXPORT +IOUSBDeviceDescriptionRef IOUSBDeviceDescriptionCreateFromController(CFAllocatorRef allocator, IOUSBDeviceControllerRef) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! @function IOUSBDeviceDescriptionCreateFromDefaults + @abstract Create a descripion based on the hardwares default usb description. + @discussion This retrieves the default description for the device. It describes the main usb functionality provided by the device and is what is used for + a normal system. Currently the description is retrieved from a plist on disk and is keyed to a sysctl that describes the hardware. + @param allocator The CF allocator to use when creating the description + */ +CF_EXPORT +IOUSBDeviceDescriptionRef IOUSBDeviceDescriptionCreateFromDefaults(CFAllocatorRef allocator) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +IOUSBDeviceDescriptionRef IOUSBDeviceDescriptionCreate(CFAllocatorRef allocator) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +uint8_t IOUSBDeviceDescriptionGetClass(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +void IOUSBDeviceDescriptionSetClass(IOUSBDeviceDescriptionRef ref, UInt8 bClass) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +uint8_t IOUSBDeviceDescriptionGetSubClass(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +uint8_t IOUSBDeviceDescriptionGetProtocol(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +uint16_t IOUSBDeviceDescriptionGetVendorID(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +void IOUSBDeviceDescriptionSetVendorID(IOUSBDeviceDescriptionRef devDesc, UInt16 vendorID) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +uint16_t IOUSBDeviceDescriptionGetProductID(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +void IOUSBDeviceDescriptionSetProductID(IOUSBDeviceDescriptionRef devDesc, UInt16 productID) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +uint16_t IOUSBDeviceDescriptionGetVersion(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +CFStringRef IOUSBDeviceDescriptionGetManufacturerString(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +CFStringRef IOUSBDeviceDescriptionGetProductString(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +CFStringRef IOUSBDeviceDescriptionGetSerialString(IOUSBDeviceDescriptionRef ref) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +void IOUSBDeviceDescriptionSetSerialString(IOUSBDeviceDescriptionRef ref, CFStringRef serial) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +int IOUSBDeviceDescriptionAppendInterfaceToConfiguration(IOUSBDeviceDescriptionRef devDesc, int config, CFStringRef name); +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +int IOUSBDeviceDescriptionAppendConfiguration(IOUSBDeviceDescriptionRef devDesc, CFStringRef textDescription, UInt8 attributes, UInt8 maxPower); +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +void IOUSBDeviceDescriptionRemoveAllConfigurations(IOUSBDeviceDescriptionRef devDesc) +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +io_service_t IOUSBDeviceControllerGetService(IOUSBDeviceControllerRef controller); +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +int IOUSBDeviceDescriptionGetMatchingConfiguration(IOUSBDeviceDescriptionRef devDesc, CFArrayRef interfaceNames); +AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +__END_DECLS + +#endif diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/Makefile b/dump-imessages/iphone-dataprotection/ramdisk_tools/Makefile new file mode 100644 index 0000000..a3bab55 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/Makefile @@ -0,0 +1,51 @@ +SDKVER?=5.1 +ARCH?=armv7 +MINIOS=4.0 +SDK=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(SDKVER).sdk/ +HGVERSION:= $(shell hg parents --template '{node|short}' || echo "unknown") +CC=clang -arch $(ARCH) +CFLAGS=-Wall -isysroot $(SDK) -DHGVERSION="\"${HGVERSION}\"" -O3 -I. +CFLAGS+= -framework CoreFoundation -framework IOKit -framework Security +CFLAGS+= -miphoneos-version-min=$(MINIOS) +CODESIGN=codesign -s - --entitlements + +all: $(SDK) IOKit IOUSBDeviceControllerLib.h device_infos restored_external bruteforce ioflashstoragekit + +$(SDK): + @echo "iOS SDK not found in $(SDK)" + @echo "=> check SDKVER/SDK in Makefile" + +IOUSBDeviceControllerLib.h: + curl -o IOUSBDeviceControllerLib.h http://www.opensource.apple.com/source/IOKitUser/IOKitUser-502/usb_device.subproj/IOUSBDeviceControllerLib.h?txt + +IOKit: + ln -s /System/Library/Frameworks/IOKit.framework/Versions/Current/Headers IOKit + +device_infos: device_infos.c device_info.c IOAESAccelerator.c AppleEffaceableStorage.c AppleKeyStore.c bsdcrypto/pbkdf2.c bsdcrypto/sha1.c bsdcrypto/key_wrap.c bsdcrypto/rijndael.c util.c IOKit.c registry.c ioflash/ioflash.c kernel_patcher.c + $(CC) $(CFLAGS) -o $@ $^ + $(CODESIGN) tfp0.plist $@ + +restored_external: restored_external.c device_info.c remote_functions.c plist_server.c AppleKeyStore.c AppleEffaceableStorage.c IOKit.c IOAESAccelerator.c util.c registry.c AppleKeyStore_kdf.c bsdcrypto/pbkdf2.c bsdcrypto/sha1.c bsdcrypto/rijndael.c bsdcrypto/key_wrap.c ioflash/ioflash.c kernel_patcher.c + $(CC) $(CFLAGS) -o $@ $^ + $(CODESIGN) keystore_device.xml $@ + +bruteforce: systemkb_bruteforce.c AppleKeyStore.c AppleEffaceableStorage.c IOKit.c IOAESAccelerator.c util.c registry.c AppleKeyStore_kdf.c bsdcrypto/pbkdf2.c bsdcrypto/sha1.c bsdcrypto/rijndael.c bsdcrypto/key_wrap.c device_info.c ioflash/ioflash.c kernel_patcher.c + $(CC) $(CFLAGS) -o $@ $^ + $(CODESIGN) keystore_device.xml $@ + +ioflashstoragekit: ioflash/ioflash.c ioflash/ioflash_kernel.c ioflash/ioflashstoragekit.c util.c + $(CC) $(CFLAGS) -o $@ $^ + $(CODESIGN) tfp0.plist $@ + +kernel_patcher: kernel_patcher.c + $(CC) $(CFLAGS) -o $@ $^ + $(CODESIGN) tfp0.plist $@ + +shsh_dump: ioflash/ioflash.c ioflash/ioflash_kernel.c shsh_dump.c util.c + $(CC) $(CFLAGS) -o $@ $^ + $(CODESIGN) tfp0.plist $@ + +clean: + rm -f bruteforce restored_external device_infos ioflashstoragekit shsh_dump + +rebuild: clean all diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/key_wrap.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/key_wrap.c new file mode 100644 index 0000000..997e742 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/key_wrap.c @@ -0,0 +1,119 @@ +/* $OpenBSD: key_wrap.c,v 1.3 2011/01/11 15:42:05 deraadt Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This code implements the AES Key Wrap algorithm described in RFC 3394. + */ + +#include +#include + +//haxs to compile on osx +#include +#define timingsafe_bcmp bcmp +#define ovbcopy bcopy +#define explicit_bzero bzero +#define htobe64 CFSwapInt64BigToHost +#include "rijndael.h" +#include "key_wrap.h" + + +static const u_int8_t IV[8] = + { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6 }; + +void +aes_key_wrap_set_key(aes_key_wrap_ctx *ctx, const u_int8_t *K, size_t K_len) +{ + rijndael_set_key(&ctx->ctx, K, K_len * NBBY); +} + +void +aes_key_wrap_set_key_wrap_only(aes_key_wrap_ctx *ctx, const u_int8_t *K, + size_t K_len) +{ + rijndael_set_key_enc_only(&ctx->ctx, K, K_len * NBBY); +} + +void +aes_key_wrap(aes_key_wrap_ctx *ctx, const u_int8_t *P, size_t n, u_int8_t *C) +{ + u_int64_t B[2], t; + u_int8_t *A, *R; + size_t i; + int j; + + ovbcopy(P, C + 8, n * 8); /* P and C may overlap */ + A = C; /* A points to C[0] */ + memcpy(A, IV, 8); /* A = IV, an initial value */ + + for (j = 0, t = 1; j <= 5; j++) { + R = C + 8; + for (i = 1; i <= n; i++, t++) { + /* B = A | R[i] */ + memcpy(&B[0], A, 8); + memcpy(&B[1], R, 8); + /* B = AES(K, B) */ + rijndael_encrypt(&ctx->ctx, (caddr_t)B, (caddr_t)B); + /* MSB(64, B) = MSB(64, B) ^ t */ + B[0] ^= htobe64(t); + /* A = MSB(64, B) */ + memcpy(A, &B[0], 8); + /* R[i] = LSB(64, B) */ + memcpy(R, &B[1], 8); + + R += 8; + } + } + explicit_bzero(B, sizeof B); +} + +int +aes_key_unwrap(aes_key_wrap_ctx *ctx, const u_int8_t *C, u_int8_t *P, size_t n) +{ + u_int64_t B[2], t; + u_int8_t A[8], *R; + size_t i; + int j; + + memcpy(A, C, 8); /* A = C[0] */ + ovbcopy(C + 8, P, n * 8); /* P and C may overlap */ + + for (j = 5, t = 6 * n; j >= 0; j--) { + R = P + (n - 1) * 8; + for (i = n; i >= 1; i--, t--) { + /* MSB(64, B) = A */ + memcpy(&B[0], A, 8); + /* MSB(64, B) = MSB(64, B) ^ t */ + B[0] ^= htobe64(t); + /* B = MSB(64, B) | R[i] */ + memcpy(&B[1], R, 8); + /* B = AES-1(K, B) */ + rijndael_decrypt(&ctx->ctx, (caddr_t)B, (caddr_t)B); + /* A = MSB(64, B) */ + memcpy(A, &B[0], 8); + /* R[i] = LSB(64, B) */ + memcpy(R, &B[1], 8); + + R -= 8; + } + } + explicit_bzero(B, sizeof B); + + /* check that A is an appropriate initial value */ + return timingsafe_bcmp(A, IV, 8) != 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/key_wrap.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/key_wrap.h new file mode 100644 index 0000000..edaf140 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/key_wrap.h @@ -0,0 +1,40 @@ +/* $OpenBSD: key_wrap.h,v 1.1 2008/08/12 15:43:00 damien Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _KEY_WRAP_H_ +#define _KEY_WRAP_H_ + +typedef unsigned char u_int8_t; + +typedef struct _aes_key_wrap_ctx { + rijndael_ctx ctx; +} aes_key_wrap_ctx; + +#include + +__BEGIN_DECLS + +void aes_key_wrap_set_key(aes_key_wrap_ctx *, const u_int8_t *, size_t); +void aes_key_wrap_set_key_wrap_only(aes_key_wrap_ctx *, const u_int8_t *, + size_t); +void aes_key_wrap(aes_key_wrap_ctx *, const u_int8_t *, size_t, u_int8_t *); +int aes_key_unwrap(aes_key_wrap_ctx *, const u_int8_t *, u_int8_t *, + size_t); +__END_DECLS + +#endif /* _KEY_WRAP_H_ */ diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/pbkdf2.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/pbkdf2.c new file mode 100644 index 0000000..507fd53 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/pbkdf2.c @@ -0,0 +1,253 @@ +/* $OpenBSD: pbkdf2.c,v 1.1 2008/06/14 06:28:27 djm Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include + +#include "sha1.h" + +#include "pbkdf2.h" + +/* #define PBKDF2_MAIN */ + +/* + * HMAC-SHA-1 (from RFC 2202). + */ +static void +hmac_sha1(const u_int8_t *text, size_t text_len, const u_int8_t *key, + size_t key_len, u_int8_t digest[SHA1_DIGEST_LENGTH]) +{ + SHA1_CTX ctx; + u_int8_t k_pad[SHA1_BLOCK_LENGTH]; + u_int8_t tk[SHA1_DIGEST_LENGTH]; + int i; + + if (key_len > SHA1_BLOCK_LENGTH) { + SHA1Init(&ctx); + SHA1Update(&ctx, key, key_len); + SHA1Final(tk, &ctx); + + key = tk; + key_len = SHA1_DIGEST_LENGTH; + } + + bzero(k_pad, sizeof k_pad); + bcopy(key, k_pad, key_len); + for (i = 0; i < SHA1_BLOCK_LENGTH; i++) + k_pad[i] ^= 0x36; + + SHA1Init(&ctx); + SHA1Update(&ctx, k_pad, SHA1_BLOCK_LENGTH); + SHA1Update(&ctx, text, text_len); + SHA1Final(digest, &ctx); + + bzero(k_pad, sizeof k_pad); + bcopy(key, k_pad, key_len); + for (i = 0; i < SHA1_BLOCK_LENGTH; i++) + k_pad[i] ^= 0x5c; + + SHA1Init(&ctx); + SHA1Update(&ctx, k_pad, SHA1_BLOCK_LENGTH); + SHA1Update(&ctx, digest, SHA1_DIGEST_LENGTH); + SHA1Final(digest, &ctx); +} + +/* + * Password-Based Key Derivation Function 2 (PKCS #5 v2.0). + * Code based on IEEE Std 802.11-2007, Annex H.4.2. + */ +int +pkcs5_pbkdf2(const char *pass, size_t pass_len, const char *salt, size_t salt_len, + u_int8_t *key, size_t key_len, u_int rounds) +{ + u_int8_t *asalt, obuf[SHA1_DIGEST_LENGTH]; + u_int8_t d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH]; + u_int i, j; + u_int count; + size_t r; + + if (rounds < 1 || key_len == 0) + return -1; + if (salt_len == 0 || salt_len > SIZE_MAX - 1) + return -1; + if ((asalt = malloc(salt_len + 4)) == NULL) + return -1; + + memcpy(asalt, salt, salt_len); + + for (count = 1; key_len > 0; count++) { + asalt[salt_len + 0] = (count >> 24) & 0xff; + asalt[salt_len + 1] = (count >> 16) & 0xff; + asalt[salt_len + 2] = (count >> 8) & 0xff; + asalt[salt_len + 3] = count & 0xff; + hmac_sha1(asalt, salt_len + 4, pass, pass_len, d1); + memcpy(obuf, d1, sizeof(obuf)); + + for (i = 1; i < rounds; i++) { + hmac_sha1(d1, sizeof(d1), pass, pass_len, d2); + memcpy(d1, d2, sizeof(d1)); + for (j = 0; j < sizeof(obuf); j++) + obuf[j] ^= d1[j]; + } + + r = MIN(key_len, SHA1_DIGEST_LENGTH); + memcpy(key, obuf, r); + key += r; + key_len -= r; + }; + bzero(asalt, salt_len + 4); + free(asalt); + bzero(d1, sizeof(d1)); + bzero(d2, sizeof(d2)); + bzero(obuf, sizeof(obuf)); + + return 0; +} + +#ifdef PBKDF2_MAIN +struct test_vector { + u_int rounds; + const char *pass; + const char *salt; + const char expected[32]; +}; + +/* + * Test vectors from RFC 3962 + */ +struct test_vector test_vectors[] = { + { + 1, + "password", + "ATHENA.MIT.EDUraeburn", + { + 0xcd, 0xed, 0xb5, 0x28, 0x1b, 0xb2, 0xf8, 0x01, + 0x56, 0x5a, 0x11, 0x22, 0xb2, 0x56, 0x35, 0x15, + 0x0a, 0xd1, 0xf7, 0xa0, 0x4b, 0xb9, 0xf3, 0xa3, + 0x33, 0xec, 0xc0, 0xe2, 0xe1, 0xf7, 0x08, 0x37 + }, + }, { + 2, + "password", + "ATHENA.MIT.EDUraeburn", + { + 0x01, 0xdb, 0xee, 0x7f, 0x4a, 0x9e, 0x24, 0x3e, + 0x98, 0x8b, 0x62, 0xc7, 0x3c, 0xda, 0x93, 0x5d, + 0xa0, 0x53, 0x78, 0xb9, 0x32, 0x44, 0xec, 0x8f, + 0x48, 0xa9, 0x9e, 0x61, 0xad, 0x79, 0x9d, 0x86 + }, + }, { + 1200, + "password", + "ATHENA.MIT.EDUraeburn", + { + 0x5c, 0x08, 0xeb, 0x61, 0xfd, 0xf7, 0x1e, 0x4e, + 0x4e, 0xc3, 0xcf, 0x6b, 0xa1, 0xf5, 0x51, 0x2b, + 0xa7, 0xe5, 0x2d, 0xdb, 0xc5, 0xe5, 0x14, 0x2f, + 0x70, 0x8a, 0x31, 0xe2, 0xe6, 0x2b, 0x1e, 0x13 + }, + }, { + 5, + "password", + "\0224VxxV4\022", /* 0x1234567878563412 */ + { + 0xd1, 0xda, 0xa7, 0x86, 0x15, 0xf2, 0x87, 0xe6, + 0xa1, 0xc8, 0xb1, 0x20, 0xd7, 0x06, 0x2a, 0x49, + 0x3f, 0x98, 0xd2, 0x03, 0xe6, 0xbe, 0x49, 0xa6, + 0xad, 0xf4, 0xfa, 0x57, 0x4b, 0x6e, 0x64, 0xee + }, + }, { + 1200, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase equals block size", + { + 0x13, 0x9c, 0x30, 0xc0, 0x96, 0x6b, 0xc3, 0x2b, + 0xa5, 0x5f, 0xdb, 0xf2, 0x12, 0x53, 0x0a, 0xc9, + 0xc5, 0xec, 0x59, 0xf1, 0xa4, 0x52, 0xf5, 0xcc, + 0x9a, 0xd9, 0x40, 0xfe, 0xa0, 0x59, 0x8e, 0xd1 + }, + }, { + 1200, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", + { + 0x9c, 0xca, 0xd6, 0xd4, 0x68, 0x77, 0x0c, 0xd5, + 0x1b, 0x10, 0xe6, 0xa6, 0x87, 0x21, 0xbe, 0x61, + 0x1a, 0x8b, 0x4d, 0x28, 0x26, 0x01, 0xdb, 0x3b, + 0x36, 0xbe, 0x92, 0x46, 0x91, 0x5e, 0xc8, 0x2a + }, + }, { + 50, + "\360\235\204\236", /* g-clef (0xf09d849e) */ + "EXAMPLE.COMpianist", + { + 0x6b, 0x9c, 0xf2, 0x6d, 0x45, 0x45, 0x5a, 0x43, + 0xa5, 0xb8, 0xbb, 0x27, 0x6a, 0x40, 0x3b, 0x39, + 0xe7, 0xfe, 0x37, 0xa0, 0xc4, 0x1e, 0x02, 0xc2, + 0x81, 0xff, 0x30, 0x69, 0xe1, 0xe9, 0x4f, 0x52 + }, + } +}; +#define NVECS (sizeof(test_vectors) / sizeof(*test_vectors)) + +#include +#include + +static void +printhex(const char *s, const u_int8_t *buf, size_t len) +{ + size_t i; + + printf("%s: ", s); + for (i = 0; i < len; i++) + printf("%02x", buf[i]); + printf("\n"); + fflush(stdout); +} + +int +main(int argc, char **argv) +{ + u_int i, j; + u_char result[32]; + struct test_vector *vec; + + for (i = 0; i < NVECS; i++) { + vec = &test_vectors[i]; + printf("vector %u\n", i); + for (j = 1; j < sizeof(result); j += 3) { + if (pkcs5_pbkdf2(vec->pass, strlen(vec->pass), + vec->salt, strlen(vec->salt), + result, j, vec->rounds) != 0) + errx(1, "pbkdf2 failed"); + if (memcmp(result, vec->expected, j) != 0) { + printhex(" got", result, j); + printhex("want", vec->expected, j); + return 1; + } + } + } + return 0; +} +#endif /* PBKDF2_MAIN */ diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/pbkdf2.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/pbkdf2.h new file mode 100644 index 0000000..2ee04c7 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/pbkdf2.h @@ -0,0 +1,24 @@ +/* $OpenBSD: pbkdf2.h,v 1.1 2008/06/14 06:28:27 djm Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Password-Based Key Derivation Function 2 (PKCS #5 v2.0). + * Code based on IEEE Std 802.11-2007, Annex H.4.2. + */ +int pkcs5_pbkdf2(const char *, size_t, const char *, size_t, + u_int8_t *, size_t, u_int); diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/rijndael.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/rijndael.c new file mode 100644 index 0000000..59ab79f --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/rijndael.c @@ -0,0 +1,1266 @@ +/* $OpenBSD: rijndael.c,v 1.19 2008/06/09 07:49:45 djm Exp $ */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +//#include + +#include "rijndael.h" + +#undef FULL_UNROLL + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int +rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) +{ + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int +rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) +{ + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } + return Nr; +} + +void +rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], + u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +static void +rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], + u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(pt + 12, s3); +} + +/* setup key context for encryption only */ +int +rijndael_set_key_enc_only(rijndael_ctx *ctx, const u_char *key, int bits) +{ + int rounds; + + rounds = rijndaelKeySetupEnc(ctx->ek, key, bits); + if (rounds == 0) + return -1; + + ctx->Nr = rounds; + ctx->enc_only = 1; + + return 0; +} + +/* setup key context for both encryption and decryption */ +int +rijndael_set_key(rijndael_ctx *ctx, const u_char *key, int bits) +{ + int rounds; + + rounds = rijndaelKeySetupEnc(ctx->ek, key, bits); + if (rounds == 0) + return -1; + if (rijndaelKeySetupDec(ctx->dk, key, bits) != rounds) + return -1; + + ctx->Nr = rounds; + ctx->enc_only = 0; + + return 0; +} + +void +rijndael_decrypt(rijndael_ctx *ctx, const u_char *src, u_char *dst) +{ + rijndaelDecrypt(ctx->dk, ctx->Nr, src, dst); +} + +void +rijndael_encrypt(rijndael_ctx *ctx, const u_char *src, u_char *dst) +{ + rijndaelEncrypt(ctx->ek, ctx->Nr, src, dst); +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/rijndael.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/rijndael.h new file mode 100644 index 0000000..6adeed6 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/rijndael.h @@ -0,0 +1,59 @@ +/* $OpenBSD: rijndael.h,v 1.13 2008/06/09 07:49:45 djm Exp $ */ + +/** + * rijndael-alg-fst.h + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __RIJNDAEL_H +#define __RIJNDAEL_H + +#define AES_MAXKEYBITS (256) +#define AES_MAXKEYBYTES (AES_MAXKEYBITS/8) +/* for 256-bit keys, fewer for less */ +#define AES_MAXROUNDS 14 + +typedef unsigned char u_char; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +/* The structure for key information */ +typedef struct { + int enc_only; /* context contains only encrypt schedule */ + int Nr; /* key-length-dependent number of rounds */ + u32 ek[4*(AES_MAXROUNDS + 1)]; /* encrypt key schedule */ + u32 dk[4*(AES_MAXROUNDS + 1)]; /* decrypt key schedule */ +} rijndael_ctx; + +int rijndael_set_key(rijndael_ctx *, const u_char *, int); +int rijndael_set_key_enc_only(rijndael_ctx *, const u_char *, int); +void rijndael_decrypt(rijndael_ctx *, const u_char *, u_char *); +void rijndael_encrypt(rijndael_ctx *, const u_char *, u_char *); + +int rijndaelKeySetupEnc(unsigned int [], const unsigned char [], int); +int rijndaelKeySetupDec(unsigned int [], const unsigned char [], int); +void rijndaelEncrypt(const unsigned int [], int, const unsigned char [], + unsigned char []); + +#endif /* __RIJNDAEL_H */ diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/sha1.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/sha1.c new file mode 100644 index 0000000..f07e92b --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/sha1.c @@ -0,0 +1,178 @@ +/* $OpenBSD: sha1.c,v 1.9 2011/01/11 15:50:40 deraadt Exp $ */ + +/* + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#define SHA1HANDSOFF + +#include +#include + +#include "sha1.h" + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void +SHA1Transform(u_int32_t state[5], const unsigned char buffer[SHA1_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e; + typedef union { + unsigned char c[64]; + unsigned int l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; +#ifdef SHA1HANDSOFF + unsigned char workspace[SHA1_BLOCK_LENGTH]; + + block = (CHAR64LONG16 *)workspace; + bcopy(buffer, block, SHA1_BLOCK_LENGTH); +#else + block = (CHAR64LONG16 *)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void +SHA1Init(SHA1_CTX *context) +{ + /* SHA1 initialization constants */ + context->count = 0; + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; +} + + +/* Run your data through this. */ + +void +SHA1Update(SHA1_CTX *context, const unsigned char *data, unsigned int len) +{ + unsigned int i; + unsigned int j; + + j = (u_int32_t)((context->count >> 3) & 63); + context->count += (len << 3); + if ((j + len) > 63) { + bcopy(data, &context->buffer[j], (i = 64 - j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + bcopy(&data[i], &context->buffer[j], len - i); +} + + +/* Add padding and return the message digest. */ + +void +SHA1Final(unsigned char digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context) +{ + unsigned int i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count >> + ((7 - (i & 7)) * 8)) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + + if (digest) + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + digest[i] = (unsigned char)((context->state[i >> 2] >> + ((3 - (i & 3)) * 8)) & 255); + } + bzero(&finalcount, 8); +#if 0 /* We want to use this for "keyfill" */ + /* Wipe variables */ + i = 0; + bzero(context->buffer, 64); + bzero(context->state, 20); + bzero(context->count, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +#endif +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/sha1.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/sha1.h new file mode 100644 index 0000000..ee31467 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/bsdcrypto/sha1.h @@ -0,0 +1,26 @@ +/* $OpenBSD: sha1.h,v 1.5 2007/09/10 22:19:42 henric Exp $ */ + +/* + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#define SHA1_BLOCK_LENGTH 64 +#define SHA1_DIGEST_LENGTH 20 + +typedef struct { + u_int32_t state[5]; + u_int64_t count; + unsigned char buffer[SHA1_BLOCK_LENGTH]; +} SHA1_CTX; + +void SHA1Init(SHA1_CTX * context); +void SHA1Transform(u_int32_t state[5], const unsigned char buffer[SHA1_BLOCK_LENGTH]); +void SHA1Update(SHA1_CTX *context, const unsigned char *data, unsigned int len); +void SHA1Final(unsigned char digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context); + +#endif /* _SHA1_H_ */ diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/device_info.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/device_info.c new file mode 100644 index 0000000..4933050 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/device_info.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include "IOAESAccelerator.h" +#include "AppleEffaceableStorage.h" +#include "bsdcrypto/rijndael.h" +#include "bsdcrypto/key_wrap.h" +#include "device_info.h" +#include "registry.h" +#include "util.h" +#include "ioflash/ioflash.h" + +uint8_t lockers[960]={0}; +uint8_t lwvm[80]={0}; + +CFDictionaryRef device_info(int socket, CFDictionaryRef request) +{ + uint8_t dkey[40]={0}; + uint8_t emf[36]={0}; + size_t bootargs_len = 255; + char bootargs[256]={0}; + + struct HFSInfos hfsinfos={0}; + + CFMutableDictionaryRef out = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + get_device_infos(out); + + CFMutableDictionaryRef nand = FSDGetInfo(0); + if (nand != NULL) + CFDictionaryAddValue(out, CFSTR("nand"), nand); + + getHFSInfos(&hfsinfos); + + uint8_t* key835 = IOAES_key835(); + uint8_t* key89A = IOAES_key89A(); + uint8_t* key89B = IOAES_key89B(); + + if (!AppleEffaceableStorage__getBytes(lockers, 960)) + { + CFDataRef lockersData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, lockers, 960, kCFAllocatorNull); + CFDictionaryAddValue(out, CFSTR("lockers"), lockersData); + CFRelease(lockersData); + + if (!AppleEffaceableStorage__getLockerFromBytes(LOCKER_DKEY, lockers, 960, dkey, 40)) + { + aes_key_wrap_ctx ctx; + + aes_key_wrap_set_key(&ctx, key835, 16); + + if(aes_key_unwrap(&ctx, dkey, dkey, 32/8)) + printf("FAIL unwrapping DKey with key 0x835\n"); + } + if (!AppleEffaceableStorage__getLockerFromBytes(LOCKER_EMF, lockers, 960, emf, 36)) + { + doAES(&emf[4], &emf[4], 32, kIOAESAcceleratorCustomMask, key89B, NULL, kIOAESAcceleratorDecrypt, 128); + } + else if (!AppleEffaceableStorage__getLockerFromBytes(LOCKER_LWVM, lockers, 960, lwvm, 0x50)) + { + doAES(lwvm, lwvm, 0x50, kIOAESAcceleratorCustomMask, key89B, NULL, kIOAESAcceleratorDecrypt, 128); + memcpy(&emf[4], &lwvm[32+16], 32); + } + } + + CFNumberRef n = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &hfsinfos.dataVolumeOffset); + CFDictionaryAddValue(out, CFSTR("dataVolumeOffset"), n); + CFRelease(n); + addHexaString(out, CFSTR("dataVolumeUUID"), (uint8_t*) &hfsinfos.volumeUUID, 8); + addHexaString(out, CFSTR("key835"), key835, 16); + addHexaString(out, CFSTR("key89A"), key89A, 16); + addHexaString(out, CFSTR("key89B"), key89B, 16); + addHexaString(out, CFSTR("EMF"), &emf[4], 32); + addHexaString(out, CFSTR("DKey"), dkey, 32); + + sysctlbyname("kern.bootargs", bootargs, &bootargs_len, NULL, 0); + if (bootargs_len > 1) + { + CFStringRef bootargsString = CFStringCreateWithBytes(kCFAllocatorDefault, bootargs, bootargs_len - 1, kCFStringEncodingASCII, 0); + CFDictionaryAddValue(out, CFSTR("kern.bootargs"), bootargsString); + CFRelease(bootargsString); + } + + CFDictionaryAddValue(out, CFSTR("ramdisk revision"), CFSTR(HGVERSION)); + CFDictionaryAddValue(out, CFSTR("ramdisk compile time"), CFSTR(__DATE__ " " __TIME__ )); + + return out; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/device_info.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/device_info.h new file mode 100644 index 0000000..b209f8a --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/device_info.h @@ -0,0 +1,5 @@ +#ifndef HGVERSION +#define HGVERSION "unknown" +#endif + +CFDictionaryRef device_info(int socket, CFDictionaryRef request); diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/device_infos.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/device_infos.c new file mode 100644 index 0000000..1faa790 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/device_infos.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "device_info.h" +#include "util.h" + +int main(int argc, char* argv[]) +{ + CFMutableDictionaryRef out = device_info(-1, NULL); + + if (out == NULL) + { + fprintf(stderr, "device_info(-1, NULL) failed\n"); + return -1; + } + + if (argc > 1 ) + { + CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingASCII); + CFTypeRef value = CFDictionaryGetValue(out, key); + if (value != NULL) + { + *stderr = *stdout;//HAX + CFShow(value); + } + else + fprintf(stderr, "key %s not found\n", argv[1]); + CFRelease(key); + CFRelease(out); + return 0; + } + + writePlistToStdout(out); + /*CFStringRef plistFileName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@.plist"), CFDictionaryGetValue(out, CFSTR("dataVolumeUUID"))); + + CFStringRef printString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Writing results to %@\n"), plistFileName); + CFShow(printString); + CFRelease(printString); + + saveResults(plistFileName, out); + CFRelease(plistFileName);*/ + CFRelease(out); + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/dump_data_partition.sh b/dump-imessages/iphone-dataprotection/ramdisk_tools/dump_data_partition.sh new file mode 100644 index 0000000..96ac835 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/dump_data_partition.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cat /dev/rdisk0s2s1 | netcat -l -p 1234 diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/image.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/image.c new file mode 100644 index 0000000..fbb162e --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/image.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +int screenWidth, screenHeight; +CGContextRef context = NULL; + +CGContextRef fb_open() { + io_connect_t conn = NULL; + int bytesPerRow; + void *surfaceBuffer; + void *frameBuffer; + CGColorSpaceRef colorSpace; + + if (context != NULL) + return context; + + io_service_t fb_service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleCLCD")); + if (!fb_service) { + fb_service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleM2CLCD")); + if (!fb_service) { + printf("Couldn't find framebuffer.\n"); + return NULL; + } + } + + IOMobileFramebufferOpen(fb_service, mach_task_self(), 0, &conn); + IOMobileFramebufferGetLayerDefaultSurface(conn, 0, &surfaceBuffer); + + screenHeight = CoreSurfaceBufferGetHeight(surfaceBuffer); + screenWidth = CoreSurfaceBufferGetWidth(surfaceBuffer); + bytesPerRow = CoreSurfaceBufferGetBytesPerRow(surfaceBuffer); + + CoreSurfaceBufferLock(surfaceBuffer, 3); + frameBuffer = CoreSurfaceBufferGetBaseAddress(surfaceBuffer); + CoreSurfaceBufferUnlock(surfaceBuffer); + + // create bitmap context + colorSpace = CGColorSpaceCreateDeviceRGB(); + context = CGBitmapContextCreate(frameBuffer, screenWidth, screenHeight, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); + if(context == NULL) { + printf("Couldn't create screen context!\n"); + return NULL; + } + + CGColorSpaceRelease(colorSpace); + + return context; +} + +int drawImage(const char* pngFileName) +{ + CGContextRef c = fb_open(); + if (c == NULL) + return -1; + + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, pngFileName, strlen(pngFileName), 0); + void* imageSource = CGImageSourceCreateWithURL(url, NULL); + CFRelease(url); + + if (imageSource != NULL) + { + CGImageRef img = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); + if (img != NULL) + { + CGContextClearRect (c, CGRectMake(0, 0, screenWidth, screenHeight)); + CGContextDrawImage(c, CGRectMake(0, 0, screenWidth, screenHeight), img); + } + } + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/image.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/image.h new file mode 100644 index 0000000..738a488 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/image.h @@ -0,0 +1 @@ +int drawImage(const char* pngFileName); diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.c new file mode 100644 index 0000000..3cc9507 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.c @@ -0,0 +1,556 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ioflash.h" + +/** +used to decrypt special pages when checking if the physical banks parameter is correct +when reading/dumping pages are not decrypted +**/ +uint8_t META_KEY[16] = {0x92, 0xa7, 0x42, 0xab, 0x08, 0xc9, 0x69, 0xbf, 0x00, 0x6c, 0x94, 0x12, 0xd3, 0xcc, 0x79, 0xa5}; +//uint8_t FILESYSTEM_KEY[16] = {0xf6, 0x5d, 0xae, 0x95, 0x0e, 0x90, 0x6c, 0x42, 0xb2, 0x54, 0xcc, 0x58, 0xfc, 0x78, 0xee, 0xce}; + + +uint32_t gDeviceReadID = 0; +uint32_t gCECount = 0; +uint32_t gBlocksPerCE = 0; +uint32_t gPagesPerBlock = 0; +uint32_t gBytesPerPage = 0; +uint32_t gBytesPerSpare = 0; +uint32_t gBootloaderBytes = 0; +uint32_t gIsBootFromNand = 0; +uint32_t gPagesPerCE = 0; +uint32_t gTotalBlocks = 0; +uint32_t metaPerLogicalPage = 0; +uint32_t validmetaPerLogicalPage = 0; +uint32_t banksPerCE = 0; +uint32_t use_4k_aes_chain = 0; +uint32_t gFSStartBlock= 0; +uint32_t gDumpPageSize = 0; +uint32_t gPPNdevice = 0; +uint32_t banksPerCEphysical = 1; +uint32_t bank_address_space = 0; +uint32_t blocks_per_bank = 0; + +//from ioflashstoragetool +io_service_t fsdService = 0; +io_connect_t fsdConnection = 0; + + +CFMutableDictionaryRef MakeOneStringProp(CFStringRef key, CFStringRef value) +{ + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(dict, key, value); + return dict; +} + +io_service_t _wait_for_io_service_matching_dict(CFDictionaryRef matching) +{ + io_service_t service = 0; + /* while(!service) { + */CFRetain(matching); + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + //if(service) break; + /*sleep(1); + //CFRelease(matching); + } + CFRelease(matching);*/ + return service; +} + + +int FSDConnect(const char* name) +{ + CFMutableDictionaryRef matching; + + matching = IOServiceMatching(name); + + fsdService = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + + IOServiceOpen(fsdService, mach_task_self(), 0, &fsdConnection); + + return fsdConnection != 0; +} + +int FSDGetPropertyForKey(io_object_t obj, CFStringRef name, void* out, uint32_t outLen, CFMutableDictionaryRef dict) +{ + CFTypeRef data = IORegistryEntryCreateCFProperty(obj, name, kCFAllocatorDefault, 0); + + if (!data) + { + return 0; + } + if (dict != NULL) + { + CFDictionaryAddValue(dict, name, data); + } + if (out == NULL) + return 0; + if(CFGetTypeID(data) == CFNumberGetTypeID()) + { + CFNumberGetValue((CFNumberRef)data, kCFNumberIntType, out); + return 1; + } + else if(CFGetTypeID(data) == CFDataGetTypeID()) + { + CFIndex dataLen = CFDataGetLength(data); + CFDataGetBytes(data, CFRangeMake(0,dataLen < outLen ? dataLen : outLen), out); + return 1; + } + return 0; +} +IOReturn FSDReadPageHelper(struct kIOFlashControllerReadPageIn* in, struct kIOFlashControllerOut* out) +{ + size_t outLen = sizeof(struct kIOFlashControllerOut); + + IOConnectCallStructMethod(fsdConnection, + kIOFlashControllerReadPage, + in, + sizeof(struct kIOFlashControllerReadPageIn), + out, + &outLen); + // fprintf(stderr, "%x %x %x %x %x\n", in->page, in->ce, r, out->ret1, out->ret2); + + return out->ret1; +} + +IOReturn FSDReadPageWithOptions(uint32_t ceNum, + uint32_t pageNum, + void* buffer, + void* spareBuffer, + uint32_t spareSize, + uint32_t options, + struct kIOFlashControllerOut* out + ) +{ + struct kIOFlashControllerReadPageIn in; + + in.page = pageNum; + in.ce = ceNum; + in.options = options; + in.buffer = buffer; + in.bufferSize = gBytesPerPage; + in.spare = spareBuffer; + in.spareSize = spareSize; + return FSDReadPageHelper(&in, out); +} + +IOReturn FSDReadBootPage(uint32_t ceNum, uint32_t pageNum,uint32_t* buffer, struct kIOFlashControllerOut* out) +{ + return FSDReadPageWithOptions(ceNum, pageNum, buffer, NULL, 0, kIOFlashStorageOptionBootPageIO, out); +} + +void findPartitionLocation(CFStringRef contentHint, CFMutableDictionaryRef dict) +{ + const void* keys[2] = {CFSTR("Block Count"), CFSTR("Block Offset")}; + const void* values[2] = {NULL, NULL}; + + CFMutableDictionaryRef match = MakeOneStringProp(CFSTR("IOProviderClass"), CFSTR("IOFlashStoragePartition")); + CFMutableDictionaryRef iopmatch = MakeOneStringProp(CFSTR("Content Hint"), contentHint); + CFDictionarySetValue(match, CFSTR("IOPropertyMatch"), iopmatch); + + CFRelease(iopmatch); + + io_service_t service = _wait_for_io_service_matching_dict(match); + if (service) + { + CFNumberRef blockCount = (CFNumberRef) IORegistryEntryCreateCFProperty(service, CFSTR("Block Count"), kCFAllocatorDefault, 0); + CFNumberRef blockOffset = (CFNumberRef) IORegistryEntryCreateCFProperty(service, CFSTR("Block Offset"), kCFAllocatorDefault, 0); + if (dict != NULL) + { + values[0] = (void*) blockCount; + values[1] = (void*) blockOffset; + CFDictionaryRef d = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (d != NULL) + CFDictionaryAddValue(dict, contentHint, d); + } + if( CFStringCompare(contentHint, CFSTR("Filesystem"), 0) == kCFCompareEqualTo) + { + CFNumberGetValue(blockOffset, kCFNumberIntType, &gFSStartBlock); + } + } + CFRelease(match); +} + + + +//openiBoot/util.c +uint32_t next_power_of_two(uint32_t n) { + uint32_t val = 1 << (31 - __builtin_clz(n)); + + if (n % val) + val *= 2; + + return val; +} + +void generate_IV(unsigned long lbn, unsigned long *iv) +{ + int i; + for(i = 0; i < 4; i++) + { + if(lbn & 1) + lbn = 0x80000061 ^ (lbn >> 1); + else + lbn = lbn >> 1; + iv[i] = lbn; + } +} + +void decrypt_page(uint8_t* data, uint32_t dataLength, uint8_t* key, uint32_t keyLength, uint32_t pn) +{ + char iv[16]; + size_t dataOutMoved=dataLength; + generate_IV(pn, (unsigned long*) iv); + + CCCryptorStatus s = CCCrypt(kCCDecrypt, + kCCAlgorithmAES128, + 0, + (const void*) key, + keyLength, + (const void*) iv, + (const void*) data, + dataLength, + (const void*) data, + dataLength, + &dataOutMoved); + if (s != kCCSuccess) + { + fprintf(stderr, "decrypt_page: CCCrypt error %x\n", s); + } +} + +void set_physical_banks(int n) +{ + banksPerCEphysical = n; + blocks_per_bank = gBlocksPerCE / banksPerCEphysical; + + if((gBlocksPerCE & (gBlocksPerCE-1)) == 0) + { + // Already a power of two. + bank_address_space = blocks_per_bank; + //total_block_space = gBlocksPerCE; + } + else + { + // Calculate the bank address space. + bank_address_space = next_power_of_two(blocks_per_bank); + //total_block_space = ((banksPerCEphysical-1)*bank_address_space) + blocks_per_bank; + } +} + +//"bruteforce" the number of physical banks +//except for PPN devices, DEVICEINFOBBT special pages should always be somewhere at the end +void check_special_pages() +{ + if(gPPNdevice) + { + fprintf(stderr, "PPN device, i don't know how to guess the number of physical banks, assuming 1\n"); + set_physical_banks(1); + return; + } + + uint32_t i,x=1; + uint32_t ok = 0; + uint32_t bank, block, page; + uint8_t* pageBuffer = (uint8_t*) valloc(gDumpPageSize); + + printf("Searching for correct banksPerCEphysical value ...\n"); + + while(!ok && x < 10) + { + set_physical_banks(x); + bank = banksPerCEphysical - 1; + + for(block = blocks_per_bank-1; !ok && block > blocks_per_bank-10 ; block--) + { + page = (bank_address_space * bank + block) * gPagesPerBlock; + + struct kIOFlashControllerOut *out = (&pageBuffer[gBytesPerPage + metaPerLogicalPage]); + + for(i=0; i < gPagesPerBlock; i++) + { + if(FSDReadPageWithOptions(0, page + i, pageBuffer, &pageBuffer[gBytesPerPage], metaPerLogicalPage, 0, out)) + continue; + + if(pageBuffer[gBytesPerPage] != 0xA5) + continue; + if(!memcmp(pageBuffer, "DEVICEINFOBBT", 13)) + { + printf("Found cleartext DEVICEINFOBBT at block %d page %d with banksPerCEphyiscal=%d\n", blocks_per_bank*bank +block, i, banksPerCEphysical); + ok = 1; + break; + } + decrypt_page(pageBuffer, gBytesPerPage, META_KEY, kCCKeySizeAES128, page + i); + if(!memcmp(pageBuffer, "DEVICEINFOBBT", 13)) + { + printf("Found encrypted DEVICEINFOBBT at block %d page %d with banksPerCEphyiscal=%d\n", blocks_per_bank*bank +block, i, banksPerCEphysical); + ok = 1; + break; + } + } + } + x++; + } + if(!ok) + { + fprintf(stderr, "Couldnt guess the number of physical banks, exiting\n"); + exit(0); + } + free(pageBuffer); + return; +} + + +//XXX dont read the NAND from this function as it can be called from multiple processes +CFMutableDictionaryRef FSDGetInfo(int printInfo) +{ + io_iterator_t iterator = 0; + io_object_t obj = 0; + + FSDConnect("IOFlashController"); + + if(IORegistryEntryCreateIterator(fsdService, "IOService",0, &iterator)) + return NULL; + + obj = IOIteratorNext(iterator); + if (!obj) + return NULL; + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + FSDGetPropertyForKey(obj, CFSTR("device-readid"), &gDeviceReadID, sizeof(gDeviceReadID), dict); + FSDGetPropertyForKey(obj, CFSTR("#ce"), &gCECount, sizeof(gCECount), dict); + FSDGetPropertyForKey(obj, CFSTR("#ce-blocks"), &gBlocksPerCE, sizeof(gBlocksPerCE), dict); + FSDGetPropertyForKey(obj, CFSTR("#block-pages"), &gPagesPerBlock, sizeof(gPagesPerBlock), dict); + FSDGetPropertyForKey(obj, CFSTR("#page-bytes"), &gBytesPerPage, sizeof(gBytesPerPage), dict); + FSDGetPropertyForKey(obj, CFSTR("#spare-bytes"), &gBytesPerSpare, sizeof(gBytesPerSpare), dict); + FSDGetPropertyForKey(obj, CFSTR("#bootloader-bytes"), &gBootloaderBytes, sizeof(gBootloaderBytes), dict); + + FSDGetPropertyForKey(obj, CFSTR("metadata-whitening"), NULL, 0, dict); + FSDGetPropertyForKey(obj, CFSTR("name"), NULL, 0, dict); + FSDGetPropertyForKey(obj, CFSTR("is-bfn-partitioned"), NULL, 0, dict); + FSDGetPropertyForKey(obj, CFSTR("bbt-format"), NULL, 0, dict); + //FSDGetPropertyForKey(obj, CFSTR("channels"), NULL, 0, dict); + FSDGetPropertyForKey(obj, CFSTR("vendor-type"), NULL, 0, dict); + FSDGetPropertyForKey(obj, CFSTR("ppn-device"), &gPPNdevice, sizeof(gPPNdevice), dict); + + + FSDGetPropertyForKey(obj, CFSTR("valid-meta-per-logical-page"), &validmetaPerLogicalPage, sizeof(gBytesPerSpare), dict); + FSDGetPropertyForKey(obj, CFSTR("meta-per-logical-page"), &metaPerLogicalPage, sizeof(gBytesPerSpare), dict); + if (metaPerLogicalPage == 0) + { + metaPerLogicalPage = 12;//default value? + } + + //XXX: returns (possibly wrong) default value (1) when nand-disable-driver is set, use vendor-type + info from openiboot to get correct value : bank-per-ce VFL (!=physical banks) + FSDGetPropertyForKey(obj, CFSTR("banks-per-ce"), &banksPerCE, sizeof(gBytesPerSpare), dict); + FSDGetPropertyForKey(obj, CFSTR("use-4k-aes-chain"), &use_4k_aes_chain, sizeof(gBytesPerSpare), dict); + + gPagesPerCE = gBlocksPerCE * gPagesPerBlock; + gTotalBlocks = gCECount * gBlocksPerCE; + + FSDGetPropertyForKey(obj, CFSTR("boot-from-nand"), &gIsBootFromNand, sizeof(gIsBootFromNand), dict); + + CFMutableDictionaryRef dictPartitions = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + findPartitionLocation(CFSTR("Boot Block"), dictPartitions); + findPartitionLocation(CFSTR("Effaceable"), dictPartitions); + findPartitionLocation(CFSTR("NVRAM"), dictPartitions); + findPartitionLocation(CFSTR("Firmware"), dictPartitions); + findPartitionLocation(CFSTR("Filesystem"), dictPartitions); + /*findPartitionLocation(CFSTR("Diagnostic Data")); + findPartitionLocation(CFSTR("System Config")); + findPartitionLocation(CFSTR("Bad Block Table"));*/ + //findPartitionLocation(CFSTR("Unpartitioned"));//never matches + + CFDictionaryAddValue(dict, CFSTR("partitions"), dictPartitions); + IOObjectRelease(obj); + IOObjectRelease(iterator); + + gDumpPageSize = gBytesPerPage + metaPerLogicalPage + sizeof(struct kIOFlashControllerOut); + CFNumberRef n = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &gDumpPageSize); + CFDictionarySetValue(dict, CFSTR("dumpedPageSize"), n); + CFRelease(n); + + if (printInfo) + { + uint64_t total_size = ((uint64_t)gBytesPerPage) * ((uint64_t) (gPagesPerBlock * gBlocksPerCE * gCECount)); + total_size /= 1024*1024*1024; + fprintf(stderr, "NAND configuration: %uGiB (%d CEs of %d blocks of %d pages of %d bytes data, %d bytes spare\n", + (uint32_t) total_size, + gCECount, + gBlocksPerCE, + gPagesPerBlock, + gBytesPerPage, + gBytesPerSpare); + } + + set_physical_banks(1); + return dict; +} + +CFDictionaryRef nand_dump(int fd) +{ + uint64_t totalSize = (uint64_t)gPagesPerBlock * (uint64_t)gBlocksPerCE * (uint64_t)gCECount * (uint64_t)gDumpPageSize; + write(fd, &totalSize, sizeof(uint64_t)); + + dump_nand_to_socket(fd); + return NULL; +} + +int dump_nand_to_socket(int fd) +{ + int ceNum=0,pageNum,bankNum; + IOReturn r; + uint64_t totalPages = gPagesPerBlock * gBlocksPerCE * gCECount; + uint64_t validPages = 0; + uint64_t blankPages = 0; + uint64_t errorPages = 0; + uint64_t otherPages = 0; + uint64_t counter = 0; + + //page data + spare metadata + kernel return values + uint8_t* pageBuffer = (uint8_t*) valloc(gDumpPageSize); + + if (pageBuffer == NULL) + { + fprintf(stderr, "valloc(%d) FAIL", gDumpPageSize); + return 0; + } + struct kIOFlashControllerOut *out = (&pageBuffer[gBytesPerPage + metaPerLogicalPage]); + + time_t start = time(NULL); + + for(bankNum=0; bankNum < banksPerCEphysical; bankNum++) + { + uint32_t start_page = bank_address_space * bankNum * gPagesPerBlock; + uint32_t end_page = start_page + gPagesPerBlock * blocks_per_bank; + for(pageNum=start_page; pageNum < end_page; pageNum++) + { + for(ceNum=0; ceNum < gCECount; ceNum++) + { + uint32_t blockNum = pageNum / gPagesPerBlock; + uint32_t boot = (blockNum < gFSStartBlock); + + if(boot) + r = FSDReadBootPage(ceNum, pageNum, pageBuffer, out); + else + r = FSDReadPageWithOptions(ceNum, pageNum, pageBuffer, &pageBuffer[gBytesPerPage], metaPerLogicalPage, 0, out); + //r = FSDReadPageWithOptions(ceNum, pageNum, pageBuffer,NULL, 0, 0x100, out); + + if(r == 0) + { + validPages++; + } + else + { + if (r == kIOReturnBadMedia) + { + fprintf(stderr, "CE %x page %x : uncorrectable ECC error\n", ceNum, pageNum); + errorPages++; + } + else if (r == kIOReturnUnformattedMedia) + { + memset(pageBuffer, 0xFF, gBytesPerPage + metaPerLogicalPage); + blankPages++; + } + else if (r == 0xdeadbeef) + { + fprintf(stderr, "0xdeadbeef return code, something is wrong with injected kernel code\n"); + exit(0); + } + else if (r == kIOReturnBadArgument || r == kIOReturnNotPrivileged) + { + fprintf(stderr, "Error 0x%x (kIOReturnBadArgument/kIOReturnNotPrivileged)\n", r); + exit(0); + } + else + { + fprintf(stderr, "CE %x page %x : unknown return code 0x%x\n", ceNum, pageNum, r); + otherPages++; + } + } + out->ret2 = 0; + + if(write(fd, pageBuffer, gDumpPageSize) != gDumpPageSize) + { + pageNum = gPagesPerBlock * gBlocksPerCE; + fprintf(stderr, "Abort dump\n"); + break; + } + if (ceNum == 0 && pageNum % gPagesPerBlock == 0) + { + fprintf(stderr, "Block %d/%d (%d%%)\n", (counter/gPagesPerBlock), gBlocksPerCE, (counter*100)/(gPagesPerBlock*gBlocksPerCE)); + } + } + counter++; + } + } + if (ceNum == gCECount && pageNum == (gPagesPerBlock * gBlocksPerCE)) + { + time_t duration = time(NULL) - start; + fprintf(stderr, "Finished NAND dump in %lu hours %lu minutes %lu seconds\n", duration / 3600, (duration % 3600) / 60, (duration % 3600) % 60); + fprintf(stderr, "Total pages %llu\n", totalPages); + fprintf(stderr, "In-use pages %llu (%d%%)\n", validPages, (int) (validPages * 100 / totalPages)); + fprintf(stderr, "Blank pages %llu (%d%%)\n", blankPages, (int) (blankPages * 100 / totalPages)); + fprintf(stderr, "Error pages %llu (%d%%)\n", errorPages, (int) (errorPages * 100 / totalPages)); + fprintf(stderr, "Other pages %llu (%d%%)\n", otherPages, (int) (otherPages * 100 / totalPages)); + } + free(pageBuffer); + return 0; +} + +int nand_proxy(int fd) +{ + struct kIOFlashControllerOut out; + proxy_read_cmd cmd; + + uint8_t* pageBuffer = (uint8_t*) valloc(gBytesPerPage); + if( pageBuffer == NULL) + { + fprintf(stderr, "pageBuffer = valloc(%d) failed\n", gBytesPerPage); + return 0; + } + + while(1) + { + int z = read(fd, &cmd, sizeof(proxy_read_cmd)); + if (z != sizeof(proxy_read_cmd)) + break; + + void* spareBuf = NULL; + + uint32_t blockNum = cmd.page / gPagesPerBlock; + uint32_t boot = (blockNum < gFSStartBlock); + if(boot) + { + cmd.spareSize = 0; + cmd.options |= kIOFlashStorageOptionBootPageIO; + } + else if (cmd.spareSize) + { + spareBuf = valloc(cmd.spareSize); + } + FSDReadPageWithOptions(cmd.ce, cmd.page, pageBuffer, spareBuf, cmd.spareSize, cmd.options, &out); + + write(fd, pageBuffer, gBytesPerPage); + if (spareBuf != NULL) + { + write(fd, spareBuf, cmd.spareSize); + } + write(fd, &out, sizeof(struct kIOFlashControllerOut)); + + if (spareBuf != NULL) + { + free(spareBuf); + } + } + free(pageBuffer); + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.h new file mode 100644 index 0000000..9053b68 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.h @@ -0,0 +1,118 @@ +#include + +#define kIOFlashStorageOptionBootPageIO 0x100 +#define kIOFlashStorageOptionRawPageIO 0x002 +#define kIOFlashStorageOptionXXXX 0x004 +//0xC0 == kIOFlashStorageOptionUseAES | kIOFlashStorageOptionHomogenize + +#define kIOFlashControllerReadPage 0x1 +#define kIOFlashControllerWritePage 0x2 +#define kIOFlashControllerDisableKeepout 0xa + + +struct kIOFlashControllerReadPageIn; +struct kIOFlashControllerOut; + +//from ioflashstoragetool +CFMutableDictionaryRef MakeOneStringProp(CFStringRef key, CFStringRef value); +io_service_t _wait_for_io_service_matching_dict(CFDictionaryRef matching); +int FSDConnect(const char* name); +int FSDGetPropertyForKey(io_object_t obj, CFStringRef name, void* out, uint32_t outLen, CFMutableDictionaryRef dict); +void findPartitionLocation(CFStringRef contentHint, CFMutableDictionaryRef dict);//findNvramLocation +IOReturn FSDReadPageHelper(struct kIOFlashControllerReadPageIn* in, struct kIOFlashControllerOut* out); +IOReturn FSDReadPageWithOptions(uint32_t ceNum, uint32_t pageNum, void* buffer, void* spareBuffer, uint32_t spareSize, uint32_t options, struct kIOFlashControllerOut* out); +IOReturn FSDReadBootPage(uint32_t ceNum, uint32_t pageNum,uint32_t* buffer, struct kIOFlashControllerOut* out); + +CFMutableDictionaryRef FSDGetInfo(int); +int IOFlashStorage_kernel_patch(); + +CFDictionaryRef nand_dump(int fd); +int dump_nand_to_socket(int fd); +int nand_proxy(int fd); + +struct kIOFlashControllerReadPageIn +{ + uint32_t page; + uint32_t ce; + uint32_t options; + void* buffer; + uint32_t bufferSize; + void* spare; + uint32_t spareSize; +};//sizeof = 0x1C + +//sizeof=0x8 +struct kIOFlashControllerOut +{ + uint32_t ret1; + uint32_t ret2; +}; + +//sizeof=0x50 AppleIOPFMIDMACommand? +struct IOFlashCommandStruct +{ + uint32_t flags0; + uint32_t flags1; + uint32_t field8; + uint32_t fieldC; + uint32_t* page_ptr; + void* bufferDesc;//IOMemoryDescriptor* + uint32_t field18; + uint32_t field1C; + uint32_t field20; + uint32_t* ce_ptr; + void* spareVA; + uint32_t spareSize; + uint32_t field30; + uint32_t field34; + uint32_t field38; + uint32_t errorCode; + uint32_t field40; + uint32_t field44; + uint32_t field48; + uint32_t field4C; +}; + +typedef struct IOExternalMethodArguments +{ + uint32_t version; + + uint32_t selector; + + mach_port_t asyncWakePort; + io_user_reference_t * asyncReference; + uint32_t asyncReferenceCount; + + const uint64_t * scalarInput; + uint32_t scalarInputCount; + + const void * structureInput; + uint32_t structureInputSize; + + //IOMemoryDescriptor * structureInputDescriptor; + void * structureInputDescriptor; + + uint64_t * scalarOutput; + uint32_t scalarOutputCount; + + void * structureOutput; + uint32_t structureOutputSize; + + void * structureOutputDescriptor; + uint32_t structureOutputDescriptorSize; + + uint32_t __reservedA; + + //OSObject ** structureVariableOutputData; + void ** structureVariableOutputData; + + uint32_t __reserved[30]; +} IOExternalMethodArguments; + +typedef struct proxy_read_cmd +{ + uint32_t ce; + uint32_t page; + uint32_t spareSize; + uint32_t options; +} proxy_read_cmd; diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash_kernel.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash_kernel.c new file mode 100644 index 0000000..2f5aa40 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash_kernel.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ioflash.h" + +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; +CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); + +mach_port_t kernel_task=0; + +void* externalMethod_original = NULL; +void** externalMethod_ptr = NULL; + + +void* (*IOMemoryDescriptor__withAddress)(void*, uint32_t, uint32_t, uint32_t) = 0x0; + +typedef int (*methodptr)(void*, ...); + +#define CALL_VTABLE(this, vtableoff, ...) \ + ((methodptr) (*(uint32_t**)this)[vtableoff/4]) (this, ##__VA_ARGS__) + +//IOReturn externalMethod( uint32_t selector, IOExternalMethodArguments * arguments,IOExternalMethodDispatch * dispatch = 0, OSObject * target = 0, void * reference = 0 ); +int myIOFlashStorage_externalMethod(uint32_t* this, uint32_t selector, IOExternalMethodArguments* arguments) +{ + if (selector == kIOFlashControllerDisableKeepout) + { + void* obj2 = (void*) this[0x78/4]; //AppleIOPFMI object + CALL_VTABLE(obj2, 0x35C, 0, 0, 1); + return 0x0; + } + if (selector != kIOFlashControllerReadPage) //only support read + return 0xE00002C2; + + if (IOMemoryDescriptor__withAddress == 0x0) + return 0xdeadbeef; + + struct IOFlashCommandStruct command = {0}; + uint32_t** bufferDesc = NULL; + uint32_t** spareDesc = NULL; + uint32_t** md = NULL; + + void* obj1 = (void*) this[0x78/4]; //AppleIOPFMI object + + struct kIOFlashControllerReadPageIn* in = (struct kIOFlashControllerReadPageIn*) arguments->structureInput; + struct kIOFlashControllerOut* out = (struct kIOFlashControllerOut*) arguments->structureOutput; + + uint32_t page = in->page; + uint32_t ce = in->ce; + + command.flags1 = 1; + if (in->options & kIOFlashStorageOptionBootPageIO) + { + if(in->spare != NULL) + return 0xE00002C2; //spare buffer is not used with bootloader page I/O + command.flags0 = 9; + } + else + { + if( !(in->options & kIOFlashStorageOptionRawPageIO) && in->spare) + { + command.flags0 = in->options & 2; + } + else + { + return 0xdeadbeef; //raw page io + } + } + command.flags1 |= in->options & 4; + command.field8 = 1; + command.fieldC = 0; + command.page_ptr = &page; + + + bufferDesc = IOMemoryDescriptor__withAddress(in->buffer, in->bufferSize, 1, this[0x7C/4]); + if (bufferDesc == NULL) + return 0xE00002C2; + + command.bufferDesc = bufferDesc; + command.field18 = 0; + command.errorCode = 0; + + if (in->spare != NULL) + { + spareDesc = IOMemoryDescriptor__withAddress(in->spare, in-> spareSize, 1, this[0x7C/4]); + if (spareDesc == NULL) + return 0xE00002C2; + + //0xAC -> desc2 __ZN25IOGeneralMemoryDescriptor5doMapEP7_vm_mapPjmmm + //virtual IOMemoryMap * map( IOOptionBits options = 0 ); + md = (void*) CALL_VTABLE(spareDesc, 0x98); //IOGeneralMemoryDescriptor_map + + command.spareSize = in->spareSize; + command.spareVA = (void*) CALL_VTABLE(md, 0x38); + } + else + { + command.spareSize = 0; + command.spareVA = NULL; + } + command.field34 = 0; + command.ce_ptr = &ce; + + out->ret1 = CALL_VTABLE(obj1, 0x344, 0, &command); + + CALL_VTABLE(bufferDesc, 0x14); //IOGeneralMemoryDescriptor_release + + if (md != NULL) + { + CALL_VTABLE(md, 0x14); //IOGeneralMemoryDescriptor_release + } + if (spareDesc != NULL) + { + CALL_VTABLE(spareDesc, 0x14); //IOGeneralMemoryDescriptor_release + } + out->ret2 = command.errorCode; + return 0; +} + +kern_return_t write_kernel(mach_port_t p, void* addr, uint32_t value) +{ + kern_return_t r = vm_write(p, (vm_address_t)addr, (vm_address_t)&value, sizeof(value )); + if (r) + fprintf(stderr, "vm_write into kernel_task failed\n"); + else + fprintf(stderr, "vm_write into kernel_task OK\n"); + return r; +} + +void __attribute__((destructor)) restore_handler() +{ + if (kernel_task != 0 && externalMethod_ptr != NULL && externalMethod_original != NULL) + { + fprintf(stderr, "Restoring IOFlashStorageControler::externalMethod ptr\n"); + write_kernel(kernel_task, externalMethod_ptr, (uint32_t) externalMethod_original+1); + } +} + +void signal_handler(int sig) +{ + restore_handler(); + signal(sig, SIG_DFL); + raise(sig); +} + +int IOFlashStorage_kernel_patch() +{ + CFStringRef version = NULL; + CFDictionaryRef versionDict = _CFCopySystemVersionDictionary(); + + if (versionDict != NULL) + { + version = CFDictionaryGetValue(versionDict, _kCFSystemVersionProductVersionKey); + } + if (version == NULL) + { + fprintf(stderr, "FAILed to get current version\n"); + return 0; + } + if (CFStringCompare(version, CFSTR("4.3.5"), 0) <= 0) + { + fprintf(stderr, "iOS 4 kernel detected, no kernel patching required\n"); + return 1; + } + fprintf(stderr, "iOS 5 kernel detected, replacing IOFlashControlerUserClient::externalMethod\n"); + + kern_return_t r = task_for_pid(mach_task_self(), 0, &kernel_task); + + if( r != 0) + { + fprintf(stderr, "task_for_pid returned %x : missing tfp0 kernel patch (use latest kernel_patcher.py) or wrong entitlements\n", r); + return 0; + } + uint32_t i; + pointer_t buf; + unsigned int sz; + + vm_address_t addr = 0x80002000; + + while( addr < (0x80002000 + 0xA00000)) + { + vm_read(kernel_task, addr, 2048, &buf, &sz); + if( buf == NULL || sz == 0) + continue; + + uint32_t* p = (uint32_t*) buf; + + for(i=0; i < sz/sizeof(uint32_t); i++) + { + if (externalMethod_original != NULL) + { + if (p[i] == (externalMethod_original+1)) + { + externalMethod_ptr = (void*) (addr + i*4); + fprintf(stderr, "Found externalMethod ptr at %x\n", (uint32_t) externalMethod_ptr); + write_kernel(kernel_task, externalMethod_ptr, (uint32_t) myIOFlashStorage_externalMethod); + + signal(SIGINT, signal_handler);//handle ctrl+c + return 1; + } + else if(IOMemoryDescriptor__withAddress == NULL && !memcmp(&p[i], "\x20\x46\x26\xB0\x5D\xF8\x04\x8B\xF0\xBD", 10)) + { + IOMemoryDescriptor__withAddress = (void*) p[i+5]; + fprintf(stderr, "IOMemoryDescriptor__withAddress=%x\n", (uint32_t) IOMemoryDescriptor__withAddress); + } + } + else if(!memcmp(&p[i], "\xF0\xB5\x03\xAF\x4D\xF8\x04\x8D\xA6\xB0\x40\xF2\xC2\x24\x13\x6A", 16)) + { + externalMethod_original = (void*) (addr + i*4); + fprintf(stderr, "Found IOFlashControlerUserClient::externalMethod at %x\n", (uint32_t) externalMethod_original); + } + } + addr += 2048; + } + fprintf(stderr, "Kernel patching failed\n"); + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflashstoragekit.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflashstoragekit.c new file mode 100644 index 0000000..755a1e4 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflashstoragekit.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include "ioflash.h" +#include "../util.h" + +#define LISTEN_PORT 2000 + +#define CMD_DUMP 0 +#define CMD_PROXY 1 + +int main(int argc, char* argv[]) +{ + int one=1; + int cmd=0; + + if(!IOFlashStorage_kernel_patch()) + return -1; + + CFMutableDictionaryRef dict = FSDGetInfo(1); + if(dict == NULL) + { + fprintf(stderr, "FAILed to get NAND infos"); + return -1; + } + + check_special_pages(); + + int sl = create_listening_socket(LISTEN_PORT); + if(sl == -1) + { + fprintf(stderr, "Error calling create_listening_socket\n"); + return -1; + } + + fprintf(stderr, "NAND dumper listening on port %d\n", LISTEN_PORT); + + while(1) + { + int s = accept(sl, NULL, NULL); + setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(int)); + + int r = read(s, (void*) &cmd, sizeof(int)); + if(r == sizeof(int)) + { + if(cmd == CMD_DUMP) + { + nand_dump(s); + } + else if(cmd == CMD_PROXY) + { + nand_proxy(s); + } + } + shutdown(s, SHUT_RDWR); + close(s); + } + return 0; +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/kernel_patcher.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/kernel_patcher.c new file mode 100644 index 0000000..c28a0ab --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/kernel_patcher.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +mach_port_t kernel_task=0; + +kern_return_t write_kernel(mach_port_t p, void* addr, uint32_t value) +{ + pointer_t buf; + unsigned int sz; + + kern_return_t r = vm_write(p, (vm_address_t)addr, (vm_address_t)&value, sizeof(value)); + if (r) + { + fprintf(stderr, "vm_write into kernel_task failed\n"); + } + else + { + //fix cache issue + vm_read(p, (vm_address_t) addr, sizeof(value), &buf, &sz); + fprintf(stderr, "vm_write into kernel_task OK %x\n", *((uint32_t*) buf)); + } + return r; +} + +int patch_IOAESAccelerator() +{ + kern_return_t r = task_for_pid(mach_task_self(), 0, &kernel_task); + + if( r != 0) + { + fprintf(stderr, "task_for_pid returned %x : missing tfp0 kernel patch or wrong entitlements\n", r); + return 0; + } + uint32_t i; + pointer_t buf; + unsigned int sz; + + vm_address_t addr = 0x80002000; + + while( addr < (0x80002000 + 0xA00000)) + { + vm_read(kernel_task, addr, 2048, &buf, &sz); + if( buf == NULL || sz == 0) + continue; + uint8_t* p = (uint8_t*) buf; + + for(i=0; i < sz; i++) + { + //"IOAESAccelerator enable UID" : (h("67 D0 40 F6"), h("00 20 40 F6")), + if (*((uint32_t*)&p[i]) == 0xF640d067) + { + fprintf(stderr, "Found IOAESAccelerator UID ptr at %x, patching kernel\n", (uint32_t) addr + i); + write_kernel(kernel_task, (void*) (addr + i), (uint32_t) 0xF6402000); + return 0; + } + } + addr += 2048; + } + fprintf(stderr, "IOAESAccelerator Kernel patching failed\n"); + return -1; +} + +/* +int main(int argc, char** argv) +{ + return patch_IOAESAccelerator(); +} +*/ \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/keystore_device.xml b/dump-imessages/iphone-dataprotection/ramdisk_tools/keystore_device.xml new file mode 100644 index 0000000..73fe0c8 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/keystore_device.xml @@ -0,0 +1,14 @@ + + + + + com.apple.keystore.device + + get-task-allow + + run-unsigned-code + + task_for_pid-allow + + + diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/plist_server.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/plist_server.c new file mode 100644 index 0000000..eb932f3 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/plist_server.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include +#include +#include "plist_server.h" +#include "util.h" + +#define TCP_PORT 1999 + +int send_progress_message(int socket, int progress, int total) +{ + const void* keys[3] = {CFSTR("MessageType"), CFSTR("Progress"), CFSTR("Total")}; + const void* values[3] = {CFSTR("Progress"), NULL, NULL}; + + CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &progress); + CFNumberRef number2 = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &total); + values[1] = number; + values[2] = number2; + CFDictionaryRef msg = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease(number); + CFRelease(number2); + int res = send_object(socket, msg); + CFRelease(msg); + return res; +} + +int send_object(int socket, CFTypeRef obj) +{ + uint32_t len = 0; + int res = -1; + + if(obj == NULL) + return res; + + CFDataRef outdata = CFPropertyListCreateData(kCFAllocatorDefault, obj, kCFPropertyListXMLFormat_v1_0, 0, NULL); + if (outdata != NULL) + { + len = CFDataGetLength(outdata); + write(socket, &len, 4); + res = write(socket, CFDataGetBytePtr(outdata), CFDataGetLength(outdata)); + CFRelease(outdata); + } + return res; +} + +int handle_client(int socket, CFDictionaryRef handlers) +{ + uint32_t len=0; + uint32_t received,i; + CFDataRef data; + CFDictionaryRef plist; + CFTypeRef out = NULL; + uint8_t* buffer; + CFTypeRef (*handler)(int, CFDictionaryRef dict) = NULL; + + while(1) + { + if(recv(socket, &len, 4, 0) != 4) + break; + //printf("len=%x\n", len); + + if (len > PLIST_MAX_SIZE) + break; + + buffer = malloc(len); + + if(buffer == NULL) + break; + + for(i=0; i < len; ) + { + received = recv(socket, &buffer[i], len - i, 0); + if (received == -1) + break; + i += received; + } + + data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, len, kCFAllocatorNull); + + if(data == NULL) + { + free(buffer); + continue; + } + + plist = (CFDictionaryRef) CFPropertyListCreateWithData (kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL); + + if(plist == NULL || CFGetTypeID(plist) != CFDictionaryGetTypeID()) + { + CFRelease(data); + free(buffer); + send_object(socket, CFSTR("invalid XML plist dictionary")); + continue; + } + + if (CFDictionaryContainsKey(plist, CFSTR("Request"))) + { + CFStringRef request = CFDictionaryGetValue(plist, CFSTR("Request")); + + handler = CFDictionaryGetValue(handlers, request); + + if (handler != NULL) + { + out = handler(socket, plist); + if (out == NULL) + out = CFSTR("Request did not return any result"); + } + else + { + out = CFSTR("No handler defined for Request"); + } + } + else + { + out = CFSTR("request dictionary needs to contain Request key"); + } + + if(out == NULL) + out = CFSTR("no response"); + + send_object(socket, out); + CFRelease(out); + + CFRelease(plist); + CFRelease(data); + free(buffer); + } + send_object(socket, CFSTR("kthxbye")); + return 0; +} + +void serve_plist_rpc(int port, CFDictionaryRef handlers) +{ + int quit = 0; + int one=1; + printf("plist_rpc: listening on port %d\n", port); + int sl = create_listening_socket(port); + + while(!quit) + { + int s = accept(sl, NULL, NULL); + setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(int)); + + handle_client(s, handlers); + shutdown(s, SHUT_RDWR); + close(s); + } + close(sl); +} + +CFStringRef testHandler(int s, CFDictionaryRef dict) +{ + printf("lol\n"); + return CFSTR("Hello, World!"); +} + diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/plist_server.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/plist_server.h new file mode 100644 index 0000000..9b1a1bf --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/plist_server.h @@ -0,0 +1,6 @@ +#define PLIST_MAX_SIZE 50*1024*1024 + +int create_listening_socket(int port); +int send_progress_message(int socket, int progress, int total); +int send_object(int socket, CFTypeRef obj); +void serve_plist_rpc(int port, CFDictionaryRef handlers); \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/registry.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/registry.c new file mode 100644 index 0000000..d987e0b --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/registry.c @@ -0,0 +1,423 @@ +/** +https://github.com/Gojohnnyboi/restored_pwn +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +//from libmobilegestalt.dylib +CFDataRef copyDataFromChosen(CFStringRef key) +{ + io_registry_entry_t chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen"); + if (chosen) + { + CFDataRef res = IORegistryEntryCreateCFProperty(chosen, key, kCFAllocatorDefault, 0); + IOObjectRelease(chosen); + return res; + } + return NULL; +} +CFStringRef copyStringFromChosen(CFStringRef key) +{ + CFStringRef s = NULL; + CFDataRef data = copyDataFromChosen(key); + if(data == NULL) + return NULL; + + if(CFGetTypeID(data) == CFDataGetTypeID()) + { + s = CFStringCreateWithCString(kCFAllocatorDefault, (const char*) CFDataGetBytePtr(data), kCFStringEncodingUTF8); + } + CFRelease(data); + return s; +} + +CFNumberRef copyNumberFromChosen(CFStringRef key) +{ + CFNumberRef num = NULL; + CFDataRef data = copyDataFromChosen(key); + + if(data == NULL) + return NULL; + if(CFGetTypeID(data) == CFDataGetTypeID()) + { + int len = CFDataGetLength(data); + + num = CFNumberCreate(kCFAllocatorDefault, + len == 4 ? kCFNumberSInt32Type : kCFNumberSInt64Type, + CFDataGetBytePtr(data) + ); + } + CFRelease(data); + return num; +} + +io_service_t get_io_service(const char *name) { + CFMutableDictionaryRef matching; + io_service_t service = 0; + + matching = IOServiceMatching(name); + if(matching == NULL) { + printf("unable to create matching dictionary for class '%s'\n", name); + return 0; + } + + while(!service) { + CFRetain(matching); + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + if(service) break; + + printf("waiting for matching IOKit service: %s\n", name); + sleep(1); + CFRelease(matching); + } + + CFRelease(matching); + + return service; +} + +CFStringRef copy_device_imei() { + CFMutableDictionaryRef matching; + io_service_t service; + CFDataRef imeiData; + const void *imeiDataPtr; + CFStringRef imeiString; + + matching = IOServiceNameMatching("baseband"); + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + + if(!service) { + return NULL; + } + + imeiData = IORegistryEntryCreateCFProperty(service, CFSTR("device-imei"), kCFAllocatorDefault, 0); + if(!imeiData) { + printf("unable to find device-imei property\n"); + IOObjectRelease(service); + return NULL; + } + + imeiDataPtr = CFDataGetBytePtr(imeiData); + imeiString = CFStringCreateWithCString(kCFAllocatorDefault, imeiDataPtr, kCFStringEncodingUTF8); + + CFRelease(imeiData); + IOObjectRelease(service); + + return imeiString; +} + +CFStringRef copy_device_serial_number() { + io_service_t service; + CFStringRef serialNumber; + + service = get_io_service("IOPlatformExpertDevice"); + if(!service) { + printf("unable to find IOPlatformExpertDevice service\n"); + return NULL; + } + + serialNumber = IORegistryEntryCreateCFProperty(service, CFSTR("IOPlatformSerialNumber"), kCFAllocatorDefault, 0); + IOObjectRelease(service); + + return serialNumber; +} + +CFStringRef copy_devicetree_option(CFStringRef key) { + io_registry_entry_t entry; + CFStringRef option; + + entry = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options"); + if(!entry) { + printf("unable to get registry entry for IODeviceTree:/options\n"); + return NULL; + } + + option = IORegistryEntryCreateCFProperty(entry, key, kCFAllocatorDefault, 0); + IOObjectRelease(entry); + + return option; +} + +CFStringRef copy_hardware_model() { + size_t buflen = 0x80; + char buf[buflen]; + CFStringRef model; + + if(sysctlbyname("hw.model", buf, &buflen, NULL, 0) != 0) { + printf("sysctlbyname for hw.model failed: %s\n", strerror(errno)); + return NULL; + } + + model = CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingUTF8); + + return model; +} + +CFStringRef copy_hardware_platform() { + io_service_t service; + CFStringRef platform; + char *platformPtr; + + service = get_io_service("IOPlatformExpertDevice"); + if(!service) { + printf("unable to find IOPlatformExpertDevice service\n"); + return NULL; + } + + platform= IORegistryEntryCreateCFProperty(service, CFSTR("platform-name"), kCFAllocatorDefault, 0); + if(platform == NULL) { + printf("platform-name not found in device tree\n"); + IOObjectRelease(service); + return NULL; + } + + platformPtr = calloc(1, CFStringGetLength(platform)+1); + if(!CFStringGetCString(platform, platformPtr, CFStringGetLength(platform)+1, kCFStringEncodingUTF8)) { + printf("unable to obtain platform-name string\n"); + IOObjectRelease(service); + return NULL; + } + + printf("platform-name = %s\n", platformPtr); + free(platformPtr); + + return platform; +} + +CFStringRef copy_bluetooth_mac_address() { + io_service_t service; + CFDataRef macaddrData; + CFStringRef macaddr; + unsigned char macaddrBytes[6]; + + service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceNameMatching("bluetooth")); + if(!service) { + printf("unable to find bluetooth service\n"); + return NULL; + } + + macaddrData= IORegistryEntryCreateCFProperty(service, CFSTR("local-mac-address"), kCFAllocatorDefault, 0); + if(macaddrData == NULL) { + printf("bluetooth local-mac-address not found\n"); + IOObjectRelease(service); + return NULL; + } + CFDataGetBytes(macaddrData, CFRangeMake(0,6), macaddrBytes); + + macaddr = CFStringCreateWithFormat(kCFAllocatorDefault, + NULL, + CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), + macaddrBytes[0], + macaddrBytes[1], + macaddrBytes[2], + macaddrBytes[3], + macaddrBytes[4], + macaddrBytes[5]); + + return macaddr; +} + +void search_wifi_mac_callback(void** context, io_iterator_t iterator) { + unsigned char macaddrBytes[6]; + io_iterator_t iterator2=0; + io_object_t obj2=0; + io_name_t name; + CFDataRef t1=0; + io_object_t next; + + while ((next = IOIteratorNext(iterator)) != 0) + { + if (!IORegistryEntryCreateIterator(next, "IOService", 3, &iterator2)) + { + while((obj2 = IOIteratorNext(iterator2)) != 0) + { + if (!IORegistryEntryGetName(obj2,name)) + { + if (!strcmp(name, "sdio") || !strcmp(name, "wlan")) + { + if((t1 = IORegistryEntryCreateCFProperty(obj2, CFSTR("local-mac-address"), kCFAllocatorDefault, 0)) != 0) + { + CFDataGetBytes(t1, CFRangeMake(0,6), macaddrBytes); + *context = (void*) CFStringCreateWithFormat(kCFAllocatorDefault, + NULL, + CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), + macaddrBytes[0], + macaddrBytes[1], + macaddrBytes[2], + macaddrBytes[3], + macaddrBytes[4], + macaddrBytes[5]); + CFRelease(t1); + } + } + } + + } + IOObjectRelease(iterator2); + } + IOObjectRelease(next); + if (*context != NULL) + break; + } +} + +CFStringRef lookup_mac_address(const char* serviceName) +{ + unsigned char macaddrBytes[6]; + CFStringRef res = NULL; + + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceNameMatching(serviceName)); + + if(service) + { + CFDataRef macData = IORegistryEntryCreateCFProperty(service, CFSTR("local-mac-address"), kCFAllocatorDefault, 0); + if(macData != NULL) + { + CFDataGetBytes(macData, CFRangeMake(0,6), macaddrBytes); + + res = CFStringCreateWithFormat(kCFAllocatorDefault, + NULL, + CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), + macaddrBytes[0], + macaddrBytes[1], + macaddrBytes[2], + macaddrBytes[3], + macaddrBytes[4], + macaddrBytes[5]); + CFRelease(macData); + } + IOObjectRelease(service); + } + return res; +} + +CFStringRef copy_wifi_mac_address() { + CFStringRef wifimac = NULL; + IONotificationPortRef notify_port = 0; + io_iterator_t iterator = 0; + + wifimac = lookup_mac_address("sdio"); + if (wifimac != NULL) + return wifimac; + + wifimac = lookup_mac_address("wlan"); + if (wifimac != NULL) + return wifimac; + + notify_port = IONotificationPortCreate(kIOMasterPortDefault); + + CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notify_port); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode); + + if (!IOServiceAddMatchingNotification( notify_port, + kIOMatchedNotification, + IOServiceMatching("IONetworkController"), + (IOServiceMatchingCallback) search_wifi_mac_callback, + &wifimac, + &iterator + )) + { + search_wifi_mac_callback((void**)&wifimac, iterator); + while( wifimac == NULL) + { + if( CFRunLoopRunInMode(kCFRunLoopDefaultMode,0, TRUE) != kCFRunLoopRunHandledSource) + { + printf("giving up on wifi mac address\n"); + break; + } + } + } + IONotificationPortDestroy(notify_port); + return wifimac; +} + +int useNewUDID(CFStringRef hw) +{ + return CFEqual(hw, CFSTR("K93AP")) || + CFEqual(hw, CFSTR("K94AP")) || + CFEqual(hw, CFSTR("K95AP")) || + CFEqual(hw, CFSTR("N92AP")) || + CFEqual(hw, CFSTR("N94AP")); +} + +//http://iphonedevwiki.net/index.php/Lockdownd +void get_device_infos(CFMutableDictionaryRef out) { + CC_SHA1_CTX sha1ctx; + uint8_t udid[20]; + char udid1[100]; + CFStringRef serial; + CFStringRef imei; + CFStringRef macwifi; + CFStringRef macbt; + + CFStringRef hw = copy_hardware_model(); + if (hw != NULL) + { + CFDictionaryAddValue(out, CFSTR("hwModel"), hw); + CFRelease(hw); + } + + serial = copy_device_serial_number(); + imei = copy_device_imei(); + macwifi = copy_wifi_mac_address(); + macbt = copy_bluetooth_mac_address(); + + CFMutableStringRef udidInput = CFStringCreateMutable(kCFAllocatorDefault, 0); + if (serial != NULL) + { + CFStringAppend(udidInput, serial); + CFDictionaryAddValue(out, CFSTR("serialNumber"), serial); + CFRelease(serial); + } + + uint64_t _ecid = 0; + CFNumberRef ecid = copyNumberFromChosen(CFSTR("unique-chip-id")); + if (ecid != NULL) + { + CFDictionaryAddValue(out, CFSTR("ECID"), ecid); + } + + if (ecid != NULL && useNewUDID(hw)) + { + CFNumberGetValue(ecid, kCFNumberSInt64Type, &_ecid); + CFStringAppendFormat(udidInput, NULL, CFSTR("%llu"), _ecid); + } + else if (imei != NULL) + { + CFStringAppend(udidInput, imei); + CFDictionaryAddValue(out, CFSTR("imei"), imei); + CFRelease(imei); + } + if (macwifi != NULL) + { + CFStringAppend(udidInput, macwifi); + CFDictionaryAddValue(out, CFSTR("wifiMac"), macwifi); + CFRelease(macwifi); + } + if (macbt != NULL) + { + CFStringAppend(udidInput, macbt); + CFDictionaryAddValue(out, CFSTR("btMac"), macbt); + CFRelease(macbt); + } + + CFStringGetCString(udidInput, udid1, 99, kCFStringEncodingASCII); + + CC_SHA1_Init(&sha1ctx); + CC_SHA1_Update(&sha1ctx, udid1, CFStringGetLength(udidInput)); + CC_SHA1_Final(udid, &sha1ctx); + + CFRelease(udidInput); + addHexaString(out, CFSTR("udid"), udid, 20); + +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/registry.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/registry.h new file mode 100644 index 0000000..5691756 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/registry.h @@ -0,0 +1 @@ +void get_device_infos(CFMutableDictionaryRef out); \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/remote_functions.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/remote_functions.c new file mode 100644 index 0000000..6e1ec87 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/remote_functions.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include "AppleKeyStore.h" +#include "IOKit.h" +#include "IOAESAccelerator.h" +#include "registry.h" +#include "util.h" +#include "plist_server.h" +#include "remote_functions.h" + +int bruteforceProgressCallback(void* ctx, int p) +{ + return send_progress_message((int) ctx, p, 10000); +} + +char* bruteforceWithAppleKeyStore(CFDataRef kbkeys, int (*callback)(void*,int), void* ctx) +{ + uint64_t keybag_id = 0; + int i; + + char* passcode = (char*) malloc(5); + memset(passcode, 0, 5); + + AppleKeyStoreKeyBagInit(); + AppleKeyStoreKeyBagCreateWithData(kbkeys, &keybag_id); + //printf("keybag id=%x\n", (uint32_t) keybag_id); + AppleKeyStoreKeyBagSetSystem(keybag_id); + + CFDataRef data = CFDataCreateWithBytesNoCopy(0, (const UInt8*) passcode, 4, NULL); + + io_connect_t conn = IOKit_getConnect("AppleKeyStore"); + + if (!AppleKeyStoreUnlockDevice(conn, data)) + { + return passcode; + } + + for(i=0; i < 10000; i++) + { + sprintf(passcode, "%04d", i); + if (callback != NULL && !(i % 10)) + { + if (callback(ctx, i) == -1) + { + printf("Bruteforce abort\n"); + break; + } + } + if (!AppleKeyStoreUnlockDevice(conn, data)) + { + return passcode; + } + } + free(passcode); + return NULL; +} + +CFDictionaryRef load_system_keybag(int socket, CFDictionaryRef dict) +{ + CFDictionaryRef kbdict = AppleKeyStore_loadKeyBag("/private/var/keybags","systembag"); + + if (kbdict == NULL) + { + mountDataPartition("/mnt2"); + + kbdict = AppleKeyStore_loadKeyBag("/mnt2/keybags","systembag"); + if (kbdict == NULL) + { + printf("FAILed to load keybag\n"); + return NULL; + } + } + return kbdict; +} + +CFDictionaryRef bruteforce_system_keybag(int socket, CFDictionaryRef dict) +{ + uint8_t passcodeKey[32]; + + CFDataRef kbkeys = CFDictionaryGetValue(dict, CFSTR("KeyBagKeys")); + if(kbkeys == NULL || CFGetTypeID(kbkeys) != CFDataGetTypeID()) + return NULL; + + char* passcode = bruteforceWithAppleKeyStore(kbkeys, bruteforceProgressCallback, (void*) socket); + + if (passcode == NULL) + return NULL; + + KeyBag* kb = AppleKeyStore_parseBinaryKeyBag(kbkeys); + if (kb == NULL) + { + printf("FAIL: AppleKeyStore_parseBinaryKeyBag\n"); + return NULL; + } + AppleKeyStore_getPasscodeKey(kb, passcode, strlen(passcode), passcodeKey); + + free(kb); + CFMutableDictionaryRef out = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef cfpasscode = CFStringCreateWithCString(kCFAllocatorDefault, passcode, kCFStringEncodingASCII); + CFDictionaryAddValue(out, CFSTR("passcode"), cfpasscode); + CFRelease(cfpasscode); + + addHexaString(out, CFSTR("passcodeKey"), passcodeKey, 32); + return out; +} + +CFDictionaryRef keybag_get_passcode_key(int socket, CFDictionaryRef dict) +{ + uint8_t passcodeKey[32]; + CFDataRef passcode_cfdata = NULL; + + CFDataRef kbkeys = CFDictionaryGetValue(dict, CFSTR("KeyBagKeys")); + if(kbkeys == NULL || CFGetTypeID(kbkeys) != CFDataGetTypeID()) + return NULL; + + KeyBag* kb = AppleKeyStore_parseBinaryKeyBag(kbkeys); + if (kb == NULL) + return NULL; + + CFTypeRef cfpasscode = CFDictionaryGetValue(dict, CFSTR("passcode")); + + if(cfpasscode == NULL) + return NULL; + if(CFGetTypeID(cfpasscode) == CFDataGetTypeID()) + { + passcode_cfdata = cfpasscode; + } + else if(CFGetTypeID(cfpasscode) == CFStringGetTypeID()) + { + passcode_cfdata = CFStringCreateExternalRepresentation(kCFAllocatorDefault, cfpasscode, kCFStringEncodingUTF8, 0); + } + else + return NULL; + + AppleKeyStore_getPasscodeKey(kb, + CFDataGetBytePtr(passcode_cfdata), + CFDataGetLength(passcode_cfdata), + passcodeKey); + free(kb); + + if (passcode_cfdata != cfpasscode) + CFRelease(passcode_cfdata); + + CFMutableDictionaryRef out = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(out, CFSTR("passcode"), cfpasscode); + addHexaString(out, CFSTR("passcodeKey"), passcodeKey, 32); + return out; +} + +CFDictionaryRef get_escrow_record(int socket, CFDictionaryRef dict) +{ + CFStringRef hostid = CFDictionaryGetValue(dict, CFSTR("HostID")); + if(hostid == NULL || CFGetTypeID(hostid) != CFStringGetTypeID()) + return NULL; + + //TODO: check return values... + CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("/mnt2/root/Library/Lockdown/escrow_records/%@.plist"), hostid); + //CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("/private/var/root/Library/Lockdown/escrow_records/%@.plist"), hostid); + + CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, FALSE); + CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); + CFReadStreamOpen(stream); + CFPropertyListRef plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, + stream, 0, kCFPropertyListImmutable, NULL, NULL); + + CFRelease(fileURL); + CFRelease(stream); + CFRelease(path); + return plist; +} + +CFDictionaryRef download_file(int socket, CFDictionaryRef dict) +{ + UInt8 buffer[8192]; + CFIndex bytesRead; + + CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path")); + if(path == NULL || CFGetTypeID(path) != CFStringGetTypeID()) + return NULL; + CFMutableDictionaryRef out = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, FALSE); + CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); + CFRelease(fileURL); + if(!CFReadStreamOpen(stream)) + { + CFErrorRef error = CFReadStreamCopyError(stream); + if (error != NULL) + { + CFStringRef errorDesc = CFErrorCopyDescription(error); + CFDictionaryAddValue(out, CFSTR("Error"), errorDesc); + CFRelease(errorDesc); + CFRelease(error); + } + CFRelease(stream); + return out; + } + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); + + while(CFReadStreamHasBytesAvailable(stream)) + { + if((bytesRead = CFReadStreamRead(stream, buffer, 8192)) <= 0) + break; + CFDataAppendBytes(data, buffer, bytesRead); + } + CFReadStreamClose(stream); + CFRelease(stream); + + CFDictionaryAddValue(out, CFSTR("Data"), data); + CFRelease(data); + + return out; +} + +CFDictionaryRef remote_aes(int socket, CFDictionaryRef dict) +{ + uint8_t* input2 = NULL; + uint32_t len = 0; + uint8_t* iv2 = NULL; + uint32_t keyMask = 0; + uint32_t mode = 0; + uint32_t bits = 0; + + CFNumberRef km = CFDictionaryGetValue(dict, CFSTR("keyMask")); + if(km == NULL || CFGetTypeID(km) != CFNumberGetTypeID()) + return NULL; + CFNumberGetValue(km, kCFNumberIntType, &keyMask); + + CFNumberRef m = CFDictionaryGetValue(dict, CFSTR("mode")); + if(m == NULL || CFGetTypeID(m) != CFNumberGetTypeID()) + return NULL; + CFNumberGetValue(m, kCFNumberIntType, &mode); + + CFNumberRef b = CFDictionaryGetValue(dict, CFSTR("bits")); + if(b == NULL || CFGetTypeID(b) != CFNumberGetTypeID()) + return NULL; + CFNumberGetValue(b, kCFNumberIntType, &bits); + + CFDataRef input = CFDictionaryGetValue(dict, CFSTR("input")); + if(input == NULL || CFGetTypeID(input) != CFDataGetTypeID()) + return NULL; + + CFDataRef iv = CFDictionaryGetValue(dict, CFSTR("iv")); + if(iv != NULL) + { + if (CFGetTypeID(iv) != CFDataGetTypeID()) + return NULL; + iv2 = CFDataGetBytePtr(iv); + } + len = CFDataGetLength(input); + if (len % 16 != 0) + { + return NULL; + } + input2 = malloc(len); + if (input2 == NULL) + { + return NULL; + } + + memcpy(input2, CFDataGetBytePtr(input), len); + + uint32_t ret = doAES(input2, input2, len, keyMask, NULL, iv2, mode, bits); + + CFMutableDictionaryRef out = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFNumberRef retCode = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ret); + CFDictionaryAddValue(out, CFSTR("returnCode"), retCode); + CFRelease(retCode); + + if (ret == 0) + { + CFDataRef dd = CFDataCreate(kCFAllocatorDefault, input2, len); + CFDictionaryAddValue(out, CFSTR("data"), dd); + CFRelease(dd); + } + free(input2); + + return out; +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/remote_functions.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/remote_functions.h new file mode 100644 index 0000000..a324f03 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/remote_functions.h @@ -0,0 +1,7 @@ + +CFDictionaryRef load_system_keybag(int socket, CFDictionaryRef dict); +CFDictionaryRef bruteforce_system_keybag(int socket, CFDictionaryRef dict); +CFDictionaryRef keybag_get_passcode_key(int socket, CFDictionaryRef dict); +CFDictionaryRef get_escrow_record(int socket, CFDictionaryRef dict); +CFDictionaryRef download_file(int socket, CFDictionaryRef dict); +CFDictionaryRef remote_aes(int socket, CFDictionaryRef dict); diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/restored_external.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/restored_external.c new file mode 100644 index 0000000..98c82a7 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/restored_external.c @@ -0,0 +1,269 @@ +/** +https://github.com/comex/bloggy/wiki/Redsn0w%2Busbmux +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_5 +#include +#include "IOUSBDeviceControllerLib.h" +#include "plist_server.h" +#include "remote_functions.h" +#include "device_info.h" +#include "registry.h" + +#define kIOSomethingPluginID CFUUIDGetConstantUUIDWithBytes(NULL, \ + 0x9E, 0x72, 0x21, 0x7E, 0x8A, 0x60, 0x11, 0xDB, \ + 0xBF, 0x57, 0x00, 0x0D, 0x93, 0x6D, 0x06, 0xD2) +#define kIOWhatTheFuckID CFUUIDGetConstantUUIDWithBytes(NULL, \ + 0xEA, 0x33, 0xBA, 0x4F, 0x8A, 0x60, 0x11, 0xDB, \ + 0x84, 0xDB, 0x00, 0x0D, 0x93, 0x6D, 0x06, 0xD2) + +void init_usb(CFStringRef serialString) { + IOUSBDeviceDescriptionRef desc = IOUSBDeviceDescriptionCreateFromDefaults(kCFAllocatorDefault); + IOUSBDeviceDescriptionSetSerialString(desc, serialString == NULL ? CFSTR("ramdisk - udid fail?") : serialString); + + CFArrayRef usb_interfaces = IOUSBDeviceDescriptionCopyInterfaces(desc); + int i; + for(i=0; i < CFArrayGetCount(usb_interfaces); i++) + { + CFArrayRef arr1 = CFArrayGetValueAtIndex(usb_interfaces, i); + + if( CFArrayContainsValue(arr1, CFRangeMake(0,CFArrayGetCount(arr1)), CFSTR("PTP"))) + { + printf("Found PTP interface\n"); + break; + } + } + + IOUSBDeviceControllerRef controller; + while (IOUSBDeviceControllerCreate(kCFAllocatorDefault, &controller)) + { + printf("Unable to get USB device controller\n"); + sleep(3); + } + IOUSBDeviceControllerSetDescription(controller, desc); + + CFMutableDictionaryRef match = IOServiceMatching("IOUSBDeviceInterface"); + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(dict, CFSTR("USBDeviceFunction"), CFSTR("PTP")); + CFDictionarySetValue(match, CFSTR("IOPropertyMatch"), dict); + io_service_t service; + while(1) { + service = IOServiceGetMatchingService(kIOMasterPortDefault, match); + if(!service) { + printf("Didn't find, trying again\n"); + sleep(1); + } else { + break; + } + } + IOCFPlugInInterface **iface; + SInt32 score; + printf("123\n"); + assert(!IOCreatePlugInInterfaceForService( + service, + kIOSomethingPluginID, + kIOCFPlugInInterfaceID, + &iface, + &score + )); + void *thing; + + assert(!((*iface)->QueryInterface)(iface, + CFUUIDGetUUIDBytes(kIOWhatTheFuckID), + &thing)); + + IOReturn (**table)(void *, ...) = *((void **) thing); + printf("%p\n", table[0x10/4]); + + //open IOUSBDeviceInterfaceInterface + (!table[0x10/4](thing, 0)); + //set IOUSBDeviceInterfaceInterface class + (!table[0x2c/4](thing, 0xff, 0)); + //set IOUSBDeviceInterfaceInterface sub-class + (!table[0x30/4](thing, 0x50, 0)); + //set IOUSBDeviceInterfaceInterface protocol + (!table[0x34/4](thing, 0x43, 0)); + //commit IOUSBDeviceInterfaceInterface configuration + (!table[0x44/4](thing, 0)); + IODestroyPlugInInterface(iface); + //assert(!table[0x14/4](thing, 0)); +} + +void init_tcp() { + // from launchd + struct ifaliasreq ifra; + struct ifreq ifr; + int s; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "lo0"); + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return; + + if (ioctl(s, SIOCGIFFLAGS, &ifr) != -1) { + ifr.ifr_flags |= IFF_UP; + assert(ioctl(s, SIOCSIFFLAGS, &ifr) != -1); + } + + memset(&ifra, 0, sizeof(ifra)); + strcpy(ifra.ifra_name, "lo0"); + ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in); + ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET); + ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in); + + assert(ioctl(s, SIOCAIFADDR, &ifra) != -1); + + assert(close(s) == 0); + +} + +CFDictionaryRef reboot__(int socket, CFDictionaryRef dict) +{ + reboot(0); + return NULL; +} + + +char* execve_env[]= {NULL}; +char* execve_params[]={"/sbin/sshd", NULL}; +char* ioflash[]={"/var/root/ioflashstoragekit", NULL}; + +size_t bootargs_len = 255; +char bootargs[256]={0}; + +int main(int argc, char* argv[]) +{ + int i; + int nandReadOnly=0; + struct stat st; + + printf("Starting ramdisk tool\n"); + printf("Compiled " __DATE__ " " __TIME__ "\n"); + printf("Revision " HGVERSION "\n"); + + CFMutableDictionaryRef matching; + io_service_t service = 0; + matching = IOServiceMatching("IOWatchDogTimer"); + if (matching == NULL) { + printf("unable to create matching dictionary for class IOWatchDogTimer\n"); + } + + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + if (service == 0) { + printf("unable to create matching dictionary for class IOWatchDogTimer\n"); + } + uint32_t zero = 0; + CFNumberRef n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); + IORegistryEntrySetCFProperties(service, n); + IOObjectRelease(service); + + CFMutableDictionaryRef deviceInfos = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + get_device_infos(deviceInfos); + init_tcp(); + + sysctlbyname("kern.bootargs", bootargs, &bootargs_len, NULL, 0); + + if (strstr(bootargs, "nand-readonly") || strstr(bootargs, "nand-disable")) + { + printf("NAND read only mode, data partition wont be mounted\n"); + nandReadOnly = 1; + } + else + { + printf("Waiting for data partition\n"); + for(i=0; i < 10; i++) + { + if(!stat("/dev/disk0s2s1", &st)) + { + system("/sbin/fsck_hfs /dev/disk0s2s1"); + break; + } + if(!stat("/dev/disk0s1s2", &st)) + { + system("/sbin/fsck_hfs /dev/disk0s1s2"); + break; + } + if(!stat("/dev/disk0s2", &st)) + { + system("/sbin/fsck_hfs /dev/disk0s2"); + break; + } + sleep(5); + } + } + init_usb(CFDictionaryGetValue(deviceInfos, CFSTR("udid"))); + printf("USB init done\n"); + + system("mount /"); //make ramdisk writable + + chmod("/var/root/.ssh/authorized_keys", 0600); + chown("/var/root/.ssh/authorized_keys", 0, 0); + chown("/var/root/.ssh", 0, 0); + chown("/var/root/", 0, 0); + + printf(" ####### ## ##\n"); + printf("## ## ## ## \n"); + printf("## ## ## ## \n"); + printf("## ## ##### \n"); + printf("## ## ## ## \n"); + printf("## ## ## ## \n"); + printf(" ####### ## ##\n"); + printf("iphone-dataprotection ramdisk\n"); + printf("revision: " HGVERSION " " __DATE__ " " __TIME__ "\n"); + + if(!stat(execve_params[0], &st)) + { + printf("Running %s\n", execve_params[0]); + if((i = posix_spawn(NULL, execve_params[0], NULL, NULL, execve_params, execve_env))) + printf("posix_spawn(%s) returned %d\n", execve_params[0], i); + } + else + { + printf("%s is missing\n", execve_params[0]); + } + + /*if (nandReadOnly) + {*/ + if(!stat(ioflash[0], &st)) + { + printf("Running %s\n", ioflash[0]); + if((i = posix_spawn(NULL, ioflash[0], NULL, NULL, ioflash, execve_env))) + printf("posix_spawn(%s) returned %d\n", execve_params[0], i); + } + /*}*/ + + CFMutableDictionaryRef handlers = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + CFDictionaryAddValue(handlers, CFSTR("DeviceInfo"), device_info); + CFDictionaryAddValue(handlers, CFSTR("GetSystemKeyBag"), load_system_keybag); + CFDictionaryAddValue(handlers, CFSTR("BruteforceSystemKeyBag"), bruteforce_system_keybag); + CFDictionaryAddValue(handlers, CFSTR("KeyBagGetPasscodeKey"), keybag_get_passcode_key); + CFDictionaryAddValue(handlers, CFSTR("GetEscrowRecord"), get_escrow_record); + CFDictionaryAddValue(handlers, CFSTR("DownloadFile"), download_file); + CFDictionaryAddValue(handlers, CFSTR("AES"), remote_aes); + CFDictionaryAddValue(handlers, CFSTR("Reboot"), reboot__); + + serve_plist_rpc(1999, handlers); + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/scripts/mount_partitions.sh b/dump-imessages/iphone-dataprotection/ramdisk_tools/scripts/mount_partitions.sh new file mode 100755 index 0000000..573fbff --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/scripts/mount_partitions.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +if [ -a /dev/disk0s1s2 ]; then # test for iOS 5 data partition + mount_hfs /dev/disk0s1s1 /mnt1 2>/dev/null + mount_hfs /dev/disk0s1s2 /mnt2 2>/dev/null +elif [ -a /dev/disk0s2s1 ]; then # test for iOS 4 data partition + mount_hfs /dev/disk0s1 /mnt1 2>/dev/null + mount_hfs /dev/disk0s2s1 /mnt2 2>/dev/null +elif [ -a /dev/disk0s2 ]; then + mount_hfs /dev/disk0s1 /mnt1 2>/dev/null + mount_hfs /dev/disk0s2 /mnt2 2>/dev/null +else + echo "Error mounting partitions. Please try it manually" +fi \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/shsh_dump.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/shsh_dump.c new file mode 100644 index 0000000..9ccfa07 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/shsh_dump.c @@ -0,0 +1,176 @@ +#include +#include +//#include +//#include +#include +#include +#include +#include "ioflash/ioflash.h" +#include "util.h" + +struct IMG2 +{ + uint32_t magic; + uint32_t block_size; + uint32_t images_offset; + uint32_t images_block; + uint32_t images_length; + uint8_t padding[0x1c]; + uint32_t crc32; +}; + +struct IMG3 +{ + uint32_t magic; + uint32_t fullSize; + uint32_t sizeNoPack; + uint32_t sigCheckArea; + uint32_t iden; +}; + +struct IMG3_TLV +{ + uint32_t tag; + uint32_t total_length; + uint32_t data_length; + uint8_t payload[1]; +}; + +void printIMG2(struct IMG2* s) +{ + printf("magic= %x\n", s->magic); + printf("block_size= %x\n", s->block_size); + printf("images_offset= %x\n", s->images_offset); + printf("images_block= %x\n", s->images_block); + printf("images_length= %x\n", s->images_length); +} +void printIMG3(struct IMG3* s) +{ + char iden[10]={0}; + memcpy(iden, &s->iden, 4); + printf("magic= %x\n", s->magic); + printf("fullSize= %x\n", s->fullSize); + printf("iden= %s\n", iden); +} + +CFDataRef getIMG3Data(struct IMG3* img3) +{ + CFDataRef data = NULL; + uint8_t* p = (uint8_t*) img3; + uint8_t* z = &p[20]; + + if(img3->magic != 'Img3') + return NULL; + + while(z < &p[img3->fullSize]) + { + struct IMG3_TLV* item = (struct IMG3_TLV*) z; + if( item->tag == 'DATA') + { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, item->payload, item->data_length, NULL); + return data; + } + z += item->total_length; + } + return NULL; +} + + +extern uint32_t gPagesPerBlock; +extern uint32_t gBytesPerPage ; +extern uint32_t gBootloaderBytes ; + +int main(int argc, char* argv[]) +{ + int one=1; + int cmd=0; + + CFMutableDictionaryRef dict = FSDGetInfo(1); + if (dict == NULL) + { + fprintf(stderr, "FAILed to get NAND infos"); + return -1; + } + if(!IOFlashStorage_kernel_patch()) + return -1; + + struct kIOFlashControllerOut out; + uint32_t bootSize = gBootloaderBytes * gPagesPerBlock * 8; + + printf("Mallocing %x bytes for boot partition\n", bootSize); + uint8_t* boot = malloc(bootSize); + if( boot == NULL) + { + return -1; + } + + uint8_t* buffer = malloc(gBytesPerPage); + if( buffer == NULL) + { + return -1; + } + + uint32_t block, page, off=0; + + for(block=8; block < 16; block++) + { + for(page=0; page < gPagesPerBlock; page++) + { + if(FSDReadBootPage(0, block*gPagesPerBlock + page, buffer, &out)) + { + //printf("FSDReadBootPage error %x\n", block*gPagesPerBlock + page); + //return -1; + } + memcpy(&boot[off], buffer, gBootloaderBytes); + off += gBootloaderBytes; + } + } + + printIMG2((struct IMG2*) boot); + struct IMG2* img2 = (struct IMG2*) boot; + + if( img2->magic != 0x494d4732) + { + printf("Bag IMG2 magic : %x\n", img2->magic); + return -1; + } + uint32_t start = img2->block_size * img2->images_block; + + uint32_t end = start + img2->block_size * img2->images_length; + + if( end < start) + { + return -1; + } + printf("start %x end %x\n", start, end); + uint8_t* p = &boot[start]; + + CFMutableDictionaryRef resultsDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if(dict == NULL) + { + return -1; + } + + while(p < &boot[end]) + { + struct IMG3* img3 = (struct IMG3*) p; + if(img3->magic != 'Img3') + break; + printIMG3(img3); + + if(img3->iden == 'SCAB') + { + CFDataRef data = getIMG3Data(img3); + if(data) + { + CFDictionaryAddValue(resultsDict, CFSTR("APTicket"), data); + writePlistToStdout(resultsDict); + CFRelease(data); + } + } + p += img3->fullSize; + + } + return 0; +} \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/systemkb_bruteforce.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/systemkb_bruteforce.c new file mode 100644 index 0000000..038c3da --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/systemkb_bruteforce.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include "AppleKeyStore.h" +#include "IOKit.h" +#include "IOAESAccelerator.h" +#include "registry.h" +#include "util.h" +#include "image.h" +#include "remote_functions.h" + +/* + #define MobileKeyBagBase 0x354cb000 + + CFDictionaryRef (*AppleKeyStore_loadKeyBag)(char*, char*) = MobileKeyBagBase + 0x50A8; + int (*AppleKeyStoreKeyBagSetSystem)(int) = MobileKeyBagBase + 0x910; + int (*AppleKeyStoreKeyBagCreateWithData)(CFDataRef, int*) = MobileKeyBagBase + 0xC88; + */ +/* + /private/var/mobile/Library/ConfigurationProfiles/PublicInfo/EffectiveUserSettings.plist.plist + plist["restrictedValue"]["passcodeKeyboardComplexity"] + */ + +void saveKeybagInfos(CFDataRef kbkeys, KeyBag* kb, uint8_t* key835, char* passcode, uint8_t* passcodeKey, CFMutableDictionaryRef classKeys) +{ + CFMutableDictionaryRef out = device_info(-1, NULL); + + CFStringRef uuid = CreateHexaCFString(kb->uuid, 16); + + CFDictionaryAddValue(out, CFSTR("uuid"), uuid); + CFDictionaryAddValue(out, CFSTR("KeyBagKeys"), kbkeys); + + addHexaString(out, CFSTR("salt"), kb->salt, 20); + + if (passcode != NULL) + { + CFStringRef cfpasscode = CFStringCreateWithCString(kCFAllocatorDefault, passcode, kCFStringEncodingASCII); + CFDictionaryAddValue(out, CFSTR("passcode"), cfpasscode); + CFRelease(cfpasscode); + } + if (passcodeKey != NULL) + addHexaString(out, CFSTR("passcodeKey"), passcodeKey, 32); + + if (key835 != NULL) + addHexaString(out, CFSTR("key835"), key835, 16); + if (classKeys != NULL) + CFDictionaryAddValue(out, CFSTR("classKeys"), classKeys); + + CFStringRef resultsFileName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@.plist"), CFDictionaryGetValue(out, CFSTR("dataVolumeUUID"))); + + CFStringRef printString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Writing results to %@.plist\n"), CFDictionaryGetValue(out, CFSTR("dataVolumeUUID"))); + + CFShow(printString); + CFRelease(printString); + + saveResults(resultsFileName, out); + + CFRelease(resultsFileName); + CFRelease(uuid); + CFRelease(out); + +} + +char* bruteforceWithAppleKeyStore(CFDataRef kbkeys) +{ + uint64_t keybag_id = 0; + int i; + + char* passcode = (char*) malloc(5); + memset(passcode, 0, 5); + + AppleKeyStoreKeyBagInit(); + AppleKeyStoreKeyBagCreateWithData(kbkeys, &keybag_id); + printf("keybag id=%x\n", (uint32_t) keybag_id); + AppleKeyStoreKeyBagSetSystem(keybag_id); + + CFDataRef data = CFDataCreateWithBytesNoCopy(0, (const UInt8*) passcode, 4, NULL); + + io_connect_t conn = IOKit_getConnect("AppleKeyStore"); + + if (!AppleKeyStoreUnlockDevice(conn, data)) + { + return passcode; + } + + for(i=0; i < 10000; i++) + { + sprintf(passcode, "%04d", i); + //if (i % 1000 == 0) + printf("%s\n", passcode); + if (!AppleKeyStoreUnlockDevice(conn, data)) + { + return passcode; + } + } + free(passcode); + return NULL; +} + +char* bruteforceUserland(KeyBag* kb, uint8_t* key835) +{ + int i; + char* passcode = (char*) malloc(5); + memset(passcode, 0, 5); + + if (AppleKeyStore_unlockKeybagFromUserland(kb, passcode, 4, key835)) + return passcode; + + for(i=0; i < 10000; i++) + { + sprintf(passcode, "%04d", i); + //if (i % 1000 == 0) + printf("%s\n", passcode); + if (AppleKeyStore_unlockKeybagFromUserland(kb, passcode, 4, key835)) + return passcode; + } + free(passcode); + return NULL; +} + + +int main(int argc, char* argv[]) +{ + u_int8_t passcodeKey[32]={0}; + char* passcode = NULL; + int bruteforceMethod = 0; + int showImages = 0; + int c; + + while ((c = getopt (argc, argv, "ui")) != -1) + { + switch (c) + { + case 'u': + bruteforceMethod = 1; + break; + case 'i': + showImages = 1; + break; + } + } + + uint8_t* key835 = IOAES_key835(); + + if (!memcmp(key835, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) + { + printf("FAIL: missing UID kernel patch\n"); + return -1; + } + + CFDictionaryRef kbdict = AppleKeyStore_loadKeyBag("/private/var/keybags","systembag"); + + if (kbdict == NULL) + { + mountDataPartition("/mnt2"); + + kbdict = AppleKeyStore_loadKeyBag("/mnt2/keybags","systembag"); + if (kbdict == NULL) + { + printf("FAILed to load keybag\n"); + return -1; + } + } + + CFDataRef kbkeys = CFDictionaryGetValue(kbdict, CFSTR("KeyBagKeys")); + CFRetain(kbkeys); + + if (kbkeys == NULL) + { + printf("FAIL: KeyBagKeys not found\n"); + return -1; + } + //write_file("kbblob.bin", CFDataGetBytePtr(kbkeys), CFDataGetLength(kbkeys)); + KeyBag* kb = AppleKeyStore_parseBinaryKeyBag(kbkeys); + if (kb == NULL) + { + printf("FAIL: AppleKeyStore_parseBinaryKeyBag\n"); + return -1; + } + + //save all we have for now + saveKeybagInfos(kbkeys, kb, key835, NULL, NULL, NULL); + + //now try to unlock the keybag + + if (bruteforceMethod == 1) + passcode = bruteforceUserland(kb, key835); + else + passcode = bruteforceWithAppleKeyStore(kbkeys); + + if (passcode != NULL) + { + if (!strcmp(passcode, "")) + printf("No passcode set\n"); + else + printf("Found passcode : %s\n", passcode); + + AppleKeyStore_unlockKeybagFromUserland(kb, passcode, 4, key835); + AppleKeyStore_printKeyBag(kb); + + CFMutableDictionaryRef classKeys = AppleKeyStore_getClassKeys(kb); + + AppleKeyStore_getPasscodeKey(kb, passcode, strlen(passcode), passcodeKey); + + printf("Passcode key : "); + printBytesToHex(passcodeKey, 32); + printf("\n"); + + printf("Key 0x835 : "); + printBytesToHex(key835, 16); + printf("\n"); + + //save all we have for now + saveKeybagInfos(kbkeys, kb, key835, passcode, passcodeKey, classKeys); + CFRelease(classKeys); + + free(passcode); + } + free(kb); + + CFRelease(kbkeys); + CFRelease(kbdict); + + return 0; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/tfp0.plist b/dump-imessages/iphone-dataprotection/ramdisk_tools/tfp0.plist new file mode 100644 index 0000000..80fce1a --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/tfp0.plist @@ -0,0 +1,12 @@ + + + + + get-task-allow + + run-unsigned-code + + task_for_pid-allow + + + diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/util.c b/dump-imessages/iphone-dataprotection/ramdisk_tools/util.c new file mode 100644 index 0000000..1fdaed4 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/util.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +void printBytesToHex(const uint8_t* buffer, size_t bytes) +{ + while(bytes > 0) { + printf("%02x", *buffer); + buffer++; + bytes--; + } +} + +void printHexString(const char* description, const uint8_t* buffer, size_t bytes) +{ + printf("%s : ", description); + printBytesToHex(buffer, bytes); + printf("\n"); +} + +int write_file(const char* filename, uint8_t* data, size_t len) +{ + int fd = open(filename, O_CREAT | O_RDWR); + if (fd < 0) + return -1; + if (write(fd, data, len) != len) + return -1; + close(fd); + return 0; +} + +void writePlistToStdout(CFDictionaryRef out) +{ + CFDataRef d = CFPropertyListCreateData(kCFAllocatorDefault, out, kCFPropertyListXMLFormat_v1_0, 0, NULL); + if (d == NULL) + return; + write(1, CFDataGetBytePtr(d), CFDataGetLength(d)); +} + +int mountDataPartition(const char* mountpoint) +{ + char* diskname = "/dev/disk0s2s1"; + int err; + printf("Trying to mount data partition\n"); + err = mount("hfs","/mnt2", MNT_RDONLY | MNT_NOATIME | MNT_NODEV | MNT_LOCAL, &diskname); + if (!err) + return 0; + + diskname = "/dev/disk0s1s2"; + err = mount("hfs","/mnt2", MNT_RDONLY | MNT_NOATIME | MNT_NODEV | MNT_LOCAL, &diskname); + if (!err) + return 0; + diskname = "/dev/disk0s2"; + err = mount("hfs","/mnt2", MNT_RDONLY | MNT_NOATIME | MNT_NODEV | MNT_LOCAL, &diskname); + + return err; +} + + +int getHFSInfos(struct HFSInfos *infos) +{ + char buf[8192] = {0}; + struct HFSPlusVolumeHeader* header; + unsigned int i; + + int fd = open("/dev/rdisk0s2", O_RDONLY); + if (fd < 0 ) + fd = open("/dev/rdisk0s1s2", O_RDONLY); //ios5 lwvm + if (fd < 0 ) + return fd; + lseek(fd, 0, SEEK_SET); + + if (read(fd, buf, 8192) != 8192) + return -1; + close(fd); + + header = (struct HFSPlusVolumeHeader*) &buf[0x400]; + + uint32_t blockSize = CFSwapInt32BigToHost(header->blockSize); + + infos->volumeUUID = header->volumeUUID; + infos->blockSize = blockSize; + + if (blockSize != 0x1000 && blockSize != 0x2000) + { + fprintf(stderr, "getHFSInfos: Unknown block size %x\n", blockSize); + } + else + { + fd = open("/dev/rdisk0", O_RDONLY); + if (fd < 0 ) + return fd; + + if (read(fd, buf, 8192) != 8192) + return -1; + + if (!memcmp(buf, LwVMType, 16)) + { + LwVM* lwvm = (LwVM*) buf; + + if (lwvm->chunks[0] != 0xF000) + { + fprintf(stderr, "getHFSInfos: lwvm->chunks[0] != 0xF000\n"); + return -1; + } + + for(i=0; i < 0x400; i++) + { + if(lwvm->chunks[i] == 0x1000) //partition 1 block 0 + { + break; + } + } + uint32_t LwVM_rangeShiftValue = 32 - __builtin_clz((lwvm->mediaSize - 1) >> 10); + + infos->dataVolumeOffset = (i << LwVM_rangeShiftValue) / blockSize; + } + else + { + lseek(fd, 2*blockSize, SEEK_SET); + + if (read(fd, buf, 8192) != 8192) + return -1; + close(fd); + + infos->dataVolumeOffset = ((unsigned int*)buf)[0xA0/4]; + } + } + return 0; +} + +CFMutableStringRef CreateHexaCFString(uint8_t* buffer, size_t len) +{ + int i; + + CFMutableStringRef s = CFStringCreateMutable(kCFAllocatorDefault, len*2); + + for(i=0; i < len; i++) + { + CFStringAppendFormat(s, NULL, CFSTR("%02x"), buffer[i]); + } + return s; +} + +void addHexaString(CFMutableDictionaryRef out, CFStringRef key, uint8_t* buffer, size_t len) +{ + CFMutableStringRef s = CreateHexaCFString(buffer, len); + CFDictionaryAddValue(out, key, s); + CFRelease(s); +} + +void saveResults(CFStringRef filename, CFMutableDictionaryRef out) +{ + CFURLRef fileURL = CFURLCreateWithFileSystemPath( NULL, filename, kCFURLPOSIXPathStyle, FALSE); + CFWriteStreamRef stream = CFWriteStreamCreateWithFile( NULL, fileURL); + CFWriteStreamOpen(stream); + CFPropertyListWriteToStream(out, stream, kCFPropertyListXMLFormat_v1_0, NULL); + CFWriteStreamClose(stream); + + CFRelease(stream); + CFRelease(fileURL); +} + +int create_listening_socket(int port) +{ + struct sockaddr_in listen_addr; + int s, one = 1; + + memset(&listen_addr, 0, sizeof(struct sockaddr)); + listen_addr.sin_family = AF_INET; + listen_addr.sin_port = htons(port); + listen_addr.sin_addr.s_addr = INADDR_ANY; + + s = socket(AF_INET, SOCK_STREAM, 0); + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + + if (bind(s, (struct sockaddr *)&listen_addr, sizeof(struct sockaddr)) < 0) + { + perror("bind"); + return -1; + } + listen(s, 10); + + return s; +} diff --git a/dump-imessages/iphone-dataprotection/ramdisk_tools/util.h b/dump-imessages/iphone-dataprotection/ramdisk_tools/util.h new file mode 100644 index 0000000..798f36f --- /dev/null +++ b/dump-imessages/iphone-dataprotection/ramdisk_tools/util.h @@ -0,0 +1,81 @@ +struct HFSInfos { + uint64_t volumeUUID; + uint32_t blockSize; + uint32_t dataVolumeOffset; +}; + +struct HFSPlusVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t lastMountedVersion; + uint32_t journalInfoBlock; + + uint32_t createDate; + uint32_t modifyDate; + uint32_t backupDate; + uint32_t checkedDate; + + uint32_t fileCount; + uint32_t folderCount; + + uint32_t blockSize; + uint32_t totalBlocks; + uint32_t freeBlocks; + + uint32_t nextAllocation; + uint32_t rsrcClumpSize; + uint32_t dataClumpSize; + uint32_t nextCatalogID; + + uint32_t writeCount; + uint64_t encodingsBitmap; + + uint32_t finderInfo[6]; + uint64_t volumeUUID; + /* + HFSPlusForkData allocationFile; + HFSPlusForkData extentsFile; + HFSPlusForkData catalogFile; + HFSPlusForkData attributesFile; + HFSPlusForkData startupFile;*/ +} __attribute__((packed)); + +//https://github.com/iDroid-Project/openiBoot/blob/master/openiboot/includes/bdev.h +typedef struct _LwVMPartitionRecord { + uint64_t type[2]; + uint64_t guid[2]; + uint64_t begin; + uint64_t end; + uint64_t attribute; // 0 == unencrypted; 0x1000000000000 == encrypted + char partitionName[0x48]; +} __attribute__ ((packed)) LwVMPartitionRecord; + +typedef struct _LwVM { + uint64_t type[2]; + uint64_t guid[2]; + uint64_t mediaSize; + uint32_t numPartitions; + uint32_t crc32; + uint8_t unkn[464]; + LwVMPartitionRecord partitions[12]; + uint16_t chunks[1024]; // chunks[0] should be 0xF000 +} __attribute__ ((packed)) LwVM; + +static const char LwVMType[] = { 0x6A, 0x90, 0x88, 0xCF, 0x8A, 0xFD, 0x63, 0x0A, 0xE3, 0x51, 0xE2, 0x48, 0x87, 0xE0, 0xB9, 0x8B }; + +int getHFSInfos(struct HFSInfos *infos); + +CFMutableStringRef CreateHexaCFString(uint8_t* buffer, size_t len); + +void printBytesToHex(const uint8_t* buffer, size_t bytes); +void printHexString(const char* description, const uint8_t* buffer, size_t bytes); +int write_file(const char* filename, uint8_t* data, size_t len); + +void addHexaString(CFMutableDictionaryRef out, CFStringRef key, uint8_t* buffer, size_t len); +void saveResults(CFStringRef filename, CFMutableDictionaryRef out); +void writePlistToStdout(CFDictionaryRef out); + +int mountDataPartition(const char* mountpoint); + +int create_listening_socket(int port); \ No newline at end of file diff --git a/dump-imessages/iphone-dataprotection/tcprelay.sh b/dump-imessages/iphone-dataprotection/tcprelay.sh new file mode 100755 index 0000000..40e74c9 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/tcprelay.sh @@ -0,0 +1,4 @@ +#/bin/sh + +python usbmuxd-python-client/tcprelay.py -t 22:2222 1999:1999 + diff --git a/dump-imessages/iphone-dataprotection/usbmuxd-python-client/relay.bat b/dump-imessages/iphone-dataprotection/usbmuxd-python-client/relay.bat new file mode 100644 index 0000000..47446c2 --- /dev/null +++ b/dump-imessages/iphone-dataprotection/usbmuxd-python-client/relay.bat @@ -0,0 +1 @@ +c:\Python26\python.exe tcprelay.py -t 22:2222 5900:5900 1999:1999 1234:1234 diff --git a/dump-imessages/iphone-dataprotection/usbmuxd-python-client/tcprelay.py b/dump-imessages/iphone-dataprotection/usbmuxd-python-client/tcprelay.py new file mode 100755 index 0000000..add200c --- /dev/null +++ b/dump-imessages/iphone-dataprotection/usbmuxd-python-client/tcprelay.py @@ -0,0 +1,148 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# tcprelay.py - TCP connection relay for usbmuxd +# +# Copyright (C) 2009 Hector Martin "marcan" +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 or version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import usbmux +import SocketServer +import select +from optparse import OptionParser +import sys +import threading + +class SocketRelay(object): + def __init__(self, a, b, maxbuf=65535): + self.a = a + self.b = b + self.atob = "" + self.btoa = "" + self.maxbuf = maxbuf + def handle(self): + while True: + rlist = [] + wlist = [] + xlist = [self.a, self.b] + if self.atob: + wlist.append(self.b) + if self.btoa: + wlist.append(self.a) + if len(self.atob) < self.maxbuf: + rlist.append(self.a) + if len(self.btoa) < self.maxbuf: + rlist.append(self.b) + rlo, wlo, xlo = select.select(rlist, wlist, xlist) + if xlo: + return + if self.a in wlo: + n = self.a.send(self.btoa) + self.btoa = self.btoa[n:] + if self.b in wlo: + n = self.b.send(self.atob) + self.atob = self.atob[n:] + if self.a in rlo: + s = self.a.recv(self.maxbuf - len(self.atob)) + if not s: + return + self.atob += s + if self.b in rlo: + s = self.b.recv(self.maxbuf - len(self.btoa)) + if not s: + return + self.btoa += s + #print "Relay iter: %8d atob, %8d btoa, lists: %r %r %r"%(len(self.atob), len(self.btoa), rlo, wlo, xlo) + +class TCPRelay(SocketServer.BaseRequestHandler): + def handle(self): + print "Incoming connection to %d"%self.server.server_address[1] + mux = usbmux.USBMux(options.sockpath) + print "Waiting for devices..." + if not mux.devices: + mux.process(1.0) + if not mux.devices: + print "No device found" + self.request.close() + return + dev = mux.devices[0] + print "Connecting to device %s"%str(dev) + dsock = mux.connect(dev, self.server.rport) + lsock = self.request + print "Connection established, relaying data" + try: + fwd = SocketRelay(dsock, lsock, self.server.bufsize * 1024) + fwd.handle() + finally: + dsock.close() + lsock.close() + print "Connection closed" + +class TCPServer(SocketServer.TCPServer): + allow_reuse_address = True + +class ThreadedTCPServer(SocketServer.ThreadingMixIn, TCPServer): + pass + +HOST = "localhost" + +parser = OptionParser(usage="usage: %prog [OPTIONS] RemotePort[:LocalPort] [RemotePort[:LocalPort]]...") +parser.add_option("-t", "--threaded", dest='threaded', action='store_true', default=False, help="use threading to handle multiple connections at once") +parser.add_option("-b", "--bufsize", dest='bufsize', action='store', metavar='KILOBYTES', type='int', default=128, help="specify buffer size for socket forwarding") +parser.add_option("-s", "--socket", dest='sockpath', action='store', metavar='PATH', type='str', default=None, help="specify the path of the usbmuxd socket") + +options, args = parser.parse_args() + +serverclass = TCPServer +if options.threaded: + serverclass = ThreadedTCPServer + +if len(args) == 0: + parser.print_help() + sys.exit(1) + +ports = [] + +for arg in args: + try: + if ':' in arg: + rport, lport = arg.split(":") + rport = int(rport) + lport = int(lport) + ports.append((rport, lport)) + else: + ports.append((int(arg), int(arg))) + except: + parser.print_help() + sys.exit(1) + +servers=[] + +for rport, lport in ports: + print "Forwarding local port %d to remote port %d"%(lport, rport) + server = serverclass((HOST, lport), TCPRelay) + server.rport = rport + server.bufsize = options.bufsize + servers.append(server) + +alive = True + +while alive: + try: + rl, wl, xl = select.select(servers, [], []) + for server in rl: + server.handle_request() + except: + alive = False diff --git a/dump-imessages/iphone-dataprotection/usbmuxd-python-client/usbmux.py b/dump-imessages/iphone-dataprotection/usbmuxd-python-client/usbmux.py new file mode 100644 index 0000000..79ec26a --- /dev/null +++ b/dump-imessages/iphone-dataprotection/usbmuxd-python-client/usbmux.py @@ -0,0 +1,246 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# usbmux.py - usbmux client library for Python +# +# Copyright (C) 2009 Hector Martin "marcan" +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 or version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import socket, struct, select, sys + +try: + import plistlib + haveplist = True +except: + haveplist = False + +class MuxError(Exception): + pass + +class MuxVersionError(MuxError): + pass + +class SafeStreamSocket: + def __init__(self, address, family): + self.sock = socket.socket(family, socket.SOCK_STREAM) + self.sock.connect(address) + def send(self, msg): + totalsent = 0 + while totalsent < len(msg): + sent = self.sock.send(msg[totalsent:]) + if sent == 0: + raise MuxError("socket connection broken") + totalsent = totalsent + sent + def recv(self, size): + msg = '' + while len(msg) < size: + chunk = self.sock.recv(size-len(msg)) + if chunk == '': + raise MuxError("socket connection broken") + msg = msg + chunk + return msg + +class MuxDevice(object): + def __init__(self, devid, usbprod, serial, location): + self.devid = devid + self.usbprod = usbprod + self.serial = serial + self.location = location + def __str__(self): + return ""%(self.devid, self.usbprod, self.serial, self.location) + +class BinaryProtocol(object): + TYPE_RESULT = 1 + TYPE_CONNECT = 2 + TYPE_LISTEN = 3 + TYPE_DEVICE_ADD = 4 + TYPE_DEVICE_REMOVE = 5 + VERSION = 0 + def __init__(self, socket): + self.socket = socket + self.connected = False + + def _pack(self, req, payload): + if req == self.TYPE_CONNECT: + return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00" + elif req == self.TYPE_LISTEN: + return "" + else: + raise ValueError("Invalid outgoing request type %d"%req) + + def _unpack(self, resp, payload): + if resp == self.TYPE_RESULT: + return {'Number':struct.unpack("I", payload)[0]} + elif resp == self.TYPE_DEVICE_ADD: + devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload) + serial = serial.split("\0")[0] + return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}} + elif resp == self.TYPE_DEVICE_REMOVE: + devid = struct.unpack("I", payload)[0] + return {'DeviceID': devid} + else: + raise MuxError("Invalid incoming request type %d"%req) + + def sendpacket(self, req, tag, payload={}): + payload = self._pack(req, payload) + if self.connected: + raise MuxError("Mux is connected, cannot issue control packets") + length = 16 + len(payload) + data = struct.pack("IIII", length, self.VERSION, req, tag) + payload + self.socket.send(data) + def getpacket(self): + if self.connected: + raise MuxError("Mux is connected, cannot issue control packets") + dlen = self.socket.recv(4) + dlen = struct.unpack("I", dlen)[0] + body = self.socket.recv(dlen - 4) + version, resp, tag = struct.unpack("III",body[:0xc]) + if version != self.VERSION: + raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version)) + payload = self._unpack(resp, body[0xc:]) + return (resp, tag, payload) + +class PlistProtocol(BinaryProtocol): + TYPE_RESULT = "Result" + TYPE_CONNECT = "Connect" + TYPE_LISTEN = "Listen" + TYPE_DEVICE_ADD = "Attached" + TYPE_DEVICE_REMOVE = "Detached" #??? + TYPE_PLIST = 8 + VERSION = 1 + def __init__(self, socket): + if not haveplist: + raise Exception("You need the plistlib module") + BinaryProtocol.__init__(self, socket) + + def _pack(self, req, payload): + return payload + + def _unpack(self, resp, payload): + return payload + + def sendpacket(self, req, tag, payload={}): + payload['ClientVersionString'] = 'usbmux.py by marcan' + if isinstance(req, int): + req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2] + payload['MessageType'] = req + payload['ProgName'] = 'tcprelay' + BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload)) + def getpacket(self): + resp, tag, payload = BinaryProtocol.getpacket(self) + if resp != self.TYPE_PLIST: + raise MuxError("Received non-plist type %d"%resp) + payload = plistlib.readPlistFromString(payload) + return payload['MessageType'], tag, payload + +class MuxConnection(object): + def __init__(self, socketpath, protoclass): + self.socketpath = socketpath + if sys.platform in ['win32', 'cygwin']: + family = socket.AF_INET + address = ('127.0.0.1', 27015) + else: + family = socket.AF_UNIX + address = self.socketpath + self.socket = SafeStreamSocket(address, family) + self.proto = protoclass(self.socket) + self.pkttag = 1 + self.devices = [] + + def _getreply(self): + while True: + resp, tag, data = self.proto.getpacket() + if resp == self.proto.TYPE_RESULT: + return tag, data + else: + raise MuxError("Invalid packet type received: %d"%resp) + def _processpacket(self): + resp, tag, data = self.proto.getpacket() + if resp == self.proto.TYPE_DEVICE_ADD: + self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID'])) + elif resp == self.proto.TYPE_DEVICE_REMOVE: + for dev in self.devices: + if dev.devid == data['DeviceID']: + self.devices.remove(dev) + elif resp == self.proto.TYPE_RESULT: + raise MuxError("Unexpected result: %d"%resp) + else: + raise MuxError("Invalid packet type received: %d"%resp) + def _exchange(self, req, payload={}): + mytag = self.pkttag + self.pkttag += 1 + self.proto.sendpacket(req, mytag, payload) + recvtag, data = self._getreply() + if recvtag != mytag: + raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag)) + return data['Number'] + + def listen(self): + ret = self._exchange(self.proto.TYPE_LISTEN) + if ret != 0: + raise MuxError("Listen failed: error %d"%ret) + def process(self, timeout=None): + if self.proto.connected: + raise MuxError("Socket is connected, cannot process listener events") + rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout) + if xlo: + self.socket.sock.close() + raise MuxError("Exception in listener socket") + if rlo: + self._processpacket() + def connect(self, device, port): + ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)}) + if ret != 0: + raise MuxError("Connect failed: error %d"%ret) + self.proto.connected = True + return self.socket.sock + def close(self): + self.socket.sock.close() + +class USBMux(object): + def __init__(self, socketpath=None): + if socketpath is None: + if sys.platform == 'darwin': + socketpath = "/var/run/usbmuxd" + else: + socketpath = "/var/run/usbmuxd" + self.socketpath = socketpath + self.listener = MuxConnection(socketpath, BinaryProtocol) + try: + self.listener.listen() + self.version = 0 + self.protoclass = BinaryProtocol + except MuxVersionError: + self.listener = MuxConnection(socketpath, PlistProtocol) + self.listener.listen() + self.protoclass = PlistProtocol + self.version = 1 + self.devices = self.listener.devices + def process(self, timeout=None): + self.listener.process(timeout) + def connect(self, device, port): + connector = MuxConnection(self.socketpath, self.protoclass) + return connector.connect(device, port) + +if __name__ == "__main__": + mux = USBMux() + print "Waiting for devices..." + if not mux.devices: + mux.process(0.1) + while True: + print "Devices:" + for dev in mux.devices: + print dev + mux.process()