从今C#到Objective-C,循序渐进学习苹果支付(7)–使用FMDB对Sqlite数据库进行操作

比如随笔系列重大介绍从一个Windows平台从事C#开暨Mac平台苹果支付的等同系列感想和心得过程,本系列文章是在启动阶段逐步积累之,希望带为大家再次好,更实的易过程体验。本篇主要开始介绍因XCode进行IOS程序的支付,介绍下FMDB对Sqlite数据库进行操作,以及针对性数据库操作类进行抽象设计,以期达成重用、简化、高效开发之目的。

   字节型:根据不同的编辑器,1独整形占用N个字节(N>1),一般生早前生产的GBA游戏为了节省出会用到这种类型。

虽然现在于IOS领域做片钻开发,即使IOS设备更多强调的是一个多媒体的装备,但是数据库的操作还是必不可少,因此自事先打自身熟悉的数据库这块入手,了解中数据库是如何操作的,有安现成的机件进行参考学习等等。

  lpBuffer:结构体指针,用于存放内存信息。

2、数据库操作层的筹划

地方小节介绍了动用FMDB类库对Sqlite数据库的操作,使我们大概了解了以IOS里给数据库操作的过程。但是只而介绍这些,我道太浅了,而且对咱运用起来为尚是异常无便于,很多时段,我老是惦记通过计划的计,来简化我之各种操作处理。如我们清楚,IOS里面的Objective
C也供了重重高等语言都有的属性,如目标继承,接口、多态等等。

本身死去活来早前写过的随笔《Winform开发框架的数访问层的规划》
,介绍了自当.NET领域内的数据库访问层的计划,由于这种计划,能挺可怜程度达到压缩代码量,并提高开支效率,因此,我哉想再度IOS里面形成这样的数据看计划,虽然IOS里面可能主要是动单机版的数据库,如SQLite数据库,所以自己想简化一些.NET里面的筹划模型。

在.NET里面,我之框架分层主要如下所示,这种框架的设计模式,已经挺好下在了自己之Winform开发框架、WCF及混合式开发框架、Web框架中。它们普遍的道岔模式,可以分成UI层、BLL层、DAL层、IDAL层、Entity层、公用类库层等等

图片 1

 而中的DAL层的计划,示意图如下所示,DAL层主要是经过持续自BaseDAL基类(如BaseDALSQL)进行,
BaseDALSQL进行再次强一层的架空,已臻更好之采用目的。

图片 2 

设我辈于IOS里面,则好要考虑SQLite数据库即可,因此,我拿设计通过简化,构造下面的筹划模型

图片 3 

每当列内,数据访问层的文本如下所示(为了演示测试好,使用User表进行操作)。

图片 4

为了实现数量的承前启后,我们需要将表的数量易为实体类进行亮与操作,因此实体类的设计呢待考虑好。由于数量访问层的基类,封装了绝大多数底数额操作,也囊括返回的多少对象同聚众,因此数据访问层的基类,也波及到了数据类型返回的题材。

由IOS里面的Objective
C里面没有泛型这样的物,因此产生点儿栽办法可来促成基类实体类的拍卖:一凡是采用动态类型id类型作为实体类品种,一种是行使同一栽半类型化的门类(实体类的基类)作为对象,我同情于采取后者,因为毕竟比较接近实际的花色了。

//
//  BaseEntity.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-4-2.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface BaseEntity : NSObject

@end

//
//  UserInfo.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-3-30.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "BaseEntity.h"

@interface UserInfo : BaseEntity

@property(nonatomic, copy) NSString *ID;
@property(nonatomic, copy) NSString *name;
@property(nonatomic, copy) NSString *fullName;

@end

这种持续模式和.NET的后续关系多了。

对此数据访问层的宏图代码,它就是于数据看基类的概念如下,基类使用了FMDB的数目访问类进行操作的,里面很多操作的接口就是法我于.NET领域里的多少访问层的设计。

//
//  BaseDAL.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-3-30.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#import "BaseEntity.h"

@interface BaseDAL : NSObject
{
    NSString *pathToDatabase;
}

#pragma 数据库相关属性

@property (nonatomic, strong) NSString *pathToDatabase;
@property (nonatomic, readonly) FMDatabase *database;  // 数据库操作对象
@property (nonatomic, strong) NSString *tableName;//表名称
@property (nonatomic, strong) NSString *primaryKey;//主键
@property (nonatomic, strong) NSString *sortField;//排序,默认为主键
@property (nonatomic, assign, getter=isDescending) BOOL descending;//是否降序查询


