Tutorial

Python Script กับการทำ Obfuscation

By Arnon Puitrakul - 14 ตุลาคม 2021 - 2 min read min(s)

Python Script กับการทำ Obfuscation

หลังจากที่เราเขียนโปรแกรมของเราเสร็จเรียบร้อยแล้ว มันก็ถึงเวลาที่เราจะต้อง Deliver Software ที่เราและทีมของเราสร้างออกไปให้ลูกค้าของเรา อย่างในภาษา Python เอง เราจะสังเกตได้ว่า ผู้ใช้จะเห็น Code ของเราทั้งหมดเลย เพราะ Python เองมันเป็นพวก Scriping Language ทำให้มันไม่จำเป็นจะต้อง Compile ก่อน นั่นนำไปสู่การลอกโปรแกรม หรือ Algorithm ของเราได้อย่างง่ายดายเลย ซึ่งแตกต่างจากภาษาอื่น ๆ เช่น C Language หรือ ภาษาที่จำเป็นจะต้อง Compile ที่มันจะแปลงให้เป็น Machine Code ซึ่งยากต่อการลอกมากกว่า การเปิด Script แบบเต็ม ๆ การที่เราทำให้ Code ของเรายากต่อการอ่าน เราเรียกมันว่าการ Obfuscate

Obfuscate คืออะไร ?

Obfuscate ในภาษาอังกฤษ ถ้าเรามาลองดูดี ๆ มันคล้ายกับคำนึงในภาษา Latin มาก ๆ คือ คำว่า obfuscat ที่แปลว่า "To darken" ซึ่งถ้าเราแยกคำออกมา เราจะได้คำว่า ob- ที่เป็น Prefix แปลว่า "against"  และ fuscat ที่แปลว่า "Become dark" ที่มาจากคำว่า fuscus ที่แปลว่า "Dark" ดังนั้น คำว่า Obfuscate แปลว่า การทำให้มันมืด ๆ หรือการปกปิดอะไรบางอย่างนั่นเอง หมดชั่วโมงภาษา อังกฤษ และ ลาตินในวันนี้จ้าาาา เอ่อ เดี๋ยว ไม่น่าใช่ อะไปต่อ

ในทาง Software คำว่า Obfuscate ก็มีความหมายเหมือนกับในภาษาอังกฤษเลย คือการที่ทำให้ Software ของเรายากสำหรับคนในการอ่าน หรืออาจจะเป็นการทำให้เข้าใจจุดประสงค์ผิดก็ได้เหมือนกัน โดยหลัก ๆ จุดประสงค์ที่เราทำ Obfuscate กันเยอะคือการป้องกันการทำ Reverse Engineering ของ Software เรา เพราะบางครั้ง Software ของเราไม่ได้เป็น Open-Source ทำให้เราไม่สามารถเอา Source Code หรือ Script ไปให้ผู้ใช้ตรง ๆ ได้เลย หรืออีกเหตุการณ์คือพวก Malware ที่มีการปกปิดจุดประสงค์ของตัวเองว่ามันเป็น Malware ทำท่าที และเขียน Code ออกมาให้หน้าตาเหมือนโปรแกรมทั่วไปเพื่อให้ Antivirus ตรวจจับไม่เจอ เป็นต้น

หรืออีกอันที่หลาย ๆ คนคิดไม่ถึงคือการทำให้ Code หรือ Script ของเราเล็กลง หรือที่เราเรียกมันว่า การทำ Minification นั่นเอง จริง ๆ แล้วมันก็เป็นหนึ่งในการทำ Obfuscate วิธีหนึ่งเหมือนกัน

เรา Obfuscate Code กันด้วยวิธีใดบ้าง ?

ในการ Obfuscate เราทำกันหลายวิธีมาก ๆ ถ้าเอา Basic เข้าใจง่ายสุดคือการทำ Minification ถ้าให้ง่ายกว่านั้นคือ การเอาพวก Whitespace ออกให้หมด เช่นพวก Newline หรือ Space ที่มันกินที่ อย่าลืมว่า อักขระพิเศษพวกนี้ ขนาดของมันก็ไม่ต่างกับตัวอักษรปกติที่เราเห็นเลย การเอาพวกมันออกไปก็ทำให้ Code ของเราเล็กลงได้

