1 คะแนน โดย GN⁺ 2025-05-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • defendnot ที่สร้างขึ้นระหว่างทริปโซลเป็นเครื่องมือที่พยายามปิดใช้งาน Windows Defender ด้วยการเรียกใช้ API ของบริการ Windows Security Center (WSC) โดยตรง และนำไปสู่การรีเวิร์สเอนจิเนียร์อยู่หลายวัน
  • เครื่องมือก่อนหน้าอย่าง no-defender ใช้วิธีนำโค้ดของแอนติไวรัสที่มีอยู่มาลงทะเบียนกับ WSC ว่ามีแอนติไวรัสตัวอื่นอยู่ หลังจากได้ดาวบน GitHub ราว 1.5k ก็ถูกลบออกตามคำขอ DMCA จากผู้พัฒนาแอนติไวรัส
  • ตอนแรกสามารถทำซ้ำการเรียก COM API ของ WSC ได้ในเวลาประมาณ 1 ชั่วโมง แต่เมื่อ WSC ตรวจสอบโปรเซสที่เรียกใช้ โปรเซสทั่วไปจึงถูกปฏิเสธการเข้าถึง
  • จากการวิเคราะห์พบว่า WSC ตรวจสอบ WinDefend SID, สิทธิ์ผู้ดูแลระบบ, ลายเซ็น และแฟล็ก ForceIntegrity ใน DllCharacteristics ของ PE และการใช้งานขั้นสุดท้ายใช้ Taskmgr.exe
  • ในสภาพแวดล้อมที่ยุ่งยากทั้ง MacBook arm64, พีซีรีโมตในสหรัฐฯ, ความหน่วง Parsec 210ms และ VM ของ Shadow.tech ผู้เขียนแก้ปัญหา path ของ ctx.bin และเงื่อนไขพลังงานของ autorun จนทำงานสำเร็จ

ที่มาของการสร้าง defendnot

  • defendnot เป็นเครื่องมือที่ใช้ API ของบริการ Windows Security Center โดยตรงเพื่อ ปิดใช้งาน Windows Defender
  • แก่นของกระบวนการพัฒนาไม่ได้อยู่ที่การเรียก WSC เอง แต่อยู่ที่การค่อย ๆ อ้อมผ่านเงื่อนไขตรวจสอบและสภาพแวดล้อมการทำงานที่ไม่สะดวก
  • เอกสารทางเทคนิคเกี่ยวกับ WSC ที่ละเอียดกว่านี้จะมีคนอื่นเผยแพร่ในภายหลัง

no-defender และ DMCA เมื่อ 1 ปีก่อน

  • no-defender ที่เผยแพร่เมื่อประมาณ 1 ปีก่อน ใช้ Windows API สำหรับแอนติไวรัสเพื่อปิด Windows Defender
  • API นี้มีไว้สำหรับแจ้งระบบว่า “มีแอนติไวรัสตัวอื่นอยู่แล้ว จึงไม่จำเป็นต้องรันการสแกนของ Defender”
  • คอมโพเนนต์ของระบบที่ดูแลส่วนนี้คือ Windows Security Center (WSC)
  • no-defender ทำงานโดยใช้โค้ด third-party ของแอนติไวรัสที่มีอยู่ เพื่อบังคับให้แอนติไวรัสนั้นถูกลงทะเบียนกับ WSC
  • หลังเผยแพร่ได้ไม่กี่สัปดาห์ก็ได้ประมาณ 1.5k stars และเมื่อผู้พัฒนาแอนติไวรัสที่ถูกใช้ยื่นคำขอ DMCA takedown ก็ลบเนื้อหาใน repository แล้วจบเรื่อง

