गतिशीलভাবে একটি # # পদ্ধতির বিষয়বস্তু প্রতিস্থাপন করবেন?


108

আমি যা করতে চাই তা হল একটি সি # পদ্ধতি যখন ডাকা হয় তখন এটি কীভাবে কার্যকর করে, যাতে আমি এরকম কিছু লিখতে পারি:

[Distributed]
public DTask<bool> Solve(int n, DEvent<bool> callback)
{
    for (int m = 2; m < n - 1; m += 1)
        if (m % n == 0)
            return false;
    return true;
}

রান-টাইমে, আমাকে এমন পদ্ধতিগুলির বিশ্লেষণ করতে সক্ষম হতে হবে যেগুলিতে ডিস্ট্রিবিউট অ্যাট্রিবিউট রয়েছে (যা আমি ইতিমধ্যে করতে পারি) এবং তারপরে ফাংশনটির বডি কার্যকর হওয়ার আগে এবং ফাংশন রিটার্নের পরে কোড সন্নিবেশ করানো উচিত। আরও গুরুত্বপূর্ণভাবে, সলভ নামক কোডটি বা ফাংশনের শুরুতে (সংকলন সময়ে; রান-টাইমে এটি করা উদ্দেশ্য) আমি কোড পরিবর্তন না করেই এটি করতে সক্ষম হব need

এই মুহুর্তে আমি এই বিট কোডটি চেষ্টা করেছি (ধরুন টি হ'ল ধরণের সমাধান সলভ করা হয় এবং এমটি সমাধানের একটি মেথডইনফো) :

private void WrapMethod(Type t, MethodInfo m)
{
    // Generate ILasm for delegate.
    byte[] il = typeof(Dpm).GetMethod("ReplacedSolve").GetMethodBody().GetILAsByteArray();

    // Pin the bytes in the garbage collection.
    GCHandle h = GCHandle.Alloc((object)il, GCHandleType.Pinned);
    IntPtr addr = h.AddrOfPinnedObject();
    int size = il.Length;

    // Swap the method.
    MethodRental.SwapMethodBody(t, m.MetadataToken, addr, size, MethodRental.JitImmediate);
}

public DTask<bool> ReplacedSolve(int n, DEvent<bool> callback)
{
    Console.WriteLine("This was executed instead!");
    return true;
}

তবে মেথোডেন্টাল.সপাপথথডবডি কেবল গতিশীল মডিউলগুলিতে কাজ করে; যাঁরা ইতিমধ্যে সংঘবদ্ধ করে সংঘবদ্ধ করে রেখেছেন

সুতরাং আমি ইতিমধ্যে লোডড এবং এক্সিকিউটিভ অ্যাসেমব্লিতে সংরক্ষিত একটি পদ্ধতিতে স্ব্যাপমেথডবডি কার্যকরভাবে করার একটি উপায় অনুসন্ধান করছি ।

দ্রষ্টব্য, আমি যদি কোনও পদ্ধতিটি ডায়নামিক মডিউলে পুরোপুরি অনুলিপি করতে হয় তবে এটি কোনও সমস্যা নয় তবে এই ক্ষেত্রে আমাকে আইএল জুড়ে অনুলিপি করার পাশাপাশি সলভের () কলগুলিতে সমস্ত আপডেট করার দরকার রয়েছে যাতে তারা এরকম হয় নতুন অনুলিপি নির্দেশ করবে।


3
ইতিমধ্যে লোড হওয়া পদ্ধতিগুলি অদলবদল করা সম্ভব নয়। তা না হলে Spring.Net প্রক্সি এবং ইন্টারফেস সঙ্গে অদ্ভুত জিনিষ করতে হতো না :-) পড়ুন এই প্রশ্নের, এটা আপনার সমস্যার স্পর্শক: stackoverflow.com/questions/25803/... (যদি আপনি তা ব্যাহত করতে পারে, আপনি কিছু মত করতে পারেন এটি পরিবর্তন করুন ... আপনি যদি 1 টি না করতে পারেন তবে পরিষ্কারভাবে আপনি 2 করতে পারবেন না)।
xanatos

সেক্ষেত্রে, কোনও গতিশীল মডিউলে কোনও পদ্ধতি অনুলিপি করার, এবং অ্যাসেমব্লির অন্যান্য অংশগুলি আপডেট করার উপায় রয়েছে যেগুলি সেই পদ্ধতির কাছে কল করে নতুন অনুলিপিটিতে?
জুন রোডস