เอา Advance ขึ้นไปอีกหน่อยคือ การเปลี่ยนชื่อมันให้หมดเลย ตัวอย่างเช่นในเว็บ Medium ถ้าเราลองเข้าไปใช้ Dev Tool เพื่อ Inspect HTML ของหน้าบทความดู เราจะเห็นว่า CSS Class ของมันเป็นชื่อที่อ่านไม่รู้เรื่องเท่าไหร่ เป็นตัวอักษร 2 ตัวต่อกันนั่นนี่ ทำให้เราคาดเดาจากชื่อไม่ได้เลยว่า มันเอาไว้ทำอะไรกันแน่ การที่เราจะรู้เราก็ต้องไล่ดูทีละตัวเลยว่ามันมี Style ยังไง มันอยู่ตรงไหนบ้าง เพื่อให้เรารู้จุดประสงค์ที่แท้จริงของมัน แต่การ Obfuscate แบบนี้ ก็ยังทำให้เรารู้อยู่ดีว่ามันทำอะไรบ้าง มันก็นำไปสู่การทำ Reverse Engineering ได้เหมือนกัน

งั้นเราลองทำแบบนี้ดูมั้ย ในเมื่อเราปิด Code ยาก เราเติมขยะลงไปบ้างละมันจะเป็นอย่างไร ใช่แล้ว เพื่อให้มัน งง ขึ้นไปอีก เราก็ใส่ Code แปลก ๆ ที่ไม่เกี่ยวกับ Logic ของเราลงไป ทำให้เวลาเราที่ไม่รู้มาอ่าน เราก็อาจจะ งง ว่า เอ๊ะ มันเกี่ยวอะไร มันก็จะ งง ได้ง่ายขึ้น รวมไปถึง ถ้าเราไปเล่นพวก Memory Access เหมือนเมื่อก่อนที่เราใช้ Cheat Engine กันนั่นแหละ ถ้าเราใส่ส่วนของ Logic แปลก ๆ ยัดเข้าไปด้วย เวลามันทำงาน มันก็จะมีส่วนนึงของ Memory ที่เอ๊ะ มันเพิ่มแปลก ๆ ทำให้คนที่มาทำ Reserve Engineering อ่านแล้วก็อาจจะเข้าใจผิดก็ได้

ไปให้พีคกว่านั้นอีก คือการที่เรา Encrypt หรือเข้ารหัส Code ของเราเลย ทำให้การที่โปรแกรมของเราจะทำงานได้ มันจะต้องมีการ Decrypt หรือการถอดรหัสก่อนถึงโปรแกรมของเราจะทำงานได้ ทำให้มันอาจจะเสียเรื่องของ Performance เล็กน้อยในการถอดรหัส หรือในวิธีนี้ ถ้าเราต้องการที่จะทำ Reverse Engineering จริง ๆ เราก็อาจจะมีการดักข้อมูล ตอนที่มันถอดรหัสนี่แหละ เข้ามาได้ หรือแม้กระทั่งการเข้าไปอ่าน Memory โดยตรงก็ยังทำได้อยู่เช่นกัน

หรือเอาให้สุดเหวี่ยงไปเลย คือการเปลี่ยน Code หรือการทำงานเองได้ เราเรียกโปรแกรมพวกนี้ว่า Polymorphic สิ่งที่มันทำคือ มันเป็นโปรแกรมที่สามารถเปลี่ยนรูปร่างส่วนต่าง ๆ ของโปรแกรมได้ เพื่อหลบเลี่ยงการตรวจจับ หรือการโดยลอกได้ แต่ ๆ Core Logic และ Method ก็ยังคงอยู่เหมือนเดิม ให้เรานึกถึงในหนังพวก Shape Changer หรือพวกที่กลายร่างเป็นอีกคนได้ คือ เขาจะเปลี่ยนเป็นใครก็ได้ แต่ Function ในการขยับแขนขา การเดิน ก็ยังคงทำได้อยู่ไม่ว่าจะเปลี่ยนไปเป็นร่างไหน ส่วนใหญ่ที่เราจะเห็นกัน เราก็จะเห็นได้จากพวก Malware ที่ใหม่ ๆ หน่อย มันจะชอบเปลี่ยนร่างของมันทำให้พวก Software ที่ใช้ตรวจจับ หาไม่เจอ คิดว่าเป็นไฟล์ทั่ว ๆ ไป

วิธีที่เราเล่ามา เป็นแค่เพียงส่วนหนึ่งเท่านั้น พวกเทคโนโลยีการทำ Obfuscate มันก็เหมือนกับแมวไล่จับหนู กินหางกันไปเรื่อย ๆ เมื่อการ Obfuscate ดีขึ้น พวก Cracker ทั้งหลายก็พยายามที่จะ Crack มัน วนแบบนี้ไปเรื่อย ๆ ทำให้เราเห็นในวงการ Pirate ที่ไม่ว่าโปรแกรมจะออกแบบมาเพื่อป้องกันขนาดไหน มันนนน ก็ยังหาวิธีในการ Crack ได้อยู่ดี