การวิเคราะห์ WSC ที่เริ่มขึ้นอีกครั้งระหว่างทริปโซล

  • ระหว่างพักอยู่ใน Airbnb ที่โซล MrBruh ส่งข้อความมาว่าเขากำลังดู no-defender และสำรวจว่าจะทำ implementation แบบ “สะอาด” โดยไม่ใช้ไบนารีของแอนติไวรัสได้หรือไม่
  • ปกติผู้เขียนจะพกโน้ตบุ๊ก x86 แยกไว้สำหรับ CTF หรือการ reverse x86 แต่ทริปนี้พกมาแค่ M4Pro MacBook
  • แม้อยู่ในสภาพเสียเปรียบเพราะไม่มีเครื่องสำหรับ reverse x86 ก็เริ่มวิเคราะห์ WSC โดยใช้ช่วงเวลาก่อนเพื่อน ๆ ตื่น

Day 1: ทำซ้ำ WSC COM API และการถูกปฏิเสธการเข้าถึงครั้งแรก

  • MrBruh ส่งไบนารี WSC เวอร์ชันล่าสุดมาให้ และใช้ implementation การลงทะเบียน WSC ของแอนติไวรัสที่เคยใช้ก่อนหน้านี้เป็นตัวอย่างอ้างอิง
  • WSC มี COM API ที่แอนติไวรัสต่าง ๆ ใช้ และสามารถทำซ้ำการเรียกแบบที่แอนติไวรัสเดิมทำได้ในเวลาประมาณ 1 ชั่วโมง
  • ทดสอบโดยบูต Windows arm64 บน Parallels แต่ผลคือ access denied
  • จากประสบการณ์เดิมรู้ว่า WSC ตรวจสอบโปรเซสที่เรียก API และตอนแรกสงสัยว่าอาจเป็นการตรวจสอบลายเซ็น
  • เมื่อ inject โค้ดเข้าไปในโปรเซสที่แอนติไวรัสเดิมใช้สำหรับงาน WSC การลงทะเบียนแอนติไวรัสใหม่และการเรียก COM เพื่ออัปเดตสถานะก็ทำงานได้ปกติ

ความพยายามลบไบนารีของแอนติไวรัสออก

  • เพื่อไม่ให้โปรเจกต์ใหม่ถูกผู้ผลิตแอนติไวรัสรายเดิมร้องเรียนอีก จึงพยายามใช้ไบนารีที่ระบบมีให้แทนไบนารีของแอนติไวรัส
  • เลือก cmd.exe เป็นโปรเซสเป้าหมายแรก แต่การเรียก WSC API ถูกปฏิเสธ
  • เมื่อดู wscsvc.dll พบโค้ดที่ตรวจสอบว่าโปรเซสที่เรียกเป็น PPL หรือไม่
  • โปรเซสที่สร้างด้วย CreateProcessA ธรรมดาไม่ใช่โปรเซสที่ได้รับการป้องกันแบบ PPL และคืนนั้นก็ไปต่อไม่ได้

Day 2: ตั้งค่าสภาพแวดล้อมรีโมตดีบักที่ไม่สะดวก

  • บน MacBook arm64 จัดการ Windows x86 ได้ยาก และไม่อยากต้องทำงานในสภาพแวดล้อม arm64 หรือใช้งาน x64dbg ไม่ได้
  • เพื่อนชื่อ pindos อนุญาตให้เข้าถึงพีซี และรีโมตเข้าไปผ่าน Parsec
  • เนื่องจากเชื่อมต่อจากเกาหลีไปยังพีซีในสหรัฐฯ ความหน่วงเฉลี่ยอยู่ที่ประมาณ 210ms
  • workflow ตอนนั้นยุ่งยากมาก
    • build โมดูลด้วย MSVC บน Windows arm64 ใน Parallels
    • แชร์ผลลัพธ์การ build กับ host ผ่าน shared folder
    • คัดลอกผลลัพธ์ไปยัง VM บนพีซีของ pindos ผ่าน AnyDesk
    • ดีบักบริการ WSC ในสภาพแวดล้อม Parsec ที่มีความหน่วง 210ms
  • สภาพแวดล้อมไม่สะดวกจนทำให้ความเร็วในการพัฒนาและวิเคราะห์ลดลงอย่างมาก

