ioncodes / dnpatch

.NET Patcher library using dnlib
MIT License
313 stars 49 forks source link

Problem about method referance and call method from another module #44

Closed gagmeng closed 7 years ago

gagmeng commented 7 years ago

I'm using dnpatch to edit an existing binary and edit an existing method to call a method in an assembly that is already referenced by the binary. Ploverinfo.Framework.dll is the binary which I need to edit, and Ploverinfo.CommonLib.dll is the referenced file. Some codes of these two files are as below. Ploverinfo.CommonLib.dll:

using xxxx.xxxx;
namespace Ploverinfo.CommonLib.App
{
    // Token: 0x020000B2 RID: 178
    public class Config
    {
        ......
        public string getValue(string strKey)
        {
            if (this.ht.ContainsKey(strKey))
            {
                return this.ht[strKey].ToString();
            }
            return "";
        }
        ......
        private Hashtable ht = new Hashtable();
    }

    public class Global
    {
        public static string get_LicenseCode()
        {
            if (!string.IsNullOrEmpty(Global._LicenseCode))
            {
                return Global._LicenseCode;
            }
            Global._LicenseCode = Global.license.getValue("LicenseCode");
            if (string.IsNullOrEmpty(Global._LicenseCode))
            {
                Global._LicenseCode = Global.config.getValue("RegCode");
            }
            return Global._LicenseCode;
        }
        ......
        public static Config license = new Config();
        protected static string _LicenseCode = "";
        ......
    }
}

Ploverinfo.Framework.dll:

using xxxx.xxxx
using Ploverinfo.CommonLib.App;
using Ploverinfo.CommonLib.App.Cryptography;
using Ploverinfo.CommonLib.Model;
using Ploverinfo.CommonLib.Net;
namespace Ploverinfo.Framework.WebServiceCaller
{
    // Token: 0x02000003 RID: 3
    public class User
    {
        ......
        public string GetLicenseCode(/* some parameters */)
        {
            ......
        }
        ......
    }
}

I want to edit the existing method GetLicenseCode to just like this, which can be edited easily by dnspy as snapshot1.

namespace Ploverinfo.Framework.WebServiceCaller
{
    // Token: 0x02000003 RID: 3
    public class User
    {
        ......
        public string GetLicenseCode(/* some parameters */)
        {
            return Global.license.getValue("LicenseCode");
        }
        ......
    }
}