Obfuscate Python Script

ส่วนของ Python เรียกได้เลยว่า ตัวของมันเองไม่ได้มีการซ่อน Source Code อะไรอยู่แล้ว เพราะจากที่เราเห็นได้เลยว่า Python ไม่ใช่ภาษาที่ต้องการ การ Compile อะไรทั้งสิ้น ตัวมันสามารถแปลงเป็น Bytecode และให้ Translator แปลงแบบ On-the-fly ได้เลย ทำให้น่าปวดหัวอยู่พอตัว

python -OO -m py_compile hello_world.py

วิธีนึงที่ พอ จะช่วยได้ คือการที่เราแปลง Source Code ทั้งหมด ให้กลายเป็น Bytecode ซะก่อน เช่น ถ้าเราสร้าง Script File ที่ชื่อว่า hello_world.py เราก็สามารถใช้ Command ด้านบน ในการแปลง Script ของเราให้เป็น Bytecode ได้

หลังจากที่เรารันไปแล้ว เราก็จะได้เป็นไฟล์นามสกุล pyc ที่อยู่ใน Folder ที่ชื่อว่า __pycache__ ในนั้น ถ้าเราลองเปิดดู เราจะเห็นว่า มันอ่านไม่ค่อยรู้เรื่องเท่าไหร่ แต่มันก็ยังพอทำให้เราเดาได้อยู่ว่ามันน่าจะทำอะไรบ้าง หรือแม้กระทั่งเห็นพวก Logic บางส่วนได้เลย แต่ส่วนที่ไม่เห็นแน่ ๆ คือ พวก Comment กับ Docstring ต่าง ๆ ที่เราใส่ลงไป เพราะ ตอนที่มันแปลง Python มันถอดพวกข้อความที่ไม่จำเป็นออก

python hello_world.pyc

ส่วนเวลาเราจะรัน เราก็ไม่ต้องห่วง เราสามารถรันเหมือน Python Script ปกติได้เลย ไม่ต้องมีการแปลงอะไรทั้งนั้น เพราะจริง ๆ แล้ว เวลาเรารันโปรแกรม Python มันก็จะ Generate Bytecode อยู่ใน Folder pycache อยู่แล้วเท่านั้นเอง ซึ่งเอาจริง ๆ คือ มันก็จะมี Tool สำหรับการแปลง Python Bytecode ให้กลายมาเป็น Semi-Readable Code อยู่ด้วยนะ ลองไปหาอ่านดูได้

อีกวิธีคือการทำ Encryption ไปเลย ซึ่งมันก็จะมี Library ให้เราเลือกใช้งานอยู่ เช่น pyconcrete ถ้าเราเข้าไปอ่าน Document เขาจะบอกว่า เขาทำ AES 128-bits โดยใช้ OpenAES ในการทำ แน่นอนว่า พอไฟล์ที่เข้ารหัสแล้วอยู่ในเครื่องขนาดนี้ เผลอ ๆ เราสามารถ Brute Force กี่ครั้งก็ได้เลย มันส์ละทีนี้

อีกตัวที่น่าสนใจมาก ๆ คือ pyarmor ลองเข้าไปอ่านคร่าว ๆ ดู ถือว่าใช้ได้เลยนะ สิ่งที่เขาทำคือ เขาเอา Bytecode มาครอบด้วย Container ของเขาเอง และป้องกันพวก Constant ด้วยการทำ Serialisation อีกชั้น ถือว่าเป็นตัวที่ทำออกมาได้ใช้ได้เลยทีเดียว เพราะมีการบังตั้งแต่ Source Code เองจนไปถึงพวก Constant และชื่อตัวแปรต่าง ๆ ไว้หมดเลย ถึงพยายามจะ Reverse ก็ไม่น่าได้ทั้งหมดกลับมา และยากที่จะติดต่อจริง ๆ แหละ

Obfuscate Python Script ด้วย Pyarmor

pip install pyarmor

ก่อนที่เราจะใช้งาน Pyarmor ได้ เราจะต้องทำการติดตั้งก่อน อาจจะผ่าน pip หรือ conda ก็ได้ ขึ้นกับเราใช้อะไร ในที่นี้คำสั่งด้านบนจะเป็นของ pip

pyarmor obfuscate hello_world.py

จากนั้น เราก็สามารถเริ่ม Obfuscate ได้เลย ผ่านคำสั่งด้านบน เราไม่ต้องทำอะไรเลย เดี๋ยว มันจะจัดการให้เราหมดเลย เมื่อเสร็จแล้ว เราก็จะได้ Folder dist ซึ่งเป็น Script ที่ทำการ Obfuscate เสร็จแล้ว พร้อม Distribute ได้เลย

