Files
psycep cae9eb3c79 install.sh: prefix cross-compile outputs with gopacket-
The native install already renamed each tool to gopacket-<toolname>
before copying to /usr/local/bin, but the portable and windows cross-
compile targets dropped raw binaries like ping.exe, net.exe, reg.exe,
attrib.exe, services.exe into ./dist/. When a user copies one of those
to a Windows host and runs it from the same directory, cmd.exe's PATH
resolution picks up the local binary before the Windows built-in of
the same name, shadowing tools users rely on. The portable target on
Linux has the same risk for binaries named ping, net, etc.

Applies the same normalization (lowercase, underscores to hyphens)
and gopacket- prefix the native install already uses, but at build
time for the cross-compile targets. Native behavior is unchanged:
./bin/ still gets raw names so install_native can prefix on copy.
2026-04-22 12:28:37 -05:00

382 lines
12 KiB
Bash
Executable File

#!/bin/bash
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# gopacket installer
# Builds all tools and installs them as gopacket-<toolname> on your PATH
#
set -e
# Default install directory
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
BUILD_DIR="./bin"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
usage() {
cat <<'EOF'
Usage: ./install.sh [OPTIONS]
Build and install gopacket tools. Run with no flags to get an interactive
prompt explaining the build targets.
Options:
--target NAME Build target. One of:
native (default) Linux/macOS cgo, installs to
/usr/local/bin. All 63 tools; proxychains
and -proxy both work. Needs GCC + libpcap-dev.
portable Static host-OS binaries, no system libs.
sniff/split stubbed; use -proxy (proxychains
won't hook). Output: ./dist/portable/
windows Windows amd64 cross-compile. sniff/split/
sniffer stubbed; -proxy only (no LD_PRELOAD
on Windows). Output: ./dist/windows/
all Build every target.
--prefix DIR For the native target, install to DIR instead of
/usr/local/bin. Has no effect on cross-compile targets.
--build-only Build but don't install (native target only).
--uninstall Remove previously installed gopacket-* binaries from
$INSTALL_DIR. Affects native installs only.
-h, --help Show this help.
Environment:
INSTALL_DIR Same as --prefix.
Examples:
./install.sh Interactive, prompts for target
./install.sh --target native Today's default (build + install)
./install.sh --target windows Cross-compile Windows .exe files
./install.sh --target all Build every target in one run
EOF
}
target=""
build_only=false
uninstall=false
while [[ $# -gt 0 ]]; do
case "$1" in
--target)
target="$2"
shift 2
;;
--prefix)
INSTALL_DIR="$2"
shift 2
;;
--build-only)
build_only=true
shift
;;
--uninstall)
uninstall=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
# Uninstall mode
if $uninstall; then
echo "Removing gopacket tools from ${INSTALL_DIR}..."
count=0
for f in "${INSTALL_DIR}"/gopacket-*; do
if [ -f "$f" ]; then
echo " removing $(basename "$f")"
rm -f "$f"
((count++))
fi
done
if [ $count -eq 0 ]; then
echo "No gopacket tools found in ${INSTALL_DIR}"
else
echo -e "${GREEN}Removed ${count} tools${NC}"
fi
exit 0
fi
# Determine script directory (where go.mod lives)
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
if [ ! -f go.mod ]; then
echo -e "${RED}Error: go.mod not found. Run this script from the gopacket directory.${NC}"
exit 1
fi
# Interactive prompt if no --target given and stdin is a terminal.
if [ -z "$target" ]; then
if [ -t 0 ]; then
cat <<'EOF'
gopacket installer
Pick a build target. If unsure, pick (1).
(1) native
Linux/macOS cgo, installs to /usr/local/bin. All 63 tools; supports
both proxychains (LD_PRELOAD) and the -proxy SOCKS5 flag.
Needs GCC + libpcap-dev installed.
(2) portable
Static single-file binaries for your host OS, no system libs needed.
sniff/split are stubs (they need libpcap). proxychains can't hook a
cgo-off Go binary, so use -proxy instead. Output: ./dist/portable/
(3) windows
Windows amd64 .exe cross-compile. sniff/split/sniffer are stubs.
No LD_PRELOAD on Windows, so -proxy is the only proxy path.
Output: ./dist/windows/
(4) all
Build every target.
EOF
read -r -p "Choice [1-4, default 1]: " choice
choice="${choice:-1}"
case "$choice" in
1) target="native" ;;
2) target="portable" ;;
3) target="windows" ;;
4) target="all" ;;
*) echo -e "${RED}Invalid choice: $choice${NC}"; exit 1 ;;
esac
echo ""
else
# Non-TTY (piped, CI). Default to native without prompting.
target="native"
fi
fi
# Validate target
case "$target" in
native|portable|windows|all) ;;
*)
echo -e "${RED}Error: unknown target '$target'${NC}"
echo "Valid targets: native, portable, windows, all"
exit 1
;;
esac
# Check the Go toolchain is present (all targets need this).
if ! command -v go &>/dev/null; then
echo -e "${RED}Error: go is not installed or not in PATH${NC}"
echo "Install Go from https://go.dev/dl/"
exit 1
fi
# build_target runs the build for one of: native, portable, windows.
# Sets the right GOOS/CGO_ENABLED, picks an output directory, and iterates
# over every tool in tools/. Stubs defined in the tools handle the cases
# where a tool cannot be built for the target.
build_target() {
local t="$1"
local goos goarch cgo outdir exe_suffix pcap_check label
case "$t" in
native)
goos=""
goarch=""
cgo=1
outdir="$BUILD_DIR"
exe_suffix=""
pcap_check=true
label="native (host OS, cgo on)"
;;
portable)
goos="" # inherit host OS
goarch="" # inherit host arch
cgo=0
outdir="./dist/portable"
exe_suffix=""
pcap_check=false
label="portable (host OS, cgo off, sniff/split stubbed)"
;;
windows)
goos="windows"
goarch="amd64"
cgo=0
outdir="./dist/windows"
exe_suffix=".exe"
pcap_check=false
label="windows (GOOS=windows amd64, sniff/split/sniffer stubbed)"
;;
esac
echo ""
echo -e "${GREEN}=== Building ${label} ===${NC}"
# Native target needs GCC for cgo and benefits from libpcap to get real
# sniff/split. Cross-compile targets use stubs and need neither.
if [ "$t" = "native" ]; then
if ! command -v gcc &>/dev/null; then
echo -e "${RED}Error: gcc is required for the native target (cgo)${NC}"
echo "Install with: apt install build-essential (Debian/Ubuntu) or yum install gcc (RHEL/CentOS)"
return 1
fi
fi
local has_libpcap=true
if $pcap_check; then
if ! [ -f /usr/include/pcap.h ] \
&& ! [ -f /usr/include/pcap/pcap.h ] \
&& ! [ -f /usr/local/include/pcap.h ] \
&& ! [ -f /opt/homebrew/include/pcap.h ] \
&& ! pkg-config --exists libpcap 2>/dev/null; then
has_libpcap=false
echo -e "${YELLOW}Warning: libpcap development headers not found${NC}"
echo " sniff and split will be skipped in this target."
echo " Install with: apt install libpcap-dev (Debian/Ubuntu/Kali)"
echo " or yum install libpcap-devel (RHEL/CentOS)"
echo " or brew install libpcap (macOS)"
echo ""
fi
fi
local tools
tools=($(ls tools/))
local total=${#tools[@]}
mkdir -p "$outdir"
local failed=0 skipped=0
for tool in "${tools[@]}"; do
# On native without libpcap, skip sniff/split entirely (same as the
# old behavior). On other targets, stubs handle the skip automatically.
if [ "$t" = "native" ] && ! $has_libpcap && { [ "$tool" = "sniff" ] || [ "$tool" = "split" ]; }; then
echo -e " ${tool}... ${YELLOW}skipped (libpcap-dev not installed)${NC}"
skipped=$((skipped + 1))
continue
fi
echo -n " ${tool}... "
# Cross-compile outputs get the same gopacket- prefix as the native
# install, so users copying a .exe to a Windows host don't end up with
# ping.exe / net.exe / reg.exe shadowing built-ins of the same name.
# Native stays as the raw name here; install_native prefixes when it
# copies to INSTALL_DIR.
local out_name
if [ "$t" = "native" ]; then
out_name="$tool"
else
local normalized
normalized=$(echo "$tool" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
out_name="gopacket-${normalized}"
fi
local out_path="${outdir}/${out_name}${exe_suffix}"
local build_cmd=(go build -o "$out_path")
if [ "$t" = "native" ]; then
# Static-link libgcc so binaries run on minimally-versioned hosts.
build_cmd+=(-ldflags '-linkmode external -extldflags "-static-libgcc"')
fi
build_cmd+=("./tools/${tool}")
if err=$(GOOS="$goos" GOARCH="$goarch" CGO_ENABLED="$cgo" "${build_cmd[@]}" 2>&1); then
echo -e "${GREEN}ok${NC}"
else
echo -e "${RED}failed${NC}"
echo "$err" | sed 's/^/ /'
failed=$((failed + 1))
fi
done
if [ $failed -gt 0 ]; then
echo -e "\n${RED}${failed} tool(s) failed to build for ${t}${NC}"
return 1
fi
local built=$((total - skipped))
echo -e "\n${GREEN}Built ${built}/${total} tools for ${t} in ${outdir}/${NC}"
if [ $skipped -gt 0 ]; then
echo -e "${YELLOW}Skipped ${skipped} tool(s), install libpcap-dev to enable them${NC}"
fi
return 0
}
# Install is only meaningful for the native target. Cross-compile outputs
# live in ./dist/ for the user to copy to the right host.
install_native() {
echo ""
echo "Installing to ${INSTALL_DIR}/ as gopacket-<toolname>..."
local SUDO=""
if [ ! -w "${INSTALL_DIR}" ]; then
echo -e "${YELLOW}Note: ${INSTALL_DIR} requires elevated permissions${NC}"
echo "Re-running install step with sudo..."
SUDO="sudo"
fi
local tools
tools=($(ls tools/))
local installed=0
for tool in "${tools[@]}"; do
if [ ! -f "${BUILD_DIR}/${tool}" ]; then
continue
fi
local normalized
normalized=$(echo "$tool" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
local dest="${INSTALL_DIR}/gopacket-${normalized}"
$SUDO cp "${BUILD_DIR}/${tool}" "$dest"
$SUDO chmod +x "$dest"
installed=$((installed + 1))
done
echo -e "${GREEN}Installed ${installed} tools to ${INSTALL_DIR}/${NC}"
echo ""
echo "Tools are available as:"
echo " gopacket-secretsdump, gopacket-smbclient, gopacket-psexec, etc."
echo ""
echo "Run 'gopacket-<tool> -h' for help on any tool."
echo "To uninstall: $0 --uninstall"
}
# Run the requested target(s).
if [ "$target" = "all" ]; then
build_target native || exit 1
build_target portable || exit 1
build_target windows || exit 1
if ! $build_only; then
install_native
fi
echo ""
echo -e "${GREEN}All targets built.${NC}"
echo " native -> ${BUILD_DIR}/"
echo " portable -> ./dist/portable/"
echo " windows -> ./dist/windows/"
exit 0
fi
build_target "$target" || exit 1
if [ "$target" = "native" ] && ! $build_only; then
install_native
elif [ "$target" = "native" ] && $build_only; then
echo ""
echo "Binaries are in ${BUILD_DIR}/"
else
echo ""
echo "Binaries are in $([ "$target" = "portable" ] && echo ./dist/portable/ || echo ./dist/windows/)"
echo "Copy them to the target host and run."
fi