একই বয়সী, একই বয়সী. এটি যদি সহজেই করা যায়, তবে বিভিন্ন আইওসি সমস্ত ধারক সম্ভবত এটি করতে পারে। তারা এটি করে না-> 99% এটি করা যায় না :-) (ভয়ঙ্কর এবং অজানা হ্যাক ছাড়া) একটি একক আশা আছে: তারা সি # 5.0 তে রূপক এবং async প্রতিশ্রুতি দিয়েছিল। অ্যাসিঙ্ক আমরা দেখেছি ... কিছুই রূপান্তরিত হয় না ... তবে এটি হতে পারে!
xanatos

1
আপনি কেন এত বেদনাদায়ক কোনও কারণে নিজেকে প্রবেশ করতে চান তা সত্যই আপনি ব্যাখ্যা করেন নি।
ড্যানিয়েলঅফটাইবেল

6
নীচে আমার উত্তর দেখুন। এটি সম্পূর্ণ সম্ভব। কোডে আপনার নিজস্ব এবং রানটাইমের সময় নেই। আমি বুঝতে পারছি না কেন এত লোক মনে করে এটি সম্ভব নয়।
Andreas Pardeike

উত্তর:


201

প্রকাশ: সম্প্রীতি একটি লাইব্রেরি যা এই পোস্টটির লেখক আমার দ্বারা রচিত এবং রক্ষণাবেক্ষণ করেছেন।

হারমনি 2 হ'ল একটি ওপেন সোর্স লাইব্রেরি (এমআইটি লাইসেন্স) রানটাইম চলাকালীন যে কোনও ধরণের বিদ্যমান সি # পদ্ধতি প্রতিস্থাপন, সাজসজ্জা বা সংশোধন করার জন্য ডিজাইন করা হয়েছে। এটির মূল ফোকাসটি গেম এবং প্লাগইনগুলিতে মনো বা .NET এ লেখা। এটি একই পদ্ধতিতে একাধিক পরিবর্তনগুলির যত্ন নেয় - তারা একে অপরকে ওভাররাইটের পরিবর্তে জমা হয়।

এটি প্রতিটি আসল পদ্ধতির জন্য গতিশীল প্রতিস্থাপনের পদ্ধতি তৈরি করে এবং তাদের কোড প্রেরণ করে যা প্রারম্ভিক এবং শেষে কাস্টম পদ্ধতিগুলিকে কল করে। এটি আপনাকে মূল আইএল কোড এবং কাস্টম ব্যতিক্রম হ্যান্ডলারগুলি প্রক্রিয়া করতে ফিল্টার লেখার অনুমতি দেয় যা মূল পদ্ধতির আরও বিস্তারিত ম্যানিপুলেশনের অনুমতি দেয়।

প্রক্রিয়াটি সম্পূর্ণ করার জন্য, এটি মূল পদ্ধতির ট্রামপোলিনে একটি সাধারণ এসেম্বলারের লাফ লেখায় যা গতিশীল পদ্ধতির সংকলন থেকে উত্পন্ন এসেমব্লারকে নির্দেশ করে। এটি উইন্ডোতে 32/64 বিটের জন্য কাজ করে, ম্যাকোস এবং কোনও লিনাক্স যা মনো সমর্থন করে।

ডকুমেন্টেশন এখানে পাওয়া যাবে

উদাহরণ

( উত্স )

আসল কোড

public class SomeGameClass
{
    private bool isRunning;
    private int counter;

    private int DoSomething()
    {
        if (isRunning)
        {
            counter++;
            return counter * 10;
        }
    }
}

সুরেলা টীকা সহ প্যাচিং

using SomeGame;
using HarmonyLib;

public class MyPatcher
{
    // make sure DoPatching() is called at start either by
    // the mod loader or by your injector

    public static void DoPatching()
    {
        var harmony = new Harmony("com.example.patch");
        harmony.PatchAll();
    }
}

[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
    static FieldRef<SomeGameClass,bool> isRunningRef =
        AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");

    static bool Prefix(SomeGameClass __instance, ref int ___counter)
    {
        isRunningRef(__instance) = true;
        if (___counter > 100)
            return false;
        ___counter = 0;
        return true;
    }

    static void Postfix(ref int __result)
    {
        __result *= 2;
    }
}

বিকল্পভাবে, প্রতিবিম্ব সহ ম্যানুয়াল প্যাচিং

using SomeGame;
using HarmonyLib;

public class MyPatcher
{
    // make sure DoPatching() is called at start either by
    // the mod loader or by your injector

