2 คะแนน โดย GN⁺ 2024-01-15 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • สภาพแวดล้อม Lua addon ของ World of Warcraft ทำให้ปรับ UI ได้ง่าย แต่เพื่อป้องกันการทำอัตโนมัติกับพฤติกรรมในเกม จึงแยกสิทธิ์การเข้าถึงข้อมูลออกจากสิทธิ์การสั่งทำงาน
  • โมเดลความปลอดภัยของ Blizzard แยก สภาพแวดล้อมที่ไม่ปลอดภัย ซึ่งอ่านข้อมูลได้มาก ออกจาก สภาพแวดล้อมที่ปลอดภัย ซึ่งทำการกระทำได้ และเส้นแบ่งนี้คือหัวใจของการป้องกัน automation
  • random ที่อนุญาตในสภาพแวดล้อมที่ปลอดภัยเป็นเพียง wrapper บาง ๆ ของ rand ใน MSVC C และเป็น linear congruential generator ที่อ่อนแอซึ่งใช้สถานะ global ร่วมกัน จึงกลายเป็นช่องทางส่งข้อมูลได้
  • จากเอาต์พุต RNG เพียงสองครั้ง สามารถลด candidate ของสถานะภายในให้เหลือมากสุด 8 รายการ แล้วใช้เอาต์พุตเพิ่มเติมเพื่อเลือก candidate เดียว จากนั้นเลื่อน RNG ไปข้างหน้าจนกว่าจะได้ดัชนีพฤติกรรมที่ต้องการ
  • ภายนอกดูเหมือนเป็นการเลือกพฤติกรรมแบบสุ่ม แต่จริง ๆ แล้วสามารถทำให้เลือกการกระทำอย่างคาถาที่ต้องการได้ และไม่แน่ชัดว่า Blizzard แก้ไขภายหลังด้วยวิธีใด

Addon ของ World of Warcraft กลายเป็นจุดเริ่มต้นในการเขียนโปรแกรม

  • Addon ของ World of Warcraft มีโครงสร้างที่เพียงใส่ไฟล์ซอร์ส .lua ที่เขียนด้วย Lua ไม่กี่ไฟล์ลงในโฟลเดอร์ แล้วเกมจะโหลดโดยตรง
  • หลังแก้ไขไฟล์และบันทึก จากนั้น reload interface ก็เห็นผลลัพธ์ได้ทันที ทำให้ กำแพงในการเริ่มต้น ต่ำมาก
  • ช่วงแรกสร้าง addon หลายตัวด้วยวิธีคัดลอกโค้ดของคนอื่น แล้ว refactor หรือประกอบใหม่เพื่อทำฟีเจอร์ที่ต้องการ
  • เมื่อเวลาผ่านไป กลับใช้เวลากับการเขียน addon เองมากกว่าการเล่นเกมจริง และ World of Warcraft ก็กลายเป็นจุดเริ่มต้นของการเขียนโปรแกรมอย่างจริงจัง

โมเดลความปลอดภัยของ addon ของ Blizzard

  • หากผู้ใช้สร้าง addon ที่เขียนโปรแกรมได้อย่างสมบูรณ์ อาจเกิดปัญหา bot ได้ Blizzard จึงสร้างโครงสร้างที่จำกัด automation ที่ผู้เล่นทั่วไปทำได้
  • UI ส่วนใหญ่และ API สำหรับรวบรวมข้อมูลสามารถใช้ได้โดยไม่มีข้อจำกัด
    • เช่น สามารถอ่านพลังชีวิตของตัวละครเพื่อสร้าง แถบพลังชีวิต ที่ปรับขนาดของ frame ด้านหน้าได้
  • API ที่ทำการกระทำในเกมจริง เช่น การเคลื่อนที่ของตัวละคร การร่ายคาถา หรือการใช้ไอเท็ม ถูกป้องกันไว้และเรียกได้เฉพาะจากโค้ดทางการของ Blizzard
  • API สำหรับรับตำแหน่งในโลกอย่างแม่นยำและทิศทางกล้องก็ถูกจัดเป็นส่วนที่ต้องป้องกันตั้งแต่ช่วงหนึ่ง
    • เป็นมาตรการรับมือ addon ที่วาดองค์ประกอบ 3D ทับบนโลกของเกมเพื่อให้การสู้บอสง่ายขึ้น

