C#代码分析器(C#游戏代码注入)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/BarleyTuan/article/details/60574225

简介

  在进行C#代码注入的过程中,写的比较复杂的逻辑,但是reflector注入只能一个个函数来进行,而且添加paramter非常复杂,这里做了一个简单的代码分析器,帮助所有代码集中到一个函数中。

      虽然感觉上比较简单,但做的过程中,花费了大半天的时间,这里把遇到的问题总结一下,给大家后续开发一些启示。

  理论上C语言格式的都可以用这个解析把多个函数包含在一个函数里面


Reflector注入过程

  • 1 导入相应的dll
  • 2 tools decompile。 点右键 replaceWithCode。
  • 3 子类添加函数基类,然后写函数。
  • 4 在dll上右键Reflexil -> SaveAs相关dll即可。

 

遇到的问题

  因为relector一次只支持注入一个函数,而我们写的显示代码是有多个函数的。

      即使新加入一个类,也需要在这个类里面单独加入各个函数。

     同时代码里面有互相的嵌套,而且有返回值和各个参数,如果单个的加非常的麻烦,而且reflector不支持返回值,相关的值只能放入输出参数,和原有的代码就大大违背了。

所以这里写了一个简单的代码分析器,用这个解析把多个函数包含在一个函数里面。


代码格式

首先读入的代码要编译通过

需要一些简单的约束:

  • 1子函数必须有返回值,且返回值不能用和次子函数的参数相同。只有一个返回的出口。上支持上任意返回类型,只是父函数必须调用一下子函数返回的值。
  • 2 调用函数的参数中不支持嵌套调用自己写的函数。
  • 3 除非是返回值,否则临时变量需要自行设的是unique的。否则会重复定义。
  • 4 “{}”这种单独占一行,一般C#编辑器会自动解析成这样。
  • 5 函数不能循环调用,解析会进入循环。
  • 6 目前只支持+=和+,如果需要其他运算符,在tpl_func_split中加。  特殊的运算序列没有做支持,当时这个分析器只是为了输出日志方便用的。

 

输入格式

  //(joint为已知的Joint数据结构)
        public string A(string p)
        {
            string tmpA = "add a" + p;
            return tmpA;
        }
        public string B(Jointjoint, string c)
        {
            string tmp = "add b\n";
            tmp += A(joint.format) + joint;
            return tmp;
        }
        public void Jump()
        {
            string tmp = "start ===\n";
            //tmp += "A " + A("12");
            //tmp += B("34", "12");
            tmp += " A B" + A("12") + B(joint, "c") + A("34");
        }


输出格式

会经过代码分析器分析为:

       public void Jump()
        {
            string tmp ="start ===\n";
            string _result_6_ ="add a"+"12";
            string _result_7_ ="add b\n";
            string _result_8_ ="add a"+joint.format;
            _result_7_ +=_result_8_+joint;
            string _result_9_ ="add a"+"34";
            tmp +=" A B"+_result_6_+_result_7_+_result_9_;
         }
 


思路

整体思路:

分治法。

每个函数有多行语句,每行语句包含多个函数,经过相互调用就相当于减了一层调用。无论有多少层调用,最终都可以解析为只包含一层可以输出的序列。

如 tmp += A(cc)

就会变为:

<<A相关的表达式,同时更新所有的输入为cc输出为_result_1_>>

Tmp += _result_1_;

  

1 将每个函数解析为结构FuncDesc, 存入一个整体的FUNC_DICT中。

class FuncDesc(object):
  def __init__(self):
    self.params_list = []
    self.func_desc = []
    self.func_desc_split = []
    self.func_name = ""
    self.out_name = ""

  

2 每个out_name和params_list的表示都用一个特殊的完全不同的字符串代替。方便以后在调用的时候,整体替换。

3 其中每个func_desc表示一行代码。

  每行代码再生成一个结构

class DescFuncSplit(object):
  def __init__(self, desc_str):
    self.desc_str = desc_str
    # (type, funcname, params), type0:normal, 1 func
    self.func_list = []
    self.opeartor_list = []
    self.has_semicolon = False

4 在调用到单个func_desc的时候,判断各个函数,调用到FuncDesc,赋值params和return_value。

  FuncDesc在依次调用每个func_desc_split。最终会得到一系列字符串。

  经过相互调用,最终会在最简单层中停止:

  code中A

这一系列的字符串就是最后希望得到的函数。

输出是最终的函数内部的内容。

需要自己加上函数头,可以放在C#编辑器里面format一下。

 

下附代码

#-*- coding:utf-8 -*-

