
macOS प्रोग्रामों को launchd के निजी XPC इंटरफ़ेस के माध्यम से exec किए बिना स्पॉन करता है, जिससे EDR launchd को पैरेंट के रूप में रिकॉर्ड करता है। one-shot, KeepAlive, और plist-आधारित जॉब्स का समर्थन करता है।
xspawn macOS पर launchd के माध्यम से एक प्रोग्राम प्रारंभ करता है और उस प्रोग्राम को स्वयं कभी exec नहीं करता। उद्देश्य यह है कि एक EDR launchd को पैरेंट के रूप में रिकॉर्ड करे, न कि इस टूल या कॉलिंग शेल को।
द्वारा cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/xspawn
xspawn xpc_pipe_create_from_port(bootstrap_port) खोलता है और _xpc_pipe_interface_routine के माध्यम से किसी प्रोग्राम के निष्पादन को बूटस्ट्रैप करता है, वही निजी XPC पाइप जिसका उपयोग launchctl करता है, और स्वयं उस प्रोग्राम को exec नहीं करता।
/bin/launchctl को exec नहीं करता।gui/<uid> सत्र के साथ macOS (Darwin)cc)makebrew install cppcheck)make
xspawn oneshot -l <label> [-o <stdout>] [-e <stderr>] [--] <program> [args...]
xspawn submit -l <label> [-o <stdout>] [-e <stderr>] [--] <program> [args...]
xspawn remove -l <label>
xspawn load -p <plist>
वन-शॉट (RunAtLoad + LaunchOnlyOnce; 0 का अर्थ है कोई sleep नहीं):
./xspawn oneshot -l com.example.once -- /tmp/helloworld 0
-- के बाद के तर्क ProgramArguments हैं। इसमें इनलाइन कोड (python3 -c, perl -e) शामिल है। CrowdStrike Falcon for macOS पूर्ण CommandLine रिकॉर्ड करता है, इसलिए इंटरप्रेटर के साथ इनलाइन कोड का प्रयोग संयम से करें।
./xspawn oneshot -l com.example.py -o /tmp/py.out -- \
/usr/bin/python3 -c "print('hello world')"
KeepAlive जॉब, launchctl submit के समान जीवनचक्र। Sleep 60 रखें ताकि CrowdStrike Falcon for macOS और launchctl print प्रक्रिया को फिर भी देख सकें:
./xspawn submit -l com.example.svc \
-o /tmp/out.log -e /tmp/err.log -- /tmp/helloworld 60
launchctl print के साथ निरीक्षण करें (केवल oracle के रूप में; यह क्लाइंट इसे कॉल नहीं करता):
launchctl print gui/$(id -u)/com.example.svc
सफलता type = LaunchAgent (न कि Submitted), program निरपेक्ष पथ के रूप में, और state = running या संक्षिप्त रूप से xpcproxy दिखाती है। Submitted का अर्थ है कि जॉब ने बूटस्ट्रैप पथ नहीं लिया।
टेस्ट जॉब साफ़ करें:
./xspawn remove -l com.example.svc
कॉलर-स्वामित्व वाली plist लोड करें (रिप्लाई के बाद हटाई नहीं जाती):
./xspawn load -p /tmp/job.plist
<program> एक निरपेक्ष पथ होना चाहिए। launchd $PATH की खोज नहीं करता।
load -p के लिए .plist में समाप्त होने वाला निरपेक्ष पथ आवश्यक है।
-o / -e सापेक्ष हो सकते हैं। plist में लिखे जाने से पहले उन्हें वर्तमान कार्य निर्देशिका के सापेक्ष हल किया जाता है। छोड़े गए -o और -e /dev/null होते हैं।
oneshot और submit अस्थायी plist लिखने से पहले gui और user (डिस्क्रिप्टर 708) में लेबल की जाँच करते हैं। एक लिया गया लेबल label already loaded संदेश के साथ और बिना stdout के बाहर निकलता है। यह जाँच इसलिए मौजूद है ताकि एक विफल 800 $TMPDIR/XXXXXX/XXXXXX.plist न लिखे (एक DFIR आर्टिफैक्ट; CrowdStrike Falcon पथ को ASEPFilePath में रखता है) या जॉब डिक्शनरी की XML प्रति प्रिंट न करे। एक मुक्त लेबल अस्थायी पथ, फिर वह XML, फिर 800 भेजता है। load -p फ़ाइल के Label पर वही जाँच चलाता है, फिर कॉलर पथ और XML प्रिंट करता है। अस्थायी निर्देशिका हर exit पर हटा दी जाती है। remove लेबल द्वारा होता है।
| कोड | अर्थ |
|---|---|
| 0 | बूटस्ट्रैप या बूटआउट XPC सफल रहा |
| 1 | उपयोग त्रुटि, अमान्य लेबल, रूट, या launchd/XPC अस्वीकृति |
बिल्ड होस्ट (make और टेस्ट ट्री; अक्सर gui सत्र के साथ सह-स्थित)। ये जाँचें स्वच्छ-रनटाइम प्रमाण नहीं हैं:
make
make test
make test-unit
make test-functional
./xspawn oneshot -l com.example.once -- /tmp/helloworld 0
gui/<uid>। रूट को अस्वीकार किया जाता है। कोई other-UID लक्ष्यीकरण नहीं।sw_vers -buildVersion बदलता है तो पुनः-पिन करें (ARCHITECTURE.md देखें)।$TMPDIR/XXXXXX/XXXXXX.plist है ($TMPDIR निरपेक्ष होना चाहिए, अन्यथा /tmp)। निर्देशिका हर exit पर हटा दी जाती है। एक लिया गया लेबल वह फ़ाइल कभी नहीं बनाता।ProcessRollup2 इवेंट में Auto-Start Extensibility Point फ़ील्ड (ASEPFilePath) में रिकॉर्ड करता है। प्रक्रिया पैरेंट launchd बना रहता है।xspawn रन इस क्लाइंट के रूप में दिखाई देता है: शेल इतिहास, और इस बाइनरी के लिए एक EDR प्रक्रिया इवेंट। CrowdStrike Falcon for macOS पूर्ण CommandLine रिकॉर्ड करता है, जिसमें प्रोग्राम पथ और उसके तर्क शामिल होते हैं। जब वह image और argv विशिष्ट हों, तो क्लाइंट को अन्य टूलिंग में संकलित करें। एम्बेडिंग ASEPFilePath या launchd.log बूटस्ट्रैप लाइन को नहीं हटाता (ARCHITECTURE.md, Parentage देखें)।launchd एक Mach बूटस्ट्रैप सर्वर है। यह क्लाइंट सार्वजनिक XPC (xpc_connection_create) का उपयोग नहीं करता। यह विरासत में मिले bootstrap_port पर xpc_pipe_create_from_port(bootstrap_port, 4) के साथ एक निजी libxpc पाइप खोलता है, फिर _xpc_pipe_interface_routine भेजता है। वे प्रतीक libxpc में हैं और SDK हेडर में नहीं हैं।
रूटीन ID डिस्क्रिप्टर तर्क है, अनुरोध डिक्शनरी में एक कुंजी नहीं। macOS 26.6.1 build 25G76 पर, लोड डिस्क्रिप्टर 800 है और बूटआउट 801 है। इंटरफ़ेस फ़्लैग 6 हैं। एक gui/<uid> सत्र आवश्यक है: विरासत में मिला पोर्ट केवल Aqua लॉगिन सत्र के अंदर gui launchd डोमेन है, और यह क्लाइंट केवल handle = uid के साथ type 8 भेजता है।
लोड (800) एक XPC डिक्शनरी है। जॉब परिभाषा संदेश बॉडी में नहीं होती।
handle uid (uint64)
type 8 (gui)
paths [absolute .plist]
by-cli true
launchd पथ को stat करता है, plist को पार्स करता है, फिर xpcproxy को posix_spawn करता है। xpcproxy उसी PID में प्रोग्राम को exec करता है। सफलता pipe रिटर्न 0, कोई xpc-fault नहीं, error 0, bootstrap-error 0 है।
बूटआउट (801) लेबल द्वारा होता है: handle, type 8, name, no-einprogress, wait। कोई plist नहीं।
यह चैनल पूर्व कला है। Jonathan Levin (launjctl, 2015; Mac OS X and iOS Internals Vol. 1) ने दिखाया कि launchctl एक निजी XPC पाइप के माध्यम से launchd से बात करता है, और type, handle, subsystem, routine, और name डिक्ट कुंजियों के साथ xpc_pipe_create_from_port / xpc_pipe_routine का दस्तावेजीकरण किया। Patrick Wardle (The Art of Mac Malware Vol. 2) ने _xpc_pipe_interface_routine को बाद के send entry के रूप में दस्तावेजीकरण किया। Csaba Fitzl और Brandon Dalton (OBTS) ने उसी डिक्ट परिवार और डोमेन प्रकार कोड (gui 8 है) का मानचित्रण किया। सार्वजनिक स्निपेट्स पहले से ही xpc_pipe_create_from_port(bootstrap_port, 4) का उपयोग करते थे।
वे लेख प्रोटोकॉल के वर्ग का वर्णन करते हैं। वे लाइव 25G76 लोड स्थिरांक प्रदान नहीं करते। Levin के 2015 कैप्चर ने subsystem और routine को डिक्शनरी के अंदर रखा और xpc_pipe_routine का उपयोग किया। 25G76 पर वे कुंजियाँ अनुपस्थित हैं। launchctl bootstrap रूटीन ID को डिस्क्रिप्टर तर्क के रूप में _xpc_pipe_interface_routine को हिट करता है। launchctl का arm64e स्थैतिक विश्लेषण अभी भी पुराने डिक्ट पथ जैसा दिखता है और लोड ID के रूप में 703 सुझाता है। लाइव x86_64 lldb और एक arm64 क्लाइंट रन दोनों उन कुंजियों की अनुपस्थिति में 800 / 801 का उपयोग करते हैं। यह क्लाइंट दोनों स्लाइस पर वही एक रूप प्रदान करता है।
रजिस्टर डंप, lldb पुनः-पिन रेसिपी, और पैरेंटेज फ़ील्ड नोट ARCHITECTURE.md में हैं।
| उपकमांड | जीवनचक्र |
|---|
oneshot | वन-शॉट (RunAtLoad + LaunchOnlyOnce) |
submit | KeepAlive |
load | कॉलर-स्वामित्व वाली plist जैसी लिखी गई है |
remove | लेबल द्वारा अनलोड |
/private/var/log/com.apple.xpc.launchd/launchd.log के अंतर्गत भी लॉग करता है (ARCHITECTURE.md, Parentage देखें)।XPCService कुंजी न डालें: तब xpcproxy फोर्क करता है और CrowdStrike Falcon for macOS पैरेंट xpcproxy रिकॉर्ड करता है।