การแยกสภาพแวดล้อมปลอดภัยและไม่ปลอดภัย

  • UI บางส่วนจำเป็นต้องโต้ตอบกับการกระทำจริงในเกม จึงสามารถสร้างปุ่มพิเศษที่เมื่อคลิกแล้วรันโค้ดใน สภาพแวดล้อมที่ปลอดภัย ได้
  • ปุ่มเหล่านี้ไม่สามารถสร้าง ลบ หรือย้ายระหว่างการต่อสู้ได้
    • จึงปิดกั้นวิธีทำ automation ด้วยการวางปุ่มแบบมีเงื่อนไขใต้ cursor ระหว่างการต่อสู้
  • สภาพแวดล้อมที่ปลอดภัยสามารถตั้งค่าแบบโปรแกรมได้ว่าจะร่ายคาถาใด แต่ปิดกั้นการเข้าถึงสถานะภายนอกที่จำเป็นต่อ automation ตามอำเภอใจ
  • อนุญาตเฉพาะ API รวบรวมข้อมูลบางส่วนในระดับระบบ macro และไม่ให้ข้อมูลที่จำเป็นต่อการเลือกคาถาที่เหมาะที่สุด เช่น cooldown ของสกิลหรือพลังชีวิตของ unit
  • ผลคือทั้งสองสภาพแวดล้อมมีสิทธิ์ต่างกัน
    • สภาพแวดล้อมที่ไม่ปลอดภัย: รับข้อมูลได้มาก แต่ทำการกระทำไม่ได้
    • สภาพแวดล้อมที่ปลอดภัย: ทำการกระทำได้ แต่รับข้อมูลที่จำเป็นต่อ automation ไม่ได้

random กลายเป็นช่องทาง backdoor

  • เป้าหมายไม่ใช่การใช้ซอฟต์แวร์ third-party เพื่อปิดข้อจำกัดด้านความปลอดภัย แต่เป็นการตรวจสอบว่าจะข้ามสภาพแวดล้อมที่ปลอดภัยด้วยเครื่องมือที่ได้รับอนุญาตทางเทคนิคเท่านั้นได้หรือไม่
  • ระหว่างดูรายชื่อฟังก์ชันที่อนุญาตในสภาพแวดล้อมที่ปลอดภัย ก็พบ random
  • ตัวสร้างเลขสุ่มในคอมพิวเตอร์ส่วนใหญ่เป็น pseudorandom number generator ที่มีสถานะภายใน จึงมองว่าหากควบคุมสถานะนั้นได้ ก็สามารถส่งข้อมูลภายนอกเข้าสู่สภาพแวดล้อมที่ปลอดภัยได้
  • random จริง ๆ แล้วเป็น wrapper เล็ก ๆ ที่ครอบ rand ของ C
  • เพราะ World of Warcraft คอมไพล์ด้วย MSVC การ implement rand จึงเป็น generator ที่อิงสถานะ global
uint32_t state;

int rand() {
    state = state * 214013 + 2531011;
    return (state >> 16) & 0x7fff;
}
  • implementation นี้แชร์สถานะเลขสุ่ม global เดียวกันทั้ง process และเป็น linear congruential generator ที่อ่อนแอ

การกู้สถานะของ MSVC rand

  • เอาต์พุตของ rand เปิดเผย 15 บิตซึ่งเป็นส่วนหนึ่งของสถานะโดยตรง
  • เพราะ state space เล็กและเห็นบางส่วนของเอาต์พุต จึงสามารถรักษารายการสถานะ candidate ที่เป็นไปได้ไว้ แล้วใช้เอาต์พุตเพิ่มเติมตัด candidate ออกได้
  • วิธีที่มีประสิทธิภาพกว่าคือสังเกตเอาต์พุต RNG สองครั้งคือ r0, r1 แล้วคำนวณ candidate ของส่วนสถานะ 16 บิตที่ซ่อนอยู่
  • ค่าคงที่ของ MSVC rand มีดังนี้
    • a = 214013
    • b = 2531011
  • หากให้ส่วน 16 บิตที่ซ่อนอยู่หลังการเรียกครั้งแรกและครั้งที่สองเป็น h0, h1 ตามลำดับ สามารถอนุมาน candidate ของ h0 ได้จากสมการเปลี่ยนสถานะ
  • ผลการคำนวณทำให้ต้องพิจารณา k เพียงช่วง -1 <= k < 7 ดังนั้น candidate ของสถานะที่ซ่อนอยู่จึงลดลงเหลือมากสุด 8 รายการ
  • จากนั้นเปรียบเทียบผลลัพธ์จากการเรียก RNG เพิ่มเติมกับเอาต์พุตของ RNG candidate เพื่อตัด candidate ที่ผิดออก
  • ในตัวอย่าง implementation ด้วย Python ใช้ลูป while แต่ในการใช้งานจริง ดูเหมือนว่าแค่เอาต์พุตครั้งที่สามก็ทำให้แคบลงเหลือ candidate เดียวได้
  • ตอนนั้นยังยากที่จะทำอนุมานทางคณิตศาสตร์ไม่ต่อเนื่องนี้เอง จึงไปถามใน crypto.SE โดยให้เหตุผลว่า “อยากให้เพื่อนร่วมงานเห็นว่า RNG นี้อ่อนแอแค่ไหน”

