├── include ├── Makefile.am └── img4tool │ ├── img4tool.hpp.in │ └── ASN1DERElement.hpp ├── Makefile.am ├── autogen.sh ├── libimg4tool.pc.in ├── .gitignore ├── img4tool ├── lzssdec.h ├── Makefile.am ├── ASN1DERElement.cpp ├── lzssdec.c ├── main.cpp └── img4tool.cpp ├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── README.md ├── configure.ac ├── LICENSE └── img4tool.xcodeproj └── project.pbxproj /include/Makefile.am: -------------------------------------------------------------------------------- 1 | nobase_dist_include_HEADERS = img4tool/img4tool.hpp \ 2 | img4tool/ASN1DERElement.hpp 3 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | ACLOCAL_AMFLAGS = -I m4 3 | SUBDIRS=img4tool include 4 | 5 | pkgconfigdir = $(libdir)/pkgconfig 6 | pkgconfig_DATA = libimg4tool.pc 7 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #cleanup cache for correct versioning when run multiple times 4 | rm -rf autom4te.cache 5 | 6 | 7 | aclocal -I m4 8 | autoconf 9 | autoheader 10 | automake --add-missing 11 | autoreconf -i 12 | ./configure "$@" 13 | -------------------------------------------------------------------------------- /libimg4tool.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libimg4tool 7 | Description: A library for manipulating IMG4, IM4M and IM4P files 8 | 9 | Requires: @openssl_requires@ @libplist_requires@ @libgeneral_requires@ 10 | Version: @VERSION_COMMIT_COUNT@ 11 | Libs: -L${libdir} -limg4tool 12 | Cflags: -I${includedir} 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.patch 3 | *.diff 4 | *.o 5 | *.a 6 | *.la 7 | *.lo 8 | aclocal.m4 9 | autom4te.cache 10 | compile 11 | config.* 12 | configure 13 | depcomp 14 | */.deps 15 | */.libs 16 | Makefile 17 | Makefile.in 18 | install-sh 19 | libtool 20 | ltmain.sh 21 | m4 22 | missing 23 | stamp-h1 24 | xcuserdata 25 | Build 26 | Index 27 | stamp-h2 28 | *.pc 29 | img4tool/img4tool 30 | include/img4tool/img4tool.hpp 31 | configure~ 32 | project.xcworkspace 33 | xcshareddata -------------------------------------------------------------------------------- /img4tool/lzssdec.h: -------------------------------------------------------------------------------- 1 | // 2 | // lzssdec.h 3 | // img4tool 4 | // 5 | // Code borrowed from: http://newosxbook.com/src.jl?tree=listings&file=joker.c 6 | // Coded by Jonathan Levin (a.k.a @Morpheus______), http://newosxbook.com 7 | 8 | #ifndef lzssdec_h 9 | #define lzssdec_h 10 | 11 | #include 12 | 13 | char *tryLZSS(const char *compressed, size_t compressedSize, size_t *outSize, const char **outHypervisor, size_t *outHypervisorSize); 14 | 15 | uint32_t lzss_compress(const uint8_t *src, uint32_t src_len,uint8_t *dst, uint32_t dst_len); 16 | 17 | 18 | #endif /* lzssdec_h */ 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [tihmstar] 4 | patreon: tihmstar 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /img4tool/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -I$(top_srcdir)/include $(GLOBAL_CFLAGS) $(libplist_CFLAGS) $(openssl_CFLAGS) $(libgeneral_CFLAGS) 2 | AM_CXXFLAGS = $(AM_CFLAGS) $(GLOBAL_CXXFLAGS) 3 | AM_LDFLAGS = $(libplist_LIBS) $(openssl_LIBS) $(libgeneral_LIBS) 4 | 5 | lib_LTLIBRARIES = libimg4tool.la 6 | bin_PROGRAMS = img4tool 7 | 8 | libimg4tool_la_CFLAGS = $(AM_CFLAGS) 9 | libimg4tool_la_CXXFLAGS = $(AM_CXXFLAGS) 10 | libimg4tool_la_LDFLAGS = $(AM_LDFLAGS) 11 | libimg4tool_la_SOURCES = ASN1DERElement.cpp \ 12 | lzssdec.c \ 13 | img4tool.cpp 14 | 15 | img4tool_CFLAGS = $(AM_CFLAGS) $(libfwkeyfetch_CFLAGS) 16 | img4tool_CXXFLAGS = $(AM_CXXFLAGS) 17 | img4tool_LDFLAGS = $(AM_LDFLAGS) $(libfwkeyfetch_LIBS) 18 | img4tool_LDADD = libimg4tool.la 19 | img4tool_SOURCES = main.cpp 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # img4tool 2 | _A tool for manipulating IMG4, IM4M and IM4P files_ 3 | 4 | # BUILD 5 | Install dependencies: 6 | * Buildsystem 7 | * autoconf 8 | * automake 9 | * libtool 10 | * pkg-config 11 | * External 12 | * openssl 13 | * libplist-2.0 14 | * tihmstar's tools 15 | * [libgeneral](https://github.com/tihmstar/libgeneral) 16 | 17 | To compile run: 18 | 19 | ```bash 20 | ./autogen.sh 21 | make 22 | sudo make install 23 | ``` 24 | 25 | 26 | # Features 27 | ## Print 28 | * Print IMG4, IM4P, IM4M 29 | * `img4tool infile.img4` 30 | * `-a` print everything from im4m 31 | * `-i` print only im4p 32 | 33 | ## Extract 34 | * ### Extract from IMG4 35 | * Extract IM4P 36 | * `img4tool -e -p out.im4p in.img4` 37 | * Extract IM4M 38 | * `img4tool -e -m out.im4m in.img4` 39 | 40 | * ### Extract from SHSH 41 | * Extract IM4P 42 | * `img4tool -e -p out.im4p -s in.shsh` 43 | 44 | * ### Extract from IM4P 45 | * Extract Payload 46 | * `img4tool -e -o out.bin in.im4p` 47 | * Extract and decrypt payload 48 | * `img4tool -e --iv --key -o out.bin in.im4p` 49 | 50 | ## Create 51 | * ### Create IMG4 52 | * Create IMG4 with IM4P 53 | * `img4tool -c out.img4 -p in.im4p` 54 | * Create IMG4 with IM4P and IM4M 55 | * `img4tool -c out.img4 -p in.im4p -m in.im4m` 56 | * Create IMG4 with IM4P and SHSH 57 | * `img4tool -c out.img4 -p in.im4p -s in.shsh` 58 | * ### Create IM4P 59 | * Create IM4P with type _ibss_ 60 | * `img4tool -c out.im4p -t ibss IBSS.raw` 61 | * Create IM4P with type _ibss_ and custom desc 62 | * `img4tool -c out.im4p -t ibss -d "Pwned iBSS no sigpatches" IBSS.raw` 63 | 64 | ## Rename (change IM4P type) 65 | * `img4tool -n rkrn -p kernel.im4p` 66 | 67 | ## Convert SHSH to IM4M 68 | * `img4tool --convert -s out.shsh in.im4m` 69 | 70 | ## Verify 71 | * ### Verify APTicket is valid for BuildManifest 72 | * Verify IM4M 73 | * `img4tool --verify BuildManifest.plist ticket.im4m` 74 | * Verify SHSH 75 | * `img4tool --verify BuildManifest.plist -s ticket.shsh` 76 | * ### Verify IMG4 is correctly signed for BuildManifest 77 | * `img4tool --verify BuildManifest.plist in.img4` //needs to contain IM4P and IM4M 78 | -------------------------------------------------------------------------------- /include/img4tool/img4tool.hpp.in: -------------------------------------------------------------------------------- 1 | // 2 | // img4tool.hpp 3 | // img4tool 4 | // 5 | // Created by tihmstar on 04.10.19. 6 | // Copyright © 2019 tihmstar. All rights reserved. 7 | // 8 | 9 | #ifndef img4tool_hpp 10 | #define img4tool_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #if @HEADER_HAVE_PLIST@ //HAVE_PLIST 20 | #include 21 | #endif //HAVE_PLIST 22 | 23 | namespace tihmstar { 24 | namespace img4tool { 25 | const char *version(); 26 | void printIMG4(const void *buf, size_t size, bool printAll, bool im4pOnly); 27 | void printIM4P(const void *buf, size_t size); 28 | void printIM4M(const void *buf, size_t size, bool printAll); 29 | void printSEPIDesc(const char *buf, size_t size); 30 | 31 | 32 | std::string getNameForSequence(const void *buf, size_t size); 33 | 34 | ASN1DERElement getIM4PFromIMG4(const ASN1DERElement &img4); 35 | ASN1DERElement getIM4MFromIMG4(const ASN1DERElement &img4); 36 | ASN1DERElement getIM4RFromIMG4(const ASN1DERElement &img4); 37 | 38 | ASN1DERElement getIM4RFromGenerator(uint64_t generator); 39 | ASN1DERElement getBNCNFromIM4R(const ASN1DERElement &im4r); 40 | 41 | ASN1DERElement getEmptyIMG4Container(); 42 | ASN1DERElement appendIM4PToIMG4(const ASN1DERElement &img4, const ASN1DERElement &im4p); 43 | ASN1DERElement appendIM4MToIMG4(const ASN1DERElement &img4, const ASN1DERElement &im4m); 44 | ASN1DERElement appendIM4RToIMG4(const ASN1DERElement &img4, const ASN1DERElement &im4r); 45 | 46 | bool im4pContainsKBAG(const ASN1DERElement &im4p); 47 | tihmstar::Mem getKBAG(const ASN1DERElement &im4p, int kbagNum); 48 | 49 | ASN1DERElement getPayloadFromIM4P(const ASN1DERElement &im4p, const char *decryptIv = NULL, const char *decryptKey = NULL, const char **outUsedCompression = NULL, ASN1DERElement *outHypervisor = NULL); 50 | ASN1DERElement getValFromIM4M(const ASN1DERElement &im4m, uint32_t val); 51 | ASN1DERElement getValFromElement(const ASN1DERElement &e, uint32_t val); 52 | 53 | 54 | ASN1DERElement genPrivTagForNumberWithPayload(size_t privnum, const ASN1DERElement &payload); 55 | 56 | #if @HEADER_HAVE_CRYPTO@ //HAVE_CRYPTO 57 | ASN1DERElement decryptPayload(const ASN1DERElement &payload, const char *decryptIv, const char *decryptKey); 58 | std::string getIM4PSHA1(const ASN1DERElement &im4p); 59 | std::string getIM4PSHA384(const ASN1DERElement &im4p); 60 | std::string dgstNameForHash(const ASN1DERElement &im4m, std::string hash); 61 | bool im4mContainsHash(const ASN1DERElement &im4m, std::string hash) noexcept; 62 | bool isGeneratorValidForIM4M(const ASN1DERElement &im4m, std::string generator) noexcept; 63 | #endif //HAVE_CRYPTO 64 | 65 | ASN1DERElement getEmptyIM4PContainer(const char *type, const char *desc); 66 | ASN1DERElement getIM4RWithElements(std::map elements); 67 | 68 | ASN1DERElement appendPayloadToIM4P(const ASN1DERElement &im4p, const void *buf, size_t size, const char *compression = NULL, const void *buf2Raw = NULL, size_t buf2RawSize = 0); 69 | 70 | bool isIMG4(const ASN1DERElement &img4) noexcept; 71 | bool isIM4P(const ASN1DERElement &im4p) noexcept; 72 | bool isIM4M(const ASN1DERElement &im4m) noexcept; 73 | bool isIM4R(const ASN1DERElement &im4m) noexcept; 74 | bool isIM4C(const ASN1DERElement &im4c) noexcept; 75 | 76 | ASN1DERElement renameIM4P(const ASN1DERElement &im4p, const char *type); 77 | 78 | std::string getDescFromIM4P(const ASN1DERElement &im4p); 79 | 80 | bool isIM4MSignatureValid(const ASN1DERElement &im4m); 81 | 82 | #if @HEADER_HAVE_PLIST@ //HAVE_PLIST 83 | bool doesIM4MBoardMatchBuildIdentity(const ASN1DERElement &im4m, plist_t buildIdentity) noexcept; 84 | bool im4mMatchesBuildIdentity(const ASN1DERElement &im4m, plist_t buildIdentity, std::vector ignoreWhitelist = {}) noexcept; 85 | const plist_t getBuildIdentityForIm4m(const ASN1DERElement &im4m, plist_t buildmanifest, std::vector ignoreWhitelist = {}); 86 | void printGeneralBuildIdentityInformation(plist_t buildidentity); 87 | bool isValidIM4M(const ASN1DERElement &im4m, plist_t buildmanifest, std::string forDGSTName = ""); 88 | plist_t getSHSH2FromIM4M(const ASN1DERElement &im4m); 89 | #endif //HAVE_PLIST 90 | 91 | }; 92 | }; 93 | #endif /* img4tool_hpp */ 94 | -------------------------------------------------------------------------------- /include/img4tool/ASN1DERElement.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DER.hpp 3 | // img4tool 4 | // 5 | // Created by tihmstar on 04.10.19. 6 | // Copyright © 2019 tihmstar. All rights reserved. 7 | // 8 | 9 | #ifndef ASN1DER_hpp 10 | #define ASN1DER_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace tihmstar { 17 | namespace img4tool { 18 | 19 | class ASN1DERElement { 20 | public: 21 | 22 | enum TagClass{ 23 | Universal = 0, 24 | Application = 1, 25 | ContextSpecific= 2, 26 | Private = 3 27 | }; 28 | 29 | enum Primitive{ 30 | Primitive = 0, 31 | Constructed = 1 32 | }; 33 | 34 | enum TagNumber{ 35 | TagEnd_of_Content = 0, 36 | TagBOOLEAN = 1, 37 | TagINTEGER = 2, 38 | TagBIT = 3, 39 | TagOCTET = 4, 40 | TagNULL = 5, 41 | TagOBJECT = 6, 42 | TagObject = 7, 43 | TagEXTERNAL = 8, 44 | TagREAL = 9, 45 | TagENUMERATED = 10, //0x0A 46 | TagEMBEDDED = 11, //0x0B 47 | TagUTF8String = 12, //0x0C 48 | TagRELATIVE_OID = 13, //0x0D 49 | TagReserved = (14 | 15), //(0x0E | 0x0F) 50 | TagSEQUENCE = 16, //0x10 51 | TagSET = 17, //0x11 52 | TagNumericString = 18, //0x12 53 | TagPrintableString = 19, //0x13 54 | TagT61String = 20, //0x14 55 | TagVideotexString = 21, //0x15 56 | TagIA5String = 22, //0x16 57 | TagUTCTime = 23, //0x17 58 | TagGeneralizedTime = 24, //0x18 59 | TagGraphicString = 25, //0x19 60 | TagVisibleString = 26, //0x1A 61 | TagGeneralString = 27, //0x1B 62 | TagUniversalString = 28, //0x1C 63 | TagCHARACTER = 29, //0x1D 64 | TagBMPString = 30, //0x1E 65 | TagPrivate = 0xff 66 | }; 67 | 68 | 69 | struct ASN1TAG{ 70 | uint8_t tagNumber : 5; 71 | uint8_t isConstructed : 1; 72 | uint8_t tagClass : 2; 73 | 74 | operator uint8_t () const{ return *(uint8_t*)this;} 75 | }; 76 | 77 | struct ASN1Len{ 78 | uint8_t len : 7; 79 | uint8_t isLong : 1; 80 | }; 81 | 82 | struct ASN1PrivateTag{ 83 | uint8_t num : 7; 84 | uint8_t more : 1; 85 | }; 86 | 87 | class ASN1DERElementIterator{ 88 | const ASN1TAG *_buf; 89 | size_t _pos; 90 | size_t _containerSize; 91 | public: 92 | ASN1DERElementIterator(const ASN1TAG *buf, size_t containerSize, size_t pos); 93 | ASN1DERElementIterator &operator++(); 94 | bool operator!=(const ASN1DERElementIterator &e); 95 | const ASN1DERElement operator*() const; 96 | }; 97 | 98 | private: 99 | const ASN1TAG *_buf; 100 | size_t _bufSize; 101 | /* 102 | If we get a buffer and a size, we never claim ownership, however 103 | if we construct an object ourselve we alloc a buffer and thus claim ownership 104 | */ 105 | bool _ownsBuffer; 106 | public: 107 | ASN1DERElement(); 108 | ASN1DERElement(const void *buf, size_t bufSize, bool ownsBuffer = false); 109 | ASN1DERElement(const ASN1TAG tag, const void *payload, size_t payloadLen); 110 | ~ASN1DERElement(); 111 | 112 | ASN1DERElement(ASN1DERElement &&old); 113 | ASN1DERElement(const ASN1DERElement &old); 114 | 115 | 116 | bool ownsBuffer() const; 117 | const void *buf() const; 118 | const void *payload() const; 119 | size_t taginfoSize() const; 120 | size_t payloadSize() const; 121 | size_t size() const; 122 | 123 | ASN1TAG tag() const; 124 | 125 | std::string getStringValue() const; 126 | uint64_t getIntegerValue() const; 127 | void print() const; 128 | std::string printString() const; 129 | 130 | 131 | ASN1DERElement operator[](uint32_t i) const; 132 | ASN1DERElement &operator+=(const ASN1DERElement &add); 133 | ASN1DERElement &operator=(ASN1DERElement &&old); 134 | ASN1DERElement &operator=(const ASN1DERElement &obj); 135 | 136 | ASN1DERElementIterator begin() const; 137 | ASN1DERElementIterator end() const; 138 | 139 | static std::string makeASN1Size(size_t size); 140 | static ASN1DERElement makeASN1Integer(uint64_t num); 141 | }; 142 | 143 | }; 144 | }; 145 | 146 | #endif /* ASN1DER_hpp */ 147 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Buildrunner 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | strategy: 9 | matrix: 10 | platform: [ubuntu-latest, macos-latest] 11 | runs-on: ${{ matrix.platform }} 12 | env: 13 | BUILDROOT: "buildroot_${{ matrix.platform }}" 14 | GIT_DEPENDENCIES: libfragmentzip,libfwkeyfetch,libgeneral,libplist 15 | MAC_DYNAMIC_LIBS: openssl 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Install pre-dependencies 22 | run: | 23 | if [ "$RUNNER_OS" == "Linux" ]; then 24 | sudo apt-get update 25 | sudo apt-get install -y jq libssl-dev 26 | 27 | # install liblzfse 28 | git clone https://github.com/lzfse/lzfse.git 29 | make -C lzfse CFLAGS="-fPIC" 30 | make -C lzfse INSTALL_PREFIX=$GITHUB_WORKSPACE/$BUILDROOT/usr/local install 31 | sudo make -C lzfse install INSTALL_PREFIX=/usr/local 32 | rm -rf lzfse 33 | 34 | elif [ "$RUNNER_OS" == "macOS" ]; then 35 | brew install autoconf automake libtool jq pkg-config 36 | brew install openssl 37 | 38 | cd $(brew --prefix openssl) 39 | sudo mkdir -p /usr/local/lib/pkgconfig/ 40 | sudo cp -r lib/pkgconfig/* /usr/local/lib/pkgconfig/ 41 | cd $GITHUB_WORKSPACE 42 | 43 | else 44 | echo "$RUNNER_OS not supported" 45 | exit 1 46 | fi 47 | shell: bash 48 | - name: download dependencies 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | run: | 52 | get_latest_release() { 53 | url="https://api.github.com/repos/$1/releases/latest" 54 | echo "url: ${url}" >&2 55 | curl --silent --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' "${url}" | # Get latest release from GitHub api 56 | jq .tag_name | # Get tag 57 | tr -d '"' # Strip quotes 58 | } 59 | mkdir depdir 60 | cd depdir 61 | mkdir $BUILDROOT 62 | IFS=',' read -r -a deparray <<< "$GIT_DEPENDENCIES"; for d in ${deparray[@]}; do 63 | dep=$d 64 | if ! echo ${dep} | grep -q '/'; then 65 | dep=${{ github.repository_owner }}/${dep} 66 | fi 67 | echo "Got dependency: ${dep}" 68 | tag=$(get_latest_release ${dep}); 69 | echo "Found tag: $tag" 70 | wget "https://github.com/${dep}/releases/download/$tag/$BUILDROOT.zip" 71 | unzip -u "$BUILDROOT.zip" 72 | rm "$BUILDROOT.zip" 73 | done 74 | echo "moving dependencies to /" 75 | sudo cp -r $BUILDROOT/* / 76 | cd .. 77 | rm -rf depdir 78 | - name: prepre buildroot 79 | run: mkdir -p $BUILDROOT 80 | - name: autogen 81 | run: ./autogen.sh --enable-static --disable-shared 82 | - name: make 83 | run: | 84 | if [ "$RUNNER_OS" == "macOS" ]; then 85 | IFS=',' read -r -a deparray <<< "$MAC_DYNAMIC_LIBS"; for d in ${deparray[@]}; do 86 | echo "moving library $d" 87 | cd $(brew --prefix $d) 88 | find . -name "*.dylib" -exec mv {} {}.bak \; 89 | done 90 | cd $GITHUB_WORKSPACE 91 | 92 | make -j || make 93 | 94 | IFS=',' read -r -a deparray <<< "$MAC_DYNAMIC_LIBS"; for d in ${deparray[@]}; do 95 | echo "restoring library $d" 96 | cd $(brew --prefix $d) 97 | find . -name "*.dylib.bak" | while read f; do o=$(echo $f | rev | cut -d '.' -f2- | rev); mv $f $o; done 98 | done 99 | cd $GITHUB_WORKSPACE 100 | else 101 | make -j || make 102 | fi 103 | - name: make install 104 | run: make DESTDIR=$GITHUB_WORKSPACE/$BUILDROOT install 105 | - uses: actions/upload-artifact@v4 106 | with: 107 | name: ${{ env.BUILDROOT }} 108 | path: ${{ env.BUILDROOT }} 109 | 110 | release: 111 | needs: build 112 | runs-on: ubuntu-latest 113 | steps: 114 | - uses: actions/checkout@v4 115 | with: 116 | fetch-depth: 0 117 | - name: Download ubuntu artifact 118 | uses: actions/download-artifact@v4 119 | with: 120 | name: buildroot_ubuntu-latest 121 | path: buildroot_ubuntu-latest 122 | - name: Download macos artifact 123 | uses: actions/download-artifact@v4 124 | with: 125 | name: buildroot_macos-latest 126 | path: buildroot_macos-latest 127 | - name: Set env vars and zip 128 | run: | 129 | echo "BUILD_VERSION_NUM=$(echo "$(git rev-list --count HEAD | tr -d '\n')")" >> $GITHUB_ENV 130 | echo "BUILD_VERSION_SHA=$(echo "$(git rev-parse HEAD | tr -d '\n'])")" >> $GITHUB_ENV 131 | echo "BUILD_VERSION_STR=$(echo "$(git rev-list --count HEAD | tr -d '\n')-$(git rev-parse HEAD | tr -d '\n'])")" >> $GITHUB_ENV 132 | echo "COMMIT_MSG=$(echo "$(git log -1 --pretty=%B)")" >> $GITHUB_ENV 133 | zip -r buildroot_macos-latest.zip buildroot_macos-latest 134 | zip -r buildroot_ubuntu-latest.zip buildroot_ubuntu-latest 135 | - name: Create Release 136 | id: create_release 137 | uses: softprops/action-gh-release@v2 138 | if: github.ref == 'refs/heads/master' 139 | env: 140 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 141 | with: 142 | prerelease: false 143 | draft: false 144 | tag_name: ${{ env.BUILD_VERSION_NUM }} 145 | name: Build ${{ env.BUILD_VERSION_STR }} 146 | body: ${{ env.COMMIT_MSG }} 147 | files: | 148 | buildroot_ubuntu-latest.zip 149 | buildroot_macos-latest.zip -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.69]) 2 | AC_INIT([img4tool], m4_esyscmd([git rev-list --count HEAD | tr -d '\n']), [tihmstar@gmail.com]) 3 | 4 | AC_CANONICAL_SYSTEM 5 | AC_CANONICAL_HOST 6 | AM_PROG_LIBTOOL 7 | 8 | AM_INIT_AUTOMAKE([subdir-objects]) 9 | AC_CONFIG_HEADERS([config.h]) 10 | AC_CONFIG_MACRO_DIRS([m4]) 11 | 12 | 13 | AC_DEFINE([VERSION_COMMIT_COUNT], "m4_esyscmd([git rev-list --count HEAD | tr -d '\n'])", [Git commit count]) 14 | AC_DEFINE([VERSION_COMMIT_SHA], "m4_esyscmd([git rev-parse HEAD | tr -d '\n'])", [Git commit sha]) 15 | AC_SUBST([VERSION_COMMIT_COUNT], ["m4_esyscmd([git rev-list --count HEAD | tr -d '\n'])"]) 16 | AC_SUBST([VERSION_COMMIT_SHA], ["m4_esyscmd([git rev-parse HEAD | tr -d '\n'])"]) 17 | 18 | # Checks for programs. 19 | AC_PROG_CXX 20 | AC_PROG_CC 21 | 22 | crypto_backend="none" 23 | # Check for operating system 24 | AC_MSG_CHECKING([whether we need platform-specific build settings]) 25 | case $host_os in 26 | darwin* ) 27 | AC_DEFINE(HAVE_COMMCRYPTO, 1, [Define if you have CommonCrypto (macOS)]) 28 | crypto_backend="CommCrypto" 29 | CXXFLAGS+=" -stdlib=libc++" 30 | ;; 31 | *mingw32*|*cygwin*|*msys*) 32 | AC_MSG_RESULT([${host_os}]) 33 | win32=true 34 | ;; 35 | esac 36 | 37 | CXXFLAGS+=" -std=c++11" 38 | CFLAGS+=" -std=c11" 39 | # Versioning. 40 | 41 | # Checks for libraries. 42 | LIBGENERAL_REQUIRES_STR="libgeneral >= 75" 43 | LIBFWKEYFETCH_REQUIRES_STR="libfwkeyfetch >= 1" 44 | LIBPLIST_REQUIRES_STR="libplist-2.0 >= 2.3.0" 45 | OPENSSL_REQUIRES_STR="openssl >= 0.9.8" 46 | 47 | PKG_CHECK_MODULES(libgeneral, $LIBGENERAL_REQUIRES_STR) 48 | PKG_CHECK_MODULES(libfwkeyfetch, $LIBFWKEYFETCH_REQUIRES_STR, have_libfwkeyfetch=yes, have_libfwkeyfetch=no) 49 | PKG_CHECK_MODULES(libplist, $LIBPLIST_REQUIRES_STR, have_plist=yes, have_plist=no) 50 | PKG_CHECK_MODULES(openssl, $OPENSSL_REQUIRES_STR, have_openssl=yes, have_openssl=no) 51 | 52 | AC_SUBST([libgeneral_requires], [$LIBGENERAL_REQUIRES_STR]) 53 | 54 | AC_ARG_ENABLE([debug], 55 | [AS_HELP_STRING([--enable-debug], 56 | [enable debug build(default is no)])], 57 | [debug_build=true], 58 | [debug_build=false]) 59 | 60 | AC_ARG_WITH([libfwkeyfetch], 61 | [AS_HELP_STRING([--without-libfwkeyfetch], 62 | [do not build with libfwkeyfetch @<:@default=yes (if available)@:>@])], 63 | [with_libfwkeyfetch=no], 64 | [with_libfwkeyfetch=yes]) 65 | 66 | AC_ARG_WITH([plist], 67 | [AS_HELP_STRING([--without-plist], 68 | [do not build with libplist @<:@default=yes@:>@])], 69 | [with_plist=no], 70 | [with_plist=yes]) 71 | 72 | AC_ARG_WITH([openssl], 73 | [AS_HELP_STRING([--without-openssl], 74 | [do not build with openssl @<:@default=yes@:>@])], 75 | [with_openssl=no], 76 | [with_openssl=yes]) 77 | 78 | AC_ARG_WITH([compression], 79 | [AS_HELP_STRING([--without-compression], 80 | [do not build with bvx2 support @<:@default=yes@:>@])], 81 | [with_compression=no], 82 | [with_compression=yes]) 83 | 84 | if test "x$have_libfwkeyfetch" == "xyes"; then 85 | if test "x$with_libfwkeyfetch" == "xyes"; then 86 | AC_DEFINE(HAVE_LIBFWKEYFETCH, 1, [Define if you have libfwkeyfetch]) 87 | AC_SUBST([HEADER_HAVE_LIBFWKEYFETCH], [1]) 88 | AC_SUBST([libfwkeyfetch_requires], [$LIBFWKEYFETCH_REQUIRES_STR]) 89 | AC_SUBST(libfwkeyfetch_CFLAGS) 90 | AC_SUBST(libfwkeyfetch_LIBS) 91 | else 92 | echo "*** Note: libfwkeyfetch has been disabled ***" 93 | AC_SUBST([HEADER_HAVE_LIBFWKEYFETCH], [0]) 94 | fi 95 | else 96 | echo "*** Note: libfwkeyfetch is unavailable ***" 97 | with_libfwkeyfetch="no" 98 | AC_SUBST([HEADER_HAVE_LIBFWKEYFETCH], [0]) 99 | fi 100 | 101 | if test "x$with_plist" == "xyes"; then 102 | if test "x$have_plist" = "xyes"; then 103 | AC_DEFINE(HAVE_PLIST, 1, [Define if you have libplist]) 104 | AC_SUBST([HEADER_HAVE_PLIST], [1]) 105 | AC_SUBST([libplist_requires], [$LIBPLIST_REQUIRES_STR]) 106 | AC_SUBST(libplist_CFLAGS) 107 | AC_SUBST(libplist_LIBS) 108 | else 109 | AC_MSG_ERROR([requested building with libplist, but library could not be found]) 110 | fi 111 | else 112 | echo "*** Note: libplist has been disabled ***" 113 | AC_SUBST([HEADER_HAVE_PLIST], [0]) 114 | fi 115 | 116 | if test "x$with_openssl" == "xyes"; then 117 | if test "x$have_openssl" = "xyes"; then 118 | AC_DEFINE(HAVE_OPENSSL, 1, [Define if you have openssl]) 119 | AC_SUBST([HEADER_HAVE_OPENSSL], [1]) 120 | AC_SUBST([openssl_requires], [$OPENSSL_REQUIRES_STR]) 121 | AC_SUBST(openssl_CFLAGS) 122 | AC_SUBST(openssl_LIBS) 123 | crypto_backend="openssl" 124 | else 125 | AC_MSG_ERROR([requested building with openssl, but library could not be found]) 126 | fi 127 | else 128 | echo "*** Note: openssl has been disabled ***" 129 | AC_SUBST([HEADER_HAVE_OPENSSL], [0]) 130 | fi 131 | 132 | if test "x$crypto_backend" != "xnone"; then 133 | AC_DEFINE(HAVE_CRYPTO, 1, [Define if you have either openssl or CommCrypto]) 134 | AC_SUBST([HEADER_HAVE_CRYPTO], [1]) 135 | else 136 | AC_SUBST([HEADER_HAVE_CRYPTO], [0]) 137 | fi 138 | 139 | if test "$debug_build" = true; then 140 | echo "*** Note: debug build requested ***" 141 | CFLAGS+=" -g -O0 -DDEBUG=1" 142 | CXXFLAGS+=" -g -O0 -DDEBUG=1" 143 | fi 144 | 145 | # Checks for header files. 146 | AC_CHECK_HEADERS([arpa/inet.h stdint.h stdlib.h string.h unistd.h winsock.h]) 147 | 148 | # Check for libraries 149 | if test "x$with_compression" == "xyes"; then 150 | AC_CHECK_LIB([compression], [compression_decode_buffer], [], [ 151 | AC_CHECK_LIB([lzfse], [lzfse_decode_buffer], [], [ 152 | echo "Error! Either libcompression or liblzfse is needed!" 153 | exit -1 154 | ]) 155 | ]) 156 | else 157 | echo "*** Note: bvx2 has been disabled ***" 158 | fi 159 | 160 | if test "x$win32" == "xtrue"; then 161 | #this needs to be done after AC_CHECK_LIB, because -no-undefined breaks that! 162 | LDFLAGS+=" -no-undefined -lws2_32" 163 | fi 164 | 165 | # Checks for typedefs, structures, and compiler characteristics. 166 | AC_CHECK_HEADER_STDBOOL 167 | AC_TYPE_SIZE_T 168 | AC_TYPE_UINT32_T 169 | AC_TYPE_UINT64_T 170 | AC_TYPE_UINT8_T 171 | 172 | AC_CHECK_FUNCS([memmem]) 173 | 174 | AC_CONFIG_FILES([Makefile 175 | include/Makefile 176 | libimg4tool.pc 177 | include/img4tool/img4tool.hpp 178 | img4tool/Makefile]) 179 | 180 | AC_OUTPUT 181 | 182 | 183 | echo " 184 | Configuration for $PACKAGE-$VERSION: 185 | ------------------------------------------- 186 | 187 | install prefix ..........: $prefix 188 | with libfwkeyfetch ......: $with_libfwkeyfetch 189 | with plist ..............: $with_plist 190 | with bvx2 ...............: $with_compression 191 | crypto backend ..........: $crypto_backend" 192 | 193 | echo " compiler ................: ${CC} 194 | 195 | Now type 'make' to build $PACKAGE-$VERSION, 196 | and then 'make install' for installation. 197 | " 198 | if test "x$crypto_backend" != "xopenssl"; then 199 | echo "WARNING: crypto_backend is not openssl, some features will be missing" 200 | fi 201 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /img4tool/ASN1DERElement.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DER.cpp 3 | // img4tool 4 | // 5 | // Created by tihmstar on 04.10.19. 6 | // Copyright © 2019 tihmstar. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "../include/img4tool/ASN1DERElement.hpp" 11 | #include 12 | #include 13 | 14 | using namespace tihmstar::img4tool; 15 | 16 | #pragma mark helper 17 | #define putStr(s,l) printf("%.*s",(int)l,s) 18 | 19 | 20 | std::string ASN1DERElement::makeASN1Size(size_t size){ 21 | assure(size < 0x100000000); 22 | if (size >= 0x1000000) { 23 | // 1+4 bytes length 24 | return {{(char)0x84,(char)((size >> 24) & 0xFF),(char)((size >> 16) & 0xFF),(char)((size >> 8) & 0xFF),(char)(size & 0xFF)}}; 25 | } else if (size >= 0x10000) { 26 | // 1+3 bytes length 27 | return {{(char)0x83,(char)((size >> 16) & 0xFF),(char)((size >> 8) & 0xFF),(char)(size & 0xFF)}}; 28 | } else if (size >= 0x100) { 29 | // 1+2 bytes length 30 | return {{(char)0x82,(char)((size >> 8) & 0xFF),(char)(size & 0xFF)}}; 31 | } else if (size >= 0x80) { 32 | // 1+1 byte length 33 | return {{(char)0x81,(char)(size & 0xFF)}}; 34 | } else { 35 | // 1 byte length 36 | return {(char)(size & 0xFF)}; 37 | } 38 | } 39 | 40 | ASN1DERElement ASN1DERElement::makeASN1Integer(uint64_t num){ 41 | uint64_t bigEndian = num; 42 | int bytes = 0; 43 | 44 | while (num) { 45 | bigEndian <<=8; 46 | bigEndian |= num & 0xff; 47 | num >>=8; 48 | bytes++; 49 | } 50 | 51 | return ASN1DERElement({ASN1DERElement::TagNumber::TagINTEGER, ASN1DERElement::Universal}, &bigEndian, bytes); 52 | } 53 | 54 | #pragma mark ASN1DERElementIterator 55 | 56 | ASN1DERElement::ASN1DERElementIterator::ASN1DERElementIterator(const ASN1DERElement::ASN1TAG *buf, size_t containerSize, size_t pos) : 57 | _buf(buf), 58 | _containerSize(containerSize), 59 | _pos(pos) 60 | { 61 | // 62 | } 63 | 64 | ASN1DERElement::ASN1DERElementIterator &ASN1DERElement::ASN1DERElementIterator::operator++(){ 65 | ASN1DERElement e(_buf+_pos,_containerSize-_pos); 66 | _pos += e.size(); 67 | assure(_pos<=_containerSize); 68 | return *this; 69 | } 70 | 71 | bool ASN1DERElement::ASN1DERElementIterator::operator!=(const ASN1DERElementIterator &e){ 72 | return (_buf != e._buf || _pos != e._pos || _containerSize != e._containerSize); 73 | } 74 | 75 | const ASN1DERElement ASN1DERElement::ASN1DERElementIterator::operator*() const{ 76 | return {_buf+_pos,_containerSize-_pos}; 77 | } 78 | 79 | #pragma mark ASN1DERElement 80 | 81 | ASN1DERElement::ASN1DERElement() : 82 | _buf(NULL), 83 | _bufSize(0), 84 | _ownsBuffer(true) 85 | { 86 | constexpr const ASN1TAG tag{img4tool::ASN1DERElement::TagNULL,img4tool::ASN1DERElement::Primitive,img4tool::ASN1DERElement::Universal}; 87 | std::string size = makeASN1Size(0); 88 | assure(_buf = (const ASN1TAG *)malloc(_bufSize = 1+size.size())); 89 | 90 | memcpy((void*)&_buf[0], &tag, 1); 91 | memcpy((void*)&_buf[1], size.c_str(), size.size()); 92 | } 93 | 94 | ASN1DERElement::ASN1DERElement(const void *buf, size_t bufSize, bool ownsBuffer) : 95 | _buf(NULL), 96 | _bufSize(0), 97 | _ownsBuffer(true) 98 | { 99 | if (ownsBuffer) { 100 | _buf = (const ASN1TAG*)buf; 101 | _bufSize = bufSize; 102 | }else{ 103 | //if we don't get the ownershipt of the buffer transfered to us, we have to make a copy! 104 | _buf = (ASN1TAG*)malloc(_bufSize = bufSize); 105 | memcpy((void*)_buf, buf, _bufSize); 106 | } 107 | 108 | assure(_bufSize >= 2); //needs at least TAG and Size 109 | if (((uint8_t*)_buf)[0] != 0xff) { 110 | assure(_buf->tagNumber <= TagBMPString); 111 | } 112 | assure(_bufSize >= size()); 113 | } 114 | 115 | ASN1DERElement::ASN1DERElement(const ASN1TAG tag, const void *payload, size_t payloadLen) : 116 | _buf(NULL), 117 | _bufSize(0), 118 | _ownsBuffer(true) 119 | { 120 | std::string size = makeASN1Size(payloadLen); 121 | assure(_buf = (const ASN1TAG *)malloc(_bufSize = 1+payloadLen+size.size())); 122 | 123 | memcpy((void*)&_buf[0], &tag, 1); 124 | memcpy((void*)&_buf[1], size.c_str(), size.size()); 125 | if (payloadLen) { 126 | memcpy((void*)&_buf[1+size.size()], payload, payloadLen); 127 | } 128 | } 129 | 130 | ASN1DERElement::ASN1DERElement(ASN1DERElement &&old){ 131 | void *buf = NULL; 132 | cleanup([&]{ 133 | safeFree(buf); 134 | }); 135 | if (_ownsBuffer){ 136 | buf = (void*)_buf; _buf = NULL; 137 | } 138 | if (old._ownsBuffer) { 139 | _buf = old._buf; 140 | _bufSize = old._bufSize; 141 | _ownsBuffer = old._ownsBuffer; old._ownsBuffer = false; 142 | }else{ 143 | /* 144 | if the old object doesn't own the buffer, we have to perform a copy! 145 | Otherwise there is no guarantee that the buffer will stay valid 146 | */ 147 | _bufSize = old._bufSize; 148 | _ownsBuffer = true; 149 | _buf = (ASN1TAG*)malloc(_bufSize); 150 | memcpy((void*)&_buf[0], old._buf, _bufSize); 151 | } 152 | } 153 | 154 | ASN1DERElement::ASN1DERElement(const ASN1DERElement &old) : 155 | _buf(NULL), 156 | _bufSize(old._bufSize), 157 | _ownsBuffer(true) 158 | { 159 | _buf = (ASN1TAG*)malloc(_bufSize); 160 | memcpy((void*)&_buf[0], old._buf, _bufSize); 161 | } 162 | 163 | 164 | ASN1DERElement::~ASN1DERElement(){ 165 | if (_ownsBuffer) { 166 | safeFreeConst(_buf); 167 | } 168 | } 169 | 170 | size_t ASN1DERElement::taginfoSize() const{ 171 | size_t tagInfoSize = 0; 172 | if (((uint8_t*)_buf)[0] == 0xff){ 173 | ASN1PrivateTag *ptag = ((ASN1PrivateTag *)_buf)+1; 174 | do { 175 | assure(_bufSize >= 2 + (++tagInfoSize)); 176 | }while (ptag++->more); 177 | } 178 | 179 | ASN1Len *tlen = (ASN1Len *)&_buf[1+tagInfoSize]; 180 | return ((!tlen->isLong) ? 2 : 2+tlen->len) + tagInfoSize; 181 | } 182 | 183 | size_t ASN1DERElement::payloadSize() const{ 184 | size_t tagInfoSize = 0; 185 | if (((uint8_t*)_buf)[0] == 0xff){ 186 | ASN1PrivateTag *ptag = ((ASN1PrivateTag *)_buf)+1; 187 | do { 188 | assure(_bufSize >= 2 + (++tagInfoSize)); 189 | }while (ptag++->more); 190 | } 191 | 192 | size_t rt = 0; 193 | ASN1Len *tlen = (ASN1Len *)&_buf[1+tagInfoSize]; 194 | if (!tlen->isLong) 195 | return tlen->len; 196 | 197 | assure(tlen->len <= sizeof(size_t)); //can't hold more than size_t 198 | assure(_bufSize > 2 + tagInfoSize + tlen->len); //len bytes shouldn't be outside of buffer 199 | 200 | for (uint8_t sizebits = 0; sizebits < tlen->len; sizebits++) { 201 | rt <<= 8; 202 | rt |= ((uint8_t*)_buf)[2+tagInfoSize+sizebits]; 203 | } 204 | 205 | return rt; 206 | } 207 | 208 | size_t ASN1DERElement::size() const{ 209 | return taginfoSize() + payloadSize(); 210 | } 211 | 212 | bool ASN1DERElement::ownsBuffer() const{ 213 | return _ownsBuffer; 214 | } 215 | 216 | 217 | const void *ASN1DERElement::buf() const{ 218 | return _buf; 219 | } 220 | 221 | const void *ASN1DERElement::payload() const{ 222 | return ((uint8_t*)_buf)+taginfoSize(); 223 | } 224 | 225 | 226 | ASN1DERElement::ASN1TAG ASN1DERElement::tag() const{ 227 | return *(ASN1TAG*)&_buf[0]; 228 | } 229 | 230 | std::string ASN1DERElement::getStringValue() const{ 231 | assure(((uint8_t*)_buf)[0] != 0xff); 232 | assure(_buf->tagNumber == TagNumber::TagIA5String || _buf->tagNumber == TagNumber::TagOCTET || _buf->tagNumber == TagNumber::TagUTF8String); 233 | 234 | return {(char*)payload(),payloadSize()}; 235 | } 236 | 237 | uint64_t ASN1DERElement::getIntegerValue() const{ 238 | uint64_t rt = 0; 239 | assure(_buf->tagNumber == TagNumber::TagINTEGER || _buf->tagNumber == TagNumber::TagBOOLEAN); 240 | uint8_t *data = (uint8_t*)payload(); 241 | size_t dataSize = payloadSize(); 242 | 243 | while (dataSize > 0 && *data == 0) { 244 | data++; 245 | dataSize--; 246 | } 247 | 248 | assure(dataSize <= sizeof(uint64_t)); 249 | for (uint8_t sizebits = 0; sizebits < dataSize; sizebits++) { 250 | rt <<= 8; 251 | rt |= data[sizebits]; 252 | } 253 | return rt; 254 | } 255 | 256 | void ASN1DERElement::print() const{ 257 | switch (tag().tagNumber) { 258 | case TagIA5String: 259 | printf("%s",getStringValue().c_str()); 260 | break; 261 | case TagOCTET: 262 | { 263 | std::string s = getStringValue(); 264 | bool isASCII = true; 265 | for (int i=0; iisConstructed); 340 | ASN1DERElement rt(payload(),payloadSize()); 341 | 342 | size_t bufSize = payloadSize(); 343 | const uint8_t *bufptr = (const uint8_t *)payload(); 344 | while (i--){ 345 | bufptr += rt.size(); 346 | bufSize -= rt.size(); 347 | rt = ASN1DERElement(bufptr, bufSize); 348 | } 349 | 350 | return rt; 351 | } 352 | 353 | ASN1DERElement &ASN1DERElement::operator+=(const ASN1DERElement &add){ 354 | if (!_ownsBuffer){ 355 | //make a copy 356 | *this = (const ASN1DERElement&)(*this); 357 | } 358 | assure(_buf->isConstructed && _ownsBuffer); 359 | 360 | std::string newSize = makeASN1Size(add.size()+payloadSize()); 361 | 362 | if (newSize.size() < taginfoSize()) { 363 | //newSize fits in the buffer without resizing at front 364 | _buf = (const ASN1TAG *)realloc((void*)_buf, _bufSize = size() + add.size()); 365 | 366 | memcpy((void*)&_buf[size()], add.buf(), add.size()); 367 | memcpy((void*)&_buf[1], newSize.c_str(), newSize.size()); 368 | }else{ 369 | size_t size = add.size() + payloadSize() + 1 + newSize.size(); 370 | const ASN1TAG *buf = (const ASN1TAG *)malloc(size); 371 | 372 | cleanup([&]{ 373 | void *b = (void*)buf; buf = NULL; 374 | safeFree(b); 375 | }) 376 | 377 | memcpy((void*)&buf[0], &_buf[0], 1); 378 | memcpy((void*)&buf[1], newSize.c_str(), newSize.size()); 379 | memcpy((void*)&buf[1+newSize.size()], payload(), payloadSize()); 380 | memcpy((void*)&buf[1+newSize.size()+payloadSize()], add.buf(), add.size()); 381 | 382 | std::swap(_buf, buf); 383 | _bufSize = size; 384 | } 385 | 386 | return *this; 387 | } 388 | 389 | ASN1DERElement &ASN1DERElement::operator=(ASN1DERElement &&old){ 390 | void *buf = NULL; 391 | cleanup([&]{ 392 | safeFree(buf); 393 | }); 394 | if (_ownsBuffer){ 395 | buf = (void*)_buf; _buf = NULL; 396 | } 397 | if (old._ownsBuffer) { 398 | _buf = old._buf; 399 | _bufSize = old._bufSize; 400 | _ownsBuffer = old._ownsBuffer; old._ownsBuffer = false; 401 | }else{ 402 | /* 403 | if the old object doesn't own the buffer, we have to perform a copy! 404 | Otherwise there is no guarantee that the buffer will stay valid 405 | */ 406 | _bufSize = old._bufSize; 407 | _ownsBuffer = true; 408 | _buf = (ASN1TAG*)malloc(_bufSize); 409 | memcpy((void*)&_buf[0], old._buf, _bufSize); 410 | } 411 | 412 | return *this; 413 | } 414 | 415 | ASN1DERElement &ASN1DERElement::operator=(const ASN1DERElement &old){ 416 | _bufSize = old._bufSize; 417 | _ownsBuffer = true; 418 | _buf = (ASN1TAG*)malloc(_bufSize); 419 | memcpy((void*)&_buf[0], old._buf, _bufSize); 420 | 421 | return *this; 422 | } 423 | 424 | ASN1DERElement::ASN1DERElementIterator ASN1DERElement::begin() const{ 425 | return {(const ASN1TAG *)payload(),payloadSize(),0}; 426 | } 427 | 428 | ASN1DERElement::ASN1DERElementIterator ASN1DERElement::end() const{ 429 | return {(const ASN1TAG *)payload(),payloadSize(),payloadSize()}; 430 | } 431 | -------------------------------------------------------------------------------- /img4tool/lzssdec.c: -------------------------------------------------------------------------------- 1 | // 2 | // lzssdec.c 3 | // img4tool 4 | // 5 | // Code borrowed from: http://newosxbook.com/src.jl?tree=listings&file=joker.c 6 | // Coded by Jonathan Levin (a.k.a @Morpheus______), http://newosxbook.com 7 | // 8 | 9 | #define _GNU_SOURCE 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "lzssdec.h" 16 | #include 17 | #include 18 | 19 | #ifdef HAVE_ARPA_INET_H 20 | #include 21 | #elif defined(HAVE_WINSOCK_H) 22 | #include 23 | #endif 24 | 25 | #ifndef HAVE_MEMMEM 26 | void *memmem(const void *haystack_start, size_t haystack_len, const void *needle_start, size_t needle_len){ 27 | const unsigned char *haystack = (const unsigned char *)haystack_start; 28 | const unsigned char *needle = (const unsigned char *)needle_start; 29 | const unsigned char *h = NULL; 30 | const unsigned char *n = NULL; 31 | size_t x = needle_len; 32 | 33 | /* The first occurrence of the empty string is deemed to occur at 34 | the beginning of the string. */ 35 | if (needle_len == 0) { 36 | return (void *)haystack_start; 37 | } 38 | 39 | /* Sanity check, otherwise the loop might search through the whole 40 | memory. */ 41 | if (haystack_len < needle_len) { 42 | return NULL; 43 | } 44 | 45 | for (; *haystack && haystack_len--; haystack++) { 46 | x = needle_len; 47 | n = needle; 48 | h = haystack; 49 | 50 | if (haystack_len < needle_len) 51 | break; 52 | 53 | if ((*haystack != *needle) || (*haystack + needle_len != *needle + needle_len)) 54 | continue; 55 | 56 | for (; x; h++, n++) { 57 | x--; 58 | 59 | if (*h != *n) 60 | break; 61 | 62 | if (x == 0) 63 | return (void *)haystack; 64 | } 65 | } 66 | return NULL; 67 | } 68 | #endif 69 | 70 | static uint32_t decompress_lzss(uint8_t *dst, const uint8_t *src, uint32_t srclen); 71 | static uint8_t *compress_lzss(uint8_t *dst, uint32_t dstlen, const uint8_t *src, uint32_t srcLen); 72 | static uint32_t lzadler32(const uint8_t *buf, int32_t len); 73 | 74 | 75 | struct compHeader { 76 | char sig[8] ; // "complzss" 77 | uint32_t adler32; 78 | uint32_t uncompressedSize; 79 | uint32_t compressedSize; 80 | uint32_t unknown1; // 1 81 | uint8_t pad[360]; 82 | uint8_t data[0]; 83 | }; 84 | 85 | char *tryLZSS(const char *compressed, size_t compressedSize, size_t *outSize, const char **outHypervisor, size_t *outHypervisorSize){ 86 | struct compHeader *compHeader = (struct compHeader*)compressed; 87 | if (!compHeader) return NULL; 88 | int sig[2] = { 0xfeedfacf, 0x0100000c }; 89 | int sig2[2] = { 0xfeedface, 0x0000000c }; 90 | 91 | char *decomp = malloc (ntohl(compHeader->uncompressedSize)); 92 | char *feed = memmem(compressed+64, 1024, sig, sizeof(sig)); 93 | 94 | if (!feed){ 95 | if (!(feed = memmem(compressed+64, 1024, sig2, sizeof(sig2)))) 96 | return NULL; 97 | } 98 | 99 | feed--; 100 | int rc = decompress_lzss((void*)decomp, (void*)feed, ntohl(compHeader->compressedSize)); 101 | if (rc != ntohl(compHeader->uncompressedSize)) { 102 | return NULL; 103 | } 104 | 105 | if (ntohl(compHeader->adler32) != lzadler32((const uint8_t*)decomp, rc)) { 106 | return NULL; 107 | } 108 | 109 | if (outHypervisorSize) { 110 | *outHypervisorSize = 0; 111 | } 112 | /* 113 | Some devices have a hypervisor behind the kernel 114 | */ 115 | 116 | //check we care about an additional macho at the end 117 | if (outHypervisor) { 118 | *outHypervisor = NULL; 119 | const char *hypervisor = compressed + sizeof(struct compHeader) + ntohl(compHeader->compressedSize); 120 | if (hypervisor - compressed + sizeof(uint32_t) <= compressedSize) { 121 | //there is something at the end of the compressed buffer 122 | if (*(uint32_t*)hypervisor == 0xfeedfacf) { 123 | //there is a macho! 124 | *outHypervisor = hypervisor; 125 | if (outHypervisorSize) { 126 | *outHypervisorSize = compressedSize - (hypervisor - compressed); 127 | } 128 | } 129 | } 130 | } 131 | 132 | *outSize = rc; 133 | return (decomp); 134 | } 135 | 136 | uint32_t lzss_compress(const uint8_t *src, uint32_t src_len,uint8_t *dst, uint32_t dst_len){ 137 | uint32_t compSize = 0; 138 | memset(dst, 0, dst_len); 139 | 140 | struct compHeader *compHeader = (struct compHeader*)dst; 141 | dst += sizeof(struct compHeader); 142 | dst_len -= sizeof(struct compHeader); 143 | 144 | 145 | memcpy(compHeader->sig, "complzss", sizeof(compHeader->sig)); 146 | 147 | compHeader->adler32 = ntohl(lzadler32(src, src_len)); 148 | compHeader->uncompressedSize = ntohl(src_len); 149 | compHeader->unknown1 = ntohl(1); 150 | 151 | uint8_t *end = compress_lzss(dst, dst_len, src, src_len); 152 | compSize = (uint32_t)(end-dst); 153 | compHeader->compressedSize = ntohl(compSize); 154 | 155 | return compSize + sizeof(struct compHeader); 156 | } 157 | 158 | #define BASE 65521L /* largest prime smaller than 65536 */ 159 | #define NMAX 5000 160 | #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} 161 | #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); 162 | #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); 163 | #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); 164 | #define DO16(buf) DO8(buf,0); DO8(buf,8); 165 | 166 | static uint32_t lzadler32(const uint8_t *buf, int32_t len) 167 | { 168 | unsigned long s1 = 1; // adler & 0xffff; 169 | unsigned long s2 = 0; // (adler >> 16) & 0xffff; 170 | int k; 171 | 172 | while (len > 0) { 173 | k = len < NMAX ? len : NMAX; 174 | len -= k; 175 | while (k >= 16) { 176 | DO16(buf); 177 | buf += 16; 178 | k -= 16; 179 | } 180 | if (k != 0) do { 181 | s1 += *buf++; 182 | s2 += s1; 183 | } while (--k); 184 | s1 %= BASE; 185 | s2 %= BASE; 186 | } 187 | return (uint32_t)((s2 << 16) | s1); 188 | } 189 | 190 | /************************************************************** 191 | LZSS.C -- A Data Compression Program 192 | *************************************************************** 193 | 4/6/1989 Haruhiko Okumura 194 | Use, distribute, and modify this program freely. 195 | Please send me your improved versions. 196 | PC-VAN SCIENCE 197 | NIFTY-Serve PAF01022 198 | CompuServe 74050,1022 199 | **************************************************************/ 200 | 201 | #define N 4096 /* size of ring buffer - must be power of 2 */ 202 | #define F 18 /* upper limit for match_length */ 203 | #define THRESHOLD 2 /* encode string into position and length 204 | if match_length is greater than this */ 205 | #define NIL N /* index for root of binary search trees */ 206 | 207 | struct encode_state { 208 | /* 209 | * left & right children & parent. These constitute binary search trees. 210 | */ 211 | int lchild[N + 1], rchild[N + 257], parent[N + 1]; 212 | 213 | /* ring buffer of size N, with extra F-1 bytes to aid string comparison */ 214 | uint8_t text_buf[N + F - 1]; 215 | 216 | /* 217 | * match_length of longest match. 218 | * These are set by the insert_node() procedure. 219 | */ 220 | int match_position, match_length; 221 | }; 222 | 223 | 224 | static uint32_t decompress_lzss(uint8_t *dst, const uint8_t *src, uint32_t srclen) 225 | { 226 | /* ring buffer of size N, with extra F-1 bytes to aid string comparison */ 227 | uint8_t text_buf[N + F - 1]; 228 | uint8_t *dststart = dst; 229 | const uint8_t *srcend = src + srclen; 230 | int i, j, k, r, c; 231 | unsigned int flags; 232 | 233 | dst = dststart; 234 | srcend = src + srclen; 235 | for (i = 0; i < N - F; i++) 236 | text_buf[i] = ' '; 237 | r = N - F; 238 | flags = 0; 239 | for ( ; ; ) { 240 | if (((flags >>= 1) & 0x100) == 0) { 241 | if (src < srcend) c = *src++; else break; 242 | flags = c | 0xFF00; /* uses higher byte cleverly */ 243 | } /* to count eight */ 244 | if (flags & 1) { 245 | if (src < srcend) c = *src++; else break; 246 | *dst++ = c; 247 | text_buf[r++] = c; 248 | r &= (N - 1); 249 | } else { 250 | if (src < srcend) i = *src++; else break; 251 | if (src < srcend) j = *src++; else break; 252 | i |= ((j & 0xF0) << 4); 253 | j = (j & 0x0F) + THRESHOLD; 254 | for (k = 0; k <= j; k++) { 255 | c = text_buf[(i + k) & (N - 1)]; 256 | *dst++ = c; 257 | text_buf[r++] = c; 258 | r &= (N - 1); 259 | } 260 | } 261 | } 262 | 263 | return (uint32_t)(dst - dststart); 264 | } 265 | 266 | /* 267 | * initialize state, mostly the trees 268 | * 269 | * For i = 0 to N - 1, rchild[i] and lchild[i] will be the right and left 270 | * children of node i. These nodes need not be initialized. Also, parent[i] 271 | * is the parent of node i. These are initialized to NIL (= N), which stands 272 | * for 'not used.' For i = 0 to 255, rchild[N + i + 1] is the root of the 273 | * tree for strings that begin with character i. These are initialized to NIL. 274 | * Note there are 256 trees. */ 275 | static void init_state(struct encode_state *sp) 276 | { 277 | int i; 278 | 279 | memset(sp, 0, sizeof(*sp)); 280 | 281 | for (i = 0; i < N - F; i++) 282 | sp->text_buf[i] = ' '; 283 | for (i = N + 1; i <= N + 256; i++) 284 | sp->rchild[i] = NIL; 285 | for (i = 0; i < N; i++) 286 | sp->parent[i] = NIL; 287 | } 288 | 289 | /* 290 | * Inserts string of length F, text_buf[r..r+F-1], into one of the trees 291 | * (text_buf[r]'th tree) and returns the longest-match position and length 292 | * via the global variables match_position and match_length. 293 | * If match_length = F, then removes the old node in favor of the new one, 294 | * because the old one will be deleted sooner. Note r plays double role, 295 | * as tree node and position in buffer. 296 | */ 297 | static void insert_node(struct encode_state *sp, int r) 298 | { 299 | int i, p, cmp; 300 | uint8_t *key; 301 | 302 | cmp = 1; 303 | key = &sp->text_buf[r]; 304 | p = N + 1 + key[0]; 305 | sp->rchild[r] = sp->lchild[r] = NIL; 306 | sp->match_length = 0; 307 | for ( ; ; ) { 308 | if (cmp >= 0) { 309 | if (sp->rchild[p] != NIL) 310 | p = sp->rchild[p]; 311 | else { 312 | sp->rchild[p] = r; 313 | sp->parent[r] = p; 314 | return; 315 | } 316 | } else { 317 | if (sp->lchild[p] != NIL) 318 | p = sp->lchild[p]; 319 | else { 320 | sp->lchild[p] = r; 321 | sp->parent[r] = p; 322 | return; 323 | } 324 | } 325 | for (i = 1; i < F; i++) { 326 | if ((cmp = key[i] - sp->text_buf[p + i]) != 0) 327 | break; 328 | } 329 | if (i > sp->match_length) { 330 | sp->match_position = p; 331 | if ((sp->match_length = i) >= F) 332 | break; 333 | } 334 | } 335 | sp->parent[r] = sp->parent[p]; 336 | sp->lchild[r] = sp->lchild[p]; 337 | sp->rchild[r] = sp->rchild[p]; 338 | sp->parent[sp->lchild[p]] = r; 339 | sp->parent[sp->rchild[p]] = r; 340 | if (sp->rchild[sp->parent[p]] == p) 341 | sp->rchild[sp->parent[p]] = r; 342 | else 343 | sp->lchild[sp->parent[p]] = r; 344 | sp->parent[p] = NIL; /* remove p */ 345 | } 346 | 347 | /* deletes node p from tree */ 348 | static void delete_node(struct encode_state *sp, int p) 349 | { 350 | int q; 351 | 352 | if (sp->parent[p] == NIL) 353 | return; /* not in tree */ 354 | if (sp->rchild[p] == NIL) 355 | q = sp->lchild[p]; 356 | else if (sp->lchild[p] == NIL) 357 | q = sp->rchild[p]; 358 | else { 359 | q = sp->lchild[p]; 360 | if (sp->rchild[q] != NIL) { 361 | do { 362 | q = sp->rchild[q]; 363 | } while (sp->rchild[q] != NIL); 364 | sp->rchild[sp->parent[q]] = sp->lchild[q]; 365 | sp->parent[sp->lchild[q]] = sp->parent[q]; 366 | sp->lchild[q] = sp->lchild[p]; 367 | sp->parent[sp->lchild[p]] = q; 368 | } 369 | sp->rchild[q] = sp->rchild[p]; 370 | sp->parent[sp->rchild[p]] = q; 371 | } 372 | sp->parent[q] = sp->parent[p]; 373 | if (sp->rchild[sp->parent[p]] == p) 374 | sp->rchild[sp->parent[p]] = q; 375 | else 376 | sp->lchild[sp->parent[p]] = q; 377 | sp->parent[p] = NIL; 378 | } 379 | 380 | static uint8_t *compress_lzss(uint8_t *dst, uint32_t dstlen, const uint8_t *src, uint32_t srcLen){ 381 | /* Encoding state, mostly tree but some current match stuff */ 382 | struct encode_state *sp; 383 | 384 | int i, c, len, r, s, last_match_length, code_buf_ptr; 385 | uint8_t code_buf[17], mask; 386 | const uint8_t *srcend = src + srcLen; 387 | uint8_t *dstend = dst + dstlen; 388 | 389 | /* initialize trees */ 390 | sp = (struct encode_state *) malloc(sizeof(*sp)); 391 | init_state(sp); 392 | 393 | /* 394 | * code_buf[1..16] saves eight units of code, and code_buf[0] works 395 | * as eight flags, "1" representing that the unit is an unencoded 396 | * letter (1 byte), "" a position-and-length pair (2 bytes). 397 | * Thus, eight units require at most 16 bytes of code. 398 | */ 399 | code_buf[0] = 0; 400 | code_buf_ptr = mask = 1; 401 | 402 | /* Clear the buffer with any character that will appear often. */ 403 | s = 0; r = N - F; 404 | 405 | /* Read F bytes into the last F bytes of the buffer */ 406 | for (len = 0; len < F && src < srcend; len++) 407 | sp->text_buf[r + len] = *src++; 408 | if (!len) { 409 | free(sp); 410 | return 0; /* text of size zero */ 411 | } 412 | /* 413 | * Insert the F strings, each of which begins with one or more 414 | * 'space' characters. Note the order in which these strings are 415 | * inserted. This way, degenerate trees will be less likely to occur. 416 | */ 417 | for (i = 1; i <= F; i++) 418 | insert_node(sp, r - i); 419 | 420 | /* 421 | * Finally, insert the whole string just read. 422 | * The global variables match_length and match_position are set. 423 | */ 424 | insert_node(sp, r); 425 | do { 426 | /* match_length may be spuriously long near the end of text. */ 427 | if (sp->match_length > len) 428 | sp->match_length = len; 429 | if (sp->match_length <= THRESHOLD) { 430 | sp->match_length = 1; /* Not long enough match. Send one byte. */ 431 | code_buf[0] |= mask; /* 'send one byte' flag */ 432 | code_buf[code_buf_ptr++] = sp->text_buf[r]; /* Send uncoded. */ 433 | } else { 434 | /* Send position and length pair. Note match_length > THRESHOLD. */ 435 | code_buf[code_buf_ptr++] = (uint8_t) sp->match_position; 436 | code_buf[code_buf_ptr++] = (uint8_t) 437 | ( ((sp->match_position >> 4) & 0xF0) 438 | | (sp->match_length - (THRESHOLD + 1)) ); 439 | } 440 | if ((mask <<= 1) == 0) { /* Shift mask left one bit. */ 441 | /* Send at most 8 units of code together */ 442 | for (i = 0; i < code_buf_ptr; i++) 443 | if (dst < dstend) 444 | *dst++ = code_buf[i]; 445 | else { 446 | free(sp); 447 | return 0; 448 | } 449 | code_buf[0] = 0; 450 | code_buf_ptr = mask = 1; 451 | } 452 | last_match_length = sp->match_length; 453 | for (i = 0; i < last_match_length && src < srcend; i++) { 454 | delete_node(sp, s); /* Delete old strings and */ 455 | c = *src++; 456 | sp->text_buf[s] = c; /* read new bytes */ 457 | 458 | /* 459 | * If the position is near the end of buffer, extend the buffer 460 | * to make string comparison easier. 461 | */ 462 | if (s < F - 1) 463 | sp->text_buf[s + N] = c; 464 | 465 | /* Since this is a ring buffer, increment the position modulo N. */ 466 | s = (s + 1) & (N - 1); 467 | r = (r + 1) & (N - 1); 468 | 469 | /* Register the string in text_buf[r..r+F-1] */ 470 | insert_node(sp, r); 471 | } 472 | while (i++ < last_match_length) { 473 | delete_node(sp, s); 474 | 475 | /* After the end of text, no need to read, */ 476 | s = (s + 1) & (N - 1); 477 | r = (r + 1) & (N - 1); 478 | /* but buffer may not be empty. */ 479 | if (--len) 480 | insert_node(sp, r); 481 | } 482 | } while (len > 0); /* until length of string to be processed is zero */ 483 | 484 | if (code_buf_ptr > 1) { /* Send remaining code. */ 485 | for (i = 0; i < code_buf_ptr; i++) 486 | if (dst < dstend) 487 | *dst++ = code_buf[i]; 488 | else { 489 | free(sp); 490 | return 0; 491 | } 492 | } 493 | 494 | free(sp); 495 | return dst; 496 | } 497 | -------------------------------------------------------------------------------- /img4tool.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2AAE83AD2906DC1500AA1083 /* libcrypto.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AAE83AC2906DC1500AA1083 /* libcrypto.3.dylib */; }; 11 | 2AAE83AE2906DC1500AA1083 /* libcrypto.3.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 2AAE83AC2906DC1500AA1083 /* libcrypto.3.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 12 | 8746A34F2C48DEDD00AB2921 /* libplist-2.0.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8746A34E2C48DEDD00AB2921 /* libplist-2.0.4.dylib */; }; 13 | 8746A3502C48DEDD00AB2921 /* libplist-2.0.4.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 8746A34E2C48DEDD00AB2921 /* libplist-2.0.4.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 14 | 8754A6B1236F16B600C0D285 /* libgeneral.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8754A6B0236F16B600C0D285 /* libgeneral.0.dylib */; }; 15 | 8754A6B2236F16B600C0D285 /* libgeneral.0.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 8754A6B0236F16B600C0D285 /* libgeneral.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 16 | 876D341425DE5E330059C1F5 /* libcompression.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 875585D6238967C500B90FC9 /* libcompression.tbd */; }; 17 | 879896AD2344A91900A98709 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 879896AC2344A91900A98709 /* main.cpp */; }; 18 | 879B59F92DDDAA1D00273349 /* libfwkeyfetch.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 879B59F82DDDAA1D00273349 /* libfwkeyfetch.0.dylib */; }; 19 | 879B59FA2DDDAA1D00273349 /* libfwkeyfetch.0.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 879B59F82DDDAA1D00273349 /* libfwkeyfetch.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 20 | 87A281342347B7B900ED08BB /* ASN1DERElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 87A281322347B7B900ED08BB /* ASN1DERElement.cpp */; }; 21 | 87A281372347B7CA00ED08BB /* img4tool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 87A281352347B7CA00ED08BB /* img4tool.cpp */; }; 22 | 87EB3A6923561EE2008F1B06 /* lzssdec.c in Sources */ = {isa = PBXBuildFile; fileRef = 87EB3A6823561EE2008F1B06 /* lzssdec.c */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 8754A6B3236F16B600C0D285 /* Embed Libraries */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 8754A6B2236F16B600C0D285 /* libgeneral.0.dylib in Embed Libraries */, 33 | 2AAE83AE2906DC1500AA1083 /* libcrypto.3.dylib in Embed Libraries */, 34 | 879B59FA2DDDAA1D00273349 /* libfwkeyfetch.0.dylib in Embed Libraries */, 35 | 8746A3502C48DEDD00AB2921 /* libplist-2.0.4.dylib in Embed Libraries */, 36 | ); 37 | name = "Embed Libraries"; 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | 879896A72344A91900A98709 /* CopyFiles */ = { 41 | isa = PBXCopyFilesBuildPhase; 42 | buildActionMask = 2147483647; 43 | dstPath = /usr/share/man/man1/; 44 | dstSubfolderSpec = 0; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 1; 48 | }; 49 | /* End PBXCopyFilesBuildPhase section */ 50 | 51 | /* Begin PBXFileReference section */ 52 | 2AAE83A92906DB0600AA1083 /* libcrypto.1.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.1.dylib; path = "../../../../opt/homebrew/Cellar/openssl@1.1/1.1.1q/lib/libcrypto.1.1.dylib"; sourceTree = ""; }; 53 | 2AAE83AC2906DC1500AA1083 /* libcrypto.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.3.dylib; path = ../../../../usr/local/lib/libcrypto.3.dylib; sourceTree = ""; }; 54 | 4281CC81234A01C500C30E6D /* libplist.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libplist.3.dylib; path = ../../../../usr/local/lib/libplist.3.dylib; sourceTree = ""; }; 55 | 42F86F722351EA61009649E4 /* libssl.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.1.0.0.dylib; path = ../../../../usr/local/Cellar/openssl/1.0.2s/lib/libssl.1.0.0.dylib; sourceTree = ""; }; 56 | 42F86F742351EAA2009649E4 /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.0.0.dylib; path = ../../../../usr/local/Cellar/openssl/1.0.2s/lib/libcrypto.1.0.0.dylib; sourceTree = ""; }; 57 | 8706A315275E624300FB4706 /* libcrypto.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.3.dylib; path = "../../../../opt/homebrew/Cellar/openssl@3/3.0.0_1/lib/libcrypto.3.dylib"; sourceTree = ""; }; 58 | 873E047727A7FDF2004BA5CE /* libssl.1.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.1.1.dylib; path = "../../../../opt/homebrew/Cellar/openssl@1.1/1.1.1m/lib/libssl.1.1.dylib"; sourceTree = ""; }; 59 | 873E047A27A7FE14004BA5CE /* libcrypto.1.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.1.dylib; path = "../../../../opt/homebrew/Cellar/openssl@1.1/1.1.1m/lib/libcrypto.1.1.dylib"; sourceTree = ""; }; 60 | 8746A34E2C48DEDD00AB2921 /* libplist-2.0.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libplist-2.0.4.dylib"; path = "../../../../usr/local/lib/libplist-2.0.4.dylib"; sourceTree = ""; }; 61 | 8754A6B0236F16B600C0D285 /* libgeneral.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libgeneral.0.dylib; path = ../../../../usr/local/lib/libgeneral.0.dylib; sourceTree = ""; }; 62 | 875585D6238967C500B90FC9 /* libcompression.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcompression.tbd; path = usr/lib/libcompression.tbd; sourceTree = SDKROOT; }; 63 | 8762F342236DDFD400F42FDB /* img4tool.hpp.in */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; name = img4tool.hpp.in; path = include/img4tool/img4tool.hpp.in; sourceTree = SOURCE_ROOT; }; 64 | 8776E51125C46299002971E6 /* libplist-2.0.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libplist-2.0.3.dylib"; path = "../../../../usr/local/lib/libplist-2.0.3.dylib"; sourceTree = ""; }; 65 | 8776E51425C462B0002971E6 /* libcrypto.1.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.1.dylib; path = "../../../../usr/local/Cellar/openssl@1.1/1.1.1i/lib/libcrypto.1.1.dylib"; sourceTree = ""; }; 66 | 8791272924D0ABA30071283F /* libcrypto.1.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.1.dylib; path = "../../../../usr/local/Cellar/openssl@1.1/1.1.1g/lib/libcrypto.1.1.dylib"; sourceTree = ""; }; 67 | 879896A92344A91900A98709 /* img4tool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = img4tool; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 879896AC2344A91900A98709 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 69 | 879B59F82DDDAA1D00273349 /* libfwkeyfetch.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libfwkeyfetch.0.dylib; path = ../../../../usr/local/lib/libfwkeyfetch.0.dylib; sourceTree = ""; }; 70 | 87A281322347B7B900ED08BB /* ASN1DERElement.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ASN1DERElement.cpp; sourceTree = ""; }; 71 | 87A281332347B7B900ED08BB /* ASN1DERElement.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ASN1DERElement.hpp; path = ../include/img4tool/ASN1DERElement.hpp; sourceTree = ""; }; 72 | 87A281352347B7CA00ED08BB /* img4tool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = img4tool.cpp; sourceTree = ""; }; 73 | 87A281362347B7CA00ED08BB /* img4tool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = img4tool.hpp; path = ../include/img4tool/img4tool.hpp; sourceTree = ""; }; 74 | 87D69FE1265BE79600AB8610 /* libcrypto.1.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.1.dylib; path = "../../../../opt/homebrew/Cellar/openssl@1.1/1.1.1k/lib/libcrypto.1.1.dylib"; sourceTree = ""; }; 75 | 87E6A5FA24A75AA10081D457 /* libssl.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.1.0.0.dylib; path = ../../../../usr/local/Cellar/openssl/1.0.2t/lib/libssl.1.0.0.dylib; sourceTree = ""; }; 76 | 87E6A5FD24A75AC70081D457 /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.0.0.dylib; path = ../../../../usr/local/Cellar/openssl/1.0.2t/lib/libcrypto.1.0.0.dylib; sourceTree = ""; }; 77 | 87EB3A6723561EE2008F1B06 /* lzssdec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lzssdec.h; sourceTree = ""; }; 78 | 87EB3A6823561EE2008F1B06 /* lzssdec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = lzssdec.c; sourceTree = ""; }; 79 | /* End PBXFileReference section */ 80 | 81 | /* Begin PBXFrameworksBuildPhase section */ 82 | 879896A62344A91900A98709 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 8746A34F2C48DEDD00AB2921 /* libplist-2.0.4.dylib in Frameworks */, 87 | 2AAE83AD2906DC1500AA1083 /* libcrypto.3.dylib in Frameworks */, 88 | 8754A6B1236F16B600C0D285 /* libgeneral.0.dylib in Frameworks */, 89 | 876D341425DE5E330059C1F5 /* libcompression.tbd in Frameworks */, 90 | 879B59F92DDDAA1D00273349 /* libfwkeyfetch.0.dylib in Frameworks */, 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 4281CC80234A01C500C30E6D /* Frameworks */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 879B59F82DDDAA1D00273349 /* libfwkeyfetch.0.dylib */, 101 | 8746A34E2C48DEDD00AB2921 /* libplist-2.0.4.dylib */, 102 | 2AAE83AC2906DC1500AA1083 /* libcrypto.3.dylib */, 103 | 873E047A27A7FE14004BA5CE /* libcrypto.1.1.dylib */, 104 | 2AAE83A92906DB0600AA1083 /* libcrypto.1.1.dylib */, 105 | 873E047727A7FDF2004BA5CE /* libssl.1.1.dylib */, 106 | 8706A315275E624300FB4706 /* libcrypto.3.dylib */, 107 | 8776E51425C462B0002971E6 /* libcrypto.1.1.dylib */, 108 | 87D69FE1265BE79600AB8610 /* libcrypto.1.1.dylib */, 109 | 8776E51125C46299002971E6 /* libplist-2.0.3.dylib */, 110 | 8791272924D0ABA30071283F /* libcrypto.1.1.dylib */, 111 | 87E6A5FD24A75AC70081D457 /* libcrypto.1.0.0.dylib */, 112 | 87E6A5FA24A75AA10081D457 /* libssl.1.0.0.dylib */, 113 | 875585D6238967C500B90FC9 /* libcompression.tbd */, 114 | 8754A6B0236F16B600C0D285 /* libgeneral.0.dylib */, 115 | 42F86F742351EAA2009649E4 /* libcrypto.1.0.0.dylib */, 116 | 42F86F722351EA61009649E4 /* libssl.1.0.0.dylib */, 117 | 4281CC81234A01C500C30E6D /* libplist.3.dylib */, 118 | ); 119 | name = Frameworks; 120 | sourceTree = ""; 121 | }; 122 | 879896A02344A91900A98709 = { 123 | isa = PBXGroup; 124 | children = ( 125 | 879896AB2344A91900A98709 /* img4tool */, 126 | 879896AA2344A91900A98709 /* Products */, 127 | 4281CC80234A01C500C30E6D /* Frameworks */, 128 | ); 129 | sourceTree = ""; 130 | }; 131 | 879896AA2344A91900A98709 /* Products */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 879896A92344A91900A98709 /* img4tool */, 135 | ); 136 | name = Products; 137 | sourceTree = ""; 138 | }; 139 | 879896AB2344A91900A98709 /* img4tool */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 87A281332347B7B900ED08BB /* ASN1DERElement.hpp */, 143 | 87A281322347B7B900ED08BB /* ASN1DERElement.cpp */, 144 | 87EB3A6723561EE2008F1B06 /* lzssdec.h */, 145 | 87EB3A6823561EE2008F1B06 /* lzssdec.c */, 146 | 8762F342236DDFD400F42FDB /* img4tool.hpp.in */, 147 | 87A281362347B7CA00ED08BB /* img4tool.hpp */, 148 | 87A281352347B7CA00ED08BB /* img4tool.cpp */, 149 | 879896AC2344A91900A98709 /* main.cpp */, 150 | ); 151 | path = img4tool; 152 | sourceTree = ""; 153 | }; 154 | /* End PBXGroup section */ 155 | 156 | /* Begin PBXNativeTarget section */ 157 | 879896A82344A91900A98709 /* img4tool */ = { 158 | isa = PBXNativeTarget; 159 | buildConfigurationList = 879896B02344A91900A98709 /* Build configuration list for PBXNativeTarget "img4tool" */; 160 | buildPhases = ( 161 | 879896A52344A91900A98709 /* Sources */, 162 | 879896A62344A91900A98709 /* Frameworks */, 163 | 879896A72344A91900A98709 /* CopyFiles */, 164 | 8754A6B3236F16B600C0D285 /* Embed Libraries */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | ); 170 | name = img4tool; 171 | productName = img4tool; 172 | productReference = 879896A92344A91900A98709 /* img4tool */; 173 | productType = "com.apple.product-type.tool"; 174 | }; 175 | /* End PBXNativeTarget section */ 176 | 177 | /* Begin PBXProject section */ 178 | 879896A12344A91900A98709 /* Project object */ = { 179 | isa = PBXProject; 180 | attributes = { 181 | LastUpgradeCheck = 1010; 182 | ORGANIZATIONNAME = tihmstar; 183 | TargetAttributes = { 184 | 879896A82344A91900A98709 = { 185 | CreatedOnToolsVersion = 10.1; 186 | }; 187 | }; 188 | }; 189 | buildConfigurationList = 879896A42344A91900A98709 /* Build configuration list for PBXProject "img4tool" */; 190 | compatibilityVersion = "Xcode 9.3"; 191 | developmentRegion = en; 192 | hasScannedForEncodings = 0; 193 | knownRegions = ( 194 | en, 195 | ); 196 | mainGroup = 879896A02344A91900A98709; 197 | productRefGroup = 879896AA2344A91900A98709 /* Products */; 198 | projectDirPath = ""; 199 | projectRoot = ""; 200 | targets = ( 201 | 879896A82344A91900A98709 /* img4tool */, 202 | ); 203 | }; 204 | /* End PBXProject section */ 205 | 206 | /* Begin PBXSourcesBuildPhase section */ 207 | 879896A52344A91900A98709 /* Sources */ = { 208 | isa = PBXSourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 879896AD2344A91900A98709 /* main.cpp in Sources */, 212 | 87A281342347B7B900ED08BB /* ASN1DERElement.cpp in Sources */, 213 | 87A281372347B7CA00ED08BB /* img4tool.cpp in Sources */, 214 | 87EB3A6923561EE2008F1B06 /* lzssdec.c in Sources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXSourcesBuildPhase section */ 219 | 220 | /* Begin XCBuildConfiguration section */ 221 | 879896AE2344A91900A98709 /* Debug */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | CLANG_ANALYZER_NONNULL = YES; 226 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 227 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 228 | CLANG_CXX_LIBRARY = "libc++"; 229 | CLANG_ENABLE_MODULES = YES; 230 | CLANG_ENABLE_OBJC_ARC = YES; 231 | CLANG_ENABLE_OBJC_WEAK = YES; 232 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 233 | CLANG_WARN_BOOL_CONVERSION = YES; 234 | CLANG_WARN_COMMA = YES; 235 | CLANG_WARN_CONSTANT_CONVERSION = YES; 236 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 237 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 238 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 239 | CLANG_WARN_EMPTY_BODY = YES; 240 | CLANG_WARN_ENUM_CONVERSION = YES; 241 | CLANG_WARN_INFINITE_RECURSION = YES; 242 | CLANG_WARN_INT_CONVERSION = YES; 243 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 245 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 246 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 247 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 248 | CLANG_WARN_STRICT_PROTOTYPES = YES; 249 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 250 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 251 | CLANG_WARN_UNREACHABLE_CODE = YES; 252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 253 | CODE_SIGN_IDENTITY = "-"; 254 | COPY_PHASE_STRIP = NO; 255 | DEBUG_INFORMATION_FORMAT = dwarf; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | ENABLE_TESTABILITY = YES; 258 | GCC_C_LANGUAGE_STANDARD = gnu11; 259 | GCC_DYNAMIC_NO_PIC = NO; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_OPTIMIZATION_LEVEL = 0; 262 | GCC_PREPROCESSOR_DEFINITIONS = ( 263 | "DEBUG=1", 264 | "$(inherited)", 265 | ); 266 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 267 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 268 | GCC_WARN_UNDECLARED_SELECTOR = YES; 269 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 270 | GCC_WARN_UNUSED_FUNCTION = YES; 271 | GCC_WARN_UNUSED_VARIABLE = YES; 272 | MACOSX_DEPLOYMENT_TARGET = 10.13; 273 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 274 | MTL_FAST_MATH = YES; 275 | ONLY_ACTIVE_ARCH = YES; 276 | SDKROOT = macosx; 277 | }; 278 | name = Debug; 279 | }; 280 | 879896AF2344A91900A98709 /* Release */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | ALWAYS_SEARCH_USER_PATHS = NO; 284 | CLANG_ANALYZER_NONNULL = YES; 285 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 287 | CLANG_CXX_LIBRARY = "libc++"; 288 | CLANG_ENABLE_MODULES = YES; 289 | CLANG_ENABLE_OBJC_ARC = YES; 290 | CLANG_ENABLE_OBJC_WEAK = YES; 291 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 292 | CLANG_WARN_BOOL_CONVERSION = YES; 293 | CLANG_WARN_COMMA = YES; 294 | CLANG_WARN_CONSTANT_CONVERSION = YES; 295 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 296 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 297 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 298 | CLANG_WARN_EMPTY_BODY = YES; 299 | CLANG_WARN_ENUM_CONVERSION = YES; 300 | CLANG_WARN_INFINITE_RECURSION = YES; 301 | CLANG_WARN_INT_CONVERSION = YES; 302 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 303 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 304 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 307 | CLANG_WARN_STRICT_PROTOTYPES = YES; 308 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 309 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 310 | CLANG_WARN_UNREACHABLE_CODE = YES; 311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 312 | CODE_SIGN_IDENTITY = "-"; 313 | COPY_PHASE_STRIP = NO; 314 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 315 | ENABLE_NS_ASSERTIONS = NO; 316 | ENABLE_STRICT_OBJC_MSGSEND = YES; 317 | GCC_C_LANGUAGE_STANDARD = gnu11; 318 | GCC_NO_COMMON_BLOCKS = YES; 319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 320 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 321 | GCC_WARN_UNDECLARED_SELECTOR = YES; 322 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 323 | GCC_WARN_UNUSED_FUNCTION = YES; 324 | GCC_WARN_UNUSED_VARIABLE = YES; 325 | MACOSX_DEPLOYMENT_TARGET = 10.13; 326 | MTL_ENABLE_DEBUG_INFO = NO; 327 | MTL_FAST_MATH = YES; 328 | SDKROOT = macosx; 329 | }; 330 | name = Release; 331 | }; 332 | 879896B12344A91900A98709 /* Debug */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | CODE_SIGN_STYLE = Automatic; 336 | GCC_PREPROCESSOR_DEFINITIONS = ( 337 | "DEBUG=1", 338 | "HAVE_PLIST=1", 339 | "HAVE_CRYPTO=1", 340 | "HAVE_OPENSSL=1", 341 | "HAVE_LIBCOMPRESSION=1", 342 | XCODE, 343 | "HAVE_LIBFWKEYFETCH=1", 344 | ); 345 | HEADER_SEARCH_PATHS = ( 346 | "$(SRCROOT)/include", 347 | "$(SRCROOT)/include/img4tool", 348 | /usr/local/include, 349 | ); 350 | LIBRARY_SEARCH_PATHS = /usr/local/lib; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | }; 353 | name = Debug; 354 | }; 355 | 879896B22344A91900A98709 /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | CODE_SIGN_STYLE = Automatic; 359 | GCC_PREPROCESSOR_DEFINITIONS = ""; 360 | HEADER_SEARCH_PATHS = ( 361 | "$(SRCROOT)/include", 362 | "$(SRCROOT)/include/img4tool", 363 | /usr/local/include, 364 | ); 365 | LIBRARY_SEARCH_PATHS = /usr/local/lib; 366 | PRODUCT_NAME = "$(TARGET_NAME)"; 367 | }; 368 | name = Release; 369 | }; 370 | /* End XCBuildConfiguration section */ 371 | 372 | /* Begin XCConfigurationList section */ 373 | 879896A42344A91900A98709 /* Build configuration list for PBXProject "img4tool" */ = { 374 | isa = XCConfigurationList; 375 | buildConfigurations = ( 376 | 879896AE2344A91900A98709 /* Debug */, 377 | 879896AF2344A91900A98709 /* Release */, 378 | ); 379 | defaultConfigurationIsVisible = 0; 380 | defaultConfigurationName = Release; 381 | }; 382 | 879896B02344A91900A98709 /* Build configuration list for PBXNativeTarget "img4tool" */ = { 383 | isa = XCConfigurationList; 384 | buildConfigurations = ( 385 | 879896B12344A91900A98709 /* Debug */, 386 | 879896B22344A91900A98709 /* Release */, 387 | ); 388 | defaultConfigurationIsVisible = 0; 389 | defaultConfigurationName = Release; 390 | }; 391 | /* End XCConfigurationList section */ 392 | }; 393 | rootObject = 879896A12344A91900A98709 /* Project object */; 394 | } 395 | -------------------------------------------------------------------------------- /img4tool/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // img4tool 4 | // 5 | // Created by tihmstar on 02.10.19. 6 | // Copyright © 2019 tihmstar. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include "../include/img4tool/img4tool.hpp" 24 | 25 | #ifdef HAVE_PLIST 26 | #include 27 | #endif //HAVE_PLIST 28 | 29 | #ifdef HAVE_LIBFWKEYFETCH 30 | #include 31 | #endif //HAVE_LIBFWKEYFETCH 32 | 33 | using namespace tihmstar::img4tool; 34 | using namespace std; 35 | 36 | #define FLAG_ALL (1 << 0) 37 | #define FLAG_IM4PONLY (1 << 1) 38 | #define FLAG_EXTRACT (1 << 2) 39 | #define FLAG_CREATE (1 << 3) 40 | #define FLAG_RENAME (1 << 4) 41 | #define FLAG_CONVERT (1 << 5) 42 | #define FLAG_VERIFY (1 << 6) 43 | 44 | static struct option longopts[] = { 45 | { "help", no_argument, NULL, 'h' }, 46 | { "print-all", no_argument, NULL, 'a' }, 47 | { "create", required_argument, NULL, 'c' }, 48 | { "desc", required_argument, NULL, 'd' }, 49 | { "extract", no_argument, NULL, 'e' }, 50 | { "generator", required_argument, NULL, 'g' }, 51 | { "im4p-only", no_argument, NULL, 'i' }, 52 | { "im4m", required_argument, NULL, 'm' }, 53 | { "rename-payload", required_argument, NULL, 'n' }, 54 | { "outfile", required_argument, NULL, 'o' }, 55 | { "im4p", required_argument, NULL, 'p' }, 56 | { "type", required_argument, NULL, 't' }, 57 | 58 | { "iv", required_argument, NULL, 0 }, 59 | { "key", required_argument, NULL, 0 }, 60 | { "compression", required_argument, NULL, 0 }, 61 | 62 | #ifdef HAVE_LIBFWKEYFETCH 63 | { "fetch", no_argument, NULL, 'f' }, 64 | #endif //HAVE_LIBFWKEYFETCH 65 | #ifdef HAVE_PLIST 66 | { "shsh", required_argument, NULL, 's' }, 67 | { "verify", optional_argument, NULL, 'v' }, 68 | { "convert", no_argument, NULL, 0 }, 69 | #endif //HAVE_PLIST 70 | { NULL, 0, NULL, 0 } 71 | }; 72 | 73 | char *readFromFile(const char *filePath, size_t *outSize){ 74 | FILE *f = fopen(filePath, "r"); 75 | if (!f) return NULL; 76 | fseek(f, 0, SEEK_END); 77 | size_t size = ftell(f); 78 | fseek(f, 0, SEEK_SET); 79 | char *ret = (char*)malloc(size); 80 | if (ret) fread(ret, size, 1, f); 81 | fclose(f); 82 | if (outSize) *outSize = size; 83 | 84 | return ret; 85 | } 86 | 87 | #ifdef HAVE_PLIST 88 | plist_t readPlistFromFile(const char *filePath){ 89 | auto f = tihmstar::readFile(filePath); 90 | plist_t plist = NULL; 91 | plist_from_memory((const char*)f.data(), (uint32_t)f.size(), &plist, NULL); 92 | return plist; 93 | } 94 | 95 | tihmstar::Mem im4mFormShshFile(const char *shshfile, char **generator){ 96 | plist_t shshplist = NULL; 97 | cleanup([&]{ 98 | safeFreeCustom(shshplist, plist_free); 99 | }); 100 | tihmstar::Mem ret; 101 | 102 | shshplist = readPlistFromFile(shshfile); 103 | 104 | if (plist_t ticket = plist_dict_get_item(shshplist, "ApImg4Ticket")){ 105 | char *im4m = 0; 106 | uint64_t im4msize=0; 107 | plist_get_data_val(ticket, &im4m, &im4msize); 108 | ret.append(im4m, im4msize); 109 | } 110 | 111 | if (generator){ 112 | if (plist_t ticket = plist_dict_get_item(shshplist, "generator")) 113 | plist_get_string_val(ticket, generator); 114 | } 115 | return ret; 116 | } 117 | #endif //HAVE_PLIST 118 | 119 | 120 | void saveToFile(const char *filePath, const void *buf, size_t bufSize){ 121 | FILE *f = NULL; 122 | cleanup([&]{ 123 | if (f) { 124 | fclose(f); 125 | } 126 | }); 127 | 128 | if (strcmp(filePath, "-") == 0) { 129 | write(STDERR_FILENO, buf, bufSize); 130 | }else{ 131 | retassure(f = fopen(filePath, "wb"), "failed to create file"); 132 | retassure(fwrite(buf, 1, bufSize, f) == bufSize, "failed to write to file"); 133 | } 134 | } 135 | 136 | void cmd_help(){ 137 | printf( 138 | "Usage: img4tool [OPTIONS] FILE\n" 139 | "Parses img4, im4p, im4m files\n\n" 140 | " -h, --help\t\t\tprints usage information\n" 141 | " -a, --print-all\t\tprint everything from im4m\n" 142 | " -e, --extract\t\t\textracts im4m/im4p payload\n" 143 | " -i, --im4p-only\t\tprint only im4p\n" 144 | " -m, --im4m\t\t\tFilepath for im4m (depending on -e being set)\n" 145 | " -p, --im4p\t\t\tFilepath for im4p (depending on -e being set)\n" 146 | " -c, --create\t\t\tcreates an img4 with the specified im4m, im4p or creates im4p with raw file (last argument)\n" 147 | " -o, --outfile\t\t\toutput path for extracting im4p payload (-e) or renaming im4p (-n)\n" 148 | " -t, --type\t\t\tset type for creating IM4P files from raw\n" 149 | " -d, --desc\t\t\tset desc for creating IM4P files from raw\n" 150 | " -n, --rename-payload\t\trename im4p payload (NAME must be exactly 4 bytes)\n" 151 | " -g, --generator\t\tAdd generator to img4 (eg. 0x726174736d686974)\n" 152 | " --iv\t\t\tIV for decrypting payload when extracting (requires -e and -o)\n" 153 | " --key\t\t\tKey for decrypting payload when extracting (requires -e and -o)\n" 154 | " --compression\t\tset compression type when creating im4p from raw file\n" 155 | #ifdef HAVE_LIBFWKEYFETCH 156 | "[libfwkeyfetch]\n" 157 | #else 158 | "[libfwkeyfetch] (UNAVAILABLE)\n" 159 | #endif //HAVE_LIBFWKEYFETCH 160 | " -f, --fetch\t\t\tTry to get IV/KEY based on KBAG from fwkeydb\n" 161 | 162 | #ifdef HAVE_PLIST 163 | "[plist]\n" 164 | #else 165 | "[plist] (UNAVAILABLE)\n" 166 | #endif //HAVE_PLIST 167 | " -s, --shsh\t\t\tFilepath for shsh (for reading/writing im4m)\n" 168 | " -v, --verify\t\tverify img4, im4m\n" 169 | " --convert\t\t\tconvert IM4M file to .shsh (use with -s)\n" 170 | "\n" 171 | 172 | "Features:\n" 173 | #ifdef HAVE_LIBFWKEYFETCH 174 | "libfwkeyfetch: yes\n" 175 | #else 176 | "libfwkeyfetch: no\n" 177 | #endif //HAVE_LIBFWKEYFETCH 178 | 179 | #ifdef HAVE_PLIST 180 | "plist: yes\n" 181 | #else 182 | "plist: no\n" 183 | #endif //HAVE_PLIST 184 | 185 | #ifdef HAVE_OPENSSL 186 | "openssl: yes\n" 187 | #else 188 | "openssl: no\n" 189 | #endif //HAVE_OPENSSL 190 | 191 | #ifdef HAVE_LIBCOMPRESSION 192 | "bvx2: yes\n" 193 | #else 194 | "bvx2: no\n" 195 | #endif //HAVE_LIBCOMPRESSION 196 | "\n" 197 | ); 198 | } 199 | 200 | MAINFUNCTION 201 | int main_r(int argc, const char * argv[]) { 202 | printf("%s\n",version()); 203 | printf("Compiled with plist: %s\n", 204 | #ifdef HAVE_PLIST 205 | "YES" 206 | #else 207 | "NO" 208 | #endif 209 | ); 210 | 211 | 212 | const char *lastArg = NULL; 213 | const char *shshFile = NULL; 214 | const char *im4mFile = NULL; 215 | const char *im4pFile = NULL; 216 | const char *outFile = NULL; 217 | const char *decryptIv = NULL; 218 | const char *decryptKey = NULL; 219 | const char *im4pType = NULL; 220 | const char *im4pDesc = "Image created by img4tool"; 221 | const char *compressionType = NULL; 222 | #ifdef HAVE_PLIST 223 | const char *buildmanifestFile = NULL; 224 | #endif //HAVE_PLIST 225 | 226 | int optindex = 0; 227 | int opt = 0; 228 | long flags = 0; 229 | 230 | tihmstar::Mem workingBuf; 231 | char *generator = NULL; 232 | uint64_t bnch = 0; 233 | bool fetchKeys = false; 234 | 235 | cleanup([&]{ 236 | safeFree(generator); 237 | }); 238 | 239 | while ((opt = getopt_long(argc, (char* const *)argv, "hac:d:efg:im:n:o:p:s:t:v::", longopts, &optindex)) >= 0) { 240 | switch (opt) { 241 | case 0: //long opts 242 | { 243 | std::string curopt = longopts[optindex].name; 244 | 245 | #ifdef HAVE_PLIST 246 | if (curopt == "convert") { 247 | flags |= FLAG_CONVERT; 248 | }else 249 | #endif //HAVE_PLIST 250 | if (curopt == "compression") { 251 | compressionType = optarg; 252 | }else if (curopt == "iv") { 253 | decryptIv = optarg; 254 | }else if (curopt == "key") { 255 | decryptKey = optarg; 256 | } 257 | break; 258 | } 259 | 260 | case 'h': 261 | cmd_help(); 262 | return 0; 263 | case 'a': 264 | flags |= FLAG_ALL; 265 | break; 266 | case 'c': 267 | flags |= FLAG_CREATE; 268 | retassure(!(flags & FLAG_EXTRACT), "Invalid command line arguments. can't extract and create at the same time"); 269 | retassure(!outFile, "Invalid command line arguments. outFile already set!"); 270 | outFile = optarg; 271 | break; 272 | case 'd': 273 | im4pDesc = optarg; 274 | break; 275 | case 'e': 276 | retassure(!(flags & FLAG_CREATE), "Invalid command line arguments. can't extract and create at the same time"); 277 | flags |= FLAG_EXTRACT; 278 | break; 279 | case 'f': 280 | fetchKeys = true; 281 | break; 282 | case 'g': //generator 283 | bnch = strtoll(optarg, NULL, 16); 284 | retassure(bnch, "Failed to set generator!"); 285 | break; 286 | case 'i': 287 | flags |= FLAG_IM4PONLY; 288 | break; 289 | case 'm': 290 | im4mFile = optarg; 291 | break; 292 | case 'n': //rename-payload 293 | retassure(!im4pType, "Invalid command line arguments. im4pType already set!"); 294 | im4pType = optarg; 295 | flags |= FLAG_RENAME; 296 | break; 297 | case 'o': 298 | retassure(!outFile, "Invalid command line arguments. outFile already set!"); 299 | outFile = optarg; 300 | break; 301 | case 'p': 302 | im4pFile = optarg; 303 | break; 304 | #ifdef HAVE_PLIST 305 | case 's': 306 | shshFile = optarg; 307 | break; 308 | #endif //HAVE_PLIST 309 | case 't': 310 | retassure(!(flags & FLAG_RENAME), "Invalid command line arguments. can't rename and create at the same time"); 311 | retassure(!im4pType, "Invalid command line arguments. im4pType already set!"); 312 | im4pType = optarg; 313 | break; 314 | #ifdef HAVE_PLIST 315 | case 'v': 316 | flags |= FLAG_VERIFY; 317 | buildmanifestFile = optarg; 318 | break; 319 | #endif //HAVE_PLIST 320 | default: 321 | cmd_help(); 322 | return -1; 323 | } 324 | } 325 | #ifdef HAVE_LIBFWKEYFETCH 326 | tihmstar::libfwkeyfetch::fw_key fwKey = {}; 327 | #endif //HAVE_LIBFWKEYFETCH 328 | 329 | if (outFile && strcmp(outFile, "-") == 0) { 330 | int s_out = -1; 331 | int s_err = -1; 332 | cleanup([&]{ 333 | safeClose(s_out); 334 | safeClose(s_err); 335 | }); 336 | s_out = dup(STDOUT_FILENO); 337 | s_err = dup(STDERR_FILENO); 338 | dup2(s_out, STDERR_FILENO); 339 | dup2(s_err, STDOUT_FILENO); 340 | } 341 | 342 | if (argc-optind == 1) { 343 | argc -= optind; 344 | argv += optind; 345 | lastArg = argv[0]; 346 | }else{ 347 | if (!shshFile && !(flags & FLAG_CREATE)) { 348 | cmd_help(); 349 | return -2; 350 | } 351 | } 352 | 353 | 354 | if (!(flags & FLAG_CREATE && im4pFile) ) { //don't load shsh if we create a new img4 file 355 | if (lastArg) { 356 | if (strcmp(lastArg, "-") == 0){ 357 | char cbuf[0x1000] = {}; 358 | ssize_t didRead = 0; 359 | 360 | while ((didRead = read(STDIN_FILENO, cbuf, sizeof(cbuf))) > 0) { 361 | workingBuf.append(cbuf, didRead); 362 | } 363 | 364 | }else{ 365 | workingBuf = tihmstar::readFile(lastArg); 366 | } 367 | } 368 | #ifdef HAVE_PLIST 369 | else if (shshFile){ 370 | try { 371 | workingBuf = im4mFormShshFile(shshFile, &generator); 372 | } catch (...) { 373 | reterror("Failed to read shshFile"); 374 | } 375 | } 376 | #endif //HAVE_PLIST 377 | } 378 | 379 | if (workingBuf.size()) { 380 | if (flags & FLAG_EXTRACT) { 381 | //extract 382 | bool didExtract = false; 383 | ASN1DERElement file(workingBuf.data(), workingBuf.size()); 384 | 385 | if (outFile) { 386 | const char *compression = NULL; 387 | ASN1DERElement payload; 388 | //check for payload extraction 389 | if (isIMG4(file)) { 390 | file = getIM4PFromIMG4(file); 391 | } else if (!isIM4P(file)){ 392 | reterror("File not recognised"); 393 | } 394 | 395 | if (fetchKeys && (!decryptIv || !strlen(decryptIv)) && (!decryptKey || !strlen(decryptKey))) { 396 | #ifndef HAVE_LIBFWKEYFETCH 397 | reterror("Compiled without libfwkeyfetch"); 398 | #else 399 | for (int i=1; i>0; i++) { 400 | std::string kbagstr; 401 | try { 402 | tihmstar::Mem kbag = getKBAG(file, i); 403 | for (int z=0; z=3); 522 | snprintf(&generatorStr[strlen(generatorStr)], 3, "%02x",(unsigned char)c); 523 | } 524 | assure(generator = plist_new_string(generatorStr)); 525 | printf("ok\n"); 526 | } catch (...) { 527 | printf("failed!\n"); 528 | } 529 | 530 | im4m = getIM4MFromIMG4(im4m); 531 | } 532 | 533 | retassure(isIM4M(im4m), "Not IM4M file"); 534 | 535 | retassure(newshsh = plist_new_dict(),"failed to create new plist dict"); 536 | retassure(data = plist_new_data((const char*)im4m.buf(), im4m.size()),"failed to create plist data from im4m buf"); 537 | 538 | plist_dict_set_item(newshsh, "ApImg4Ticket", data); data = NULL; 539 | if (generator) { 540 | plist_dict_set_item(newshsh, "generator", generator); generator = NULL; 541 | } 542 | 543 | retassure((plist_to_xml(newshsh, &xml, &xmlSize),xml), "failed to convert plist to xml"); 544 | saveToFile(shshFile, xml, xmlSize); 545 | printf("Saved IM4M to %s\n",shshFile); 546 | } else if (flags & FLAG_VERIFY){ 547 | //verify 548 | ASN1DERElement file(workingBuf.data(), workingBuf.size()); 549 | std::string im4pSHA1; 550 | std::string im4pSHA384; 551 | 552 | if (isIMG4(file)) { 553 | #ifdef HAVE_CRYPTO 554 | ASN1DERElement im4p = getIM4PFromIMG4(file); 555 | file = getIM4MFromIMG4(file); 556 | 557 | printIM4P(im4p.buf(), im4p.size()); 558 | 559 | im4pSHA1 = getIM4PSHA1(im4p); 560 | im4pSHA384 = getIM4PSHA384(im4p); 561 | #else 562 | printf("[WARNING] COMPILED WITHOUT CRYPTO: can not verify im4p payload hash!\n"); 563 | #endif //HAVE_CRYPTO 564 | } 565 | 566 | if (isIM4M(file)) { 567 | plist_t buildmanifest = NULL; 568 | cleanup([&]{ 569 | if (buildmanifest) { 570 | plist_free(buildmanifest); 571 | } 572 | }); 573 | std::string im4pElemDgstName; 574 | #ifdef HAVE_CRYPTO 575 | if (im4pSHA1.size() || im4pSHA384.size()) { 576 | //verify payload hash too 577 | try { 578 | im4pElemDgstName = dgstNameForHash(file, im4pSHA1); 579 | } catch (...) { 580 | // 581 | } 582 | try { 583 | im4pElemDgstName = dgstNameForHash(file, im4pSHA384); 584 | } catch (...) { 585 | // 586 | } 587 | retassure(im4pElemDgstName.size(), "IM4P hash not in IM4M"); 588 | } 589 | #endif //HAVE_CRYPTO 590 | if (buildmanifestFile){ 591 | retassure(buildmanifest = readPlistFromFile(buildmanifestFile),"failed to read buildmanifest"); 592 | } 593 | 594 | bool isvalid = isValidIM4M(file, buildmanifest, im4pElemDgstName); 595 | printf("\n"); 596 | printf("[IMG4TOOL] APTicket is %s!\n", isvalid ? "GOOD" : "BAD"); 597 | if (im4pElemDgstName.size()) { 598 | printf("[IMG4TOOL] IMG4 contains an IM4P which is correctly signed by IM4M\n"); 599 | } 600 | if (generator) { 601 | bool isGenValid = isGeneratorValidForIM4M(file,generator); 602 | printf("[IMG4TOOL] SHSH2 contains generator %s which is %s for nonce in IM4M!\n", generator, isGenValid ? "GOOD" : "BAD"); 603 | } 604 | 605 | }else if (isIM4M(file)){ 606 | reterror("Verify does not make sense on IM4P file!"); 607 | }else{ 608 | reterror("File not recognised"); 609 | } 610 | } 611 | #endif //HAVE_PLIST 612 | else { 613 | //printing only 614 | string seqName = getNameForSequence(workingBuf.data(), workingBuf.size()); 615 | if (seqName == "IMG4") { 616 | printIMG4(workingBuf.data(), workingBuf.size(), flags & FLAG_ALL, flags & FLAG_IM4PONLY); 617 | } else if (seqName == "IM4P"){ 618 | printIM4P(workingBuf.data(), workingBuf.size()); 619 | } else if (seqName == "IM4M"){ 620 | printIM4M(workingBuf.data(), workingBuf.size(), flags & FLAG_ALL); 621 | } 622 | else{ 623 | reterror("File not recognised"); 624 | } 625 | } 626 | } else if (flags & FLAG_CREATE){ 627 | //create IMG4 file 628 | ASN1DERElement img4 = getEmptyIMG4Container(); 629 | 630 | retassure(im4pFile, "im4p file is required for img4"); 631 | 632 | if (im4pFile) { 633 | tihmstar::Mem wbuf = tihmstar::readFile(im4pFile); 634 | ASN1DERElement im4p(wbuf.data(),wbuf.size()); 635 | 636 | img4 = appendIM4PToIMG4(img4, im4p); 637 | } 638 | 639 | if (im4mFile || shshFile){ 640 | tihmstar::Mem obuf; 641 | 642 | if (im4mFile) { 643 | obuf = tihmstar::readFile(im4mFile); 644 | } 645 | #ifdef HAVE_PLIST 646 | else if (shshFile){ 647 | obuf = im4mFormShshFile(shshFile, NULL); 648 | } 649 | #endif //HAVE_PLIST 650 | assure(obuf.size()); 651 | ASN1DERElement im4m(obuf.data(),obuf.size()); 652 | img4 = appendIM4MToIMG4(img4, im4m); 653 | } 654 | 655 | if (bnch){ 656 | img4 = appendIM4RToIMG4(img4, getIM4RFromGenerator(bnch)); 657 | } 658 | 659 | saveToFile(outFile, img4.buf(), img4.size()); 660 | printf("Created IMG4 file at %s\n",outFile); 661 | } 662 | else{ 663 | reterror("No working buffer"); 664 | } 665 | 666 | return 0; 667 | } 668 | -------------------------------------------------------------------------------- /img4tool/img4tool.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // img4tool.cpp 3 | // img4tool 4 | // 5 | // Created by tihmstar on 04.10.19. 6 | // Copyright © 2019 tihmstar. All rights reserved. 7 | // 8 | 9 | #include "../include/img4tool/img4tool.hpp" 10 | #include "../include/img4tool/ASN1DERElement.hpp" 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | extern "C"{ 20 | #include "lzssdec.h" 21 | }; 22 | 23 | #ifdef HAVE_ARPA_INET_H 24 | #include 25 | #elif defined(HAVE_WINSOCK_H) 26 | #include 27 | #endif 28 | 29 | #if defined(HAVE_LIBCOMPRESSION) 30 | # include 31 | # define lzfse_decode_buffer(dst, dst_size, src, src_size, scratch) \ 32 | compression_decode_buffer(dst, dst_size, src, src_size, scratch, COMPRESSION_LZFSE) 33 | # define lzfse_encode_buffer(dst, dst_size, src, src_size, scratch) \ 34 | compression_encode_buffer(dst, dst_size, src, src_size, scratch, COMPRESSION_LZFSE) 35 | #elif defined(HAVE_LIBLZFSE) 36 | # include 37 | #endif 38 | 39 | #ifdef HAVE_OPENSSL 40 | # include 41 | # include 42 | 43 | #warning TODO adjust this for HAVE_COMMCRYPTO 44 | # include //not replaced by CommCrypto 45 | # include //not replaced by CommCrypto 46 | #else 47 | # ifdef HAVE_COMMCRYPTO 48 | # include 49 | # include 50 | # define SHA1(d, n, md) CC_SHA1(d, n, md) 51 | # define SHA384(d, n, md) CC_SHA384(d, n, md) 52 | # define SHA_DIGEST_LENGTH CC_SHA1_DIGEST_LENGTH 53 | # define SHA384_DIGEST_LENGTH CC_SHA384_DIGEST_LENGTH 54 | # endif //HAVE_COMMCRYPTO 55 | #endif // HAVE_OPENSSL 56 | 57 | using namespace tihmstar; 58 | using namespace tihmstar::img4tool; 59 | 60 | #define putPrivtag(s) do {static_assert(sizeof(s) >= 4, "bad privtag size"); printf("[%.4s]",(char*)&s);}while(0) 61 | #define INDENTVALUE 3 62 | 63 | namespace tihmstar { 64 | namespace img4tool { 65 | void printKBAG(const void *buf, size_t size, int indent = 0); 66 | void printMANB(const void *buf, size_t size, bool printAll, int indent = 0); 67 | void printMANP(const void *buf, size_t size, int indent = 0); 68 | void printPAYP(const void *buf, size_t size, int indent = 0); 69 | void printIM4R(const void *buf, size_t size, int indent = 0); 70 | void printWithName(const char *name, const void *buf, size_t size, int indent = 0); 71 | 72 | void printRecSequence(const void *buf, size_t size, int indent = 0, bool dontIndentNext = false); 73 | 74 | ASN1DERElement parsePrivTag(const void *buf, size_t size, size_t *outPrivTag); 75 | ASN1DERElement uncompressIfNeeded(const ASN1DERElement &compressedOctet, const ASN1DERElement &origIM4P, const char **outUsedCompression = NULL, const char **outHypervisor = NULL, size_t *outHypervisorSize = NULL); 76 | }; 77 | }; 78 | 79 | #pragma mark private 80 | 81 | void tihmstar::img4tool::printKBAG(const void *buf, size_t size, int indent){ 82 | ASN1DERElement octet(buf,size); 83 | 84 | assure(!octet.tag().isConstructed); 85 | assure(octet.tag().tagNumber == ASN1DERElement::TagOCTET); 86 | assure(octet.tag().tagClass == ASN1DERElement::TagClass::Universal); 87 | 88 | ASN1DERElement sequence(octet.payload(),octet.payloadSize()); 89 | 90 | assure(sequence.tag().isConstructed); 91 | assure(sequence.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 92 | assure(sequence.tag().tagClass == ASN1DERElement::TagClass::Universal); 93 | 94 | printf("KBAG\n"); 95 | for (auto &kbtag : sequence) { 96 | assure(kbtag.tag().isConstructed); 97 | assure(kbtag.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 98 | assure(kbtag.tag().tagClass == ASN1DERElement::TagClass::Universal); 99 | int i=-1; 100 | for (auto &elem : kbtag) { 101 | switch (++i) { 102 | case 0: 103 | printf("num: %llu\n",elem.getIntegerValue()); 104 | break; 105 | case 1: 106 | case 2: 107 | { 108 | std::string kbagstr = elem.getStringValue(); 109 | for (int i=0; i= taginfoSize); 326 | assure(*(uint8_t*)buf == ASN1DERElement::TagPrivate); 327 | 328 | 329 | ptag = ((ASN1DERElement::ASN1PrivateTag *)buf) + 1; 330 | tlen = ((ASN1DERElement::ASN1Len *)buf) + 6; 331 | 332 | for (int i=0; iisLong){ 341 | taginfoSize += tlen->len; 342 | } 343 | 344 | size_t playloadLen = 0; 345 | { 346 | if (!tlen->isLong){ 347 | playloadLen = tlen->len; 348 | }else{ 349 | assure(tlen->len <= sizeof(size_t)); //can't hold more than size_t 350 | assure(size > taginfoSize); //len bytes shouldn't be outside of buffer 351 | 352 | for (uint8_t sizebits = 0; sizebits < tlen->len; sizebits++) { 353 | playloadLen <<= 8; 354 | playloadLen |= ((uint8_t*)tlen)[1+sizebits]; 355 | } 356 | } 357 | } 358 | 359 | assure(size >= playloadLen+taginfoSize); 360 | return {(uint8_t*)buf+taginfoSize,playloadLen}; 361 | } 362 | 363 | 364 | #pragma mark public 365 | 366 | const char *tihmstar::img4tool::version(){ 367 | return VERSION_STRING; 368 | } 369 | 370 | void tihmstar::img4tool::printIMG4(const void *buf, size_t size, bool printAll, bool im4pOnly){ 371 | ASN1DERElement sequence(buf,size); 372 | 373 | assure(sequence.tag().isConstructed); 374 | assure(sequence.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 375 | assure(sequence.tag().tagClass == ASN1DERElement::TagClass::Universal); 376 | 377 | ASN1DERElement filetype = sequence[0]; 378 | 379 | assure(!filetype.tag().isConstructed); 380 | assure(filetype.tag().tagNumber == ASN1DERElement::TagIA5String); 381 | assure(filetype.tag().tagClass == ASN1DERElement::TagClass::Universal); 382 | { 383 | int i=-1; 384 | for (auto &tag : sequence) { 385 | switch (++i) { 386 | case 0: 387 | assure(tag.getStringValue() == "IMG4"); 388 | printf("IMG4:\n"); 389 | printf("size: 0x%08llx\n",(uint64_t)sequence.size()); 390 | break; 391 | case 1: 392 | printIM4P(tag.buf(), tag.size()); 393 | break; 394 | case 2: 395 | if (!im4pOnly){ 396 | assure(tag.tag().isConstructed); 397 | assure(tag.tag().tagNumber == 0); 398 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::ContextSpecific); 399 | 400 | printIM4M(tag.payload(), tag.payloadSize(), printAll); 401 | } 402 | break; 403 | case 3: 404 | if (!im4pOnly){ 405 | assure(tag.tag().isConstructed); 406 | assure(tag.tag().tagNumber == 1); 407 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::ContextSpecific); 408 | 409 | printIM4R(tag.payload(), tag.payloadSize()); 410 | } 411 | break; 412 | default: 413 | reterror("[%s] unexpected element at SEQUENCE index %d",__FUNCTION__,i); 414 | break; 415 | } 416 | } 417 | } 418 | } 419 | 420 | void tihmstar::img4tool::printIM4P(const void *buf, size_t size){ 421 | ASN1DERElement sequence(buf,size); 422 | bool hasKBAG = false; 423 | 424 | assure(sequence.tag().isConstructed); 425 | assure(sequence.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 426 | assure(sequence.tag().tagClass == ASN1DERElement::TagClass::Universal); 427 | 428 | { 429 | int i=-1; 430 | for (auto &tag : sequence) { 431 | switch (++i) { 432 | case 0: 433 | assure(tag.getStringValue() == "IM4P"); 434 | printf("IM4P: ---------\n"); 435 | break; 436 | case 1: 437 | assure(!tag.tag().isConstructed); 438 | assure(tag.tag().tagNumber == ASN1DERElement::TagIA5String); 439 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::Universal); 440 | printf("type: %s\n",tag.getStringValue().c_str()); 441 | break; 442 | case 2: 443 | assure(!tag.tag().isConstructed); 444 | assure(tag.tag().tagNumber == ASN1DERElement::TagIA5String); 445 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::Universal); 446 | try { 447 | printSEPIDesc((const char *)tag.payload(),tag.payloadSize()); 448 | break; 449 | } catch (...) { 450 | // 451 | } 452 | printf("desc: %s\n",tag.getStringValue().c_str()); 453 | break; 454 | case 3: 455 | assure(!tag.tag().isConstructed); 456 | assure(tag.tag().tagNumber == ASN1DERElement::TagOCTET); 457 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::Universal); 458 | printf("size: 0x%08lx\n\n",tag.payloadSize()); 459 | break; 460 | case 4: 461 | { 462 | ASN1DERElement octet = tag; 463 | if (!octet.tag().isConstructed 464 | && octet.tag().tagNumber == ASN1DERElement::TagOCTET 465 | && octet.tag().tagClass == ASN1DERElement::TagClass::Universal){ 466 | printKBAG(tag.buf(),tag.size()); 467 | printf("\n"); 468 | hasKBAG = true; 469 | break; 470 | }else{ 471 | debug("Warning: got more than 3 elements, but element is not octet!\n"); 472 | ++i; //skip to step 5 473 | } 474 | } 475 | case 5: 476 | case 6: 477 | if (tag.tag().isConstructed && tag.tag().tagNumber == 0 && tag.tag().tagClass == ASN1DERElement::TagClass::ContextSpecific){ 478 | ASN1DERElement payp = tag[0]; 479 | printPAYP(payp.buf(), payp.size()); 480 | }else{ 481 | assure(tag.tag().isConstructed); 482 | assure(tag.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 483 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::Universal); 484 | { 485 | ASN1DERElement versionTag = tag[0]; 486 | ASN1DERElement sizeTag = tag[1]; 487 | 488 | assure(versionTag.tag().isConstructed == ASN1DERElement::Primitive); 489 | assure(versionTag.tag().tagNumber == ASN1DERElement::TagINTEGER); 490 | assure(versionTag.tag().tagClass == ASN1DERElement::Universal); 491 | if (versionTag.getIntegerValue() != 1){ 492 | reterror("unexpected compression number %llu",versionTag.getIntegerValue()); 493 | } 494 | printf("Compression: bvx2\n"); 495 | assure(sizeTag.tag().isConstructed == ASN1DERElement::Primitive); 496 | assure(sizeTag.tag().tagNumber == ASN1DERElement::TagINTEGER); 497 | assure(sizeTag.tag().tagClass == ASN1DERElement::Universal); 498 | printf("Uncompressed size: 0x%08llx\n",sizeTag.getIntegerValue()); 499 | } 500 | } 501 | break; 502 | default: 503 | reterror("[%s] unexpected element at SEQUENCE index %d",__FUNCTION__,i); 504 | break; 505 | } 506 | } 507 | if (!hasKBAG) { 508 | printf("IM4P does not contain KBAG values\n\n"); 509 | } 510 | } 511 | } 512 | 513 | 514 | void tihmstar::img4tool::printIM4M(const void *buf, size_t size, bool printAll){ 515 | ASN1DERElement sequence(buf,size); 516 | 517 | assure(sequence.tag().isConstructed); 518 | assure(sequence.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 519 | assure(sequence.tag().tagClass == ASN1DERElement::TagClass::Universal); 520 | 521 | { 522 | int i=-1; 523 | for (auto &tag : sequence) { 524 | switch (++i) { 525 | case 0: 526 | assure(tag.getStringValue() == "IM4M"); 527 | printf("IM4M: ---------\n"); 528 | break; 529 | case 1: 530 | printf("Version: %llu\n",tag.getIntegerValue()); 531 | break; 532 | case 2: 533 | assure(tag.tag().isConstructed); 534 | assure(tag.tag().tagNumber == ASN1DERElement::TagSET); 535 | assure(tag.tag().tagClass == ASN1DERElement::TagClass::Universal); 536 | printMANB(tag.payload(), tag.payloadSize(), printAll, 1); 537 | break; 538 | case 3: //signature 539 | case 4: //certificate 540 | break; 541 | default: 542 | reterror("[%s] unexpected element at SEQUENCE index %d",__FUNCTION__,i); 543 | break; 544 | } 545 | } 546 | } 547 | } 548 | 549 | void tihmstar::img4tool::printSEPIDesc(const char *buf, size_t size){ 550 | uint8_t *payload = NULL; 551 | cleanup([&]{ 552 | safeFree(payload); 553 | }); 554 | 555 | assure(payload = (uint8_t*)malloc(size / 2)); 556 | 557 | for (size_t i=0; i> modval) modval+=7; 966 | int i=1; 967 | for (;i0; i++) { 968 | modval-=7; 969 | ((ASN1DERElement::ASN1PrivateTag*)buf)[i].num = (privnum>>modval) & 0x7f; 970 | if (modval) { 971 | ((ASN1DERElement::ASN1PrivateTag*)buf)[i].more = 1; 972 | } 973 | } 974 | std::string payloadSize = ASN1DERElement::makeASN1Size(payload.size()); 975 | 976 | elembuf = (char*)malloc(elemSize = (payload.size() +payloadSize.size() + i)); 977 | memcpy(&elembuf[0], buf, i); 978 | memcpy(&elembuf[i], payloadSize.c_str(), payloadSize.size()); 979 | memcpy(&elembuf[i+payloadSize.size()], payload.buf(), payload.size()); 980 | 981 | ASN1DERElement local(elembuf,elemSize); 982 | 983 | //by casting to const we make sure to create a copy which owns the buffer so we can free our local buffer 984 | return static_cast(local); 985 | } 986 | 987 | #pragma mark begin_needs_crypto 988 | #ifdef HAVE_CRYPTO 989 | ASN1DERElement tihmstar::img4tool::decryptPayload(const ASN1DERElement &payload, const char *decryptIv, const char *decryptKey){ 990 | uint8_t iv[16] = {}; 991 | uint8_t key[32] = {}; 992 | retassure(decryptIv, "decryptPayload requires IV but none was provided!"); 993 | retassure(decryptKey, "decryptPayload requires KEY but none was provided!"); 994 | 995 | assure(!payload.tag().isConstructed); 996 | assure(payload.tag().tagNumber == ASN1DERElement::TagOCTET); 997 | assure(payload.tag().tagClass == ASN1DERElement::TagClass::Universal); 998 | 999 | ASN1DERElement decPayload(payload); 1000 | 1001 | assure(strlen(decryptIv) == sizeof(iv)*2); 1002 | assure(strlen(decryptKey) == sizeof(key)*2); 1003 | for (int i=0; i tmp{'\0'}; 1031 | std::string hash{tmp.begin(),tmp.end()}; 1032 | SHA1((unsigned char*)im4p.buf(), (unsigned int)im4p.size(), (unsigned char *)hash.c_str()); 1033 | return hash; 1034 | } 1035 | 1036 | std::string tihmstar::img4tool::getIM4PSHA384(const ASN1DERElement &im4p){ 1037 | std::array tmp{'\0'}; 1038 | std::string hash{tmp.begin(),tmp.end()}; 1039 | SHA384((unsigned char*)im4p.buf(), (unsigned int)im4p.size(), (unsigned char *)hash.c_str()); 1040 | return hash; 1041 | } 1042 | 1043 | std::string tihmstar::img4tool::dgstNameForHash(const ASN1DERElement &im4m, std::string hash){ 1044 | assure(isIM4M(im4m)); 1045 | ASN1DERElement set = im4m[2]; 1046 | ASN1DERElement manbpriv = set[0]; 1047 | size_t privTagVal = 0; 1048 | ASN1DERElement manb = parsePrivTag(manbpriv.buf(), manbpriv.size(), &privTagVal); 1049 | assure(privTagVal == *(uint32_t*)"MANB"); 1050 | assure(manb[0].getStringValue() == "MANB"); 1051 | 1052 | ASN1DERElement manbset = manb[1]; 1053 | 1054 | for (auto &e : manbset) { 1055 | size_t pTagVal = 0; 1056 | ASN1DERElement me = parsePrivTag(e.buf(), e.size(), &pTagVal); 1057 | if (pTagVal == *(uint32_t*)"MANP") 1058 | continue; 1059 | 1060 | ASN1DERElement set = me[1]; 1061 | std::string dgstName = me[0].getStringValue(); 1062 | 1063 | for (auto &se : set) { 1064 | size_t pTagVal = 0; 1065 | ASN1DERElement sel = parsePrivTag(se.buf(), se.size(), &pTagVal); 1066 | switch (pTagVal) { 1067 | case 'TSGD': //DGST 1068 | { 1069 | std::string selDigest = sel[1].getStringValue(); 1070 | if (selDigest == hash){ 1071 | return dgstName; 1072 | } 1073 | } 1074 | break; 1075 | default: 1076 | break; 1077 | } 1078 | } 1079 | } 1080 | reterror("Hash not in IM4M"); 1081 | } 1082 | 1083 | 1084 | bool tihmstar::img4tool::im4mContainsHash(const ASN1DERElement &im4m, std::string hash) noexcept{ 1085 | try { 1086 | dgstNameForHash(im4m,hash); 1087 | return true; 1088 | } catch (...) { 1089 | return false; 1090 | } 1091 | } 1092 | 1093 | bool tihmstar::img4tool::isGeneratorValidForIM4M(const ASN1DERElement &im4m, std::string generator) noexcept{ 1094 | try { 1095 | ASN1DERElement bnch = getValFromIM4M(im4m,'BNCH'); 1096 | uint64_t gen = 0; 1097 | 1098 | sscanf(generator.c_str(), "0x%16llx", &gen); 1099 | 1100 | if (bnch.payloadSize() == SHA_DIGEST_LENGTH) { 1101 | std::array tmp{'\0'}; 1102 | std::string hash{tmp.begin(),tmp.end()}; 1103 | SHA1((unsigned char*)&gen, sizeof(gen), (unsigned char *)hash.c_str()); 1104 | return memcmp(hash.c_str(), bnch.payload(), bnch.payloadSize()) == 0; 1105 | }else{ 1106 | std::array tmp{'\0'}; 1107 | std::string hash{tmp.begin(),tmp.end()}; 1108 | SHA384((unsigned char*)&gen, sizeof(gen), (unsigned char *)hash.c_str()); 1109 | return memcmp(hash.c_str(), bnch.payload(), bnch.payloadSize()) == 0; 1110 | } 1111 | } catch (...) { 1112 | return false; 1113 | } 1114 | } 1115 | #endif //HAVE_CRYPTO 1116 | #pragma mark end_needs_crypto 1117 | 1118 | 1119 | ASN1DERElement tihmstar::img4tool::getEmptyIM4PContainer(const char *type, const char *desc){ 1120 | ASN1DERElement im4p({ASN1DERElement::TagSEQUENCE, ASN1DERElement::Constructed, ASN1DERElement::Universal},NULL,0); 1121 | ASN1DERElement im4p_str({ASN1DERElement::TagIA5String, ASN1DERElement::Primitive, ASN1DERElement::Universal},"IM4P",4); 1122 | ASN1DERElement im4p_type({ASN1DERElement::TagIA5String, ASN1DERElement::Primitive, ASN1DERElement::Universal},type,strlen(type)); 1123 | ASN1DERElement im4p_desc({ASN1DERElement::TagIA5String, ASN1DERElement::Primitive, ASN1DERElement::Universal},desc,strlen(desc)); 1124 | 1125 | retassure(im4p_type.payloadSize() == 4, "Type needs to be exactly 4 bytes long"); 1126 | 1127 | im4p += im4p_str; 1128 | im4p += im4p_type; 1129 | im4p += im4p_desc; 1130 | 1131 | return im4p; 1132 | } 1133 | 1134 | ASN1DERElement tihmstar::img4tool::getIM4RWithElements(std::map elements){ 1135 | ASN1DERElement im4r({ASN1DERElement::TagSEQUENCE, ASN1DERElement::Constructed, ASN1DERElement::Universal},NULL,0); 1136 | ASN1DERElement im4r_str({ASN1DERElement::TagIA5String, ASN1DERElement::Primitive, ASN1DERElement::Universal},"IM4R",4); 1137 | im4r += im4r_str; 1138 | 1139 | ASN1DERElement set({ASN1DERElement::TagSET, ASN1DERElement::Constructed, ASN1DERElement::Universal},NULL,0); 1140 | 1141 | for (auto e : elements) { 1142 | ASN1DERElement bnch_seq({ASN1DERElement::TagSEQUENCE, ASN1DERElement::Constructed, ASN1DERElement::Universal},NULL,0); 1143 | ASN1DERElement bnch_str({ASN1DERElement::TagIA5String, ASN1DERElement::Primitive, ASN1DERElement::Universal},e.first.c_str(),4); 1144 | ASN1DERElement bnch_payload({ASN1DERElement::TagOCTET, ASN1DERElement::Primitive, ASN1DERElement::Universal},e.second.data(),e.second.size()); 1145 | bnch_seq += bnch_str; 1146 | bnch_seq += bnch_payload; 1147 | ASN1DERElement bnch = genPrivTagForNumberWithPayload(htonl(*(uint32_t*)e.first.c_str()),bnch_seq); 1148 | set += bnch; 1149 | } 1150 | 1151 | im4r += set; 1152 | #ifdef DEBUG 1153 | assure(isIM4R(im4r)); 1154 | #endif 1155 | return im4r; 1156 | } 1157 | 1158 | ASN1DERElement tihmstar::img4tool::appendPayloadToIM4P(const ASN1DERElement &im4p, const void *buf, size_t size, const char *compression, const void *buf2Raw, size_t buf2RawSize){ 1159 | assure(im4p.tag().isConstructed); 1160 | assure(im4p.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1161 | assure(im4p.tag().tagClass == ASN1DERElement::TagClass::Universal); 1162 | 1163 | retassure(im4p[0].getStringValue() == "IM4P", "Container is not a IM4P"); 1164 | retassure(im4p[1].getStringValue().size() == 4, "IM4P type has size != 4"); 1165 | retassure(im4p[2].getStringValue().size(), "IM4P description is empty"); 1166 | ASN1DERElement newim4p(im4p); 1167 | 1168 | ASN1DERElement im4p_payload({ASN1DERElement::TagOCTET, ASN1DERElement::Primitive, ASN1DERElement::Universal},buf,size); 1169 | 1170 | if (compression) { 1171 | if (strcmp(compression, "complzss") == 0) { 1172 | uint8_t *packed = NULL; 1173 | cleanup([&]{ 1174 | safeFree(packed); 1175 | }); 1176 | size_t packedSize = size + buf2RawSize; 1177 | 1178 | printf("Compression requested, compressing (%s): ", "complzss"); 1179 | packed = (uint8_t *)malloc(packedSize); 1180 | 1181 | packedSize = lzss_compress((const uint8_t *)buf, (uint32_t)size, packed, (uint32_t)packedSize); 1182 | assure(packedSize < size); 1183 | 1184 | printf("ok\n"); 1185 | 1186 | if (buf2Raw && buf2RawSize) { 1187 | printf("Requested appending uncompressed buffer at the end!\n"); 1188 | //we optionally can append a buffer after compression 1189 | packed = (uint8_t *)realloc(packed, packedSize + buf2RawSize); 1190 | memcpy(&packed[packedSize], buf2Raw, buf2RawSize); 1191 | packedSize += buf2RawSize; 1192 | } 1193 | 1194 | im4p_payload = ASN1DERElement({ASN1DERElement::TagNumber::TagOCTET,ASN1DERElement::Primitive, ASN1DERElement::Universal}, packed, packedSize); 1195 | 1196 | newim4p += im4p_payload; 1197 | } else if (strcmp(compression, "bvx2") == 0) { 1198 | printf("Compression requested, compressing (%s): ", "bvx2"); 1199 | #if defined(HAVE_LIBCOMPRESSION) || defined(HAVE_LIBLZFSE) 1200 | uint8_t *packed = NULL; 1201 | cleanup([&]{ 1202 | safeFree(packed); 1203 | }); 1204 | size_t packedSize = size; 1205 | packed = (uint8_t *)malloc(packedSize); 1206 | 1207 | packedSize = lzfse_encode_buffer(packed, packedSize, (const uint8_t *)buf, size, NULL); 1208 | 1209 | printf("ok\n"); 1210 | 1211 | im4p_payload = ASN1DERElement({ASN1DERElement::TagNumber::TagOCTET,ASN1DERElement::Primitive, ASN1DERElement::Universal}, packed, packedSize); 1212 | newim4p += im4p_payload; 1213 | 1214 | ASN1DERElement bvx2Info({ASN1DERElement::TagNumber::TagSEQUENCE,ASN1DERElement::Constructed, ASN1DERElement::Universal}, NULL, 0); 1215 | { 1216 | int one = 1; 1217 | bvx2Info += ASN1DERElement({ASN1DERElement::TagNumber::TagINTEGER, ASN1DERElement::Universal}, &one, 1); 1218 | } 1219 | 1220 | bvx2Info += ASN1DERElement::makeASN1Integer(size); 1221 | newim4p += bvx2Info; 1222 | #else 1223 | reterror("img4tool was build without bvx2 support"); 1224 | #endif 1225 | }else { 1226 | reterror("unknown compression=%s",compression); 1227 | } 1228 | }else{ 1229 | newim4p += im4p_payload; 1230 | } 1231 | 1232 | 1233 | return newim4p; 1234 | } 1235 | 1236 | bool tihmstar::img4tool::isIMG4(const ASN1DERElement &img4) noexcept{ 1237 | try{ 1238 | assure(img4.tag().isConstructed); 1239 | assure(img4.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1240 | assure(img4.tag().tagClass == ASN1DERElement::TagClass::Universal); 1241 | 1242 | retassure(img4[0].getStringValue() == "IMG4", "Not an IMG4 file"); 1243 | return true; 1244 | }catch (tihmstar::exception &e){ 1245 | // 1246 | } 1247 | return false; 1248 | } 1249 | 1250 | bool tihmstar::img4tool::isIM4P(const ASN1DERElement &im4p) noexcept{ 1251 | try { 1252 | assure(im4p.tag().isConstructed); 1253 | assure(im4p.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1254 | assure(im4p.tag().tagClass == ASN1DERElement::TagClass::Universal); 1255 | 1256 | retassure(im4p[0].getStringValue() == "IM4P", "Container is not a IM4P"); 1257 | retassure(im4p[1].getStringValue().size() == 4, "IM4P type has size != 4"); 1258 | retassure(im4p[2].getStringValue().size(), "IM4P description is empty"); 1259 | 1260 | ASN1DERElement payload = im4p[3]; 1261 | assure(!payload.tag().isConstructed); 1262 | assure(payload.tag().tagNumber == ASN1DERElement::TagOCTET); 1263 | assure(payload.tag().tagClass == ASN1DERElement::TagClass::Universal); 1264 | 1265 | return true; 1266 | } catch (tihmstar::exception &e) { 1267 | // 1268 | } 1269 | return false; 1270 | } 1271 | 1272 | bool tihmstar::img4tool::isIM4M(const ASN1DERElement &im4m) noexcept{ 1273 | try { 1274 | assure(im4m.tag().isConstructed); 1275 | assure(im4m.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1276 | assure(im4m.tag().tagClass == ASN1DERElement::TagClass::Universal); 1277 | 1278 | retassure(im4m[0].getStringValue() == "IM4M", "Container is not a IM4M"); 1279 | retassure(im4m[1].getIntegerValue() == 0, "IM4M has weird version number"); 1280 | 1281 | auto set = im4m[2]; 1282 | assure(set.tag().isConstructed); 1283 | assure(set.tag().tagNumber == ASN1DERElement::TagSET); 1284 | assure(set.tag().tagClass == ASN1DERElement::TagClass::Universal); 1285 | 1286 | auto octet = im4m[3]; 1287 | assure(!octet.tag().isConstructed); 1288 | assure(octet.tag().tagNumber == ASN1DERElement::TagOCTET); 1289 | assure(octet.tag().tagClass == ASN1DERElement::TagClass::Universal); 1290 | 1291 | auto seq = im4m[4]; 1292 | assure(seq.tag().isConstructed); 1293 | assure(seq.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1294 | assure(seq.tag().tagClass == ASN1DERElement::TagClass::Universal); 1295 | 1296 | return true; 1297 | } catch (tihmstar::exception &e) { 1298 | // 1299 | } 1300 | return false; 1301 | } 1302 | 1303 | bool tihmstar::img4tool::isIM4R(const ASN1DERElement &im4r) noexcept{ 1304 | try { 1305 | assure(im4r.tag().isConstructed); 1306 | assure(im4r.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1307 | assure(im4r.tag().tagClass == ASN1DERElement::TagClass::Universal); 1308 | 1309 | retassure(im4r[0].getStringValue() == "IM4R", "Container is not a IM4R"); 1310 | 1311 | auto set = im4r[1]; 1312 | assure(set.tag().isConstructed); 1313 | assure(set.tag().tagNumber == ASN1DERElement::TagSET); 1314 | assure(set.tag().tagClass == ASN1DERElement::TagClass::Universal); 1315 | return true; 1316 | } catch (tihmstar::exception &e) { 1317 | // 1318 | } 1319 | return false; 1320 | } 1321 | 1322 | bool tihmstar::img4tool::isIM4C(const ASN1DERElement &im4c) noexcept{ 1323 | try { 1324 | assure(im4c.tag().isConstructed); 1325 | assure(im4c.tag().tagNumber == ASN1DERElement::TagSEQUENCE); 1326 | assure(im4c.tag().tagClass == ASN1DERElement::TagClass::Universal); 1327 | 1328 | retassure(im4c[0].getStringValue() == "IM4C", "Container is not a IM4C"); 1329 | 1330 | auto set = im4c[2]; 1331 | assure(set.tag().isConstructed); 1332 | assure(set.tag().tagNumber == ASN1DERElement::TagSET); 1333 | assure(set.tag().tagClass == ASN1DERElement::TagClass::Universal); 1334 | 1335 | auto sig = im4c[3]; 1336 | assure(sig.tag().isConstructed == 0); 1337 | assure(sig.tag().tagNumber == ASN1DERElement::TagOCTET); 1338 | assure(sig.tag().tagClass == ASN1DERElement::TagClass::Universal); 1339 | 1340 | return true; 1341 | } catch (tihmstar::exception &e) { 1342 | // 1343 | } 1344 | return false; 1345 | } 1346 | 1347 | ASN1DERElement tihmstar::img4tool::renameIM4P(const ASN1DERElement &im4p, const char *type){ 1348 | assure(isIM4P(im4p)); 1349 | retassure(strlen(type) == 4, "type has size != 4"); 1350 | ASN1DERElement newIm4p(im4p); 1351 | 1352 | uint8_t *ptr = (uint8_t*)newIm4p.payload(); 1353 | size_t size = newIm4p.payloadSize(); 1354 | { 1355 | ASN1DERElement e0(ptr,size); 1356 | retassure(e0.size()<=size, "im4p too small for e0"); 1357 | ptr += e0.size(); 1358 | size -= e0.size(); 1359 | } 1360 | 1361 | { 1362 | ASN1DERElement e1(ptr,size); 1363 | retassure(e1.taginfoSize()+4<=size, "im4p too small for e1"); 1364 | ptr += e1.taginfoSize(); 1365 | } 1366 | 1367 | memcpy(ptr,type,4); 1368 | 1369 | return newIm4p; 1370 | } 1371 | 1372 | std::string tihmstar::img4tool::getDescFromIM4P(const ASN1DERElement &im4p){ 1373 | assure(isIM4P(im4p)); 1374 | auto descTag = im4p[2]; 1375 | assure(!descTag.tag().isConstructed); 1376 | assure(descTag.tag().tagNumber == ASN1DERElement::TagIA5String); 1377 | assure(descTag.tag().tagClass == ASN1DERElement::TagClass::Universal); 1378 | return descTag.getStringValue(); 1379 | } 1380 | 1381 | bool tihmstar::img4tool::isIM4MSignatureValid(const ASN1DERElement &im4m){ 1382 | #ifndef XCODE 1383 | try { 1384 | #endif 1385 | assure(isIM4M(im4m)); 1386 | ASN1DERElement data = im4m[2]; 1387 | ASN1DERElement sig = im4m[3]; 1388 | ASN1DERElement certelem = im4m[4][0]; 1389 | 1390 | // if (sig.payloadSize() == 256 /*tested (A8)*/ || sig.payloadSize() == 512 /*untested*/) 1391 | { 1392 | #ifndef HAVE_OPENSSL 1393 | reterror("Compiled without openssl"); 1394 | #else 1395 | EVP_MD_CTX *mdctx = NULL; 1396 | X509 *cert = NULL; 1397 | EVP_PKEY *certpubkey = NULL; 1398 | const unsigned char* certificate = NULL; 1399 | bool useSHA384 = false; 1400 | cleanup([&]{ 1401 | if(mdctx) EVP_MD_CTX_destroy(mdctx); 1402 | }); 1403 | try { 1404 | //bootAuthority is 0 1405 | //tssAuthority is 1 1406 | certelem = im4m[4][1]; 1407 | } catch (tihmstar::exception &e) { 1408 | //however bootAuthority does not exist on iPhone7 1409 | useSHA384 = true; 1410 | } 1411 | 1412 | certificate = (const unsigned char*)certelem.buf(); 1413 | 1414 | assure(mdctx = EVP_MD_CTX_create()); 1415 | assure(cert = d2i_X509(NULL, &certificate, certelem.size())); 1416 | assure(certpubkey = X509_get_pubkey(cert)); 1417 | 1418 | assure(EVP_DigestVerifyInit(mdctx, NULL, (useSHA384) ? EVP_sha384() : EVP_sha1(), NULL, certpubkey) == 1); 1419 | 1420 | assure(EVP_DigestVerifyUpdate(mdctx, data.buf(), data.size()) == 1); 1421 | 1422 | assure(EVP_DigestVerifyFinal(mdctx, (unsigned char*)sig.payload(), sig.payloadSize()) == 1); 1423 | #endif //HAVE_OPENSSL 1424 | } 1425 | #ifndef XCODE 1426 | } catch (tihmstar::exception &e) { 1427 | printf("[IMG4TOOL] failed to verify IM4M signature with error:\n"); 1428 | e.dump(); 1429 | return false; 1430 | } 1431 | #endif 1432 | return true; 1433 | } 1434 | 1435 | #pragma mark begin_needs_plist 1436 | #ifdef HAVE_PLIST 1437 | bool tihmstar::img4tool::doesIM4MBoardMatchBuildIdentity(const ASN1DERElement &im4m, plist_t buildIdentity) noexcept{ 1438 | plist_t ApBoardID = NULL; 1439 | plist_t ApChipID = NULL; 1440 | plist_t ApSecurityDomain = NULL; 1441 | try{ 1442 | assure(isIM4M(im4m)); 1443 | 1444 | assure(ApBoardID = plist_dict_get_item(buildIdentity, "ApBoardID")); 1445 | assure(ApChipID = plist_dict_get_item(buildIdentity, "ApChipID")); 1446 | assure(ApSecurityDomain = plist_dict_get_item(buildIdentity, "ApSecurityDomain")); 1447 | 1448 | assure(plist_get_node_type(ApBoardID) == PLIST_STRING); 1449 | assure(plist_get_node_type(ApChipID) == PLIST_STRING); 1450 | assure(plist_get_node_type(ApSecurityDomain) == PLIST_STRING); 1451 | 1452 | 1453 | ASN1DERElement set = im4m[2]; 1454 | ASN1DERElement manbpriv = set[0]; 1455 | size_t privTagVal = 0; 1456 | ASN1DERElement manb = parsePrivTag(manbpriv.buf(), manbpriv.size(), &privTagVal); 1457 | assure(privTagVal == *(uint32_t*)"MANB"); 1458 | assure(manb[0].getStringValue() == "MANB"); 1459 | 1460 | ASN1DERElement manbset = manb[1]; 1461 | 1462 | ASN1DERElement manppriv = manbset[0]; 1463 | privTagVal = 0; 1464 | ASN1DERElement manp = parsePrivTag(manppriv.buf(), manppriv.size(), &privTagVal); 1465 | assure(privTagVal == *(uint32_t*)"MANP"); 1466 | assure(manp[0].getStringValue() == "MANP"); 1467 | 1468 | ASN1DERElement manpset = manp[1]; 1469 | 1470 | 1471 | for (auto &e : manpset) { 1472 | char *pstrval= NULL; 1473 | uint64_t val = 0; 1474 | size_t ptagVal = 0; 1475 | plist_t currVal = NULL; 1476 | cleanup([&]{ 1477 | safeFree(pstrval); 1478 | }); 1479 | ASN1DERElement ptag = parsePrivTag(e.buf(), e.size(), &ptagVal); 1480 | 1481 | switch (ptagVal) { 1482 | case 'DROB': //BORD 1483 | assure(ptag[0].getStringValue() == "BORD"); 1484 | currVal = ApBoardID;ApBoardID = NULL; 1485 | break; 1486 | case 'PIHC': //CHIP 1487 | assure(ptag[0].getStringValue() == "CHIP"); 1488 | currVal = ApChipID;ApChipID = NULL; 1489 | break; 1490 | case 'MODS': //SDOM 1491 | assure(ptag[0].getStringValue() == "SDOM"); 1492 | currVal = ApSecurityDomain;ApSecurityDomain = NULL; 1493 | break; 1494 | default: 1495 | continue; 1496 | } 1497 | 1498 | plist_get_string_val(currVal, &pstrval); 1499 | if (strncmp("0x", pstrval, 2) == 0){ 1500 | sscanf(pstrval, "0x%llx",&val); 1501 | }else{ 1502 | sscanf(pstrval, "%lld",&val); 1503 | } 1504 | assure(ptag[1].getIntegerValue() == val); 1505 | } 1506 | //make sure we verified all 3 values we wanted to check 1507 | assure(!ApBoardID && !ApChipID && !ApSecurityDomain); 1508 | }catch (...){ 1509 | return false; 1510 | } 1511 | return true; 1512 | } 1513 | 1514 | bool tihmstar::img4tool::im4mMatchesBuildIdentity(const ASN1DERElement &im4m, plist_t buildIdentity, std::vector ignoreWhitelist) noexcept{ 1515 | plist_t manifest = NULL; 1516 | try { 1517 | bool checksPassed = true; 1518 | std::string findDGST; 1519 | if (ignoreWhitelist.size() == 1 && ignoreWhitelist[0][0] == '!') { 1520 | checksPassed = false; 1521 | findDGST = ignoreWhitelist[0]+1; 1522 | } 1523 | 1524 | printf("[IMG4TOOL] checking buildidentity matches board ... "); 1525 | if (!doesIM4MBoardMatchBuildIdentity(im4m, buildIdentity)) { 1526 | printf("NO\n"); 1527 | return false; 1528 | } 1529 | printf("YES\n"); 1530 | 1531 | printf("[IMG4TOOL] checking buildidentity has all required hashes:\n"); 1532 | ASN1DERElement set = im4m[2]; 1533 | ASN1DERElement manbpriv = set[0]; 1534 | size_t privTagVal = 0; 1535 | ASN1DERElement manb = parsePrivTag(manbpriv.buf(), manbpriv.size(), &privTagVal); 1536 | assure(privTagVal == *(uint32_t*)"MANB"); 1537 | assure(manb[0].getStringValue() == "MANB"); 1538 | 1539 | ASN1DERElement manbset = manb[1]; 1540 | 1541 | assure(manifest = plist_dict_get_item(buildIdentity, "Manifest")); 1542 | assure(plist_get_node_type(manifest) == PLIST_DICT); 1543 | 1544 | plist_dict_iter melems = NULL; 1545 | cleanup([&]{ 1546 | safeFree(melems); 1547 | }); 1548 | plist_dict_new_iter(manifest, &melems); 1549 | assure(melems); 1550 | plist_t eVal = NULL; 1551 | char *eKey = NULL; 1552 | 1553 | while (((void)plist_dict_next_item(manifest, melems, &eKey, &eVal),eVal)) { 1554 | plist_t pInfo = NULL; 1555 | plist_t pDigest = NULL; 1556 | plist_t pTrusted = NULL; 1557 | uint8_t isTrusted = 0; 1558 | char *digest = NULL; 1559 | uint64_t digestLen = 0; 1560 | bool hasDigit = false; 1561 | cleanup([&]{ 1562 | safeFree(digest); 1563 | }); 1564 | 1565 | int didprint = printf("[IMG4TOOL] checking hash for \"%s\"",eKey); 1566 | while (didprint++< 55) { 1567 | printf(" "); 1568 | } 1569 | 1570 | assure(pInfo = plist_dict_get_item(eVal, "Info")); 1571 | 1572 | if ((pTrusted = plist_dict_get_item(eVal, "Trusted"))){ 1573 | assure(plist_get_node_type(pTrusted) == PLIST_BOOLEAN); 1574 | plist_get_bool_val(pTrusted, &isTrusted); 1575 | if (!isTrusted){ 1576 | printf("OK (untrusted)\n"); 1577 | continue; 1578 | } 1579 | } 1580 | 1581 | if (!(pDigest = plist_dict_get_item(eVal, "Digest"))) { 1582 | printf("IGN (no digest in BuildManifest)\n"); 1583 | continue; 1584 | } 1585 | 1586 | assure(plist_get_node_type(pDigest) == PLIST_DATA); 1587 | plist_get_data_val(pDigest, &digest, &digestLen); 1588 | 1589 | 1590 | for (auto &e : manbset) { 1591 | size_t pTagVal = 0; 1592 | ASN1DERElement me = parsePrivTag(e.buf(), e.size(), &pTagVal); 1593 | if (pTagVal == *(uint32_t*)"MANP") 1594 | continue; 1595 | 1596 | ASN1DERElement set = me[1]; 1597 | 1598 | for (auto &se : set) { 1599 | size_t pTagVal = 0; 1600 | ASN1DERElement sel = parsePrivTag(se.buf(), se.size(), &pTagVal); 1601 | switch (pTagVal) { 1602 | case 'TSGD': //DGST 1603 | { 1604 | std::string selDigest = sel[1].getStringValue(); 1605 | if (selDigest.size() == digestLen && memcmp(selDigest.c_str(), digest, digestLen) == 0){ 1606 | hasDigit = true; 1607 | if (findDGST == me[0].getStringValue()) { 1608 | checksPassed = true; 1609 | } 1610 | printf("OK (found \"%s\" with matching hash)\n",me[0].getStringValue().c_str()); 1611 | goto continue_plist; 1612 | } 1613 | } 1614 | break; 1615 | default: 1616 | break; 1617 | } 1618 | } 1619 | } 1620 | continue_plist: 1621 | if (!hasDigit) { 1622 | if (!(ignoreWhitelist.size() == 1 && ignoreWhitelist[0][0] == '!')) { 1623 | for (auto &ignore : ignoreWhitelist) { 1624 | if (!strcmp(eKey, ignore)) { 1625 | hasDigit = true; 1626 | printf("BAD! (but ignoring due to whitelist)\n"); 1627 | break; 1628 | } 1629 | } 1630 | } 1631 | } 1632 | 1633 | if (!hasDigit) { 1634 | if (findDGST.size()) { 1635 | printf("IGN (hash not found in im4m, but ignoring since we only care about '%s')\n",findDGST.c_str()); 1636 | }else if (!pTrusted){ 1637 | printf("IGN (hash not found in im4m, but ignoring since not explicitly enforced through \"Trusted\"=\"YES\" tag)\n"); 1638 | }else{ 1639 | printf("BAD! (hash not found in im4m)\n"); 1640 | checksPassed = false; 1641 | } 1642 | } 1643 | } 1644 | retassure(checksPassed, "verification failed!"); 1645 | } catch (tihmstar::exception &e) { 1646 | printf("\nfailed verification with error:\n"); 1647 | e.dump(); 1648 | return false; 1649 | } 1650 | return true; 1651 | } 1652 | 1653 | const plist_t tihmstar::img4tool::getBuildIdentityForIm4m(const ASN1DERElement &im4m, plist_t buildmanifest, std::vector ignoreWhitelist){ 1654 | plist_t buildidentities = NULL; 1655 | 1656 | assure(buildmanifest); 1657 | assure(buildidentities = plist_dict_get_item(buildmanifest, "BuildIdentities")); 1658 | assure(plist_get_node_type(buildidentities) == PLIST_ARRAY); 1659 | 1660 | for (int i=0; i=3); 1763 | snprintf(&generatorStr[strlen(generatorStr)], 3, "%02x",(unsigned char)c); 1764 | } 1765 | assure(generator = plist_new_string(generatorStr)); 1766 | 1767 | real_im4m = getIM4MFromIMG4(real_im4m); 1768 | } 1769 | 1770 | retassure(isIM4M(real_im4m), "Not IM4M file"); 1771 | 1772 | retassure(newshsh = plist_new_dict(),"failed to create new plist dict"); 1773 | retassure(data = plist_new_data((const char*)im4m.buf(), im4m.size()),"failed to create plist data from im4m buf"); 1774 | 1775 | plist_dict_set_item(newshsh, "ApImg4Ticket", data); data = NULL; 1776 | if (generator) { 1777 | plist_dict_set_item(newshsh, "generator", generator); generator = NULL; 1778 | } 1779 | 1780 | { 1781 | plist_t rt = newshsh; newshsh = NULL; 1782 | return rt; 1783 | } 1784 | } 1785 | 1786 | #endif //HAVE_PLIST 1787 | #pragma mark end_needs_plist 1788 | --------------------------------------------------------------------------------