2015年11月3日星期二

最近项目的几个问题 - XiaoH在博客园

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
最近项目的几个问题 - XiaoH在博客园  阅读原文»

  最近项目压力比较大,为了赶时间很多代码都得图简便,然而碰到的问题还是需要重新整理一下,即便当时不懂事后也得弄清楚。项目的主要任务是一个C6678的PCI板卡驱动,用于FFT计算,一个图形界面显示程序显示处理前后结果。设备操作上,需要实时从C6678的内存中读取两个数据,一个是64KB的unsigned short型原始数据,一个是128KB处理后的float型数据。

项目里使用了一个不错的绘图类库:

  • QCustomplot:包含了许多基本的绘图操作,可以在其基础上作二次开发。

官网:http://www.qcustomplot.com/

先说说PCI设备驱动的一些重要接口和流程,Linux设备驱动框架不想谈了,随便找本驱动的书都有。首先是设备的探测,在linux系统下使用命令lspci -vv可以看到已插入的板卡的一些信息,包括设备名称及地址空间等。板卡上有4个C6678 DSP,所以能够发现4路设备。

程序中初始化设备驱动,首先调用pci_get_device接口发现PCI设备,因为有4路DSP,所以循环调用了4次,而且这里将板卡当做字符设备处理。

1 for (index=0;index<MAX_NUM;index++) {
2 C6678DSP = pci_get_device(VENDOR_ID , DEVICE_ID , prev_node);
3 if (!C6678DSP) {
4 printk("<C6678DSP>: No C6678DSP%d Card Found!\n",index);
5 return -ENXIO;
6 }
7 else {
8 prev_node = C6678DSP;
9 printk("<C6678DSP>: C6678DSP%d Card Found!\n",index);
10 }

  接着第二步,使能设备:

1 if (pci_enable_device(C6678DSP))
2 return -EIO;

  第三步,获取设备的起始和结束地址,获取的这个值可以和前面lspci -vv的结果对比看是否一致。

1 base0start=pci_resource_start(C6678DSP,0);
2 endsrc0=pci_resource_end(C6678DSP,0);
3 base0len=endsrc0-base0start;

第四步,这一步是本项目里面难以理解的一个问题,甚至我自己都有点没弄清。将读取到的起始地址又回写到PCI设备的地址空间中,这点非常不解,一开始我并没有在驱动的初始化里加这两句,因为一般的PCI设备驱动都没见这么做的。但驱动程序加载后看到分配的地址是错误的,后来在别人的提示下加了这两句,果然能够正常工作了,理由是可能BIOS做的不够好,不能正确获取到板卡配置空间的信息,而Linux则可以。

1 pci_write_config_dword(C6678DSP,0x10,base0start);
2 pci_write_config_dword(C6678DSP,0x14,base1start);

接下来调用ioremap进行内存映射,中间的一些代码我都省略了,不通用。

1 baseAddrDoThingA = ioremap(base0start,base0len);
2 baseAddrDoThingB = ioremap(base1start,base1len);

后面就是自负设备驱动的一些流程了,多说无益。创建设备文件,注册设备。

1 //register major device
2 dev = MKDEV(c6678_major, 0);
3 if (c6678_major) {
4 ret = register_chrdev_region(dev, 1, "C6678DSP");
5 printk("<C6678DSP>:C6678DSP major is defined!\n");
6 } else {
7 ret = alloc_chrdev_region(&dev, 0, 1, "C6678DSP");
8 c6678_major = MAJOR(dev);
9 printk("<C6678DSP>:C6678DSP major is not defined!\n");
10 }
11 if (ret <0) {
12 printk("<C6678DSP>: alloc C6678DSP device number error\n");
13 return ret;
14 }
15
16 c6678_setup_cdev(&c6678_cdev, 0);
17 printk("<C6678DSP>: C6678DSP major is %d. \n", c6678_major);
18
19 /* make fs node */
20 ret = mknod("/dev/C6678", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, dev);
21 if (ret) {
22 printk("<C6678>Mknod error.\n"):
23 return ret;
24 }
25
26 printk("Node make finished.\n");
27 return 0;
28
29 fail_req_irq:
30 fail_alloc_sendq:
31 cdev_del(&用 C++ 写一个简易的《生化危机 4》修改器 - akima  阅读原文»

思路比较简单,实现方法也不难,我这里大概说一下流程:
♦ 用 CheatEngine 或者类似的内存搜索工具定位要修改的数值的内存地址;
♦ 确定要修改的数值所占的字节数;
♦ 提升修改器进程自身的 Privilege(特权);
♦ 查找并打开目标进程,获取目标进程句柄;
♦ 读取目标进程内存地址上的值;
♦ 把值写入目标进程的内存地址上;

第一第二步不属于本文的讨论范围,我们从第三步开始吧;

微软从 Windows Vista 开始,引入了新的安全机制,一个进程如果没有足够的权限,是无法读写或者查询另外一个进程的内容与信息的,而我们要做的内存修改器需要读写另外一个进程的内存,所以必须要提升修改器进程自身的权限;
每个新启动的进程,都会被系统分配一个 Access Token ,里面包含一个 Privilege 列表,Privilege 列表描述了该进程拥有哪些特权,我们如果要对另外一个进程进行读写操作,则可以为修改器进程增加一个 Debug Privilege,这个 Privilege 允许我们的进程去调试以及调整另外一个进程的内存,是微软为调试器专门开放的 Privilege,正好可以为我们所用;
Privilege 包含两个属性,一个叫做 Name,是以字符串形式来表示的特权名称,一个叫做 LUID,用于保证特权的本地唯一性,要调节进程特权,需要先获取 Name 所对应的 LUID,LookupPrivilegeValue( ) 函数可以查询一个 Privilege Name 所对应的 Privilege LUID;
通过 OpenProcessToken( ) 函数打开进程的 Access Token,随后把查询到的 Privilege LUID 填充到特定的结构体,然后通过这个结构体,调用 AdjustTokenPrivileges( ) 函数来调节进程特权,至此,进程特权调节完毕,我们看下面代码:

接下来,我们要查找并打开游戏进程,目前的状况是这样的,我们只知道游戏的 exe 文件名称,但是我们需要的是游戏的进程句柄,对此,我们可以通过 Win32 SDK API 提供的 Tool Help Library 里面的函数来实现;
首先,调用 CreateToolhelp32Snapshot( ) 函数创建一个进程快照,然后使用 Process32First( ) 和 Process32Next( ) 函数来枚举所有进程,这两个函数会返回一个 PROCESSENTRY32 结构体,里面包含了被枚举的进程的 exe 文件名以及进程 ID,我们可以通过对比进程的 exe 文件名来获取进程的 ID,代码如下:

有了游戏的进程 ID,我们就可以通过 OpenProcess( ) 函数打开游戏进程,并且得到游戏的进程句柄,为最后两布做准备,代码如下图所示:

OK,万事俱备,只欠东风,我们的最后一步,就是通过 ReadProcessMemory( ) 函数与 WriteProcessMemory( ) 函数来读写游戏进程的内存数据,这里的内存,明确上来讲,应该是虚拟内存,也就是所谓的 4GB 内存,这是微软自己的定义,Windows 规定,任何一个进程都拥有自己的虚拟内存,在运行时会把虚拟内存映射到真实的物理内存上;
因此,我们打开游戏进程的时候,需要指明 PROCESS_VM_OPERATION、PROCESS_VM_READ 与 PROCESS_VM_WRITE 三个标志,用以向系统说明我们的意图是要操作和读写游戏进程的虚拟内存的内容;
内存的读写操作都需要四个参数,进程句柄、内存地址、数据内容、数据大小(所占字节数),进程句柄有了,内存地址与数据大小我们在第一步已经查明了(笑),所以,我们只需要安心地写出下面代码即可:

以下代码读取游戏中 Leon 的血量、最大血量以及金钱数目:

以下代码把数值写入到游戏中 Leon 的血量、最大血量以及金钱数目:

最后,我用对话框简单地做了一个界面程序,我们来看一看最终成果图:

刚运行游戏的时候,Leon 的血量等数据没有被载入,所以皆显示为 0;

New Game 之后,Leon 默认血量以及最大血上限皆为 1200,并且没有任何金钱;

打死一个乌鸦,捡到 400 pts 之后,修改器显示 400 pts,数据正确;

被游戏中第一个敌人攻击之后,剩下 630 血,随后敌人被愤怒的 Leon 一阵乱揍,惨死街头;

然而敌人的牺牲只是白费的,因为我点击【Full HP】 按钮之后,Leon 又恢复到了满血状态,我的血满了,而你,却永远站不起来了;

随后我点击了【PTS + 2000】 ,即使不用上战场,也能够得到一大堆金钱奖励;


本文链接:用 C++ 写一个简易的《生化危机 4》修改器,转载请注明。

阅读更多内容

没有评论:

发表评论