    public static void DoPatching()
    {
        var harmony = new Harmony("com.example.patch");

        var mOriginal = typeof(SomeGameClass).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.NonPublic);
        var mPrefix = typeof(MyPatcher).GetMethod("MyPrefix", BindingFlags.Static | BindingFlags.Public);
        var mPostfix = typeof(MyPatcher).GetMethod("MyPostfix", BindingFlags.Static | BindingFlags.Public);
        // add null checks here

        harmony.Patch(mOriginal, new HarmonyMethod(mPrefix), new HarmonyMethod(mPostfix));
    }

    public static void MyPrefix()
    {
        // ...
    }

    public static void MyPostfix()
    {
        // ...
    }
}

উত্স কোডটি একবার দেখেছিল, খুব আকর্ষণীয়! আপনি কী ব্যাখ্যা করতে পারেন (এখানে এবং / অথবা ডকুমেন্টেশনে) নির্দিষ্ট নির্দেশাবলী কীভাবে জাম্পটি সম্পাদন করতে ব্যবহৃত হয় ( Memory.WriteJumpকীভাবে)?
টম

আংশিকভাবে আমার নিজের মন্তব্যের জবাব দিতে: 48 B8 <QWord>কিউবার্ডের তাত্ক্ষণিক মানটির দিকে সরানো হয় rax, তবে FF E0তা jmp rax- সেখানে সমস্ত পরিষ্কার! আমার বাকী প্রশ্নটি E9 <DWord>কেসটি (একটি কাছাকাছি জাম্প) সম্পর্কে: এটি মনে হচ্ছে এই ক্ষেত্রে নিকট জাম্পটি সংরক্ষণ করা হয়েছে এবং পরিবর্তনটি লাফের লক্ষ্যবস্তুতে রয়েছে; মনো কখন প্রথম স্থানে এমন কোড তৈরি করে এবং কেন এটি এই বিশেষ চিকিত্সা পায়?
টম

1
যতদূর আমি এটি বলতে পারি এটি এখনও সমর্থন করে না। নেট কোর 2, অ্যাপডোমাইন.কন্টেনডোমাইন.ডাইফাইনডাইনামিক অ্যাসাপাসেস
ম্যাক্স

1
আমার এক বন্ধু, 0x0ade আমাকে উল্লেখ করেছিলেন যে একটি কম পরিপক্ক বিকল্প আছে যা .NET কোর, NUGet- এ মনোমড.রুনটাইম ডেটারে কাজ করে।
Andreas Pardeike

1
আপডেট: সিস্টেম.প্রকাশন.এমিট রেফারেন্স অন্তর্ভুক্ত করে, হারমনি এখন। নেট কোর 3
Andreas Pardeike

181

.NET 4 এবং উপরের জন্য

using System;
using System.Reflection;
using System.Runtime.CompilerServices;


namespace InjectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Target targetInstance = new Target();

            targetInstance.test();

            Injection.install(1);
            Injection.install(2);
            Injection.install(3);
            Injection.install(4);

            targetInstance.test();

            Console.Read();
        }
    }

    public class Target
    {
        public void test()
        {
            targetMethod1();
            Console.WriteLine(targetMethod2());
            targetMethod3("Test");
            targetMethod4();
        }

        private void targetMethod1()
        {
            Console.WriteLine("Target.targetMethod1()");

        }

        private string targetMethod2()
        {
            Console.WriteLine("Target.targetMethod2()");
            return "Not injected 2";
        }

        public void targetMethod3(string text)
        {
            Console.WriteLine("Target.targetMethod3("+text+")");
        }

        private void targetMethod4()
        {
            Console.WriteLine("Target.targetMethod4()");
        }
    }

    public class Injection
    {        
        public static void install(int funcNum)
        {
            MethodInfo methodToReplace = typeof(Target).GetMethod("targetMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            MethodInfo methodToInject = typeof(Injection).GetMethod("injectionMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
                    Console.WriteLine("\nVersion x86 Debug\n");

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x86 Release\n");
                    *tar = *inj;
#endif
                }
                else
                {

                    long* inj = (long*)methodToInject.MethodHandle.Value.ToPointer()+1;
                    long* tar = (long*)methodToReplace.MethodHandle.Value.ToPointer()+1;
#if DEBUG
                    Console.WriteLine("\nVersion x64 Debug\n");
                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;


                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x64 Release\n");
                    *tar = *inj;
#endif
                }
            }
        }

        private void injectionMethod1()
        {
            Console.WriteLine("Injection.injectionMethod1");
        }

        private string injectionMethod2()
        {
            Console.WriteLine("Injection.injectionMethod2");
            return "Injected 2";
        }

        private void injectionMethod3(string text)
        {
            Console.WriteLine("Injection.injectionMethod3 " + text);
        }

        private void injectionMethod4()
        {
            System.Diagnostics.Process.Start("calc");
        }
    }

}