ดีบักบริการ WSC และ WinDefend SID

  • บริการ WSC เป็น DLL ที่รันโดย svchost และปัจจัยหลักที่ขัดขวางการเชื่อมต่อ debugger คือ การป้องกันแบบ PPL
  • เปิด test mode ใน VM และใช้ไดรเวอร์ที่มีโค้ดไม่กี่บรรทัดใน kernel mode เพื่อลบ PPL ของโปรเซสเป้าหมาย
  • จุดที่ cmd.exe ล้มเหลวเมื่อขอลงทะเบียนแอนติไวรัสกับ WSC คือ WscServiceUtils::CreateExternalBaseFromCaller
  • ฟังก์ชันนี้ impersonate เป็น RPC client แล้วตรวจสอบว่า token ของโปรเซสที่เรียกมี WinDefend SID หรือไม่
  • ตอนนั้นผู้เขียนยังไม่คุ้นกับพฤติกรรมของ Windows token จึงคิดว่าถ้า impersonate เป็น WinDefend ก็น่าจะผ่านการตรวจสอบได้
  • ในสภาพอดนอน ยังตีความผิดว่าไบนารีของแอนติไวรัสเดิมก็ผ่านการตรวจ IsMember นี้เช่นกัน

ความพยายาม impersonate เป็น WinDefend และความล้มเหลว

  • หลังศึกษา Windows token อยู่หลายชั่วโมง จึง implement แนวทางให้รันด้วย token ที่มี WinDefend SID เพื่อให้ผ่านการตรวจของ WSC
  • ลองรันโค้ดโดยแนบ WinDefend SID เข้ากับ cmd แล้ว แม้ COM call จะคืนค่า STATUS_SUCCESS แต่การลงทะเบียนแอนติไวรัสใหม่จริง ๆ ไม่เกิดขึ้น
  • แนวทางนี้สุดท้ายไม่เป็นประโยชน์ และต้องกลับมาตรวจสอบอีกครั้งในวันถัดไป

Day 3: สร้างอัลกอริทึมตรวจสอบจริงขึ้นใหม่

  • เมื่อตรวจสอบอีกครั้งพบว่าไบนารีของแอนติไวรัสเดิม ไม่ผ่านการตรวจ WinDefend SID
  • หากผ่านการตรวจนั้น จะได้จัดการ object ของ WSC ของ Windows Defender แต่ไม่สามารถปิด Defender ได้ทันทีด้วยการเรียก WSC เพียงอย่างเดียว จึงไม่มีประโยชน์
  • ลบโค้ด impersonate เป็น WinDefend ออก แล้ววิเคราะห์ branch อื่นของเงื่อนไข
  • branch นั้นตรวจสอบสิ่งต่อไปนี้กับไบนารีที่เรียกใช้
    • โปรเซสอยู่ในสถานะ elevated หรือไม่
    • ลายเซ็นของไบนารีถูกต้องหรือไม่
    • มีแฟล็กเฉพาะใน DllCharacteristics ของ PE หรือไม่
  • แฟล็ก DllCharacteristics ที่ตรวจใน CSecurityVerificationManager::CreateExternalBaseFromPESettings คือ ForceIntegrity
  • สร้างโค้ดที่ทำซ้ำการตรวจไบนารีแบบที่ WSC ทำไว้ในโฟลเดอร์ wsc-binary-check ของ repository defendnot และทดสอบกับไบนารีใน System32