วิธีเลือกการกระทำที่ต้องการในสภาพแวดล้อมที่ปลอดภัย

  • เมื่อกู้สถานะภายในของ RNG ได้แล้ว ก็สามารถส่งการตัดสินใจ automation ใด ๆ เข้าไปในสภาพแวดล้อมที่ปลอดภัยได้
  • กระบวนการทำงานเป็นดังนี้
    • ลงทะเบียน hook ที่ไม่ปลอดภัย ซึ่งจะรันทันทีก่อนโค้ดในสภาพแวดล้อมที่ปลอดภัยรัน
    • ภายใน hook เข้าถึงข้อมูลทั้งหมดได้ จึงตัดสินใจว่าจะทำการกระทำใด
    • ค้นหาการกระทำที่ตัดสินใจในรายการการกระทำที่ hardcode ไว้ แล้วแปลงเป็นดัชนี
    • กู้สถานะ RNG ปัจจุบันด้วยวิธีก่อนหน้า
    • หากเอาต์พุต RNG ถัดไป เมื่อนำมา mod ด้วยความยาวของรายการการกระทำแล้ว ไม่ได้ดัชนีที่ต้องการ ก็เลื่อน RNG ไปข้างหน้าแล้วตรวจสอบอีกครั้ง
    • เมื่อถึงรอบที่เอาต์พุตที่ต้องการจะออกมา hook จะ return และสภาพแวดล้อมที่ปลอดภัยจะเรียก random เพื่อทำการกระทำตามดัชนีนั้น
  • ในสภาพแวดล้อมที่ปลอดภัย ดูเหมือนเป็นการใช้ตัวเลขสุ่มเพื่อ index รายการการกระทำ แต่จริง ๆ แล้วจังหวะของการเรียก random ครั้งถัดไปถูกควบคุมเพื่อให้เลือกการกระทำที่ต้องการ
  • หากขนาดรายการการกระทำคือ n โดยเฉลี่ยต้องเลื่อน RNG ไปข้างหน้า n ขั้นเพื่อส่งค่าที่ต้องการ แต่ในการใช้งานจริงไม่เป็นปัญหา

