• .net wpf程序 移花接木


    最近在研究C# .net桌面程序。非常有趣
    软件是国外作者写的 公司要求修改翻译
    从最初的开源变成闭源再到加壳. 一路让我这个小小的职员好蛋疼.
    软件是VS2015 C# WPF 程序 在不脱壳的情况下 实现挂钩类托管函数 遍历WPF控件汉化

    首先通过各种手段脱壳目标程序,哪怕是不能运行都没关系。只要找到程序main函数。,找到Main的命名空间,然后自己创建一个一样的WPF程序 和他的命名空间一样就可以
    这里我的目标程序命名空间是 MainApp

    生成以后把文件后缀改成DLL
    之后我们创建一个 C# winform 程序删除程序中默认的对话框 添加引用上面生成的文件
    因为目标程序是WPF 程序 所以还得添加默认程序集
    WindowsBase 和 PresentationFramework
    下面开始写代码
     

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    using HookDetour;

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Reflection;

    using System.Runtime.CompilerServices;

    using System.Threading.Tasks;

    using System.Windows.Forms;

    namespace Load

    {

        //这里是一个开源的hook库 根据名字自己google

        class hookClass : IMethodMonitor

        {

            [MethodImpl(MethodImplOptions.NoInlining)]

            [Monitor("System.Windows""Application")] //目标方法的名称空间,类名

            public static void LoadComponent(Uri resourceLocator,bool b)

            {

                Console.WriteLine(resourceLocator);

                //mainwindow.baml

                var uri = new System.Uri(@"pack://application:,,,/MainApp;component/MainWindow.xaml");

                old_LoadComponent(uri, b);

            }

            [MethodImpl(MethodImplOptions.NoInlining)]

            [Original] //原函数标记

            public static void old_LoadComponent(Uri resourceLocator, bool b)

            {

                Console.WriteLine("null");

            }

        }

        static class Program

        {

            static MainApp.App pMainApp;

            private static void AppRun(object sender, object args)

            {

                //在这里开始干坏事

                Console.WriteLine("程序已经启动");

            }

            [STAThread]

            static void Main()

            {

                //建立目标程序导出的类 储存起来,方便找对应方法和变量

                pMainApp = new MainApp.App();

                //需要程序托管之类的操作需要初始化以后才能干,这里监听 Startup

                pMainApp.Startup += new System.Windows.StartupEventHandler(AppRun);

                //部分.net 程序查找不到自身目录资源,需要HOOK添加空间名

                //这个HOOK 不一定需要,有些程序加壳以后会自行修复这个问题,

                //hook LoadComponent 这个函数

                hookClass hk = new hookClass();

                var hookfun = hk.GetType().GetMethod("LoadComponent");

                var hookori = hk.GetType().GetMethod("old_LoadComponent");

                //通过 MainApp.App 得到父类 ,根据父类查找 LoadComponent函数

                MethodInfo LoadComponentFunction = pMainApp.GetType().BaseType.GetMethod("LoadComponent", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, System.Type.DefaultBinder, new Type[] { typeof(Uri),typeof(bool) },null);

                IDetour engine = DetourFactory.CreateDetourEngine();

                engine.Patch(LoadComponentFunction, hookfun, hookori);

                pMainApp.InitializeComponent();

                pMainApp.Run();

            }

        }

    }



    到了这里程序已经启动好了。把目标程序启动了以后 程序进程看见的就是自己的进程,
    运行内容就是目标程序内容
    然后根据反射调用和获取目标程序的函数和变量

    这里需要注意的是有些.net壳会导致无法 Assembly.LoadFrom 失败,
    但是你编写的程序是VS已经帮你加载到程序里面了。所以可以通过遍历获取到Assembly
    目标程序包含的程序集也可以通过这个方式查找,然后根据Assembly获取对应类的函数
     

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    //这里节省资源,我就把Assembly储存起来了

    public static Assembly GetAssembly()

    {

        if (m_MainAppAssembly == null)

        {

            foreach (Assembly ay in AppDomain.CurrentDomain.GetAssemblies())

            {

                if (ay.FullName.IndexOf("AppMain") > -1)

                {

                    m_CatchemAssembly = ay;

                    break;

                }

            }

        }

        return m_MainAppAssembly ;

    }



    获取到Assembly 基本上你可以控制整个程序的流程了
     


    //根据Assembly得到MianApp.MainWindow类 然后在类里找到 GlobalValue 变量
    Assembly amy = GetAssembly();
    var GlobalValue = amy.GetType("MianApp.MainWindow").GetField("GlobalValue");
    //得到变量内容,参数是对应的对象
    GlobalValue.GetValue(null);
    //设置变量内容
    GlobalValue.SetValue(null, "GGGG");



    剩下的就是挂钩了  当然建议你还是使用第三方的库
    pMainApp.GetType().GetMethod("LoadComponent").MethodHandle.GetFunctionPointer();

    这样就可以获取 LoadComponent 的函数地址, 如果你不知道目标类有什么函数的话可以通过
    GetMethods 来获取到,记得查看参数BindingFlags 选项,这个选项可以影响到你获取的结果
    如果函数名有相同的 需要自己定义参数类型获取,不然会抛异常

    这里值得注意的是 GetFunctionPointer 函数不一定就能返回正确的结果,在加壳的程序里
    你有可能获取的地址是错误的,因为程序没执行过这个函数。所以你可以选择等待程序调用过
    这个函数你再去挂钩,或者自己手动调用一次,随便传入参数。捕获一下异常,然后再挂钩。

    遍历WPF控件 获取到主窗口对象然后 VisualTreeHelper 这个类可以帮助你获取控件对象

  • 相关阅读:
    最后的挣扎 - Qt For Android on HuaWei Mate 60Pro (v4.0.0)
    工程化(产研流程规范)
    Docker从入门到上天系列第三篇:docker官网介绍与docker的三要素
    【python】之常用类型(包括进制)之间的转换
    HMI/SCADA软件架构和编程
    C++语言之组合、聚合类间关系
    rabbitmq 安装
    C#学习笔记(3)——类型系统、命名系统、类简介、记录
    【Go实现】实践GoF的23种设计模式:迭代器模式
    排序算法-----快速排序(非递归实现)
  • 原文地址:https://blog.csdn.net/jmm18363027827/article/details/133776350