//将DataReader的属性值转化为实体类的属性值,返回实体类(子类必须重写)
-(id) rsToEntity:(FMResultSet *)rs;

//将实体对象的属性值转化为字典列表对应的键值(子类必须重写)
-(NSDictionary *) dictByEntity:(BaseEntity *) info;


#pragma 基础操作接口

//根据指定对象的ID,从数据库中删除指定对象
- (BOOL) deleteById:(id) key;

//根据指定条件,从数据库中删除指定对象
- (BOOL) deleteByCondition:(NSString *) condition;

//更新对象属性到数据库中
-(BOOL) update:(BaseEntity *) info byKey:(id) key;

//插入指定对象到数据库中
-(BOOL) insert:(BaseEntity *) info;

//插入指定对象到数据库中,并返回最后插入的ID
-(NSInteger) insert2:(BaseEntity *) info;

//查询数据库,检查是否存在指定ID的对象
- (BaseEntity *) findById:(id) key;

//根据条件查询数据库,如果存在返回第一个对象
-(BaseEntity *) findSingle:(NSString *) condition;


//根据条件查询数据库,如果存在返回第一个对象
-(BaseEntity *) findSingle:(NSString *) condition orderBy:(NSString *) orderBy;

//根据条件查询数据库,并返回对象集合
- (NSArray *) find:(NSString *) condition;

//根据条件查询数据库,并返回对象集合
- (NSArray *) find:(NSString *) condition orderBy:(NSString *) orderBy;

//获取表的全部数据
- (NSArray *) getAll;

//获取表的全部数据
- (NSArray *) getAll:(NSString *) orderBy;

//获取某字段数据字典列表
-(NSArray *) getFieldList:(NSString *) fieldName;

//获取表的所有记录数量
-(int) getRecordCount;

//根据条件,获取表查询的记录数量
-(int) getRecordCount:(NSString *) condition;

//根据条件,判断是否存在记录
-(BOOL) isExistRecord:(NSString *)condition;

//查询数据库,检查是否存在指定键值的对象
-(BOOL) isExist:(NSString *)fieldName value:(id) value;

//根据主键和字段名称,获取对应字段的内容
-(NSString *) getFieldValue:(NSString *)key fieldName:(NSString *)fieldName;

//执行SQL查询语句,返回查询结果的所有记录的第一个字段,用逗号分隔。
-(NSString *) sqlValueList:(NSString *)query;

#pragma 数据库初始化函数及关闭操作

//根据SQLite数据库地址初始化数据库
-(id) initWithPath:(NSString *)filePath;

//根据SQLite数据库名称初始化数据库
-(id) initWithFileName:(NSString *)fileName;

// 关闭连接
-(void) close;

@end

同自身的.NET框架内的数码访问层设计相同,数据访问基类已经封装了绝大多数底多少看操作,因此各个表的多寡访问对象,它的代码就可以生简单了。从上面的基类接口设计好看来,里面有实体类归函数或者列表返回函数,都下了BaseEntity作为靶子,我们具体以起类使用的时候,把她回到的目标还同蹩脚变即可。对于数据库访问基类,我们坐一个回到集合的接口实现来分析。

- (NSArray *) find:(NSString *) condition orderBy:(NSString *) orderBy {
    NSString *query = [NSString stringWithFormat:@"SELECT * FROM %@ ", self.tableName];
    if (condition != nil) {
        query = [query stringByAppendingFormat:@" where %@ ", condition];
    }

    if (orderBy != nil) {
        query = [query stringByAppendingString:orderBy];
    }
    else {
        query = [query stringByAppendingFormat:@" ORDER BY %@ %@ ", self.sortField, self.isDescending ? @"DESC" : @"ASC"];
    }

    FMResultSet *rs = [self.database executeQuery:query];
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:[rs columnCount]];

    BaseEntity *info = nil; //默认初始化为空
    while ([rs next]) {
        info = [self rsToEntity:rs];
        [array addObject:info];
    }
    [rs close];

    return array;
}

上面代码用了参数化的SQL语句进行查询,并且,对回到的数据库的ResultSet进行更换为实体类。

info = [self rsToEntity:rs];