"""
python XX.py inputfilename.cs out_func_name
第一个参数是要读入的文件名称
第二个参数是要输出的函数名
"""
import os, sys
import re

TOTAL_NUM = 0;
FUNC_DICT = {}
def GetIndexNum():
  global TOTAL_NUM
  TOTAL_NUM += 1
  return TOTAL_NUM

def replacer(search_obj, replace_str):
  groups = search_obj.groups()
  return groups[0] + replace_str + groups[1]
   
def change_descs(init_str, change_param, replace_param):
  cal = r"(\W)%s(\W)" % change_param;
  replacer2 = lambda search_obj: replacer(search_obj, replace_param)
  return re.sub(cal, replacer2, init_str)
   
class DescFuncSplit(object):
  def __init__(self, desc_str):
    self.desc_str = desc_str
    # (type, funcname, params), type0:normal, 1 func
    self.func_list = []
    self.opeartor_list = []
    self.has_semicolon = False
    
  def debug_info(self):
    print "=============="
    print "debug_info: ", self.desc_str
    print self.func_list
    print self.opeartor_list
    
  def get_split_out_result(self, param_dict):
    print "get_split_out_result" , param_dict
    total_len = len(self.func_list)
    out_str = ""
    out_list = []
    for idx in xrange(total_len):
      func_tuple = self.func_list[idx]
      if func_tuple[0] == 0:
        out_str += func_tuple[1]
      else:
        func_result_name = "_result_%s_" % GetIndexNum()
        func_desc = FUNC_DICT[func_tuple[1]]
        params = func_tuple[2]
        new_params = []
        for new_param in params:
          for k,v in param_dict.items():
            new_param = new_param.replace(k, v)
          new_params.append(new_param)
          
        tmp_list = func_desc.get_end_result(new_params, func_result_name)
        out_list.extend(tmp_list)
        out_str += func_result_name
      if idx < total_len - 1:
        out_str += self.opeartor_list[idx]
    if self.has_semicolon:
      out_str += ";"
    out_str += "\n"
    out_list.append(out_str)
    return out_list
      
  def tpl_func_split(self):
    total_split = []
    out_str = ""
    if self.desc_str.find("+=") >= 0:
      self.func_list.append((0, self.desc_str[:self.desc_str.find("+=")]))
      out_str = self.desc_str[self.desc_str.find("+=") + 2:]
      self.opeartor_list.append("+=")
    elif self.desc_str.find("=") >= 0:
      self.func_list.append((0, self.desc_str[:self.desc_str.find("=")]))
      out_str = self.desc_str[self.desc_str.find("=") + 1:]
      self.opeartor_list.append("=")
    else:
      out_str = self.desc_str
    
    #鍘婚櫎瀛楃涓插唴閮ㄧ殑+ -> _
    total_len = len(out_str)
    idx = 0
    while(idx < total_len):
      if out_str[idx] == "\"":
        idy = idx + 1
        while(idy < total_len):
          if out_str[idy] == "+":
            out_str[idy] = "_"
          if out_str[idy] == "\"":
            idy += 1
            break
          idy += 1
        idx = idy
      else:
        idx += 1
    
    func_split = out_str.split("+")
    for sp in func_split:
      sp = sp.strip()
      if sp[-1] == ";":
        sp = sp[:-1].strip()
        self.has_semicolon = True
        
      if sp[0] == "\"":
        self.func_list.append((0, sp))
        self.opeartor_list.append("+")
      elif sp[-1] == ")":
        l = sp.find("(")
        funcname = sp[:l].strip()
        if not FUNC_DICT.has_key(funcname):
          self.func_list.append((0, sp))
          self.opeartor_list.append("+")
          continue
        params_str = sp[l+1:-1]
        params_split = params_str.split(",")
        params = []
        for param in params_split:
          param = param.strip()
          params.append(param)
        self.func_list.append((1, funcname, params))
        self.opeartor_list.append("+")
      else:
        self.func_list.append((0, sp))
        self.opeartor_list.append("+")
    return
    
    
    