14
এটি আরও অনেকগুলি উপার্জনের দাবিদার। আমার পুরোপুরি আলাদা পরিস্থিতি আছে তবে এই স্নিপেটটি আমাকে সঠিক দিকে চালিত করার জন্য ঠিক আমার প্রয়োজন ছিল। ধন্যবাদ।
এসসি

2
@ লোগম্যান দুর্দান্ত উত্তর। তবে আমার প্রশ্নটি: ডিবাগ মোডে কী চলছে? এবং শুধুমাত্র একটি নির্দেশ প্রতিস্থাপন করা সম্ভব? উদাহরণস্বরূপ, আমি যদি শর্তযুক্ত শর্তে শর্তযুক্ত জাম্প প্রতিস্থাপন করতে চাই? আফাইক আপনি সংকলিত পদ্ধতিটি প্রতিস্থাপন করছেন, সুতরাং আমাদের কোন শর্তটি প্রতিস্থাপন করা উচিত তা নির্ধারণ করা সহজ নয় ...
অ্যালেক্স ঝুকভস্কিই

2
@ আলেক্সজুকভস্কি যদি আপনি এটি স্ট্যাকের উপর পোস্ট করতে চান এবং আমাকে লিঙ্ক পাঠান। আমি এটি সন্ধান করব এবং সপ্তাহান্তের পরে আপনাকে একটি উত্তর দেব। মেশিন আমি উইকএন্ডের পরে আপনার প্রশ্নটিও দেখব।
লগম্যান

2
এমএসটিস্টের সাথে ইন্টিগ্রেশন পরীক্ষার জন্য এটি করার সময় দুটি জিনিস আমি লক্ষ্য করেছি: (1) আপনি যখন এটির thisঅভ্যন্তরীণ ব্যবহার করবেন তখন সংকলনের সময়injectionMethod*() একটি Injectionউদাহরণ উল্লেখ করা হবে , তবে রানটাইম চলাকালীন একটি উদাহরণ (এটি ইনজেকশনের অভ্যন্তরে ব্যবহার করা উদাহরণ সদস্যদের সমস্ত রেফারেন্সের জন্য সত্য) পদ্ধতি)। (২) কোনও কারণে অংশটি কেবলমাত্র পরীক্ষা ডিবাগ করার সময় কাজ করছিল , তবে ডিবাগ-সংকলিত পরীক্ষা চালানোর সময় নয় । আমি সবসময় অংশ ব্যবহার করে শেষ । আমি বুঝতে পারি না কেন এটি কাজ করে তবে তা করে। Target#DEBUG#else
গুড নাইট নার্ড অহংকার

2
খুব সুন্দর. সব কিছু ভাঙার সময়! ব্যবহার @GoodNightNerdPride Debugger.IsAttachedপরিবর্তে #if প্রাক প্রসেসর
M.kazem Akhgary

25

আপনি রানটাইমের সময় কোনও পদ্ধতির সামগ্রী পরিবর্তন করতে পারেন। তবে আপনার অনুমিত হওয়া উচিত নয় এবং এটি পরীক্ষার উদ্দেশ্যে রাখার জন্য দৃ strongly়ভাবে সুপারিশ করা হয়।

শুধু একবার দেখুন:

http://www.codeproject.com/Articles/463508/NET-CLR-Injection-Modify-IL-Code-during-Run-time