由于基类封装了绝大多数底数据库操作函数,因此数据访问层的具体表的实现类似,可以生简单,但是曾经拥有了宽广的CRUD操作,以及一些分页查询等繁杂的数码操作功能。

//
//  UserDAL.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-3-30.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#import "BaseDAL.h"
#import "UserInfo.h"

@interface UserDAL : BaseDAL
{
}

//单例模式
+(UserDAL *) defaultDAL;

@end

据悉篇幅的因由,我拿当生同样篇介绍如何当界面层中采取这样的数量访问计划类,先放大上有测试程序的界面截图。

图片 5图片 6图片 7

图片 8 图片 9

  • 进程:每一个应用程序/游戏启动,都见面时有发生至少一个进程Process,在任务管理器里可看到进程名称及过程PID。

1、FMDB的操作

为较好介绍整体性的情节,我们事先打FMDB的各种操作进行介绍。

1)数据库打开操作

FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ;  
if (![db open]) {  
NSLog(@"无法打开数据库");  
return ;  
}  

2)数据库操作executeUpdate

用FMDB,对于从未回记录之操作,都好用executeUpdate进行操作,如下是删除记录之函数

- (BOOL) deleteByCondition:(NSString *) condition {
    NSString *query = [NSString stringWithFormat:@"Delete FROM %@ Where %@ ", self.tableName, condition];
    BOOL result = [self.database executeUpdate:query];
    return result;
}

本,对于SQLite很多数据库操作,我们得应用参数化语词进行操作,如下例子所示

[db executeUpdate:@"INSERT INTO User (Name,Age) VALUES (?,?)",@"张三",[NSNumber numberWithInt:30]]  

参数化也堪运用 :
字符作为参数标识,如下所示,是自包的一个数据库操作函数

- (BOOL) deleteById:(id) key
{
    NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:key, @"id", nil];
    NSString *query = [NSString stringWithFormat:@"Delete FROM %@ Where %@ =:id", self.tableName, self.primaryKey];
    BOOL result = [self.database executeUpdate:query withParameterDictionary:argsDict];
    return result;
}

3)返回集合的操作

操作发生归集合的言语,我们就算需要因此到FMResultSet对象了,这个目标类似于以前见了之游标,不过不等同的事物而已。

FMResultSet *rs=[db executeQuery:@"SELECT * FROM User"];
rs=[db executeQuery:@"SELECT * FROM User WHERE Age = ?",@"20"];
while ([rs next]){
NSLog(@"%@ %@",[rs stringForColumn:@"Name"],[rs stringForColumn:@"Age"]);
}

上面的代码操作,返回一个Resultset集合进行遍历使用,我们得以依据Resultset的片法取得到不同之数内容。

对立于FMResult的操作办法,原生态之Sqlite数据库操作代码如下所示。看罢是休是觉得用FMDB类库操作数据库方便多也。

 NSString *sqlQuery = @"SELECT * FROM User";
    sqlite3_stmt * statement;

    if (sqlite3_prepare_v2(db, [sqlQuery UTF8String], -1, &statement, nil) == SQLITE_OK) {
        while (sqlite3_step(statement) == SQLITE_ROW) {
            char *name = (char*)sqlite3_column_text(statement, 1);
            NSString *nsNameStr = [[NSString alloc]initWithUTF8String:name];
            int age = sqlite3_column_int(statement, 2);
            char *address = (char*)sqlite3_column_text(statement, 3);
            NSString *nsAddressStr = [[NSString alloc]initWithUTF8String:address];

            NSLog(@"name:%@  age:%d  address:%@",nsNameStr,age, nsAddressStr);
        }
    }
    sqlite3_close(db);

FMResultSet方法来脚几乎栽,分别用于获取不同的数量:

  • intForColumn:
  • longForColumn:
  • longLongIntForColumn:
  • boolForColumn:
  • doubleForColumn:
  • stringForColumn:
  • dateForColumn:
  • dataForColumn:
  • dataNoCopyForColumn:
  • UTF8StringForColumnIndex:
  • objectForColumn:

 

