#A batch file with unix line-endings is sensitive to label positioning.
#batch file with windows crlf line endings can exhibit this problem - but probably only if specifically crafted with long lines deliberately designed to trigger it.
#see: https://www.dostips.com/forum/viewtopic.php?t=8988#p58888 (Call and goto may fail when the batch file has Unix line endings)
#The windows batch file scanner appears to parse in 512 Byte chunks.
#If a label following a call/goto is at a position spanning a 512 byte block as counted from the call/goto site (callsite assumed to be EOL - works for basic cases at least) then the label won't be found.
#A label preceding a call/goto site can't span a 512 byte boundary as counted from the beginning of the file
#checkoutput produces warnings and errors in ansi-coloured form (both to stdout and a summary in the return value)
#The script should then be adjusted with comments and/or whitespace and checkoutput should be re-run to confirm there are no new boundary-spanning labels.
#checkoutput needs to be run even on previously tested scriptwrapper templates because the final :exit label is beyond the payloads and so could span a 512 Byte block
#It is more likely to catch issues if adjustments are made to the initial batch-script code in a template.
puts stdout "[a+ bold red]ERROR: label '$trimpline' at line $p_linenum and offset from file start: $prior_label_posn total offset: $prior_total_offset[a]"
puts stdout "[a+ bold red] This label appears to span the 512byte boundary at byte $p_ubound[a] [a+ yellow bold]from file start[a]"
} else {
puts stdout "[a+ bold green]OK: prior label '$trimpline' at offset from file start: $prior_label_posn total offset: $prior_total_offset[a]"
}
}
if {[dict get $spaninfo is_span]} {
#check boundaries within the line
set boundaries [dict get $spaninfo boundaries]
foreach b $boundaries {
if {$b == 0} {
#skip - beginning of line already handled (review?)
continue
}
#overlap test is just a warning - we have a label-like thing overlapping the boundary
set overlaptail [string range $pline [expr {$b - $labelsize}] [expr {($b + $labelsize) -1}]] ;#subtracting labelsize gives earliest possible overlap
puts stdout "[a+ bold yellow] WARNING: possible label $label spans boundary $b from start of file"
}
set pline_tail [string range $pline $b end]
#if {[string match ":$label *" $pline_tail]} {}
set re1 {\s*:%lbl%[\s|^|=].*}
set re1 [string map [list %lbl% $label] $re1]
set re2 {\s*:%lbl%$}
set re2 [string map [list %lbl% $label] $re2]
if {[regexp $re1 $pline_tail] || [regexp $re2 $pline_tail]} {
lappend error_labels [list label $label file_offset_bytes $b note "label at boundary but no preceeding newline - cmd may interpret as label and execute following line or code at next boundary" callsite [list call ${call}${labelplus} call_linenum $linenum]]
puts stdout "[a+ bold red]ERROR: *possible* label '$label' at line $p_linenum and offset from file start: $b total offset: $prior_total_offset[a]"
puts stdout "[a+ bold red] This label with no preceeding newline appears to span the 512byte boundary at byte $b[a] [a+ yellow bold]from file start[a]"
append result "The following labels had warnings" \n
foreach w $warning_labels {
append result " [a+ bold yellow]$w[a]" \n
}
}
if {[llength $error_labels]} {
append result "ERROR: label location errors found" \n
append result "The following labels appear to span 512 Byte boundaries or occur on boundaries without a preceding newline and are likely to cause batch script errors" \n
append result "For labels spanning boundaries the label is likely to be missed by the batch interpreter" \n
append result "For labels occuring at boundaries but not at the beginning of a line, the label may be interpreted as a label when not expected, and the interpreter may run code on next line or next boundary" \n
append result "Try adding comments and/or spacing between the call site at the call_lineum indicated and the label and then re-test in case there are further boundary collisions" \n
foreach err $error_labels {
append result " [a+ bold red]$err[a]" \n
}
}
return $result
}
#specific filepath to just wrap one script at the tcl-payload or xxx-payload-pre-tcl site
#specific filepath to just wrap one script at the tcl-payload or xxx-payload-pre-tcl site
#scriptset name to substiture multiple scriptset.xxx files at the default locations - or as specified in scriptset.wrapconf
#scriptset name to substiture multiple scriptset.xxx files at the default locations - or as specified in scriptset.wrapconf
:"[rename set s;proc Hide x {proc $x args {}};Hide :]" "\$(function : {<#pwsh#>})" ^
:"[rename set s;proc Hide x {proc $x args {}};Hide :]" "\$(function : {<#pwsh#>})" ^
set-- "$@""a=[list shebangless punk MULTISHELL tclsh sh bash cmd pwsh powershell;proc Hide x {proc $x args {}}; Hide :;Hide <#;Hide set;s 1 list]"; set -- : "$@";$1 = @'
set-- "$@""a=[list shebangless punk MULTISHELL tclsh sh bash cmd pwsh powershell;proc Hide x {proc $x args {}}; Hide <#;Hide set;s 1 list]"; set -- : "$@";$1 = @'
:heredoc1 - hide from powershell using @ and squote above. (close sqote for unix shells) ' \
:heredoc1 - hide from powershell using @ and squote above. (close sqote for unix shells) ' \
:.bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl \
:.bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl \
:"[Hide @ECHO; Hide ); Hide (;Hide echo; Hide @REM]#not necessary but can help avoid errs in testing"
:"[Hide @ECHO; Hide ); Hide (;Hide echo; Hide @REM]#not necessary but can help avoid errs in testing"
:<< 'HEREDOC1B_HIDE_FROM_BASH_AND_SH'
:<< 'HEREDOC1B_HIDE_FROM_BASH_AND_SH'
:Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \
:Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \
@REM {
:{
@REM DO NOT MODIFY FIRST LINE OF THIS SCRIPT. shebang #! line is not required and will reduce functionality.
:STRONG SUGGESTION: DO NOT MODIFY FIRST LINE OF THIS SCRIPT. shebang #! line is not required on unix or windows and will reduce functionality and/or portability.
@REM Even comment lines can be part of the functionality of this script - modify with care.
:Even comment lines can be part of the functionality of this script (both on unix and windows) - modify with care.
@REM Change the value of nextshell in the next line if desired, and code within payload sections as appropriate.
@REM On windows, change the value of nextshell to one of the listed 2 digit values if desired, and add code within payload sections for tcl,sh,bash,powershell as appropriate.
@REM This wrapper can be edited manually (carefully!) - or sh,bash,tcl,powershell scripts can be wrapped using the Tcl-based punkshell system
@REM e.g from within a running punkshell: pmix scriptwrap.multishell <inputfilepath> -outputfolder <folderpath>
@REM On unix-like systems, call with sh, bash or tclsh. (powershell untested on unix - and requires wscript if security elevation is used)
@REM Due to lack of shebang (#! line) Unix-like systems will probably (hopefully) default to sh if the script is called without an interpreter - but it may depend on the shell in use when called.
@REM If you find yourself really wanting/needing to add a shebang line - do so on the basis that the script will exist on unix-like systems only.
@SET"validshells=pwsh,sh,bash,tclsh"
@SETshells[10]="pwsh"
@SETshells[11]="sh"
@setshells[12]="bash"
@SETshells[13]="tclsh"
:<nextshell>
@SET"nextshell=tclsh"
@SET"nextshell=tclsh"
:</nextshell>
@rem asadmin is for automatic elevation to administrator. Separate window will be created (seems unavoidable with current elevation mechanism) and user will still get security prompt (probably reasonable).
:<asadmin>
@SET"asadmin=0"
:</asadmin>
@REM nextshell set to pwsh,sh,bash or tclsh
@REM nextshell set to pwsh,sh,bash or tclsh
@REM @ECHO nextshell is %nextshell%
@REM @ECHO nextshell is %nextshell%
@SET"validshells=pwsh,sh,bash,tclsh"
@CALLSETkeyRemoved=%%validshells:%nextshell%=%%
@CALLSETkeyRemoved=%%validshells:%nextshell%=%%
@REM Note that 'powershell' e.g v5 is just a fallback for when pwsh is not available
@REM Note that 'powershell' e.g v5 is just a fallback for when pwsh is not available
@REM -- This section intended only to launch the next shell
@REM -- This section intended mainly to launch the next shell (and to escalate privileges if necessary)
@REM -- Avoid customising this if possible. cmd/batch script is probably the least expressive language and most error prone.
@REM -- Avoid customising this if you are not familiar with batch scripting. cmd/batch script is useful, but is probably the least expressive language and most error prone.
@REM -- For example - as this file needs to use unix-style lf line-endings - the label scanner is susceptible to the 512Byte boundary issue: https://www.dostips.com/forum/viewtopic.php?t=8988#p58888
@REM -- This label issue can be triggered/abused in files with crlf line endings too - but it is less likely to happen accidentaly.
@REm -- See also: https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/4095133#4095133
@REM -- Due to this issue -seemingly trivial edits of the batch file section can break the script! (for Windows anyway)
@REM -- Even something as simple as adding or removing an @REM
@REM -- From within punkshell - use:
@REM -- pmix scriptwrap.checkoutput <filepath>
@REM -- to check your templates or final wrapped scripts for byte boundary issues
@REM -- It will report any labels that are on boundaries
@REM -- This is why the nextshell value above is a 2 digit key instead of a string - so that editing the value doesn't change the byte offsets.
@REM -- Editing your sh,bash,tcl,pwsh payloads is much less likely to cause an issue. There is the possibility of the final batch :exit_multishell label spanning a boundary - so testing using pmix scriptwrap.checkoutput is still recommended.
@REM -- Alternatively, as you should do anyway - test the final script on windows
@REM -- Aside from adding comments/whitespace to tweak the location of labels - you can try duplicating the label (e.g just add the label on a line above) but this is not guaranteed to work in all situations.
@REM -- '@REM' is a safer comment mechanism than a leading colon - which is used sparingly here.
@REM -- A colon anywhere in the script that happens to land on a 512 Byte boundary (from file start or from a callsite) could be misinterpreted as a label
@REM -- It is unknown what versions of cmd interpreters behave this way - and pmix scriptwrap.checkoutput doesn't check all such boundaries.
@REm -- For this reason, batch labels should be chosen to be relatively unlikely to collide with other strings in the file, and simple names such as :exit or :end should probably be avoided