snapshot1:(http://www.hostpic.org/images/1708260636320102.jpg)

Then I write some codes to insert il instructions into the previous GetLicenseCode method as below.

ModuleRef mod_CommonLib = new ModuleRefUser(ModuleDefMD.Load("Ploverinfo.CommonLib.dll"));
ModuleDef mod_Framework = ModuleDefMD.Load("Ploverinfo.Framework.dll");
//ldsfld Ploverinfo.CommonLib.App.Config Ploverinfo.CommonLib.App.Global::license
TypeRef globalRef = new TypeRefUser(mod_Framework, "Ploverinfo.CommonLib.App", "Global", mod_CommonLib);
TypeRef configRef = new TypeRefUser(mod_Framework, "Ploverinfo.CommonLib.App", "Config", mod_CommonLib);
TypeSig type2 = configRef.ToTypeSig();
MemberRef LicenseRef = new MemberRefUser(mod_Framework, "license", new FieldSig(type2), globalRef);
Instruction InsLicenseCodeRef = Instruction.Create(OpCodes.Ldsfld, LicenseRef);
//ldstr "LicenseCode"
Instruction InsLicenseCode = Instruction.Create(OpCodes.Ldstr, "LicenseCode");
//callvirt System.String Ploverinfo.CommonLib.App.Config::getValue(System.String)
MemberRef getValCall = new MemberRefUser(mod_Framework, "getValue",
MethodSig.CreateStatic(mod_Framework.CorLibTypes.String, mod_Framework.CorLibTypes.String),
                configRef);
Instruction InsgetValCall = OpCodes.Callvirt.ToInstruction(getValCall);
//ret
 Instruction InsRet = Instruction.Create(OpCodes.Ret);
Instruction[] toPatch = 
{
     InsLicenseCodeRef,
     InsLicenseCode,
     InsgetValCall,
     InsRet
 };
...
/* Insert instructions into GetLicenseCode method */

After the patch, the codes of GetLicenseCode are as below, and the il codes in dnspy are as snapshot2.

public string GetLicenseCode(/* some parameters */)
{
    Global.license;
    return Config.getValue("LicenseCode");
}

snapshot2: (http://www.hostpic.org/images/1708260636320081.jpg) I do not know where the problem is. Is there anyone who can help me? Thanks.

ioncodes commented 7 years ago

I'm not quite sure too. I'll have a look into this. Can you test it with dnpatch v1 too? I can help you how to use the v1 for this if you need any.

gagmeng commented 7 years ago

how to download dnpatch V1 and how to patch the case ? Thanks.

ioncodes commented 7 years ago

Clone it from here: https://github.com/ioncodes/dnpatch/tree/v1 and compile it. Have a look at this function: https://github.com/ioncodes/dnpatch/blob/v1/dnpatch/Processors/InstructionProcessor.cs#L29 You probably want to do it like this: https://github.com/ioncodes/dnpatch/blob/v1/dnpatch.tests/PatchTests.cs#L23

ioncodes commented 7 years ago

I recommend loading your types via dnlib first and then overwrite it with dnpatch first.

ioncodes commented 7 years ago

Can you also send me the binaries via Gitter?

gagmeng commented 7 years ago

I'll attach the download link by mega, thanks for your reply.

gagmeng commented 7 years ago

These binaries can be downloaded by the below mega link:(https://mega.nz/#F!7JBymKCT!7UROTiEorrQHbn9wS4wFHQ)

ioncodes commented 7 years ago

I need all assemblies. I'm missing for example: "Ploverinfo.CRM.Model;"

ioncodes commented 7 years ago

I extracted them from PPDownloader

gagmeng commented 7 years ago

Does this have any progress?

ioncodes commented 7 years ago

Yes, I'm still working on the Lsfld part. I'll post my code below later.

gagmeng commented 7 years ago

any progress now?

ioncodes commented 7 years ago

I just want to apologize, I completely forgot to upload my snippet, I'll do so in about 6 hours.

ioncodes commented 7 years ago

Do this:

var common = ModuleDefMD.Load("Ploverinfo.CommonLib.dll");
// Do you LDSFLD stuff e.g. the field
var method = common.Find("Ploverinfo.CommonLib.App.Config", true).FindMethod("getValue");

Loader loader = new Loader();

loader.Initialize("framework", "Ploverinfo.Framework.dll", "Ploverinfo.Framework.dnpatch.dll", false, true,
    true);

Assembly framework = loader.LoadAssembly("framework");
IMethod getValueCall = framework.Instructions.BuildCall(method);

framework.Model.SetNamespace("Ploverinfo.Framework.WebServiceCaller");
framework.Model.SetType("User");
framework.Model.SetMethod("GetLicenseCode");

var instructions = new Instruction[]
{
    Instruction.Create(OpCodes.Ldsfld, field),
    Instruction.Create(OpCodes.Ldstr, "LicenseCode"),
    Instruction.Create(OpCodes.Callvirt, getValueCall),
    Instruction.Create(OpCodes.Ret)
};

framework.IL.Overwrite(instructions);

foreach (var inst in instructions)
{
    Console.WriteLine(inst);
}

loader.Save();
gagmeng commented 7 years ago

I write some codes to do the ldsfld stuff as below:

var common = ModuleDefMD.Load("Ploverinfo.CommonLib.dll");
ModuleRef mod_CommonLib = new ModuleRefUser(common);
ModuleDef mod_Framework = ModuleDefMD.Load("Ploverinfo.Framework.dll");
//Setup IL_0012: ldsfld Ploverinfo.CommonLib.App.Config Ploverinfo.CommonLib.App.Global::license
TypeRef globalRef = new TypeRefUser(mod_Framework, "Ploverinfo.CommonLib.App", "Global", mod_CommonLib);
TypeRef configRef = new TypeRefUser(mod_Framework, "Ploverinfo.CommonLib.App", "Config", mod_CommonLib);
TypeSig type2 = configRef.ToTypeSig();
MemberRef LicenseRef = new MemberRefUser(mod_Framework, "license", new FieldSig(type2), globalRef);

But the result is incorrect as the below snapshot. http://www.hostpic.org/images/1709080959420083.jpg

Could you give me some guidances to setup the ldsfld ? Thanks.

gagmeng commented 7 years ago

I have solved it , the codes are as below:

var common = ModuleDefMD.Load("Ploverinfo.CommonLib.dll");
var frameworkMD = ModuleDefMD.Load("Ploverinfo.Framework.dll");
var LicensefieldDef = common.FindReflection("Ploverinfo.CommonLib.App.Global").FindField("license");
var method = common.Find("Ploverinfo.CommonLib.App.Config", true).FindMethod("getValue");
var memberRef = frameworkMD.Import(LicensefieldDef);

Do you have any user guidances for dnpatch-V1? And is it any plan to support this in latest dnpatch?

gagmeng commented 7 years ago

I have solved it with dnpatch-V2 , the codes are as below:

var common      = ModuleDefMD.Load("Ploverinfo.CommonLib.dll");
var frameworkMD = ModuleDefMD.Load("Ploverinfo.Framework.dll");
//ldsfld Ploverinfo.CommonLib.App.Config Ploverinfo.CommonLib.App.Global::license
var LicensefieldDef = common.FindReflection("Ploverinfo.CommonLib.App.Global").FindField("license");
var fieldRef = frameworkMD.Import(LicensefieldDef);
Instruction InsLicenseCodeRef = Instruction.Create(OpCodes.Ldsfld, fieldRef);
//IL_0017: ldstr "LicenseCode"
Instruction InsLicenseCode = Instruction.Create(OpCodes.Ldstr, "LicenseCode");
//IL_001C: callvirt System.String Ploverinfo.CommonLib.App.Config::getValue(System.String)
var method = common.Find("Ploverinfo.CommonLib.App.Config", true).FindMethod("getValue");          
var methodRef = frameworkMD.Import(method);
Instruction InsgetValCall = OpCodes.Callvirt.ToInstruction(methodRef);
//IL_004B: ret
Instruction InsRet = Instruction.Create(OpCodes.Ret);

Instruction[] toPatch =
{
    InsLicenseCodeRef,
    InsLicenseCode,
    InsgetValCall,
    InsRet
};
Patcher patcher = new Patcher("Ploverinfo.Framework.dll", true);
Target target = new Target()
{
    Namespace = "Ploverinfo.Framework.WebServiceCaller",
    Class = "User",
    Method = "GetLicenseCode",
    Instructions = toPatch
};
patcher.Patch(target);
patcher.Save(true)

Thanks for your great work.

ioncodes commented 7 years ago

Great :)