在.NET领域支出了众多年,一般大的种类还急需操作数据库,包括有Oracle、SqlServer、Mysql、Sqlite、Access等数据库,这些数据库是很常见的,我们于.NET环境中开发的各种系统,可能还还是多要丢失得同内同样种植以上的数据库打交道,这为是本身事为提炼我的.NET领域的Winform开发框架、Web开发框架、混合式开发框架的目的,尽可能达到简化、重用、高效开发之目的。

  /// <summary>
  /// 查询地址空间中内存地址存储的信息
  /// </summary>
  /// <param name="hProcess">句柄</param>
  /// <param name="lpAddress">内存地址</param>
  /// <param name="lpBuffer">结构体指针</param>
  /// <param name="dwLength">结构体大小</param>
  /// <returns>写入字节数</returns>
  [DllImport("kernel32.dll")]     
      public static extern int VirtualQueryEx(
      IntPtr hProcess, 
      IntPtr lpAddress, 
      out MEMORY_BASIC_INFORMATION lpBuffer, 
      int dwLength);

在IOS里面开发,提起和数据库打交道,可能过多口犹如数家珍FMDB这个数据库的组件,它对IOS里面操作Sqlite数据库进行了异常要命程度的简化,简化后,我们大部分情形下,只需要同FMDatabase和FMResultSet打交道即可,使用起来十分方便。

      /// <summary>
      /// 根据进程句柄读入该进程的某个内存空间
      /// </summary>
      /// <param name="lpProcess">进程句柄</param>
      /// <param name="lpBaseAddress">内存读取的起始地址</param>
      /// <param name="lpBuffer">写入地址</param>
      /// <param name="nSize">写入字节数</param>
      /// <param name="BytesRead">实际传递的字节数</param>
      /// <returns>读取结果</returns>
      [DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
      public static extern bool ReadProcessMemory
      (
          IntPtr lpProcess,
          IntPtr lpBaseAddress,
          IntPtr lpBuffer,
          int nSize,
          IntPtr BytesRead
      );    

  到了这边,修改游戏数值的原理同套路已经充分懂了,甚至脱离游戏来讲,任何的应用如果无针对缓存中之数额进行好的加密,都是有很挺之高风险隐患的,这同一段节关键了解部分常用到之名词和API的行使。具体哪用代码进行调用API,以及更详实的剖析每一样步的逻辑,将以产一样段节讲述。

  lpAddress:查询的内存地址,输入参数,需要主动提供地方。但我们并不知道我们用之数值它为寄放的地点,我们只能一个页面一个页面的猜测,直到扫描到某页面的某块内存里面存放的音讯正是同我们得之消息相同或者在必然的函数关系。其次,对于同一个数值或出现在多独地方,比如
在有时刻间隔人物的攻击数值与防守数值等与都也1200,那么证明至少发生一定量独地点存放了是数额。我们得把这些地方都筛选出。

  进入正题,根据目的反向推导下要取什么样信息,我们最终才能够想如果落实修改数价值。

  /// <summary>
  /// 写入某一进程的内存区域
  /// </summary>
  /// <param name="lpProcess">进程句柄</param>
  /// <param name="lpBaseAddress">写入的内存首地址</param>
  /// <param name="lpBuffer">写入数据的指针</param>
  /// <param name="nSize">写入字节数</param>
  /// <param name="BytesWrite">实际写入字节数的指针</param>
  /// <returns>大于0代表成功</returns>
  [DllImportAttribute("kernel32.dll", EntryPoint = "WriteProcessMemory")]
  public static extern bool WriteProcessMemory
      (
          IntPtr lpProcess,
          IntPtr lpBaseAddress,
          int [] lpBuffer,
          int nSize,
          IntPtr BytesWrite
      );

 

  本章旨在讲解外挂实现原理,未深入涉及到代码层面。希望会及针对及时地方感兴趣之爱人多交流,毕竟理论是非常的,套路是一贯的,只有破解经验是花费大量时光及心血积累的。

  我们下逐个分析参数:

  对于经过PID各种编程语言有投机的落方式,以C#言语为条例(针对非多开客户端):

 

  PS:转载请附带原文路径:http://www.cnblogs.com/lene-y/p/7096485.html 。

  但以此函数只当取内存信息,而查询内存信息遭到现实存放数值则用到其他一样部数ReadProcessMemory,来拘禁一下函数原型:

  推荐阅读《计算机操作系统》相关书籍更多的了解计算机原理。

  根据上面的API,先来拘禁怎么抱第一只参数:窗体句柄,同样的kernel32.dll提供了名为也OpenProcess的函数用来打开一个早就是的经过对象,并回经过的句柄。

   文本型:比如世界喊话,人物命名,一般用文本型保存这类似数据。

  //根据进程名称获取PID
  public int GetPIDByPName(string ProcessName)
      {
          Process[] ArrayProcess = Process.GetProcessesByName(processName);
        foreach (Process pro in ArrayProcess)
        {
            return pro.Id;
        }
        return -1;
    }

   推荐阅读《数据结构》相关书籍更好之熟稔数据结构,有编程经验的即使无须多说了。

   图片 10

  • 句柄:英文HANDLE,一个整数值。数据的地点需要改,变动后就需要有人来记录管理变动,就类似户籍管理一样,因此系统就此句柄来记载数据地址的更改。肉眼看到底一个个文本夹,窗口,按钮,图标,应用程序能够通过句柄访问相应的目标的音讯
  • 于页游、网游和手游,虽然服务器保存了汪洋底要的参数,但鉴于客户端不可避免的用开展大量之算计和资源的加载,本地内存种必定存来部分的临时变量,通过判断这些变量的变化规律和函数的破密寻到便利己之参数,比如伤害值一像样,继而寻找该变量的内存地址,根据指针偏移分析得到内存基址,再升格权限行使Windows
    API把打定义数值写副该内存块,就做到了改某起数值的操作,一般的话,只要破解了千篇一律桩数值,利用规律继而破解其他数值便愈加容易了。

 

  bInheritHandle :是否延续句柄,FALSE即可。

  函数原型:

  返回值:函数写副lpBuffer的字节数,如果字节数等于结构体PMEMORY_BASIC_INFORMATION的大小,表示函数执行成功。

  此函数将基于句柄读博该过程的之一内存空间,并以读取到的字节数写副到我们开辟的如出一辙片空间中,而之空间存放的难为我们苦苦寻找的“有义的数值”。此函数的一些参数依赖让上一个函数VirtualQueryEx产生的结果。

  根据什么依据推导出的路吧?

  参数就不再一一说了,注释都来,最后一个参数默认填写Null或者IntPtr.ZeroI即可。

  hProcess:顾名思义,进程句柄,也就是说想如果查询地址存放的音,首先得得进程的句柄。

   函数原型:

    在此之前,我们来打听一些基础知识:

  • 对于单机游戏而言,游戏中多方面底参数(比如血、蓝、能量也或金币)都存储于电脑的库房中,一些看似剧情快的尽管加密后形容副当地的自定义配置文件被;

  我们获取到过程的PID以后,就可调用OpenProcess获取窗体的句柄,然后使函数VirtualQueryEx遍历内存查找地方信息,根据地点以ReadProcessMemory查找具体存放的价,最后使WriteProcessMemory把修改后的值写副该地点,这样就是得了一样次数据的改动。来拘禁一下API的函数原型:

  dwLength:上述结构体的大大小小。

 

  Windows系统库的kernel32.dll库文件中含了内存操作的API,其中VirtualQueryEx用以查询地址空间中内存地址的音信。

  
 一般套路就是上述,一些防护性强大的游戏会在上述的各国一样步着还安装难题,等正咱错过破解。

  • 数据类型:游戏中之血量、蓝、生命值,我们以他们叫做变量,变量包含了变量名称与数据类型,那么其的讳就是”血量、生命值”等等,而它们的数据类型决定了累价为何种方式囤到电脑的内存中,想使找到自己要之变量,如果得以确定这个变量的数据类型或者发因的推测来压缩变量的数据类型范围,那么对于速稳定该变量是怀有帮助性的。以下是几乎种植普遍的变量类型:

 

 

   浮点型:带有小数点之数字,如果金币或者伤害值带有小数点,那不行可能是这种数量类保存的。

  修改变量的数值→得事先找到存储变量的内存地址→得先找到游戏窗口的语句柄→得事先找到打之长河。

  

  /// <summary>
  /// 打开一个已存在的进程对象,并返回进程的句柄
  /// </summary>
  /// <param name="iAccess">渴望得到的访问权限</param>
  /// <param name="Handle">是否继承句柄</param>
  /// <param name="ProcessID">进程PID</param>
  /// <returns>进程的句柄</returns>
  [DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
  public static extern IntPtr OpenProcess
  (
      int iAccess,
      bool Handle,
      int ProcessID
  );

  dwProcessId :进程标示符PID。

   整数型:游戏中仍血量、法力可能就此到这种类型。

  dwDesiredAccess :渴望获得的访权限,这里默认填写PROCESS_ALL_ACCESS |
0x1F0FFF 施负有可能同意的权能。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注