-
-
Save mickael9/0b902da7c13207d1b86e to your computer and use it in GitHub Desktop.
| #!/usr/bin/env tclkit | |
| # | |
| # Bitrock unpacking script | |
| # | |
| # This script must be executed using 32-bit tclkit | |
| # | |
| # Author : mickael9 <mickael9 at gmail dot com> | |
| # | |
| # Latest version can be found at: | |
| # https://gist.github.com/mickael9/0b902da7c13207d1b86e | |
| source /usr/bin/sdx.kit | |
| if {$argc < 2} { | |
| puts "Usage: $argv0 installerFile outputDirectory" | |
| exit 1 | |
| } | |
| set installerFile [lindex $argv 0] | |
| set destDir [lindex $argv 1] | |
| set installerMount /installer | |
| set dataMount /installerData | |
| vfs::mk4::Mount $installerFile $installerMount -readonly | |
| lappend auto_path $installerMount/libraries/ | |
| package require vfs::cookfs | |
| catch {package require Tcllzmadec} | |
| # progress from http://wiki.tcl.tk/16939 (sligtly modified) | |
| # thanks to the author | |
| proc progress {cur tot} { | |
| # set to total width of progress bar | |
| set total 76 | |
| if {$cur == $tot} { | |
| # cleanup | |
| set str "\r[string repeat " " [expr $total + 4]]\r" | |
| } else { | |
| set half [expr {$total/2}] | |
| set percent [expr {100.*$cur/$tot}] | |
| set val (\ [format "%6.2f%%" $percent]\ ) | |
| set str "\r|[string repeat = [ | |
| expr {round($percent*$total/100)}]][ | |
| string repeat { } [expr {$total-round($percent*$total/100)}]]|" | |
| set str "[string range $str 0 $half]$val[string range $str [expr {$half+[string length $val]-1}] end]" | |
| } | |
| puts -nonewline stderr $str | |
| } | |
| # Read cookfs options | |
| set optionsFile [open $installerMount/cookfsinfo.txt] | |
| set options [read $optionsFile] | |
| close $optionsFile | |
| # Read the manifest | |
| set manifestFile [open $installerMount/manifest.txt] | |
| set manifest [read $manifestFile] | |
| close $manifestFile | |
| # Mount the files to $dataMount | |
| vfs::cookfs::Mount {*}$options $installerFile $dataMount | |
| puts "Creating directories..." | |
| foreach {fileName props} $manifest { | |
| set type [lindex $props 0] | |
| if {$type == "directory"} { | |
| set mode [lindex $props 1] | |
| file mkdir $destDir/$fileName | |
| file attributes $destDir/$fileName -permissions $mode | |
| } | |
| } | |
| puts "Unpacking files, please wait..." | |
| set entryCount [expr [llength $manifest] / 2] | |
| set entryIndex 0 | |
| foreach {fileName props} $manifest { | |
| set type [lindex $props 0] | |
| if {$type == "file"} { | |
| set mode [lindex $props 1] | |
| set sizes [lindex $props 4] | |
| set nparts [llength $sizes] | |
| set index 1 | |
| file mkdir [file dirname $destDir/$fileName] | |
| file copy -force $dataMount/$fileName $destDir/$fileName | |
| if {$nparts > 0} { | |
| set fp [open $destDir/$fileName a] | |
| fconfigure $fp -translation binary | |
| while {$index < $nparts} { | |
| set chunkName $dataMount/${fileName}___bitrockBigFile$index | |
| set fp2 [open $chunkName r] | |
| fconfigure $fp2 -translation binary | |
| puts -nonewline $fp [read $fp2] | |
| close $fp2 | |
| incr index | |
| } | |
| close $fp | |
| } | |
| file attributes $destDir/$fileName -permissions $mode | |
| } | |
| incr entryIndex | |
| progress $entryIndex $entryCount | |
| } | |
| puts "Creating links..." | |
| foreach {fileName props} $manifest { | |
| set type [lindex $props 0] | |
| if {$type == "link"} { | |
| set linkTarget [lindex $props 1] | |
| file delete $destDir/$fileName | |
| file link -symbolic $destDir/$fileName $linkTarget | |
| } | |
| } | |
| puts "Done" |
password protected (encrypted) installer have "-decompresscommand {::maui::util::MI_oJ zip}" in cookfsinfo.txt and will cause file copy error at line 97.
this happened on installer 19.4.1 or maybe every build with encrypted cookfs.
seems ::maui::util::MI_oJ is a built-in package inside installer bootstrap executable and can't extract as standalone package
## mount cookfs
set cookfsHandle [vfs::cookfs::Mount $src $src -readonly {*}$cookfsinfo]
## prepare decryption
set payloadinfo [$cookfsHandle getmetadata "installbuilder.payloadinfo"]
maui::util::m4ZQu $password $payloadinfo
And I prefer some hack on the installer/pak to turn it into a tclkit.
Something like sed --in-place --null-data --expression='/^if {\[file isfile \[file join \$::tcl::kitpath main.tcl/{s/^./\x1A/}' installer
@zhangyoufu that's cool I'm stealing this!
Wold you care to explain why this works ? Is \x1A considered a comment character?
@mickael9 It seems that Tcl treat \x1A (Ctrl-Z) as eofchar by default.
By terminate execution before reaching main.tcl, an installer is effectively a tclkit without payload script to execute.
I really like this idea because I don’t have to looking for dependencies. The installer already packs them together. You only need to set auto_path correctly and load them.
I use paks/linux-x64-noupx.pak & paks/optional.pak from InstallBuilder installation directory. It’s handy for CI, and it works pretty well against win/mac installers.
@zhangyoufu excellent hack , seems it applies Tcl_EvalEx invoke in TclKit_AppInit?
I have never used tcl before, my goal is extract payload encryption algorithm (I guess it decrypted by tcltwofish10.dll and cookfs14.dll), so I decide hook Tcl_CreateObjCommand in installer stub and hijack ::sha2::sha256c_update/::tcltwofish::decrypt to insert logger.
I think my way is somewhat ugly, could you point me better tcl style methods?
PS: I can't find maui::util package over web. is it contains in sdx.kit or somewhere else?
thank you
@banxian maui is a proprietary package of Bitrock InstallBuilder, you can find it inside the metakit VFS.
I posted my code at https://gist.github.com/zhangyoufu/b85496abe9d9301e2d422858330a471a
Hello I am new to this but I tried to run this script using tclkit-8.6.3-win32-x86_64.exe and got an error "couldn't read file "/usr/bin/sdx.kit": no such file or directory" and when I tried to make an executable from the tcl file I got a similar error What am I doing wrong here ?