মূলত, আপনি:

  1. মেথডেইনফো.গেটমেথডবডি () এর মাধ্যমে আইএল পদ্ধতির সামগ্রী পান Get গেটআইলাসবাইটআর্রে ()
  2. এই বাইট সঙ্গে জগাখিচুড়ি।

    আপনি যদি কিছু কোড পুনরায় সংশোধন বা সংযোজন করতে চান তবে কেবল নিজের পছন্দসই অপকডগুলি প্রিপ্রেন্ড / সংযোজন করুন (স্ট্যাকটি পরিষ্কার রাখার বিষয়ে সতর্কতা অবলম্বন করুন)

    বিদ্যমান আইএলটিকে "কমপাইল" করার জন্য এখানে কিছু টিপস রয়েছে:

    • ফিরে আসা বাইটগুলি আইএল নির্দেশাবলীর অনুক্রম হয়, তারপরে তাদের যুক্তিগুলি অনুসরণ করা হয় (উদাহরণস্বরূপ, '.call' এর একটি যুক্তি রয়েছে: যাকে বলা হয় পদ্ধতি টোকেন, এবং '.পপ'-এর কোনও কিছুই নেই)
    • প্রত্যাবর্তিত অ্যারেতে পাওয়া আইএল কোডগুলি এবং বাইটগুলির মধ্যে চিঠিপত্রগুলি অপকোড ব্যবহার করে পাওয়া যেতে পারে our আপনার আপপোড কোড V ভ্যালু (যা আপনার সমাবেশে সংরক্ষিত হিসাবে আসল অপকোড বাইট মান)
    • আইপিএল কোড যুক্ত হওয়ার পরে যুক্ত আর্গুমেন্টগুলির বিভিন্ন আকার থাকতে পারে (এক থেকে একাধিক বাইট), নামক অপকোডের উপর নির্ভর করে
    • আপনি এই টপিকগুলি খুঁজে পেতে পারেন যে এইগুলি যুক্তিগুলি যথাযথ পদ্ধতির মাধ্যমে উল্লেখ করা হচ্ছে। উদাহরণস্বরূপ, যদি আপনার আইএলটিতে ".call 354354" থাকে (হেক্সায় 28 00 05 68 32 হিসাবে কোডড, 28 ঘন্টা = 40 হচ্ছে '.কেল' অপকোড এবং 56832h = 354354), সম্পর্কিত নামক পদ্ধতিটি মেথডবেস ব্যবহার করে খুঁজে পাওয়া যাবে .GetMethodFromHandle (354354) )
  3. একবার সংশোধন করা হলে, আপনাকে আইএল বাইট অ্যারেটি ইনজেকশনহেলপারের মাধ্যমে পুনরায় সংযুক্ত করা যেতে পারে pঅপাদেটিলকোডস (মেথডইনফো পদ্ধতি, বাইট [] আইলকোডস) - উপরে উল্লিখিত লিঙ্কটি দেখুন

    এটি "অনিরাপদ" অংশ ... এটি ভালভাবে কাজ করে তবে এটি অভ্যন্তরীণ সিএলআর প্রক্রিয়া হ্যাক করার অন্তর্ভুক্ত ...


7
কেবলমাত্র পেডেন্টিক হতে, 354354 (0x00056832) একটি বৈধ মেটাডেটা টোকেন নয়, উচ্চ-অর্ডার বাইট 0x06 (মেথডেফ), 0x0A (সদস্যরাফ) বা 0x2B (মেথডস্পেক) হওয়া উচিত। এছাড়াও, মেটাডেটা টোকেনটি লিটল এন্ডিয়ান বাইট ক্রমে লেখা উচিত। অবশেষে, মেটাডেটা টোকেনটি মডিউল নির্দিষ্ট এবং মেথডিআইএনফোও M
ব্রায়ান রিচল

13

পদ্ধতিটি যদি ভার্চুয়াল, জেনারিক, জেনেরিক প্রকারে নয়, ইনলাইনড এবং এক্স ৮ plate প্ল্যাটফর্মে না থাকে তবে আপনি এটি প্রতিস্থাপন করতে পারেন:

MethodInfo methodToReplace = ...
RuntimeHelpers.PrepareMetod(methodToReplace.MethodHandle);

var getDynamicHandle = Delegate.CreateDelegate(Metadata<Func<DynamicMethod, RuntimeMethodHandle>>.Type, Metadata<DynamicMethod>.Type.GetMethod("GetMethodDescriptor", BindingFlags.Instance | BindingFlags.NonPublic)) as Func<DynamicMethod, RuntimeMethodHandle>;

var newMethod = new DynamicMethod(...);
var body = newMethod.GetILGenerator();
body.Emit(...) // do what you want.
body.Emit(OpCodes.jmp, methodToReplace);
body.Emit(OpCodes.ret);

var handle = getDynamicHandle(newMethod);
RuntimeHelpers.PrepareMethod(handle);

*((int*)new IntPtr(((int*)methodToReplace.MethodHandle.Value.ToPointer() + 2)).ToPointer()) = handle.GetFunctionPointer().ToInt32();

//all call on methodToReplace redirect to newMethod and methodToReplace is called in newMethod and you can continue to debug it, enjoy.

দেখতে পাগলটিকে বিপজ্জনক মনে হচ্ছে। আমি সত্যিই আশা করি যে কেউ এটি উত্পাদন কোড ব্যবহার করে না।
ব্রায়ান রিচল

