প্রথমত, আসলে অনেক কম হ্যাকি উপায় আছে। আমরা যা করতে চাই তা হ'ল কি print
প্রিন্ট পরিবর্তন করা উচিত ?
_print = print
def print(*args, **kw):
args = (arg.replace('cat', 'dog') if isinstance(arg, str) else arg
for arg in args)
_print(*args, **kw)
বা, একইভাবে, আপনি sys.stdout
পরিবর্তে monkeypatch করতে পারেন print
।
এছাড়াও, exec … getsource …
ধারণাটি নিয়ে কোনও ভুল নেই । ঠিক আছে, অবশ্যই এটিতে প্রচুর ভুল আছে , তবে এখানে যা অনুসরণ করা হয় তার চেয়ে কম ...
তবে আপনি যদি ফাংশন অবজেক্টের কোড কনস্ট্যান্টগুলি সংশোধন করতে চান তবে আমরা এটি করতে পারি।
আপনি যদি সত্যিকারের জন্য কোড অবজেক্টের সাথে চারপাশে খেলতে চান তবে আপনার নিজের মতো লাইব্রেরিটি bytecode
(এটি শেষ হয়ে গেলে) বা byteplay
(ততক্ষণ, অথবা পুরানো পাইথন সংস্করণগুলির জন্য) ম্যানুয়ালি না করে ব্যবহার করা উচিত। এমনকি এই তুচ্ছ জিনিসের জন্যও, CodeType
আরম্ভকারীটি একটি ব্যথা হয়; আপনার যদি ঠিক করার মতো জিনিসগুলি করার দরকার হয় তবে lnotab
কেবল একজন পাগল নিজেই এটি করতে পারে।
এছাড়াও, এটি বলার অপেক্ষা রাখে না যে সমস্ত পাইথন বাস্তবায়ন সিপিথন-স্টাইলের কোড অবজেক্ট ব্যবহার করে না। এই কোডটি সিপথন ৩.7-এ কাজ করবে এবং সম্ভবত কিছু সংখ্যক পরিবর্তন (এবং কোড-হ্যাকিংয়ের জিনিস নয়, তবে জেনারেটর এক্সপ্রেশনগুলির মতো জিনিস) সহ কমপক্ষে ২.২-এর সমস্ত সংস্করণ ফিরে আসবে, তবে এটি আয়রন পাইথনের কোনও সংস্করণে কাজ করবে না।
import types
def print_function():
print ("This cat was scared.")
def main():
# A function object is a wrapper around a code object, with
# a bit of extra stuff like default values and closure cells.
# See inspect module docs for more details.
co = print_function.__code__
# A code object is a wrapper around a string of bytecode, with a
# whole bunch of extra stuff, including a list of constants used
# by that bytecode. Again see inspect module docs. Anyway, inside
# the bytecode for string (which you can read by typing
# dis.dis(string) in your REPL), there's going to be an
# instruction like LOAD_CONST 1 to load the string literal onto
# the stack to pass to the print function, and that works by just
# reading co.co_consts[1]. So, that's what we want to change.
consts = tuple(c.replace("cat", "dog") if isinstance(c, str) else c
for c in co.co_consts)
# Unfortunately, code objects are immutable, so we have to create
# a new one, copying over everything except for co_consts, which
# we'll replace. And the initializer has a zillion parameters.
# Try help(types.CodeType) at the REPL to see the whole list.
co = types.CodeType(
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize, co.co_flags, co.co_code,
consts, co.co_names, co.co_varnames, co.co_filename,
co.co_name, co.co_firstlineno, co.co_lnotab,
co.co_freevars, co.co_cellvars)
print_function.__code__ = co
print_function()
main()
কোড অবজেক্টগুলি হ্যাক করে কী ভুল হতে পারে? প্রায়শই কেবল সেগফাল্টস, RuntimeError
যেগুলি পুরো স্ট্যাক খায়, আরও সাধারণ RuntimeError
যেগুলি পরিচালনা করা যায়, বা আবর্জনার মানগুলি সম্ভবত একটি TypeError
বা AttributeError
যখন আপনি সেগুলি ব্যবহার করার চেষ্টা করবেন তখন উত্থাপন করবে। উদাহরণস্বরূপ, RETURN_VALUE
স্ট্যাকের সাথে কিছুই না দিয়ে একটি কোড অবজেক্ট তৈরি করার চেষ্টা করুন ( আগে b'S\0'
3.6+ এর জন্য বাইটকোড b'S'
), বা বাইটোকোডে co_consts
যখন রয়েছে তখন একটি ফাঁকা টিপল LOAD_CONST 0
সহ বা varnames
1 দ্বারা হ্রাস LOAD_FAST
পেয়েছে তাই সর্বোচ্চটি একটি ফ্রিভারকে বোঝায় / সেলভার সেল কিছু বাস্তব মজাদার জন্য, আপনি যদি lnotab
ভুলটি যথেষ্ট পরিমাণে পান তবে আপনার কোডটি কেবলমাত্র ডিবাগারে চালিত হলে সেগফল্ট হবে।
ব্যবহার bytecode
বা byteplay
যারা সমস্যার সমস্ত থেকে আপনাকে রক্ষা করবে না, কিন্তু তারা কিছু মৌলিক মানসিক সুস্থতা চেক, এবং চমৎকার সাহায্যকারী যে আপনার কোড একটি খণ্ড সন্নিবেশ ভালো জিনিস না দেওয়া আছে এবং এটা এত আপনি যা করতে পারেন 'সব অফসেট এবং লেবেল আপডেট সম্পর্কে চিন্তা করা যাক না এটি ভুল না হওয়া, ইত্যাদি। (প্লাস, তারা আপনাকে সেই হাস্যকর 6-লাইনের কনস্ট্রাক্টর টাইপ করা থেকে বিরত রাখে এবং এমনটি করে আসে এমন নির্বোধ টাইপগুলি ডিবাগ করতে বাধ্য করে))
এখন # 2 এ।
আমি উল্লেখ করেছি যে কোড অবজেক্টগুলি অপরিবর্তনীয়। এবং অবশ্যই কনসেটগুলি একটি টিপল, তাই আমরা সরাসরি এটি পরিবর্তন করতে পারি না। এবং কনস্ট টিউপলের জিনিসটি একটি স্ট্রিং, যা আমরা সরাসরিও পরিবর্তন করতে পারি না। এ কারণেই আমাকে একটি নতুন কোড অবজেক্ট তৈরি করতে একটি নতুন টিউপল তৈরি করতে একটি নতুন স্ট্রিং তৈরি করতে হয়েছিল।
কিন্তু আপনি যদি সরাসরি একটি স্ট্রিং পরিবর্তন করতে পারে?
আচ্ছা, প্রচ্ছদের নীচে যথেষ্ট গভীর, সবকিছু কিছু সি-এর ডেটা কেবলমাত্র পয়েন্টার, তাই না? আপনি যদি সিপিথন ব্যবহার করছেন তবে অবজেক্টগুলিকে অ্যাক্সেস করার জন্য একটি সিআই এপিআই রয়েছে এবং আপনি ctypes
পাইথনের মধ্যে থেকেই সেই এপিআইটি অ্যাক্সেস করতে ব্যবহার করতে পারেন , এটি এমন এক ভয়ানক ধারণা যে তারা pythonapi
স্টাডলিবের ctypes
মডিউলে একটি অধিকার রেখেছিল । :) আপনার জানা সবচেয়ে গুরুত্বপূর্ণ কৌশলটি id(x)
হ'ল x
মেমরির আসল পয়েন্টার (যেমন হিসাবে int
) an
দুর্ভাগ্যক্রমে, স্ট্রিংগুলির সিআইপি আমাদের নিরাপদভাবে ইতিমধ্যে হিমায়িত স্ট্রিংয়ের অভ্যন্তরীণ স্টোরেজে যেতে দেয় না। তাই নিরাপদে স্ক্রু করুন, আসুন আমরা কেবল শিরোনাম ফাইলগুলি পড়ি এবং সেই সঞ্চয়টি নিজেরাই সন্ধান করি।
যদি আপনি সিপথন ৩.৪ - ৩.7 ব্যবহার করেন (এটি পুরানো সংস্করণগুলির চেয়ে পৃথক, এবং ভবিষ্যতের জন্য কে জানে), খাঁটি ASCII দ্বারা তৈরি মডিউলটির একটি স্ট্রিং আক্ষরিক সংক্ষিপ্ত ASCII ফর্ম্যাট ব্যবহার করে সংরক্ষণ করা হবে, যার অর্থ কাঠামো তাড়াতাড়ি শেষ হয় এবং এএসসিআইআই বাইটের বাফারটি মেমরির সাথে সাথেই অনুসরণ করে। আপনি যদি স্ট্রিং-এ অ-এএসসিআইআই অক্ষর বা কিছু ধরণের অ-আক্ষরিক স্ট্রিং রাখেন তবে এটি বিভাজিত হবে (সম্ভবত সেগফল্টে) তবে আপনি বিভিন্ন ধরণের স্ট্রিংয়ের জন্য বাফারটি অ্যাক্সেস করার অন্যান্য 4 টি পদ্ধতিতে পড়তে পারেন।
জিনিসগুলিকে কিছুটা সহজ করার জন্য, আমি superhackyinternals
আমার গিটহাব বন্ধ করে দিয়ে প্রকল্পটি ব্যবহার করছি । (এটি ইচ্ছাকৃতভাবে পাইপ-ইনস্টলযোগ্য নয় কারণ আপনার স্থানীয় দোভাষী এবং এই জাতীয় বিল্ডের সাথে পরীক্ষা ব্যতীত সত্যই আপনি এটি ব্যবহার করা উচিত নয়))
import ctypes
import internals # https://github.com/abarnert/superhackyinternals/blob/master/internals.py
def print_function():
print ("This cat was scared.")
def main():
for c in print_function.__code__.co_consts:
if isinstance(c, str):
idx = c.find('cat')
if idx != -1:
# Too much to explain here; just guess and learn to
# love the segfaults...
p = internals.PyUnicodeObject.from_address(id(c))
assert p.compact and p.ascii
addr = id(c) + internals.PyUnicodeObject.utf8_length.offset
buf = (ctypes.c_int8 * 3).from_address(addr + idx)
buf[:3] = b'dog'
print_function()
main()
আপনি যদি এই স্টাফ নিয়ে খেলতে চান int
তবে কভারগুলির আওতায় সম্পূর্ণ সহজ str
। এবং এটা অনুমান করার কি মান পরিবর্তন করে ভাঙ্গতে পারে অনেক সহজ 2
করতে 1
, ডান? আসলে, কল্পনা ভুলে যান, আসুন কেবল এটি করা ( superhackyinternals
আবার থেকে টাইপগুলি ব্যবহার করে ):
>>> n = 2
>>> pn = PyLongObject.from_address(id(n))
>>> pn.ob_digit[0]
2
>>> pn.ob_digit[0] = 1
>>> 2
1
>>> n * 3
3
>>> i = 10
>>> while i < 40:
... i *= 2
... print(i)
10
10
10
… ভান করুন যে কোড বাক্সে একটি অসীম দৈর্ঘ্যের স্ক্রোলবার রয়েছে।
আমি আইপিথনেও একই জিনিসটি চেষ্টা করেছি 2
এবং প্রম্পটে প্রথমবার যখন মূল্যায়ন করার চেষ্টা করেছি তখন এটি একরকম নিরবচ্ছিন্ন অসীম লুপে চলে গেল। সম্ভবত এটি 2
তার REPL লুপের কোনও কিছুর জন্য নম্বরটি ব্যবহার করছে , যখন স্টক ইন্টারপ্রেটার নেই?
42
করতে23
কেন এটি একটি খারাপ ধারণা মান পরিবর্তন করতে চেয়ে"My name is Y"
থেকে"My name is X"
।