สถานะหลังจากนั้น

  • เมื่อลองอีกครั้งหลังผ่านไปหลายปี วิธีนี้ก็ใช้งานไม่ได้แล้ว
  • ไม่อาจทราบได้ว่า Blizzard แก้ปัญหาสถานะ RNG ที่อ่อนแอและแชร์ร่วมกันเมื่อใด หรือมองว่านี่เป็นปัญหาหรือไม่
  • ความเปลี่ยนแปลงที่เป็นไปได้คือเปลี่ยนไปใช้อัลกอริทึมอื่น หรือแยกสถานะ RNG อย่างถูกต้องให้กับสภาพแวดล้อมที่ปลอดภัย
  • แม้ไม่ใช่โค้ดที่ตั้งใจจะใช้จริง แต่นี่เป็นกรณีแคบ ๆ ของการข้ามระบบความปลอดภัยในเกมที่ทำสำเร็จในการบิดสิ่งที่ดูเหมือนสุ่มให้ได้ผลลัพธ์ตามต้องการ

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

 
GN⁺ 2024-01-15
ความคิดเห็นบน Hacker News
  • สมัยก่อนที่ผมทำงานเป็น GM ของ WoW มีบั๊กใน Titan Bar แอดออนยอดนิยมบางเวอร์ชัน ที่คอยเรียก API สำหรับเปิด UI “เขียนตั๋วถึง GM” ซ้ำ ๆ
    ถึงกดยกเลิกก็เด้งขึ้นมาใหม่ และนอกจากลบแอดออนที่มีปัญหาแล้ว วิธีเดียวที่จะปิดหน้าต่างได้คือพิมพ์อะไรสักอย่างในกล่องข้อความแล้วส่ง
    ในมุมของ GM นี่เป็นวันที่ลำบากมากอยู่วันสองวัน และผู้เล่นจำนวนมากไม่รู้ด้วยซ้ำว่า GM คืออะไร หรือรู้ว่าสามารถส่งตั๋วได้
    การอธิบายว่า “ลูกค้าน่าจะส่งตั๋วมาเพราะแอดออน ผมซึ่งเป็นฝ่ายซัพพอร์ตลูกค้าจึงติดต่อมา” ก็มักไม่ง่ายนัก
    เพราะมันทำให้เวลาตอบกลับตามปกติพังไปโดยปริยาย นักพัฒนาจึงออก hotfix และเอาการเรียก API นั้นไปไว้หลังกลไกป้องกัน ให้เรียกได้เฉพาะจาก โค้ด LUA ของ Blizzard เอง เท่านั้น

    • ชื่อน่าจะเป็น Titan Panel
      เป็นแอดออนที่รวมองค์ประกอบ UI หลายอย่าง เช่น ทองทั้งหมด อัตราส่วนการซ่อมแซม ไว้ในแถบเดียว
  • แปลกดีที่ผมเคยตอบคำถามที่แทบเหมือนกันเรื่อง “การแก้ย้อนตัวสร้างเลขสุ่ม” บน StackOverflow ไม่กี่เดือนก่อนจะมีคำถามบน Crypto.SE: https://stackoverflow.com/a/15237585/1204143
    แต่ตอนนั้นโจมตี ตัวสร้างเลขสุ่มของ Java แทนที่จะเป็นของ MSVC
    ทั้งคู่เป็นโครงสร้าง linear congruential generator (LCG) แบบง่าย ๆ ที่เอาค่าสถานะภายในมาตัดบางส่วนก่อนส่งออก วิธีโจมตีก็เลยคล้ายกันมาก

    • ผมไม่เก่งคณิตศาสตร์ เลยให้ Z3 ช่วยหา seed ที่เหมาะ ๆ ให้ และผลลัพธ์ก็คือโค้ดด้านล่าง
      import java.util.Random;
      public class Hayley {
      public static void main(String... args) {
      byte[] b1 = new byte[4], b2 = new byte[2];
      (new Random(0x2effe2140e00L)).nextBytes(b1);
      (new Random(0xc2f0097)).nextBytes(b2);
      System.out.println(new String(b1) + new String(b2));
      }
      }
  • ผมสงสัยว่าเป็นไปได้ไหมที่ฝั่งเซิร์ฟเวอร์ก็ใช้ตัวสร้างเลขสุ่มแบบเดียวกัน
    ถ้าใช่ ในทางทฤษฎีก็ดูเหมือนจะเอาไปใช้โจมตีได้ เมื่อมีเซิร์ฟเวอร์ที่มีการใช้งานต่ำมาก, latency ต่ำ, ความสามารถในการจำกัดช่วงสถานะปัจจุบันของตัวสร้างเลขสุ่มได้ค่อนข้างแม่นยำ และความสามารถในการทำให้อีเวนต์ที่มีความต่างทางเศรษฐกิจสูงตามผลสุ่มเกิดซ้ำได้อย่างรวดเร็ว

    • เซิร์ฟเวอร์ Vanilla WoW ใช้ Mersenne Twister และถึงจะมีปัญหาหลายอย่าง แต่มันก็ไม่ได้ถูกเจาะง่ายขนาดนั้น
      Blizzard เปิดเผยรายละเอียดของวิธีสร้างเลขสุ่มฝั่งเซิร์ฟเวอร์ไว้ค่อนข้างมาก ในความพยายามที่โดยรวมแล้วไม่ค่อยสำเร็จนักเพื่อสยบข่าวลือจากผู้เล่นสายเชื่อโชคลางที่ปักใจว่ามีสิ่งอย่าง “seed ของ loot” อยู่
  • เศรษฐศาสตร์และแรงจูงใจของตัวสร้างเลขสุ่ม เป็นเรื่องน่าสนใจ
    ถ้าไม่มีอะไรเป็นเดิมพันจริง ๆ และผู้คนสังเกตได้ไม่ง่าย ตัวสร้างเลขสุ่มที่ไม่สมบูรณ์หรือถึงขั้นแย่ก็อาจเพียงพอสำหรับเกม
    แต่พอมีเงินหรือความปลอดภัยเข้ามาเกี่ยว เช่น กระเป๋าเงินคริปโตหรือคาสิโนออนไลน์ มันก็กลายเป็นเรื่องสำคัญมหาศาลทันที

  • ที่ร้ายแรงกว่านั้นคือ ตัวสร้างเลขสุ่มแบบ linear congruential ที่ยกมาสร้างตัวเลขที่แตกต่างกันได้เพียง 12,445 ค่าเท่านั้นก่อนจะวนซ้ำ
    แค่ใช้พารามิเตอร์ที่ดีกว่าสำหรับอัลกอริทึมนี้ก็น่าจะสมเหตุสมผลกว่านี้มากแล้ว

    • งานวิศวกรรมคือการประนีประนอม
      งานง่าย ๆ อย่างการเคลื่อนที่ของอนุภาคในรอยควัน ต้องการตัวสร้างเลขสุ่มที่เรียบง่ายแต่ เร็ว
      ในงานเข้ารหัสหรือเกมคาสิโน ฯลฯ ไม่เพียงแต่ใช้ตัวสร้างเลขสุ่มที่ดีกว่าพร้อมสถานะอย่างน้อย 32 ไบต์เท่านั้น แต่ยังทำให้เอนจินเดินหน้าต่อไปแบบสุ่มช่วงเวลาเพิ่มเติม เพื่อขัดขวางการทำนายผลให้มากขึ้นด้วย
  • World of Warcraft เป็นจุดเริ่มต้นที่ทำให้ผมสนใจการเขียนโปรแกรม
    ตอนเกมออกผมอายุ 6 ขวบ และพอเซิร์ฟเวอร์ส่วนตัวเริ่มเกิดขึ้น ผมก็สงสัยว่าจะสร้าง NPC เองได้อย่างไร จึงเรียน Lua
    กระทู้ในฟอรัม MMOwned ที่ผมเคยเข้าไปเล่นตอนนั้นยังเหลืออยู่เยอะ และหนึ่งในคนที่ช่วยให้ผมเรียนรู้ตอนนั้นก็ยังโพสต์เป็นครั้งคราว
    การคอมไพล์คอร์ของเซิร์ฟเวอร์ส่วนตัวเองทำให้ผมได้เรียนเรื่องการคอมไพล์, SVN และการ apply patch และเหมือนจะมีบั๊กเกี่ยวกับ Fireball ของ Mage อยู่ แต่จำรายละเอียดแน่ชัดไม่ได้

    • ทุกครั้งที่นึกถึงฟอรัมเก่า ๆ ก็รู้สึกคิดถึง
      ทุกวันนี้รู้สึกว่าส่วนดี ๆ จำนวนมากของอินเทอร์เน็ตถูกแทนที่ด้วยแอปโซเชียลมีเดียที่กระตุ้นโดพามีน และ ฟอรัมแบบปิด อย่าง Reddit
      เหตุผลที่ผมยังอยู่บน HN ก็คงเพราะมันทำให้นึกถึงฟอรัมสมัยก่อน
  • “ใครก็ตามที่พยายามสร้างตัวเลขสุ่มด้วยวิธีทางคณิตศาสตร์ ย่อมอยู่ในสภาวะแห่งบาป ดังที่มีคนชี้ให้เห็นหลายครั้งแล้วว่า ไม่มีสิ่งที่เรียกว่าตัวเลขสุ่ม มีเพียงวิธีสร้างตัวเลขสุ่มเท่านั้น และกระบวนการทางคณิตศาสตร์ที่เคร่งครัดย่อมไม่ใช่วิธีเช่นนั้นแน่นอน” — John von Neumann

    • ในฐานะนักเข้ารหัส ผมทนคำคมนี้ไม่ค่อยไหวจริง ๆ
      ไม่ได้มีปัญหากับ Neumann แต่คนที่เอามันไปเผยแพร่เหมือนเป็นสุภาษิตอันชาญฉลาด โดยมากแล้วถ่ายทอดแต่ข้อมูลผิด ๆ
      ในบริบทเดิมมันใกล้เคียงกับมุกเชิงปรัชญาที่ไม่จำเป็นมากกว่า
      เรามี ตัวสร้างเลขสุ่มเชิงเข้ารหัส ที่เร็วและเชื่อถือได้มาหลายสิบปีแล้ว
      แค่ seed ขนาดเล็กมากกับการคำนวณทางคณิตศาสตร์ก็ทำงานได้ถูกต้อง และตัวเลขที่สร้างขึ้นก็แยกไม่ออกจากแหล่งสุ่มที่ถูกต้องอื่น ๆ และใช้ได้กับทุกวัตถุประสงค์
      มีคนจำนวนมากเกินไปที่ไม่เข้าใจเรื่องนี้ จนถึงขั้นมีบริษัทขายน้ำมันงูในชื่อ “เลขสุ่มแท้” ด้วย
  • ตัวอย่างดี ๆ ของตัวสร้างเลขสุ่มที่เลวร้ายจริง ๆ อยู่ใน Monster Hunter 3 Ultimate และอาจมีในภาคอื่นด้วย
    ไม่เพียงแต่ถูกใช้หาประโยชน์เพื่อให้ได้ loot ดี ๆ ได้อย่างสมบูรณ์ แต่ยังทำให้เกิด “charm table” ด้วย
    ไอเทมบางอย่างมีค่าสเตตัสที่สร้างแบบสุ่ม แต่ seed เริ่มต้นถูกบันทึกไว้ในไฟล์เซฟ ทำให้ช่วงของไอเทมที่หาได้ถูกจำกัด
    ถ้าโชคร้าย คุณอาจติดอยู่ใน “ตารางต้องคำสาป” ซึ่งโดยพื้นฐานแล้วเป็นตัวสร้างเลขสุ่มที่มีคาบสั้นมาก จนไม่มีวันได้ค่าสูงสุด
    ไม่ถึงกับทำลายเกม แต่ไม่ดีต่อการเล่นระดับสูง

  • ผมเขียนสคริปต์สำหรับ Auction House
    มันสแกนการประมูลทั้งหมด ซื้อไอเทมที่ตั้งราคาต่ำกว่ามูลค่า แล้วนำไปลงขายใหม่
    ถ้ามีคนลงขายถูกกว่า ก็ยกเลิกแล้วลงใหม่ให้ต่ำกว่านั้นเล็กน้อย ทำให้มี ทองแบบไม่จำกัด โดยปริยาย

    • หลังจากมีคนเมาจริง ๆ ที่ทำงานที่ Goldman อธิบายให้ฟังว่าเงินหมุนเวียนอย่างไร ผมก็เอาไปอธิบายให้น้องชายฟัง แล้วน้องชายตอบว่า “งั้น IRS ก็คือ gold sink ใช่ไหม?”
      “อืม จริง ๆ ก็เป็นการเปรียบเทียบที่ดีทีเดียว ต่างกันตรงที่… อืม… เป็นการเปรียบเทียบที่ดีทีเดียวแหละ!”
    • นี่โดยพื้นฐานแล้วเหมือนกับ การซื้อขายความถี่สูง ในโลกจริง
    • คุณเป็นคนสร้าง Auctioneer หรือเปล่า? หรือทำเครื่องมือที่คล้าย Auctioneer แต่เลือกไม่ใช้เครื่องมือ “มาตรฐานอุตสาหกรรม”?
    • อยากรู้ว่าตัดสินอย่างไรว่าอะไรถูกตั้งราคาต่ำกว่ามูลค่า
    • เป็นผู้สร้าง แอดออน Auctioneer หรือเปล่า? ถ้าใช่ มันก็น่าจะเป็นหนึ่งในแอดออน WoW ที่ได้รับความนิยมที่สุดมานานกว่า 10 ปีแล้ว
  • “เมื่อตัวแปรไม่เปลี่ยน และค่าคงที่ไม่คงที่”

    • น่าสนใจ ดูเหมือนเป็นมีมที่รากลึก
      https://www.theregister.com/2006/07/26/constants_are_not/
      “ผมอ่านคำคร่ำครวญถึงความแปรปรวนของการเขียนโปรแกรมในชื่อบทความนี้ ‘Variables Won't Constants Aren't’ ครั้งแรกใน Creative Computing หลายปีก่อนที่ C จะถูกสร้างขึ้น”
      ถ้ามีเอกสารเฉพาะสักชิ้นที่คุณรู้สึกว่าเป็นฉบับมาตรฐานของมีมนี้ ผมก็อยากอ่าน