2
এগুলি অ্যাপ্লিকেশন কর্মক্ষমতা পর্যবেক্ষণ (এপিএম) সরঞ্জামগুলি দ্বারা ব্যবহৃত হয় এবং পাশাপাশি উত্পাদনেও ব্যবহৃত হয়।
মার্টিন কার্স্টেন

1
জবাব দেওয়ার জন্য আপনাকে ধন্যবাদ, আমি এপেক্ট ওরিয়েন্টেড প্রোগ্রামিং এপিআই হিসাবে এই ধরণের সক্ষমতার অফার করার জন্য একটি প্রকল্পে কাজ করছি। আমি x86 এবং x64 উভয় ভার্চুয়াল পদ্ধতি এবং জেনেরিক পদ্ধতি পরিচালনা করতে আমার সীমাবদ্ধতার সমাধান করেছি। আপনার আরও বিশদ প্রয়োজন হলে আমাকে জানান Let
Teter28

6
ক্লাস মেটাডাটা কী?
সেবাস্তিয়ান

এই উত্তরটি সিউডো কোড এবং পুরানো। অনেকগুলি পদ্ধতির আর অস্তিত্ব নেই।
এন-এট

9

এখানে বেশ কয়েকটি ফ্রেমওয়ার্ক রয়েছে যা আপনাকে রানটাইমে কোনও পদ্ধতি পরিবর্তনশীল করতে দেয় (তারা ব্যবহারকারীর দ্বারা উল্লিখিত আইসিএলআর প্রফিলিং ইন্টারফেস ব্যবহার করে):

এছাড়াও কয়েকটি ফ্রেমওয়ার্ক রয়েছে যা। নেট এর অভ্যন্তরীনদের সাথে মশকরা করে, এগুলি সম্ভবত আরও ভঙ্গুর এবং সম্ভবত ইনলাইনড কোডটি পরিবর্তন করতে পারে না, তবে অন্যদিকে এগুলি সম্পূর্ণরূপে স্ব-অন্তর্নিহিত এবং আপনাকে কোনও ব্যবহারের প্রয়োজন হয় না কাস্টম প্রবর্তক

  • সম্প্রীতি : এমআইটি লাইসেন্সধারী। মনে হচ্ছে আসলে কয়েকটি গেমের মোডে সাফল্যের সাথে ব্যবহার করা হয়েছে,। নেট এবং মনো উভয় সমর্থন করে supports
  • ডিভিয়ার ইন প্রসেস ইনস্ট্রুমেন্টেশন ইঞ্জিন : জিপিএলভি 3 এবং কমার্শিয়াল। .NET সমর্থন বর্তমানে পরীক্ষামূলক হিসাবে চিহ্নিত হয়েছে, তবে অন্যদিকে বাণিজ্যিকভাবে ব্যাক হওয়ার সুবিধা রয়েছে।

8

লগম্যানের সমাধান , তবে মেথড বডিগুলি অদলবদলের জন্য একটি ইন্টারফেস সহ। এছাড়াও, একটি সহজ উদাহরণ।

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace DynamicMojo
{
    class Program
    {
        static void Main(string[] args)
        {
            Animal kitty = new HouseCat();
            Animal lion = new Lion();
            var meow = typeof(HouseCat).GetMethod("Meow", BindingFlags.Instance | BindingFlags.NonPublic);
            var roar = typeof(Lion).GetMethod("Roar", BindingFlags.Instance | BindingFlags.NonPublic);

            Console.WriteLine("<==(Normal Run)==>");
            kitty.MakeNoise(); //HouseCat: Meow.
            lion.MakeNoise(); //Lion: Roar!

            Console.WriteLine("<==(Dynamic Mojo!)==>");
            DynamicMojo.SwapMethodBodies(meow, roar);
            kitty.MakeNoise(); //HouseCat: Roar!
            lion.MakeNoise(); //Lion: Meow.

            Console.WriteLine("<==(Normality Restored)==>");
            DynamicMojo.SwapMethodBodies(meow, roar);
            kitty.MakeNoise(); //HouseCat: Meow.
            lion.MakeNoise(); //Lion: Roar!

            Console.Read();
        }
    }

    public abstract class Animal
    {
        public void MakeNoise() => Console.WriteLine($"{this.GetType().Name}: {GetSound()}");

        protected abstract string GetSound();
    }

    public sealed class HouseCat : Animal
    {
        protected override string GetSound() => Meow();

        private string Meow() => "Meow.";
    }

    public sealed class Lion : Animal
    {
        protected override string GetSound() => Roar();

        private string Roar() => "Roar!";
    }

    public static class DynamicMojo
    {
        /// <summary>
        /// Swaps the function pointers for a and b, effectively swapping the method bodies.
        /// </summary>
        /// <exception cref="ArgumentException">
        /// a and b must have same signature
        /// </exception>
        /// <param name="a">Method to swap</param>
        /// <param name="b">Method to swap</param>
        public static void SwapMethodBodies(MethodInfo a, MethodInfo b)
        {
            if (!HasSameSignature(a, b))
            {
                throw new ArgumentException("a and b must have have same signature");
            }

            RuntimeHelpers.PrepareMethod(a.MethodHandle);
            RuntimeHelpers.PrepareMethod(b.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)b.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)a.MethodHandle.Value.ToPointer() + 2;

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    int tmp = *tarSrc;
                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
                    *injSrc = (((int)tarInst + 5) + tmp) - ((int)injInst + 5);
                }
                else
                {
                    throw new NotImplementedException($"{nameof(SwapMethodBodies)} doesn't yet handle IntPtr size of {IntPtr.Size}");
                }
            }
        }

        private static bool HasSameSignature(MethodInfo a, MethodInfo b)
        {
            bool sameParams = !a.GetParameters().Any(x => !b.GetParameters().Any(y => x == y));
            bool sameReturnType = a.ReturnType == b.ReturnType;
            return sameParams && sameReturnType;
        }
    }
}

