碳基体

奋斗在产品安全第一线的安全妹子

PoedCrackMod源码分析

由于lofter有长度限制,完整源码见

https://danqingdani.blog.163.com/blog/static/18609419520129261354800/

关键步骤解说(以crack arm6+arm7 on arm7 devices的完整过程为例
第一步:提取出mach-o文件中armv6部分lipo -thin armv6 "$WorkDir/$AppName/$AppExec" -output "$WorkDir/$AppName/IWantYourSix"
第二步:提取出mach-o文件中armv7部分方法一:交换armv6和armv7 头部echo -ne "\x09" | dd bs=1 seek=15 conv=notrunc  of="$WorkDir/$AppName/$AppExec"
 

将第16个字节处的06变成09

echo -ne "\x06" | dd bs=1 seek=35 conv=notrunc  of="$WorkDir/$AppName/$AppExec" 

将第36个字节处的09变成06

如下图所示,右边为原始mach-o文件,经过上面两步,我们发现第16个字节处由06变成09,第36个字节处由09变成06

 提取mach-o中armv7部分

lipo -thin armv6 "$WorkDir/$AppName/$AppExec" -output "$WorkDir/$AppName/SevenTrumpets"


方法二:

 lipo -thin armv7 "$WorkDir/$AppName/$AppExec" -output "$WorkDir/$AppName/SevenTrumpets"


第三步:获得cryptid,cryptsize,cryptoffset等重要数值

    CryptID=$(otool -l "$WorkDir/$AppName/$AppExecCur" | grep cryptid | awk '{print $2}')

    if [ $CryptID != "1" ]; then

        echo "Application is not encrypted"

        rm -fr "$WorkDir"

        exit 1

    fi


    CryptSize=$(otool -l "$WorkDir/$AppName/$AppExecCur" | grep cryptsize | awk '{print $2}')

    if [ ! $CryptSize ]; then

        echo "Unable to find CryptSize"

        rm -fr "$WorkDir"

        exit 1

    fi


    CryptOff=$(otool -l "$WorkDir/$AppName/$AppExecCur" | grep cryptoff | awk '{print $2}')

    if [ ! $CryptOff ]; then

        echo "Unable to find CryptOff"

        rm -fr "$WorkDir"

        exit 1

    fi

cryptid为加密状态,0表示未加密,1表示解密;

cryptoffset未加密部分的偏移量,单位bytes

cryptsize加密段的大小,单位bytes


第四步:将armv6部分IWantYourSix的cryptID修改为0

方法一:

  dd bs=4096 count=1 if="$WorkDir/$AppName/$AppExecCur" 2> /dev/null | \

    od -A n -t x1 -v | \
    tr -d ' ','\n' | \
    sed "s/0000002100000014......................01.*/ThisIsItThisIsItThisIsItThisIsItHere!!/g" > "$WorkDir/hex.tmp"
    foo=$(echo -ne "\x00" | dd bs=1 seek=$(expr $(($(stat -c%s "$WorkDir/hex.tmp"))) / 2) conv=notrunc status=noxfer of="$WorkDir/$AppName/$AppExecCur" 2>&1> /dev/null)
    rm -f "$WorkDir/hex.tmp"


方法二:

也可以直接使用十六进制编辑器例如mac上的0xED打开mach-O文件,搜索2100000014000000,一般会找到2处,如果从21开始第17个字节处为01,则将其改成00


第五步:使用gdb将解密部分导出

方法一:

  # Creating GDB script

    echo -e "set sharedlibrary load-rules \".*\" \".*\" none\r\n\

    set inferior-auto-start-dyld off\r\n\

    set sharedlibrary preload-libraries off\r\n\

    set sharedlibrary load-dyld-symbols off\r\n\

    handle all nostop\r\n\

    rb doModInitFunctions\r\n

    command 1\r\n\

    dump memory $WorkDir/dump.bin 0x2000 $(($CryptSize + 0x2000))\r\n\

    kill\r\n\

    quit\r\n\

    end\r\n\

    start" > $WorkDir/batch.gdb


    # Cracking the previously swapped fat binary in WorkDir

#注意:要在arm 7的设备上运行arm 6部分,需要swap header处理后的mach-o文件

(If first part (arm6) of a fat binary and running an arm7 iDevice, swap trick)

         foo=$(gdb -q -e "$WorkDir/$AppName/$AppExec" -x $WorkDir/batch.gdb -batch > /dev/null 2>&1> /dev/null)


方法二:

danimato-iPod:/private/var/mobile/Applications/CF3BBE01-40FB-48D2-BF6C-FF6D04916E6A/name.app root# gdb -q -e AppName

Reading symbols for shared libraries .. done

(gdb) set sharedlibrary load-rules .* .* none    

(gdb) set inferior-auto-start-dyld off

(gdb) set sharedlibrary preload-libraries off

(gdb) rb doModInitFunctions                

Breakpoint 1 at 0x2fe0cece

<function, no debug info> __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE;

(gdb) r

Starting program: /private/var/mobile/Applications/CF3BBE01-40FB-48D2-BF6C-FF6D04916E6A/Mole's World.app/Mole's World


Breakpoint 1, 0x2fe0cece in __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE ()


(gdb) x /10i 0x3390   查看是否解密成功 (0x3390为程序加载地址,可以通过otool工具查看加载地址)

0x3af8:  00 00 9d e5                   ldr r0, [sp]

0x3afc:  04 10 8d e2                   add r1, sp, #4 ; 0x4

0x3b00:  01 40 80 e2                   add r4, r0, #1 ; 0x1

0x3b04:  04 21 81 e0                   add r2, r1, r4, lsl #2

0x3b08:  07 d0 cd e3                   bic sp, sp, #7 ; 0x7

0x3b0c:  02 30 a0 e1                   mov r3, r2

0x3b10:  04 40 93 e4                   ldr r4, [r3], #4

0x3b14:  00 00 54 e3                   cmp r4, #0 ; 0x0

0x3b18:  fc ff ff 1a                   bne 0x3b10

0x3b1c:  18 c0 9f e5                   ldr r12, [pc, #24] ; 0x3b3c


(gdb) dump memory AppNamearmv6.bin 0x2000 0x2000+cryptsize 导出解密部分



加载地址(__text __TEXT 专门存放executable machine code

        

 DANI-LEE-2:name.app$ otool -l appname | grep -i "sectname __text" -A 11 -B 1

Section

  sectname __text

   segname __TEXT

      addr 0x00003390

      size 0x00438b24

    offset 11000

     align 2^3 (8)

    reloff 0

    nreloc 0

     flags 0x80000400

reserved1 0

reserved2 0

Section


注意:实际上只有这一步必须在iOS设备上运行,gdb调试时,由于设备可用内存小常常导致中断,所以调试时,最好关闭其他程序


第六步:用第四步中的解密部分替换IWantYourSix加密部分

foo=$(dd seek=1 count=1 obs=4096 ibs=$DumpSize conv=notrunc if="$WorkDir/dump.bin" of="$WorkDir/$AppName/$AppExecCur" 2>&1> /dev/null)

第七步:lamerpatcher处理(不一定管用,主要是为了绕过常规的破解检测,常规破解检测一般根据破解前后文件系统的变化作为特征来定位,lamperpatcher做的就是将这些特征字符串替换掉)(不是必须的步骤)

            if [ $ PCMlamerpatcher = "YES" ]; then
        # MinOs check and Signer check: Lite or Full ?
        if [  ${MinOS:0:1} -gt 2 -o $PCMsigner != "YES" ]; then
            echo -n "${Meter58}Trying  LamerPatcherLite... "
            sed --in-place=.BCK \
                -e 's=/Cydia\.app=/Czdjb\.bpp=g' \
                -e 's=/private/var/lib/apt=/prjvbtf/vbr/ljb/bpt=g' \
                -e 's=/Applicat\d0\d0\d0ions/dele\d0\d0\d0teme\.txt=/Bppljcbt\d0\d0\d0jpns/dflf\d0\d0\d0tfmf\.txt=g' \
                -e 's=/Appl\d0\d0\d0ications/C\d0\d0ydi\d0a\.app=/Bppl\d0\d0\d0jcbtjpns/C\d0\d0zdj\d0b\.bpp=g' \
                -e 's=ations/Cy\d0\d0\d0/Applic\d0pp\d0\d0dia.a=btjpns/Cz\d0\d0\d0/Bppljc\d0pp\d0\d0djb.b=g' \
                -e 's=ate/va\d0\d0/priv\d0\d0\d0pt/\d0b/a\d0r/li=btf/vb\d0\d0/prjv\d0\d0\d0pt/\d0b/b\d0r/lj=g' \
                -e 's=pinchmedia\.com=pjnchmfdjb\.cpm=g' \
                -e 's=admob\.com=bdmpb\.cpm=g' \
                -e 's=doubleclick\.net=dpvblfcljck\.nft=g' \
                -e 's=googlesyndication\.com=gppglfszndjcbtjpn\.cpm=g' \
                -e 's=flurry\.com=flvrrz\.cpm=g' \
                -e 's=qwapi\.com=qwbpj\.cpm=g' \
                -e 's=mobclix\.com=mpbcljx\.cpm=g' \
                -e 's=https://ad\.=https://bd/=g' \
                -e 's=https://ads\.=https://bds/=g' \
                -e 's=https://ads2\.=https://bds2/=g' \
                -e 's=adwhirl\.com=bdwhjrl\.cpm=g' \
                -e 's=vdopia\.com=vdppjb\.cpm=g' \
                "$WorkDir/$AppName/$AppExecCur"

            #    "/Applications/Icy\.app"
            #    "/Applications/SBSettings\.app"
            #    "/Library/MobileSubstrate"
        else
            echo -n "${Meter58}Trying  LamerPatcherFull... "
            sed --in-place=.BCK \
                -e 's=SignerIdentity=SjgnfrJdfntjtz=g' \
                -e 's=Jdfnuiuy=Kdgnujuz=g' \
                -e 's=S\d0\d0\d0h\d0\d0\d0g\d0\d0\d0f\d0\d0\d0e=S\d0\d0\d0i\d0\d0\d0g\d0\d0\d0f\d0\d0\d0f=g' \
                -e 's=S\d0\d0\d0i\d0\d0\d0g\d0\d0\d0n\d0\d0\d0e\d0\d0\d0r=S\d0\d0\d0j\d0\d0\d0g\d0\d0\d0n\d0\d0\d0f\d0\d0\d0r=g' \
                -e 's=PfdkboFabkqfqv=PgdkcoGackqgqw=g' \
                -e 's=Sign\d0\d0\d0\d0erId\d0\d0\d0\d0entity=Sjgn\d0\d0\d0\d0frJd\d0\d0\d0\d0fntjtz=g' \
                -e 's=S\d0\d0\d0g\d0\d0\d0n\d0\d0\d0e\d0\d0\d0r=S\d0\d0\d0h\d0\d0\d0o\d0\d0\d0f\d0\d0\d0r=g' \
                -e 's=/Cydia\.app=/Czdjb\.bpp=g' \
                -e 's=/private/var/lib/apt=/prjvbtf/vbr/ljb/bpt=g' \
                -e 's=/Applicat\d0\d0\d0ions/dele\d0\d0\d0teme\.txt=/Bppljcbt\d0\d0\d0jpns/dflf\d0\d0\d0tfmf\.txt=g' \
                -e 's=/Appl\d0\d0\d0ications/C\d0\d0ydi\d0a\.app=/Bppl\d0\d0\d0jcbtjpns/C\d0\d0zdj\d0b\.bpp=g' \
                -e 's=ations/Cy\d0\d0\d0/Applic\d0pp\d0\d0dia.a=btjpns/Cz\d0\d0\d0/Bppljc\d0pp\d0\d0djb.b=g' \
                -e 's=ate/va\d0\d0/priv\d0\d0\d0pt/\d0b/a\d0r/li=btf/vb\d0\d0/prjv\d0\d0\d0pt/\d0b/b\d0r/lj=g' \
                -e 's=pinchmedia\.com=pjnchmfdjb\.cpm=g' \
                -e 's=admob\.com=bdmpb\.cpm=g' \
                -e 's=doubleclick\.net=dpvblfcljck\.nft=g' \
                -e 's=googlesyndication\.com=gppglfszndjcbtjpn\.cpm=g' \
                -e 's=flurry\.com=flvrrz\.cpm=g' \
                -e 's=qwapi\.com=qwbpj\.cpm=g' \
                -e 's=mobclix\.com=mpbcljx\.cpm=g' \
                -e 's=https://ad\.=https://bd/=g' \
                -e 's=https://ads\.=https://bds/=g' \
                -e 's=https://ads2\.=https://bds2/=g' \
                -e 's=adwhirl\.com=bdwhjrl\.cpm=g' \
                -e 's=vdopia\.com=vdppjb\.cpm=g' \
                "$WorkDir/$AppName/$AppExecCur"

            #    "/Applications/Icy\.app"
            #    "/Applications/SBSettings\.app"
            #    "/Library/MobileSubstrate"
            #    "%si %sg %sn %se %sr %sI %sd %st %sy"
            #    "Sig nerId%@%@     ent ity "
            #    "Si  gne rIde    ntity"
        fi

        cmp --silent "$WorkDir/$AppName/$AppExecCur.BCK" "$WorkDir/$AppName/$AppExecCur"
        if [ "$?" != "0" ]; then
            echo "${Meter59}patched something"
            Patched="$Patched patched"
            if [ $PCMkeepunpatched != "YES" ]; then
                rm "$WorkDir/$AppName/$AppExecCur.BCK"
            fi
        else
            echo "${Meter59}found nothing"
            rm "$WorkDir/$AppName/$AppExecCur.BCK"
        fi

    fi

第八步:签名IWantYourSix

        

foo=$(ldid -s "$WorkDir/$AppName/$AppExecCur" 2>&1> /dev/null

第九步:armv7部分SevenTrumpets的cryptID修改为0(同第四步)

第十步:使用gdb将解密部分导出(同第五步)

   # Cracking genuine executable in AppPath

#注意:对于fat binary,arm 7的设备上是默认按arm 7运行,所以需要使用没有经过swap header处理的原始mach-o文件         foo=$(gdb -q -e "$AppPath/$AppName/$AppExec" -x $WorkDir/batch.gdb -batch > /dev/null 2>&1> /dev/null)


第十一步:用第九步中的解密部分替换SevenTrumpets加密部分(同第六步)

第十二步:使用lamerpatcher处理(不是必须的步骤)(同第七步)

第十三步:签名SevenTrumpets(同第八步)

第十四步:合并IWantYourSix和SevenTrumpets

if [ $LipoFail ]; then
    rm -f "$WorkDir/$AppName/$AppExec"
    echo "${Meter61}---"
    if [ -e "$WorkDir/$AppName/SevenTrumpets" ]; then
        echo "${Meter62}Combining both parts into a fat binary"
        lipo -create "$WorkDir/$AppName/IWantYourSix" "$WorkDir/$AppName/SevenTrumpets" -output "$WorkDir/$AppName/$AppExec"
        chmod 777 "$WorkDir/$AppName/$AppExec"

        if [ $PCMkeepunpatched != "YES" ]; then
            rm "$WorkDir/$AppName/IWantYourSix"
            rm "$WorkDir/$AppName/SevenTrumpets"
        fi
    else
        mv "$WorkDir/$AppName/IWantYourSix" "$WorkDir/$AppName/$AppExec"
        chmod 777 "$WorkDir/$AppName/$AppExec"
    fi
fi


第十五步:伪造可执行文件的时间戳,以应付破解检测(不是必须的步骤)

touch -r "$AppPath/$AppName/$AppExec" "$WorkDir/$AppName/$AppExec"

第十六步:根据MinOS版本(plutil -key MinimumOSVersion "Info.plist"),伪造SignerIdentity,目前主流是3.0版本,不需要伪造(不是必须的步骤)

echo -n "${Meter63}MinOS is '$MinOS': "
if [ ${MinOS:0:1} -gt 2 ]; then
    echo "${Meter64}no SignerIdentity needed"
else
    if [ $PCMsigner = "YES" ]; then
        echo "${Meter64}adding SignerIdentity"
        cp -a "$WorkDir/$AppName/Info.plist" "$WorkDir/$AppName/Info.backup.plist"
        plutil -key 'SignerIdentity' -value 'Apple iPhone OS Application Signing' "$WorkDir/$AppName/Info.plist" 2>&1> /dev/null
        plutil -binary "$WorkDir/$AppName/Info.plist" 2>&1> /dev/null
        # Timestamp-back Info.plist to defeat checks
        touch -r "$AppPath/$AppName/Info.plist" "$WorkDir/$AppName/Info.plist"
    else
        echo "${Meter64}but no SignerIdentity !"
    fi
fi

第十七步:伪造iTunesMetadata.plist(不是必须的步骤)

                

        

cp "$AppPath/iTunesMetadata.plist" "$WorkDir/iTunesMetadataSource.plist"

plutil -xml "$WorkDir/iTunesMetadataSource.plist" 2>&1> /dev/null

echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > "$WorkDir/iTunesMetadata.plist"

echo "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> "$WorkDir/iTunesMetadata.plist"

echo "<plist version=\"1.0\">" >> "$WorkDir/iTunesMetadata.plist"

echo "<dict>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<key>appleId</key>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<string>ChatMauve@apple.com</string>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<key>purchaseDate</key>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<date>2010-08-08T08:08:08Z</date>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>artistId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>artistName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>buy-only</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>buyParams</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>copyright</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>drmVersionNumber</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>fileExtension</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 -m1 "<key>genre</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 -m1 "<key>genreId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>itemId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>itemName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>kind</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>playlistArtistName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>playlistName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>price</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>priceDisplay</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>rating</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</dict>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>releaseDate</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>s</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareIcon57x57URL</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareIconNeedsShine</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>softwareSupportedDeviceIds</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</array>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareVersionBundleId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareVersionExternalIdentifier</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>softwareVersionExternalIdentifiers</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</array>" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>subgenres</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</array>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>vendorId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>versionRestrictions</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

echo "</dict>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "</plist>\n" >> "$WorkDir/iTunesMetadata.plist"

# Timestamp Metadata to protect cracker

touch -r "$AppPath/$AppName/Info.plist" "$WorkDir/iTunesMetadata.plist"

第十八步:打包成ipa安装包

        

zip -y -r "$IPAName" Payload/* -x Payload/iTunesArtwork Payload/iTunesMetadata.plist "Payload/Documents/*" "Payload/Library/*" "Payload/tmp/*" "Payload/$AppName/$AppExec" "Payload/$AppName/Info.plist" "Payload/$AppName/SC_Info/*" "Payload/$AppName/_CodeSignature/*" "Payload/$AppName/CodeResources" "Payload/$AppName/ResourceRules.plist" 2>&1> /dev/null



整个过程,除去前期检测必要工具是否存在(例如ldid,plutil,otool),伪造特征文件,可以总结为以下几步:

第一步. 将fat binary切分为armv6,armv7部分(采用swap header技巧)

第二步:获得cryptid,cryptsize,cryptsize

第三步. 将armv6部分的cryptid修改为0,gdb导出对应的armv6解密部分(对经过swap header处理的Mach-O文件进行操作,使得在arm 7设备上,强制运行arm 6部分),替换掉armv6加密部分,签名

第四步. 将armv7部分的cryptid修改为0,gdb导出对应的armv7解密部分(对原Mach-O文件进行操作),替换掉armv7加密部分,签名

第五步.合并解密过的armv6,armv7

第六步.打包成ipa安装包


注明:第三步和第四步是破解的关键,破解是否成功的关键在于导出的解密部分是否正确完整。

由于binary fat格式的mach-o文件在arm 7设备上默认运行arm 7对应代码,当需要导出arm 6对应的解密部分时,要先经过swap header处理,使其在arm 7 设备上按arm 6运行。

来源:碳基体

评论