class FuncDesc(object):
  def __init__(self):
    self.params_list = []
    self.func_desc = []
    self.func_desc_split = []
    self.func_name = ""
    self.out_name = ""
  
  def debug_info(self):
    print "========"
    print "params_list:", self.params_list
    print "func_desc:"
    print self.func_desc
    print "func_name: %s, out_name: %s" % (self.func_name, self.out_name)
    
  def get_func_name(self, fs):
    fp = fs.split("(")
    tmp_list = fp[0].split(" ")
    self.func_name = tmp_list[-1];
    fp[1] = fp[1].replace(")", "")
    tmp_list = fp[1].split(",")
  
    for tmp in tmp_list:
      tt = tmp.split(" ")
      for idtt in xrange(len(tt) - 1, -1, -1):
        if tt[idtt].strip() != "":
          self.params_list.append(tt[idtt].strip())   
          break 
         
  def change_params_to_unique(self):
    for params_key in xrange(len(self.params_list)):
      new_param = "_param_%s_" % GetIndexNum()
      param = self.params_list[params_key]
      
      self.params_list[params_key] = new_param
      for desc_key in xrange(len(self.func_desc)):
        desc = self.func_desc[desc_key]
        new_desc = change_descs(desc, param, new_param)
        self.func_desc[desc_key] = new_desc
    return
  
  def change_out_name_to_unique(self):
    return_desc = self.func_desc[-1]
    new_desc = return_desc.replace(";", "")
    if new_desc.find("return") < 0:
      return
    out_name = new_desc[new_desc.find("return") + 6:].strip()
    if out_name == "":
      self.func_desc.pop()
      return
    self.out_name = "_result_%s_" % GetIndexNum()
    for desc_key in xrange(len(self.func_desc)):
      desc = self.func_desc[desc_key]
      new_desc = change_descs(desc, out_name, self.out_name)
      self.func_desc[desc_key] = new_desc
    self.func_desc.pop()
    return
  
  def change_desc_value_to_func_split(self):
    for desc in self.func_desc:
      desc_func_split = DescFuncSplit(desc)
      desc_func_split.tpl_func_split()
      self.func_desc_split.append(desc_func_split)
     
  
  def get_end_result(self, params, func_result_name):
    print "get_end_result ", params, func_result_name
    if self.out_name != "" and func_result_name == "":
      print "error %s has no func_result_name" % self.func_name
      raise 1
    if len(params) != len(self.params_list):
      print "error %s params error %s %s" % (self.func_name, self.params_list, params)
      raise 1
    
    param_dict = {}
    for idx in xrange(len(self.params_list)):
      param_dict[self.params_list[idx]] = params[idx]
    out_list = []
    for func_split in self.func_desc_split:
      func_split.debug_info()
      func_str_list = func_split.get_split_out_result(param_dict)
      for func_str in func_str_list:
        out_str = func_str;
        for k,v in param_dict.items():
          out_str = out_str.replace(k,v)
        out_list.append(out_str)

    if self.out_name != "":
      for idx in xrange(len(out_list)):
        out_str = out_list[idx]
        out_list[idx] = out_str.replace(self.out_name, func_result_name)
    return out_list

def main(filename, out_func_name):
  f = open(filename, "r")
  lines = f.readlines();
  f.close();
  total_line = len(lines)
  for line in lines:
    if line.startswith("{") or line.endswith("}"):
      if len(line.strip()) != 1:
        print "not strip one line", line
        raise 1
  cur_num = 0
  while(cur_num < total_line):
    if lines[cur_num].find("public") < 0:
      cur_num += 1
      continue
    func_desc = FuncDesc()
    func_desc.get_func_name(lines[cur_num])
    cur_num += 1
    while(cur_num < total_line):
      if lines[cur_num].strip() == "{":
        break
      cur_num += 1
      
    cur_num += 1
    cur_left = 1
    while(cur_num < total_line):
      if lines[cur_num].strip() == "{":
        cur_left += 1
      if lines[cur_num].strip() == "}":
        cur_left -= 1
        if cur_left == 0:
          break 
      strip_line = lines[cur_num].strip()
      if strip_line != "" and not strip_line.startswith("//"):
        func_desc.func_desc.append(lines[cur_num])
      cur_num += 1

    FUNC_DICT[func_desc.func_name] = func_desc
    cur_num += 1
  
  print FUNC_DICT.keys()
  
  for func_desc in FUNC_DICT.values():
    func_desc.change_params_to_unique()
    func_desc.change_out_name_to_unique()
    func_desc.change_desc_value_to_func_split()
    func_desc.debug_info()
  
  if not FUNC_DICT.has_key(out_func_name):
    print "error outfuncname in readfile", filename
    return
  
  out_str_list = []
  out_str_list = FUNC_DICT[out_func_name].get_end_result([], "")
  f = open("out.txt", "w")
  for out_str in out_str_list:
    f.write(out_str)
  f.close()
   
if __name__ == "__main__":
  if len(sys.argv) < 3:
    print "please input: readfilename outfuncname"
    exit()
  out_func_name = sys.argv[2]
  filename = sys.argv[1]
  main(filename, out_func_name)




没有更多推荐了,返回首页