前言

最近在反编译Unity游戏的时候,遇到了需要增加反作弊机制的要求。一开始利用网上的Process进程读取去操作,经过多番尝试,发现类似于Cheat Engine和变速精灵这类型的进程,居然查找不到Process的Name,同时据网上所说的读取Handle方法,我分别测试了C#(其实使用Unity实现的,但是本质还是C#)的Handle结合User32.dll来读取Handle,但是很遗憾,通过读取Handle,我依然无法找到标题的名称,因此我便换用了下述方法。

当然,完全可能是因为我本人对于语言的了解不够透彻而导致的无法操作Handle的情况,在此还请大家多多包涵

正经办法

1 . 通过读取进程列表判断进程是否已经启动

其实很显然,最简单的一种反作弊手段就是监测是否开启了作弊程序。网上我找到了相关的C#代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Diagnostics;
 
public class NewBehaviourScript : MonoBehaviour
{
  	......
    public void ReadProgramm()
    {
       Process[] ps = Process.GetProcesses();
       foreach(Process p in ps){
          string info = "";
          try
          {
              info = p.Id + "  " + p.ProcessName + "  " + p.MainWindowTitle + "  " + p.StartTime;
          }catch(Exception e){
              info = e.Message;
          }
          Console.WriteLine(info);
       }
    }
  ......
}

很不幸的是,也许是因为我的技术问题,在不论是以管理员模式启动,还是非管理员模式启动,均可以获取到程序列表,但是针对CE此类进程时,却往往无法查询到其ProcessNameMainWindowTitle,或者说为空。所以在此情况下,我不得不换一种方法。

2. 通过User32读取程序窗口名

我们首先直接给出代码。

//获取窗口标题
[DllImport("user32", SetLastError = true)]
private static extern int GetWindowText(
    IntPtr hWnd,//窗口句柄
    StringBuilder lpString,//标题
    int nMaxCount //最大值
);

//获取类的名字
[DllImport("user32.dll")]
private static extern int GetClassName(
    IntPtr hWnd,//句柄
    StringBuilder lpString, //类名
    int nMaxCount //最大值
);

仍然有些遗憾的是,在此处我始终认为因该是我的技术问题导致读取出来的窗口名和类名仍然检测不到CE窗口和其它一些作弊程序窗口的名字,但是具体不知道该怎么应用,所以只得作罢,希望大家有知道的可以教授我一下。

一个旁门左道

既然上述两种方法我都无法实现,甚至有些情况下我们还需要获取管理员权限,那么有没有不需要管理员权限,而且可以简便读取的方式呢。我想是有的。

在Windows的任务管理器下面,我们不难发现其实所有进程和进程名和窗口名我们都是可以看到的,而这个读取可以通过命令行的方式tasklist来读取,并且可以读取到程序ID,而且完全不需要管理员权限。

理论存在,实践开始。

那么首先第一步我们需要做的就是编写一个运行命令行的代码,使用C#本身调用一个进程的类方法。

public void RunCmd(string cmd)
{
  	Process proc = new Process();
    proc.StartInfo.FileName = @"cmd.exe";
    proc.StartInfo.CreateNoWindow = true;//不显示程序窗口
    proc.StartInfo.UseShellExecute = false;//使用shell启动
    proc.StartInfo.RedirectStandardError = true;//重定向标准错误
    proc.StartInfo.RedirectStandardInput = true;//重定向标准输入
    proc.StartInfo.RedirectStandardOutput = true;//重定向标准输出
    proc.Start();
    proc.StandardInput.WriteLine(cmd);//标准输入,输入cmd命令
    proc.Close();
}

那么很显然,通过此程序我们可以得到输出,或者将函数返回值设置为string然后在获得标准输出string outStr = proc.StandardOutput.ReadtoEnd();这样我们即可以通过return返回一个输出结果。然后通过对输出结果做字符串分割分割再分割得到所有在运行程序的信息,然后通过程序ID和名字判断关闭程序(需要管理员权限),或者关闭自己的程序(或者其他措施)均可达到效果。

后话

说到这里,我觉得还是有必要了解一下User32.dll中的内容了,我觉得这个其实才是最有用的办法,我觉得依旧有必要了解学习。不过偶尔想到这种歪门旁道,也挺有意思的。不过如果大家有什么其他办法,也希望可以和我教授一下,十分感谢。