การใช้ Taskmgr.exe และบั๊ก ctx.bin

  • เมื่อเพื่อนต้องกลับไปใช้พีซี จึงเชื่อมต่อเข้า VM โดยตรงผ่าน Parsec แต่พอมี software encoding ด้วย สภาพแวดล้อมก็ยิ่งช้าลง
  • ใช้ Taskmgr.exe แทน cmd.exe แต่ยังเกิด RPC error อยู่
  • เพราะความหน่วงและปัญหาการพิมพ์ ทำให้ดีบักใน VM เดิมได้ยากขึ้น และหลังได้รับคำแนะนำจึงจ่าย $30 สมัคร shadow.tech
  • VM ของ Shadow.tech ใช้ Windows เวอร์ชันเก่ากว่า ทำให้โค้ด wscsvc ไม่ได้ถูก inline รวมไว้ในฟังก์ชันเดียวมากเหมือนเวอร์ชันล่าสุด และผล decompile ก็อ่านง่ายกว่า
  • สาเหตุจริงของ error คือ ชื่อ AV ที่ส่งให้ WSC ผิด
    • defendnot-loader ใช้ ctx.bin ที่บรรจุพารามิเตอร์แบบ serialized เพื่อส่งข้อมูลไปยัง defendnot.dll
    • แม้จะ implement IPC แยกไว้สำหรับติดตามสถานะ แต่การส่งค่า config ยังเหลือวิธี ctx.bin ซึ่งเป็นซากจากโค้ด no-defender เดิม
    • ฟังก์ชันที่หา path ของ ctx.bin ใช้โฟลเดอร์พื้นฐานของโมดูล Taskmgr.exe เป็นฐาน ไม่ใช่ nodefend.dll
    • ผลคือส่ง null byte เป็นชื่อ AV และ WSC ปฏิเสธ buffer นั้น
  • หลังแก้ปัญหา path แล้ว การทดสอบก็สำเร็จ

เก็บโค้ดให้เรียบร้อยและปัญหา autorun

  • เพื่อให้เสร็จภายในวันเดียวกัน จึงเก็บโค้ดและ implement ฟีเจอร์เพิ่มเติมจนถึง 8 โมงเช้า
  • ฟีเจอร์เพิ่มเติมรวมถึงความสามารถในการเพิ่มตัวเองเข้า autorun
  • ตอน 8 โมงเช้าเหลือแค่ autorun ที่ยังทำงานไม่ถูกต้อง หลังทดสอบหลายวิธีแต่ล้มเหลวจึงไปนอน
  • วันถัดมาตรวจสอบอีกครั้ง พบว่าสาเหตุคือ checkbox เกี่ยวกับพลังงานสองรายการตอนสร้าง task ใน Task Scheduler
  • เนื่องจากโน้ตบุ๊กไม่ได้เสียบไฟ AC task จึงไม่ถูกเรียกใช้ และเมื่อปิดแฟล็กดังกล่าว autorun ก็ทำงานได้
  • จากนั้นเก็บโค้ดต่ออีกไม่กี่ชั่วโมงและจบงาน

สรุป

  • งานนี้สนุกก็จริง แต่ปัญหาสภาพแวดล้อมและ workflow รีโมตดีบักที่เจอซ้ำ ๆ ตลอดหลายวันเป็นประสบการณ์ที่ไม่อยากเจออีก
  • โดยเฉพาะ MacBook arm64, VM x86 ระยะไกล, ความหน่วงสูง, encoding ที่ช้า และบั๊ก path ของไฟล์ที่เพิ่งมาโผล่ทีหลัง ทำให้ความยากในการ implement เพิ่มขึ้นมาก
  • เอกสาร WSC เชิงเทคนิคเพิ่มเติมจะเผยแพร่แยกต่างหากในภายหลัง

