热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#Hook原理及EasyHookC#Hook原理及EasyHook简易教程

C#Hook原理及EasyHook简易教程在说C#Hook之前,我们先来说说什么是Hook技术。相信大家都接触过外挂,不管是修改游戏客户端的也好,盗取密码的也罢,它们都是如何实现的
C# Hook原理及EasyHook简易教程

  在说C# Hook之前,我们先来说说什么是Hook技术。相信大家都接触过外挂,不管是修改游戏客户端的也好,盗取密码的也罢,它们都是如何实现的呢?

  实际上,Windows平台是基于事件驱动机制的,整个系统都是通过消息的传递来实现的。当进程有响应时(包括响应鼠标和键盘事件),则Windows会向应用程序发送一个消息给应用程序的消息队列,应用程序进而从消息队列中取出消息并发送给相应窗口进行处理。

  而Hook则是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

  所以Hook就可以实现在键盘/鼠标响应后,窗口处理消息之前,就对此消息进行处理,比如监听键盘输入,鼠标点击坐标等等。某些盗号木马就是Hook了指定的进程,从而监听键盘输入了什么内容,进而盗取账户密码。

C# Hook

  我们知道C#是运行在.NET平台之上,而且是基于CLR动态运行的,所以只能操作封装好的函数,且无法直接操作内存数据。而且在C#常用的功能中,并未封装Hook相关的类与方法,所以如果用C#实现Hook,必须采用调用WindowsAPI的方式进行实现。

  WindowsAPI函数属于非托管类型的函数,我们在调用时必须遵循以下几步:

  1、查找包含调用函数的DLL,如User32.dll,Kernel32.dll等。

  2、将该DLL加载到内存中,并注明入口

  3、将所需参数转化为C#存在的类型,如指针对应Intptr,句柄对应int类型等等

  4、调用函数

  我们本篇需要使用的函数有以下几个:

  SetWindowsHookEx     用于安装钩子

  UnhookWindowsHookEx   用于卸载钩子

  CallNextHookEx      执行下一个钩子

  详细API介绍请参考MSDN官方声明

  接下来在C#中需要首先声明此API函数:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