1
এটি আমাকে দিয়েছে: MA.ELCalc.FunctionalTests.dll এ টাইপ করা হয়েছে 'System.AccessViolationException' টাইপের একটি ব্যতিক্রম তবে ব্যবহারকারীর কোডে পরিচালিত হয়নি অতিরিক্ত তথ্য: সুরক্ষিত মেমরিটি পড়ার বা লেখার চেষ্টা করা হয়েছিল। এটি প্রায়শই এমন একটি ইঙ্গিত যা অন্য স্মৃতিশক্তি দূষিত ,,
এন-এট

আমি ব্যতিক্রম পেয়েছি "wapMethodBody এখনও 8 ইন্টিপিটিআর আকার হ্যান্ডেল করে না"
ফোং দাও

6

এই প্রশ্নের উত্তর এবং অন্যটির ভিত্তিতে ive এই পরিপাটি সংস্করণটি নিয়ে আসে:

// Note: This method replaces methodToReplace with methodToInject
// Note: methodToInject will still remain pointing to the same location
public static unsafe MethodReplacementState Replace(this MethodInfo methodToReplace, MethodInfo methodToInject)
        {
//#if DEBUG
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
//#endif
            MethodReplacementState state;

            IntPtr tar = methodToReplace.MethodHandle.Value;
            if (!methodToReplace.IsVirtual)
                tar += 8;
            else
            {
                var index = (int)(((*(long*)tar) >> 32) & 0xFF);
                var classStart = *(IntPtr*)(methodToReplace.DeclaringType.TypeHandle.Value + (IntPtr.Size == 4 ? 40 : 64));
                tar = classStart + IntPtr.Size * index;
            }
            var inj = methodToInject.MethodHandle.Value + 8;
#if DEBUG
            tar = *(IntPtr*)tar + 1;
            inj = *(IntPtr*)inj + 1;
            state.Location = tar;
            state.OriginalValue = new IntPtr(*(int*)tar);

            *(int*)tar = *(int*)inj + (int)(long)inj - (int)(long)tar;
            return state;

#else
            state.Location = tar;
            state.OriginalValue = *(IntPtr*)tar;
            * (IntPtr*)tar = *(IntPtr*)inj;
            return state;
#endif
        }
    }

    public struct MethodReplacementState : IDisposable
    {
        internal IntPtr Location;
        internal IntPtr OriginalValue;
        public void Dispose()
        {
            this.Restore();
        }

        public unsafe void Restore()
        {
#if DEBUG
            *(int*)Location = (int)OriginalValue;
#else
            *(IntPtr*)Location = OriginalValue;
#endif
        }
    }

এই মুহুর্তের জন্য
এটির

ব্যবহারের উদাহরণ যুক্ত করতে সহায়ক হবে
কোফফাস

5

আইসিএলআরপিআর ফিলিং ইন্টারফেস ব্যবহার করে আপনি রানটাইমে কোনও পদ্ধতি প্রতিস্থাপন করতে পারেন ।

  1. প্রক্রিয়াটি সংযুক্ত করতে AttachProfiler কল করুন ।
  2. পদ্ধতি কোডটি প্রতিস্থাপন করতে সেটিলফান্শনবডিকে কল করুন ।