1 ความคิดเห็น

 
GN⁺ 2025-05-13
ความคิดเห็นจาก Hacker News
  • วิธีที่รุกรานระบบที่สุดแต่ได้ผลที่สุดในการปิด Defender คือบูตด้วย live Linux USB แล้วเปลี่ยนชื่อ C:\ProgramData\Microsoft\Windows Defender จากนั้นสร้างไฟล์เปล่าไว้แทนที่ตรงนั้น

    • Group Policy ยังใช้ได้ดีมาก ผมเลยตั้ง local domain controller ไว้ในโฮมแล็บ แล้วให้มันเปลี่ยนเฉพาะนโยบาย Defender สำหรับผู้ใช้ทุกคนโดยอัตโนมัติ
    • ผลิตภัณฑ์ยอดนิยมตัวหนึ่งก็ทำงานแทบจะด้วยวิธีเดียวกัน แล้วดันทำให้ ประมาณ 25% ของอินเทอร์เน็ตทั้งหมด ล่มไปด้วย
    • แปลกที่ Windows ไม่ตรวจจับการเปลี่ยนแปลงแบบนั้นด้วย signed manifest
  • เผื่ออ้างอิง WSC ย่อมาจาก Windows Security Center

  • ผมค่อนข้างสับสนเพราะสำนวน the antivirus I was using ไม่เข้าใจว่าทำไมบริษัทแอนติไวรัสนั้นถึงมีเหตุผลที่จะส่ง คำขอลบตาม DMCA ไปให้ผู้เขียน
    น่าจะหมายถึงผู้เขียนทำ reverse engineering แอนติไวรัสตัวอื่นแล้วเอาบางส่วนไปใส่ในโปรเจกต์โอเพนซอร์ส แต่ก็เห็นชื่ออย่าง Impersonating WinDefend ด้วย เลยสงสัยว่าท้ายที่สุดแล้วผู้เขียนละเมิดกฎหมายลิขสิทธิ์ในรูปแบบใดรูปแบบหนึ่งหรือเปล่า

    • เท่าที่เข้าใจคือเขาใช้ เปลือกของเครื่องมือแอนติไวรัส ตัวอื่นเพื่อเลี่ยงข้อกำหนดเรื่องลายเซ็น เข้าใจได้ว่าเป็นพื้นที่สีเทา และผมคิดว่ายังมีช่องให้มองว่าเป็นการใช้งานเชิงแปรรูปได้ แต่ผมไม่ใช่นักกฎหมาย
    • ถูกต้อง เขาคัดลอกบางส่วนของโปรแกรมแอนติไวรัสเดิม จึงละเมิด กฎหมายลิขสิทธิ์
      ย่อหน้าก่อนหน้าที่อ้างถึงอธิบายไว้ว่า “โปรเจกต์ใช้โค้ด third-party ของแอนติไวรัสที่มีอยู่แล้ว และบังคับให้แอนติไวรัสนั้นลงทะเบียนกับ WSC ในฐานะแอนติไวรัส”
  • โค้ดนี้ให้ความรู้สึกต้องสาป:
    https://github.com/es3n1n/defendnot/blob/master/defendnot-lo...
    ถ้าอยากรู้ว่าจริง ๆ แล้วเกิดอะไรขึ้น ดูตรงนี้:
    https://github.com/es3n1n/defendnot/blob/master/cxx-shared/s...

    • มีใครที่เชี่ยวชาญเวทมนตร์ C++ ช่วยอธิบายได้ไหมว่าเกิดอะไรขึ้น และทำไมถึงเรียกว่าต้องสาป?
    • เพราะข้อจำกัดด้านเวลา เลยไม่อยาก implement RAII เองสำหรับ object ที่เกี่ยวกับ COM แต่ก็มีแผนจะเปลี่ยนในอัปเดตถัดไป
    • ไม่รู้ว่าตรงไหนต้องสาป รูปแบบ signature ฝั่งเรียกใช้ต่างจากรสนิยมส่วนตัวผมนิดหน่อย แต่ในโค้ดผมก็ใช้ pattern นี้อยู่หลายที่
      ตัวอย่างเช่น ภาษา D มีแนวคิดของ statement ที่รันเมื่อจบ scope ติดมากับภาษาอยู่แล้ว
    • “โค้ดคือวิธีที่คุณปฏิบัติต่อเพื่อนร่วมงาน” - Michael Feather, https://x.com/mfeathers/status/1031176879577780224
      สรุปคือไม่ใช่สิ่งที่ AI เขียน โค้ดนี้หน่วงการเรียกฟังก์ชันไว้จนกว่า object จะหลุดออกจาก scope การ implement ใช้ C macro เพื่อสร้าง syntax ที่สั้นกว่า โดยละบางส่วนของการนิยาม C lambda/anonymous function ที่จำเป็น และสร้างชื่อตัวแปรไม่ซ้ำเพื่อจัดการ deferred call
      แต่ syntax ที่ได้หลีกเลี่ยงการเขียนตัวพิมพ์ใหญ่ซึ่งเป็นธรรมเนียมทั่วไปในการสื่อว่าเป็น C macro และมองเผิน ๆ เหมือนการเรียกฟังก์ชันของ object pointer ถ้าไม่คุ้นกับ pattern นี้ หรือคาดหวังว่า macro จะแสดงต่างออกไป ก็อาจทำให้งงได้
      สำหรับบางคน มันพบได้บ่อยและมีประโยชน์พอที่ในบางบริบทแทบจะมองเป็น idiom ได้ คำอธิบายเชิงเทคนิคใน https://news.ycombinator.com/item?id=43959403#43960905 อธิบายการทำงานของ macro ได้ดี
  • ช่วงวันหยุดผมไป reverse engineering virtual desktop ของ Windows แล้วทำให้วันหยุดดีขึ้นมาก ความทรงจำที่ดีที่สุดของปีที่แล้วคือการได้รู้สึกว่า reverse engineering สนุกจริง ๆ
    ยังได้เรียนรู้เรื่องน่าสนใจเยอะด้วย โดยใต้ RPC ของ Windows มีเลเยอร์ messaging ที่ไม่ได้ทำเอกสารไว้: https://csandker.io/2022/05/24/Offensive-Windows-IPC-3-ALPC....

  • ทุกครั้งผมจะรู้เลยว่าถ้ารูปโปรไฟล์เป็นตัวละครอนิเมะ บทความน่าจะดี เซฟไว้เผื่อตอนที่ต้องกลับไปใช้ สภาพแวดล้อม Windows แย่ ๆ ในภายหลัง

  • สำหรับคนที่สงสัย: WSC ย่อมาจาก Windows Security Center ผมเองก็ต้องไปค้นเหมือนกัน

    • ในบทความมีเขียนว่า “ส่วนของระบบที่จัดการความวุ่นวายนี้เรียกว่า Windows Security Center หรือย่อว่า WSC”
  • ทำไมถึงอยากปิด WSC กันนะ?

    • อาจเป็นเพราะประสิทธิภาพ หรืออาจเป็นเพราะการพัฒนามัลแวร์หรือการแฮ็กก็ได้
    • ถ้าเป็นผู้โจมตี ก็อาจหวังสถานการณ์โชคดีที่ไม่มี ผลิตภัณฑ์ตรวจจับและตอบสนองปลายทาง ตัวอื่นติดตั้งอยู่ ถ้ามี ผลิตภัณฑ์พวกนั้นแทบจะดักจับได้แน่นอน
      ถ้าเป็นผู้ขาย EDR นี่คือ API call แบบพรางตัวที่ใช้กดหรือปิดใช้งาน Windows Firewall ได้ เช่น CrowdStrike น่าจะใช้ได้ทั้ง Windows Firewall หรือ implementation ของตัวเอง
    • ก็เป็นฮาร์ดแวร์ของผม ผมก็แค่ทำตามที่อยากทำ
    • ซอฟต์แวร์แอนติไวรัสทุกตัวอย่างน้อยก็เป็น powervirus ผมไม่อยากสนใจการจู้จี้แบบพี่เลี้ยงที่บอกว่าใช้ netcat.exe ไม่ได้
    • เพราะไม่มีเหตุผลอะไรที่จะอยากฝัง rootkit ให้ตัวเองโดยตั้งใจ
  • เมื่อไม่นานมานี้ผมอ่าน https://nostarch.com/windows-security-internals แล้วบทความนี้ทำให้เข้าใจได้ลึกซึ้งขึ้นมาก
    ผมพอรู้อยู่บ้างว่าการทำงานเบื้องหลังแบบนี้ใน Windows โดยคร่าว ๆ เป็นอย่างไร แต่จังหวะเหมาะมาก บทสุดท้ายของหนังสือเล่มนั้นก็เจาะลึก token และ SID ค่อนข้างละเอียด เหมือนที่ผู้เขียนบทความนี้พูดถึง