[DllImport("user32.dll",CharSet=CharSet.Auto,CallingCOnvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,IntPtr hInstance, int threadId);

[DllImport("user32.dll",CharSet=CharSet.Auto,CallingCOnvention=CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);

[DllImport("user32.dll",CharSet=CharSet.Auto,CallingCOnvention=CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode,IntPtr wParam, IntPtr lParam);
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  声明后即可实现调用,SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中,SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。 且钩子使用完成后需要调用UnhookWindowsHookEx进行卸载,否则容易影响到其他钩子的执行,并且钩子太多会影响目标进程的正常运行。

  关于实例详细操作过程不再赘述,请参考:http://blog.csdn.net/ensoo/article/details/2045101 及 https://www.cnblogs.com/ceoliujia/archive/2010/05/20/1740217.html

EasyHook

  C#本身调用WindowsAPI进行Hook功能受到很大的限制,而C++则不受此限制,因此就有一些聪明的人想到了聪明的方法:使用C++将基本操作封装成库,由C#进行调用,由此诞生了伟大的EasyHook,它不仅使用方便,而且开源免费,还支持64位版本。

  接下来我们一起使用C#操作EasyHook来实现一个Demo,完成对MessageBox的改写。

  首先我们建立一个WinForm项目程序,并添加一个类库ClassLibrary1,再从官网https://easyhook.github.io/或Nuget获取到dll后引用到我们的项目中,注意:32位和64位版本都需要引用,建立项目如图所示:

   C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  其中WinForm程序用于获取目标进程,并对目标进程进行注入,相关步骤如下:

  1、根据进程ID获取相关进程,并判断是否为64位;

  2、将所需DLL注册到GAC(全局程序集缓存),注册到GAC的目的是需要在目标进程中调用EasyHook及我们所编写的DLL;

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
private bool RegGACAssembly()
 {
     var dllName = "EasyHook.dll";
     var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dllName);
     if (!RuntimeEnvironment.FromGlobalAccessCache(Assembly.LoadFrom(dllPath)))
     {
         new System.EnterpriseServices.Internal.Publish().GacInstall(dllPath);
         Thread.Sleep(100);
     }
   dllName = "ClassLibrary1.dll";
     dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dllName);
     new System.EnterpriseServices.Internal.Publish().GacRemove(dllPath);
     if (!RuntimeEnvironment.FromGlobalAccessCache(Assembly.LoadFrom(dllPath)))
     {
           new System.EnterpriseServices.Internal.Publish().GacInstall(dllPath);
           Thread.Sleep(100);
     }
     return true;
  } 
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  此处需要注意,要将自己编写的类库DLL加入GAC,需要对DLL进行强签名操作,操作方法请参考:https://docs.microsoft.com/zh-cn/dotnet/framework/app-domains/how-to-sign-an-assembly-with-a-strong-name

  3、注入目标进程,此处需使用EasyHook的RemoteHooking.Inject()方法进行注入:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
private static bool InstallHookInternal(int processId)
{
  try
  {
     var parameter = new HookParameter
     {
         Msg = "已经成功注入目标进程",
         HostProcessId = RemoteHooking.GetCurrentProcessId()
      };
    RemoteHooking.Inject(
                    processId,
                    InjectionOptions.Default,
                    typeof(HookParameter).Assembly.Location,
                    typeof(HookParameter).Assembly.Location,
                    string.Empty,
                    parameter
                );
            }
            catch (Exception ex)
            {
                Debug.Print(ex.ToString());
                return false;
            }
            return true;
}
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
  HookParameter类为定义在ClassLibrary1中的一个类,包含消息与进程ID:
 [Serializable]
    public class HookParameter
    {
        public string Msg { get; set; }
        public int HostProcessId { get; set; }
    }

  到这一步我们就完成了对主窗体代码的编写,现在我们开始编写注入DLL的方法:

  1、先引入MessageBox相关的WindowsAPI:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
#region MessageBoxW

        [DllImport("user32.dll", EntryPoint = "MessageBoxW", CharSet = CharSet.Unicode)]
        public static extern IntPtr MessageBoxW(int hWnd, string text, string caption, uint type);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        delegate IntPtr DMessageBoxW(int hWnd, string text, string caption, uint type);

        static IntPtr MessageBoxW_Hooked(int hWnd, string text, string caption, uint type)
        {
            return MessageBoxW(hWnd, "已注入-" + text, "已注入-" + caption, type);
        }

        #endregion
        
        #region MessageBoxA

        [DllImport("user32.dll", EntryPoint = "MessageBoxA", CharSet = CharSet.Ansi)]
        public static extern IntPtr MessageBoxA(int hWnd, string text, string caption, uint type);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        delegate IntPtr DMessageBoxA(int hWnd, string text, string caption, uint type);

        static IntPtr MessageBoxA_Hooked(int hWnd, string text, string caption, uint type)
        {
            return MessageBoxA(hWnd, "已注入-" + text, "已注入-" + caption, type);
        }

        #endregion
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  其中MessageBoxA与MessageBoxW是微软用于区分不同操作系统中的编码类型,早期的Windows并不属于真正的32位操作系统,执行的API函数属于ANSI类型,而从Windows2000开始,属于Unicode类型,Windows在实际操作中,调用的MessageBox会自动根据平台区分使用前者还是后者,我们在这里就需要把二者都包含其中。

  而DMessageBoxA与DMessageBoxW属于IntPtr类型的委托,用于我们在Hook函数之后传入我们需要修改的方法,此处我们改变了MessageBox的内容和标题,分别在前缀加上了"已注入-"的标记。

  2、完成定义之后我们就需要对函数进行Hook,此处使用LocalHook.GetProcAddress("user32.dll", "MessageBoxW")函数,通过指定的DLL与函数名,获取函数在实际内存中的地址,获取到之后,传入LocalHook.Create()方法,用于创建本地钩子:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
public void Run(
            RemoteHooking.IContext context,
            string channelName
            , HookParameter parameter
            )
        {
            try
            {
                MessageBoxWHook = LocalHook.Create(
                    LocalHook.GetProcAddress("user32.dll", "MessageBoxW"),
                    new DMessageBoxW(MessageBoxW_Hooked),
                    this);
                MessageBoxWHook.ThreadACL.SetExclusiveACL(new int[1]);

                MessageBoxAHook = LocalHook.Create(
                    LocalHook.GetProcAddress("user32.dll", "MessageBoxA"),
                    new DMessageBoxW(MessageBoxA_Hooked),
                    this);
                MessageBoxAHook.ThreadACL.SetExclusiveACL(new int[1]);     
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }

            try
            {
                while (true)
                {
                    Thread.Sleep(10);
                }
            }
            catch
            {

            }
        }
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  其中MessageBoxWHook与MessageBoxAHook均为LocalHook类型的变量,MessageBoxAHook.ThreadACL.SetExclusiveACL(new int[1]); 这句代码用于将本地钩子加入当前线程中执行。

  运行之后我们来查看Hook的效果,先打开一个测试窗体,弹出MessageBox,这时候MessageBox没有标题,且内容是正常的:

    C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

 

  接着我们对目标进程进行注入,获取进程ID后点击注入,提示已经成功注入目标进程:

 

    C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

 

  此时点击目标进程MessageBox,可以发现已经Hook成功,并改变了内容和标题:

 

    C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

 

  至此,C#调用EasyHook对目标进程Hook已经实现。

后记

  从这次实践中我们可以感受到,C#对程序进行Hook是完全可行的,虽然不能直接操作内存和地址,但是我们可以通过操作WindowsAPI与使用EasyHook的方式完成,尤其是后者,大大减少了代码数量与使用难度。

  但是EasyHook目前中文资料非常少,我在使用的过程中也遇到了很大困难,Hook其他函数的方法也未能完全实现,希望能够集思广益,与大家共同思考交流!

  本人刚研究Hook时间不久,文中难免出现纰漏,恳请各位评论指正。

    源代码已经上传至百度网盘:链接: https://pan.baidu.com/s/1wyin9Ezn6AwFQlQxMenQeg 密码: dv9b

  在说C# Hook之前,我们先来说说什么是Hook技术。相信大家都接触过外挂,不管是修改游戏客户端的也好,盗取密码的也罢,它们都是如何实现的呢?

  实际上,Windows平台是基于事件驱动机制的,整个系统都是通过消息的传递来实现的。当进程有响应时(包括响应鼠标和键盘事件),则Windows会向应用程序发送一个消息给应用程序的消息队列,应用程序进而从消息队列中取出消息并发送给相应窗口进行处理。

  而Hook则是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

  所以Hook就可以实现在键盘/鼠标响应后,窗口处理消息之前,就对此消息进行处理,比如监听键盘输入,鼠标点击坐标等等。某些盗号木马就是Hook了指定的进程,从而监听键盘输入了什么内容,进而盗取账户密码。

C# Hook

  我们知道C#是运行在.NET平台之上,而且是基于CLR动态运行的,所以只能操作封装好的函数,且无法直接操作内存数据。而且在C#常用的功能中,并未封装Hook相关的类与方法,所以如果用C#实现Hook,必须采用调用WindowsAPI的方式进行实现。

  WindowsAPI函数属于非托管类型的函数,我们在调用时必须遵循以下几步:

  1、查找包含调用函数的DLL,如User32.dll,Kernel32.dll等。

  2、将该DLL加载到内存中,并注明入口

  3、将所需参数转化为C#存在的类型,如指针对应Intptr,句柄对应int类型等等

  4、调用函数

  我们本篇需要使用的函数有以下几个:

  SetWindowsHookEx     用于安装钩子

  UnhookWindowsHookEx   用于卸载钩子

  CallNextHookEx      执行下一个钩子

  详细API介绍请参考MSDN官方声明

  接下来在C#中需要首先声明此API函数:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
[DllImport("user32.dll",CharSet=CharSet.Auto,CallingCOnvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,IntPtr hInstance, int threadId);

[DllImport("user32.dll",CharSet=CharSet.Auto,CallingCOnvention=CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);

[DllImport("user32.dll",CharSet=CharSet.Auto,CallingCOnvention=CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode,IntPtr wParam, IntPtr lParam);
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  声明后即可实现调用,SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中,SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。 且钩子使用完成后需要调用UnhookWindowsHookEx进行卸载,否则容易影响到其他钩子的执行,并且钩子太多会影响目标进程的正常运行。

  关于实例详细操作过程不再赘述,请参考:http://blog.csdn.net/ensoo/article/details/2045101 及 https://www.cnblogs.com/ceoliujia/archive/2010/05/20/1740217.html

EasyHook

  C#本身调用WindowsAPI进行Hook功能受到很大的限制,而C++则不受此限制,因此就有一些聪明的人想到了聪明的方法:使用C++将基本操作封装成库,由C#进行调用,由此诞生了伟大的EasyHook,它不仅使用方便,而且开源免费,还支持64位版本。

  接下来我们一起使用C#操作EasyHook来实现一个Demo,完成对MessageBox的改写。

  首先我们建立一个WinForm项目程序,并添加一个类库ClassLibrary1,再从官网https://easyhook.github.io/或Nuget获取到dll后引用到我们的项目中,注意:32位和64位版本都需要引用,建立项目如图所示:

   C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  其中WinForm程序用于获取目标进程,并对目标进程进行注入,相关步骤如下:

  1、根据进程ID获取相关进程,并判断是否为64位;

  2、将所需DLL注册到GAC(全局程序集缓存),注册到GAC的目的是需要在目标进程中调用EasyHook及我们所编写的DLL;

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
private bool RegGACAssembly()
 {
     var dllName = "EasyHook.dll";
     var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dllName);
     if (!RuntimeEnvironment.FromGlobalAccessCache(Assembly.LoadFrom(dllPath)))
     {
         new System.EnterpriseServices.Internal.Publish().GacInstall(dllPath);
         Thread.Sleep(100);
     }
   dllName = "ClassLibrary1.dll";
     dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dllName);
     new System.EnterpriseServices.Internal.Publish().GacRemove(dllPath);
     if (!RuntimeEnvironment.FromGlobalAccessCache(Assembly.LoadFrom(dllPath)))
     {
           new System.EnterpriseServices.Internal.Publish().GacInstall(dllPath);
           Thread.Sleep(100);
     }
     return true;
  } 
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  此处需要注意,要将自己编写的类库DLL加入GAC,需要对DLL进行强签名操作,操作方法请参考:https://docs.microsoft.com/zh-cn/dotnet/framework/app-domains/how-to-sign-an-assembly-with-a-strong-name

  3、注入目标进程,此处需使用EasyHook的RemoteHooking.Inject()方法进行注入:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
private static bool InstallHookInternal(int processId)
{
  try
  {
     var parameter = new HookParameter
     {
         Msg = "已经成功注入目标进程",
         HostProcessId = RemoteHooking.GetCurrentProcessId()
      };
    RemoteHooking.Inject(
                    processId,
                    InjectionOptions.Default,
                    typeof(HookParameter).Assembly.Location,
                    typeof(HookParameter).Assembly.Location,
                    string.Empty,
                    parameter
                );
            }
            catch (Exception ex)
            {
                Debug.Print(ex.ToString());
                return false;
            }
            return true;
}
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
  HookParameter类为定义在ClassLibrary1中的一个类,包含消息与进程ID:
 [Serializable]
    public class HookParameter
    {
        public string Msg { get; set; }
        public int HostProcessId { get; set; }
    }

  到这一步我们就完成了对主窗体代码的编写,现在我们开始编写注入DLL的方法:

  1、先引入MessageBox相关的WindowsAPI:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
#region MessageBoxW

        [DllImport("user32.dll", EntryPoint = "MessageBoxW", CharSet = CharSet.Unicode)]
        public static extern IntPtr MessageBoxW(int hWnd, string text, string caption, uint type);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        delegate IntPtr DMessageBoxW(int hWnd, string text, string caption, uint type);

        static IntPtr MessageBoxW_Hooked(int hWnd, string text, string caption, uint type)
        {
            return MessageBoxW(hWnd, "已注入-" + text, "已注入-" + caption, type);
        }

        #endregion
        
        #region MessageBoxA

        [DllImport("user32.dll", EntryPoint = "MessageBoxA", CharSet = CharSet.Ansi)]
        public static extern IntPtr MessageBoxA(int hWnd, string text, string caption, uint type);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        delegate IntPtr DMessageBoxA(int hWnd, string text, string caption, uint type);

        static IntPtr MessageBoxA_Hooked(int hWnd, string text, string caption, uint type)
        {
            return MessageBoxA(hWnd, "已注入-" + text, "已注入-" + caption, type);
        }

        #endregion
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  其中MessageBoxA与MessageBoxW是微软用于区分不同操作系统中的编码类型,早期的Windows并不属于真正的32位操作系统,执行的API函数属于ANSI类型,而从Windows2000开始,属于Unicode类型,Windows在实际操作中,调用的MessageBox会自动根据平台区分使用前者还是后者,我们在这里就需要把二者都包含其中。

  而DMessageBoxA与DMessageBoxW属于IntPtr类型的委托,用于我们在Hook函数之后传入我们需要修改的方法,此处我们改变了MessageBox的内容和标题,分别在前缀加上了"已注入-"的标记。

  2、完成定义之后我们就需要对函数进行Hook,此处使用LocalHook.GetProcAddress("user32.dll", "MessageBoxW")函数,通过指定的DLL与函数名,获取函数在实际内存中的地址,获取到之后,传入LocalHook.Create()方法,用于创建本地钩子:

C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程
public void Run(
            RemoteHooking.IContext context,
            string channelName
            , HookParameter parameter
            )
        {
            try
            {
                MessageBoxWHook = LocalHook.Create(
                    LocalHook.GetProcAddress("user32.dll", "MessageBoxW"),
                    new DMessageBoxW(MessageBoxW_Hooked),
                    this);
                MessageBoxWHook.ThreadACL.SetExclusiveACL(new int[1]);

                MessageBoxAHook = LocalHook.Create(
                    LocalHook.GetProcAddress("user32.dll", "MessageBoxA"),
                    new DMessageBoxW(MessageBoxA_Hooked),
                    this);
                MessageBoxAHook.ThreadACL.SetExclusiveACL(new int[1]);     
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }

            try
            {
                while (true)
                {
                    Thread.Sleep(10);
                }
            }
            catch
            {

            }
        }
C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

  其中MessageBoxWHook与MessageBoxAHook均为LocalHook类型的变量,MessageBoxAHook.ThreadACL.SetExclusiveACL(new int[1]); 这句代码用于将本地钩子加入当前线程中执行。

  运行之后我们来查看Hook的效果,先打开一个测试窗体,弹出MessageBox,这时候MessageBox没有标题,且内容是正常的:

    C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

 

  接着我们对目标进程进行注入,获取进程ID后点击注入,提示已经成功注入目标进程:

 

    C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

 

  此时点击目标进程MessageBox,可以发现已经Hook成功,并改变了内容和标题:

 

    C# Hook原理及EasyHook
    

C# Hook原理及EasyHook简易教程

 

  至此,C#调用EasyHook对目标进程Hook已经实现。

后记

  从这次实践中我们可以感受到,C#对程序进行Hook是完全可行的,虽然不能直接操作内存和地址,但是我们可以通过操作WindowsAPI与使用EasyHook的方式完成,尤其是后者,大大减少了代码数量与使用难度。

  但是EasyHook目前中文资料非常少,我在使用的过程中也遇到了很大困难,Hook其他函数的方法也未能完全实现,希望能够集思广益,与大家共同思考交流!

  本人刚研究Hook时间不久,文中难免出现纰漏,恳请各位评论指正。

    源代码已经上传至百度网盘:链接: https://pan.baidu.com/s/1wyin9Ezn6AwFQlQxMenQeg 密码: dv9b


推荐阅读
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
xiaojin
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有