আরও বিশদ জন্য এই ব্লগ দেখুন


3

আমি জানি এটি আপনার প্রশ্নের সঠিক উত্তর নয়, তবে এটি করার স্বাভাবিক উপায় হ'ল কারখানা / প্রক্সি পদ্ধতির ব্যবহার।

প্রথমে আমরা একটি বেস ধরণের ঘোষণা করি।

public class SimpleClass
{
    public virtual DTask<bool> Solve(int n, DEvent<bool> callback)
    {
        for (int m = 2; m < n - 1; m += 1)
            if (m % n == 0)
                return false;
        return true;
    }
}

তারপরে আমরা একটি উদ্ভূত প্রকার ঘোষণা করতে পারি (এটিকে প্রক্সি বলুন)।

public class DistributedClass
{
    public override DTask<bool> Solve(int n, DEvent<bool> callback)
    {
        CodeToExecuteBefore();
        return base.Slove(n, callback);
    }
}

// At runtime

MyClass myInstance;

if (distributed)
    myInstance = new DistributedClass();
else
    myInstance = new SimpleClass();

উদ্ভূত প্রকারটি রানটাইমেও উত্পন্ন হতে পারে।

public static class Distributeds
{
    private static readonly ConcurrentDictionary<Type, Type> pDistributedTypes = new ConcurrentDictionary<Type, Type>();

    public Type MakeDistributedType(Type type)
    {
        Type result;
        if (!pDistributedTypes.TryGetValue(type, out result))
        {
            if (there is at least one method that have [Distributed] attribute)
            {
                result = create a new dynamic type that inherits the specified type;
            }
            else
            {
                result = type;
            }

            pDistributedTypes[type] = result;
        }
        return result;
    }

    public T MakeDistributedInstance<T>()
        where T : class
    {
        Type type = MakeDistributedType(typeof(T));
        if (type != null)
        {
            // Instead of activator you can also register a constructor delegate generated at runtime if performances are important.
            return Activator.CreateInstance(type);
        }
        return null;
    }
}

// In your code...

MyClass myclass = Distributeds.MakeDistributedInstance<MyClass>();
myclass.Solve(...);

একমাত্র পারফরম্যান্স ক্ষতি হ'ল উদ্ভূত বস্তুটি নির্মাণের সময়, প্রথম বারটি বেশ ধীর কারণ এটি প্রচুর প্রতিবিম্ব এবং প্রতিবিম্ব নির্গত করে। অন্যান্য সমস্ত সময়ে, এটি একসাথে টেবিল দেখার এবং একজন নির্মাতার ব্যয়। যেমনটি বলা হয়েছে, আপনি ব্যবহার করে নির্মাণকে অনুকূল করতে পারেন

ConcurrentDictionary<Type, Func<object>>.

1
হুম .. যে বিতরণ প্রক্রিয়া সম্পর্কে সক্রিয়ভাবে সচেতন হতে প্রোগ্রামারের পক্ষে এখনও কাজ প্রয়োজন; আমি এমন একটি সমাধান খুঁজছিলাম যা কেবলমাত্র তাদের উপর (বিতরণকারী) বৈশিষ্ট্যটি (পদ্ধতিতে সাবক্লাসিং বা কনটেক্সটবাউন্ডঅবজেক্ট থেকে উত্তরাধিকার সূত্রে নয়) সেট করার উপর নির্ভর করে। দেখে মনে হচ্ছে Mono.Cecil বা এর মতো কিছু ব্যবহার করে অ্যাসেমব্লিতে আমার কিছু পোস্ট-সংকলন সংশোধন করার দরকার হতে পারে।
জুন রোডস

আমি বলব না, এটি স্বাভাবিক উপায়। প্রয়োজনীয় দক্ষতার ক্ষেত্রে এই পদ্ধতিটি সহজ (সিএলআর বোঝার দরকার নেই) তবে প্রতিস্থাপিত পদ্ধতি / শ্রেণীর প্রতিটি ক্ষেত্রে একই পদক্ষেপ পুনরাবৃত্তি করা প্রয়োজন। যদি পরে আপনি কিছু পরিবর্তন করতে চান (উদাহরণস্বরূপ, কেবলমাত্র আগে নয় কিছু কোড পরে চালিত করুন) তবে আপনাকে এন বার এটি করতে হবে (অনিরাপদ কোডের বিপরীতে যা একবারে এটি করা প্রয়োজন)। সুতরাং এটি এন ঘন্টা জব বনাম 1 ঘন্টা কাজ)
ইউজিন গর্বোভয়
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.