-
-
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 ?