from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'\x50\x59\x41\x52\x4d\x4f\x52\x00\x00\x03\x09\x00\x61\x0d\x0d\x0a\x08\x2d\xa0\x01\x00\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\xd9\x00\x00\x00\x00\x00\x00\x18\x27\x4f\x25\x9b\x06\x8e\xc5\x74\xc4\xad\xac\x83\x95\xdc\xb8\xf5\x00\x00\x00\x00\x00\x00\x00\x00\x97\x7c\x24\x78\xd1\x1f\x80\x9c\xed\xd6\x0d\x69\x71\xa2\x1f\x2d\x5d\x37\x27\x8e\x4d\x64\x49\xa8\x56\x44\x4e\x84\x16\x7a\xdc\xeb\x80\x06\x17\x26\x88\x18\xd8\xa0\x18\xe4\x10\x09\x5a\x36\x44\xf2\xe2\x66\x17\x7c\x37\x00\xb3\xf5\x89\x8a\x23\xf4\x8e\x66\xf4\xe3\xb7\xac\xa8\x9b\xf6\x2b\x9d\x2f\x28\xd9\x30\xe8\x82\xf0\x37\x6a\x5a\x54\xa7\x18\x54\x70\x95\xef\x42\xfa\xc5\x14\xc2\xb5\x43\xcf\xd1\x6d\x5c\x5a\x64\x90\x32\xe6\x13\xb6\xa6\xa4\xd5\x24\x51\x92\x66\xfe\x91\xb2\x53\xd1\x38\xdd\x1d\x23\x4a\x77\x5f\xd7\x44\x33\xb5\x4f\x7e\x58\xa2\x11\x9d\x4c\x80\x35\x11\xde\xbe\x9e\xb6\xd9\x07\xfe\x63\x26\x60\x51\x39\x31\x24\x64\xbb\xd1\xb8\x21\x6d\x8e\xe8\x82\x3d\xd9\x56\x0b\xf3\x7f\x83\x77\x51\xbb\x08\x41\x63\xae\xa3\xc5\xc5\xa8\xbd\xb8\xd0\x90\x05\x3a\xec\xbd\xe0\x5e\x87\x7f\xe5\x94\x6b\x5c\x4e\x63\x62\x4f\x68\x96\x4d\xb7\x25\xa0\x3e\x28\xba\xc8\x32\x7c\xa9\xf8\x0f\x41\x87', 2)

ถ้าเราเข้าไปดูใน hello_world.py ที่ผ่านการ Obfuscate แล้ว เราจะเห็นว่ามันเป็น Function ง่าย ๆ เลย แต่มันจะมีอะไรแปลก ๆ เยอะ ๆ เลยไม่รู้ เราก็ไม่ต้องสนแล้ว เพราะมันถูกทำให้เราอ่านได้ยากแล้ว เท่านี้เราก็สามารถที่จะ Obfuscate Python Script ของเราได้แล้ว

สรุป

Obfuscate ในฝั่งของการทำ Software คือการปิดปังตัวตน หรือ จุดประสงค์ของโปรแกรมของเรา เพื่อไม่ให้ความลับของโปรแกรมรั่วไหล หรืออาจจะนำไปใช้เพื่อหลบหลีกการตรวจจับของอีกโปรแกรมก็เป็นได้ ตัวอย่างของการใช้งาน ก็คือพวก Game DRM ต่าง ๆ ที่พยายามจะปิดบัง Source Code และป้องกันการทำ Reverse Engineering อย่างเต็มกำลังเลย เพราะไม่อยากให้โดน Crack นั่นเอง หรือฝั่งของ Malware เองก็เอาไปใช้งานในการหลบหลีกพวก Software รักษาความปลอดภัยต่าง ๆ อาจจะให้มองเป็นไฟล์ธรรมดาไม่มีพิษภัย แต่แท้จริงแล้วมันเป็น Malware นั่นเอง ในฝั่งของ Python เอง เนื่องด้วยมันเป็น Scripting Language ทำให้เวลาเรา Distribute Runtime เราออกไป ก็มักจะต้องเอา Source Code นั่นแหละ ส่งไป ทำให้เป็นเรื่องกันมาแล้ว ดังนั้น ใน Python ก็จะมีการทำ Obfuscate Code เหมือนกัน โดยที่มันมีหลาย Module และ Library ให้เราใช้งานเยอะมาก ตัวอย่างที่เราเอามาทำให้ดูในวันนี้ก็จะเป็น Pyarmor ที่เราบอกได้เลยว่า มันก็ป้องกันได้พอประมาณ และ ที่สำคัญมาก ๆ คือ ใช้งานได้ง่ายมาก ๆ