最近看了下"大富翁8",玩了一下。玩的时候想用个修改器改改东西,就粗略的研究了一下。

一、用户内存数据

我使用WinDbg挂住richman8.dat后,通过内存搜索命令查找现金、存储、点券等数据,在内存中定位到对应的用户数据位置。

下面是我找到的

 

快乐8大数据分析网站 快乐8分析yuce_数据结构与算法

如上找到对应的几个数据,从这可以推断Rich8的用户数据可能在一块,对应着一个数据结构。

 

二、反汇编相应部分代码

在对应的内存数据(现金、点券等)上设置内存断点。这样代码在访问内存时会断下来,比如在现金上设置写内存断点,这样会在用户金钱变动时断下来,

那么对应的调用代码就是和修改金钱对应的代码。分析断下时的调用堆栈,找出可能相关的代码段。

下面是我分析的,使用的是IDA5.5,richman8.da是使用PECompact 2.x -> Jeremy Collake加壳的。可以使用工具或自己手工脱。

gRichCardsTable, *((_DWORD *)v13 + *((_DWORD *)v13 + 96) + 7));
      if ( *(_DWORD *)(gRichDataInfo + 0xC) <= 0 )
      {
        v24 = 0;
      }
      else
      {
        IndexOfUser = *(_DWORD *)(gRichDataInfo + 0x2C);
        if ( IndexOfUser >= 0 && *(_DWORD *)(gRichDataInfo + 12) > IndexOfUser )
          v25 = *(void **)(*(_DWORD *)gRichDataInfo + 4 * IndexOfUser);
        else
          v25 = 0;
        v24 = v25;
      }
      v2 = sub_4E6880(v30);
      CardAdd(v24, *((_DWORD *)v13 + *((_DWORD *)v13 + 96) + 7), v2);


这里v24是一个玩家信息对象,v2是第几个卡片,第二参数可以不管

而v24是从gRichDataInfo这个对象中取得,gRichDataInfo是全局的对象

signed int __thiscall CardAdd(void *this, int a2, int a3)
{
  signed int result; // eax@3
  int v4; // [sp+0h] [bp-8h]@1
  int ValueOfCard; // [sp+4h] [bp-4h]@1  v4 = (int)this;
  ValueOfCard = CardValueCalcFromIndex(gRichCardsTable, a2);
  if ( ValueOfCard && UserAddCard((void *)(v4 + 692), ValueOfCard, -1) )
  {
    sub_4898C0(v4, a3, 0);
    sub_480A60(v4);
    sub_480C00(v4);
    result = 1;
  }
  else
  {
    result = 0;
  }
  return result;
}


这里通过CardValueCalcFromIndex函数取得卡片索引对应的实际值。 

signed int __thiscall UserAddCard(void *this, int ValueOfCard, signed int IndexOfCard)
{
  signed int result; // eax@12
  signed int i; // [sp+8h] [bp-8h]@4
  signed int v5; // [sp+Ch] [bp-4h]@2  if ( !ValueOfCard )
    goto LABEL_16;
  v5 = IndexOfCard;
  if ( IndexOfCard < 0 || IndexOfCard >= 8 )
  {
    for ( i = 0; i < 8; ++i )
    {
      if ( !*((_DWORD *)this + 3 * i) )         // 找到第一个为空的位置
      {
        v5 = i;
        break;
      }
    }
  }
  if ( v5 < 0 || v5 >= 8 || *((_DWORD *)this + 3 * v5) )
  {
LABEL_16:
    result = 0;
  }
  else
  {
    *((_DWORD *)this + 3 * v5) = ValueOfCard; //设置卡片的值
    *((_DWORD *)this + 3 * v5 + 1) = 0;
    CardTotalCountCalc(this);
    result = 1;
  }
  return result;
}
这里就是把卡片对应的这设置到对应的内存位置中
 
int __thiscall CardTotalCountCalc(int this)
{
  int result; // eax@1
  signed int i; // [sp+4h] [bp-4h]@1  result = this;
  *(_DWORD *)(this + 0x6C) = 0;                 // 计算总的卡数
  for ( i = 0; i < 8; ++i )
  {
    result = this;
    if ( *(_DWORD *)(this + 12 * i) )
    {
      result = this;
      ++*(_DWORD *)(this + 0x6C);               //卡片数增加    
    }
  }
  return result;
}


这里从新统计玩家对应的卡片数量

 

三、确定内存数据结构

现在根据上面分析出的数据关系,确定对应的数据结构。

这里涉及到3个对象

gRichDataInfo     内存中全局变量
gRichCardsTable   内中所有卡片的表
v24               内存中对应单个玩家的数据

 

分析他们之间的对应的偏移、包含关系,确定数据结构。

下面是我分析的数据结构

00000000 RICH_DATA_INFO struc ; (sizeof=0x30) 
00000000 UserDataArray dd ? 
00000004 Reserved1 dd ?
00000008 Reserved2 dd ? 
0000000C UserTotalCount dd ? 
00000010 Reserved3 db 28 dup(?) 
0000002C IndexOfUserSelected dd ? 
00000030 RICH_DATA_INFO ends 
 
00000000 RICH_CARD_INFO struc ; (sizeof=0x10) 
00000000 CardValueTable dd ? 
00000004 Reserved1 dd ? 
00000008 Reserved2 dd ? 
0000000C CardTotalCount dd ? 
00000010 RICH_CARD_INFO ends 
 
00000000 RICH_USER_INFO struc ; (sizeof=0x328) 
00000000 Reserved1 db 656 dup(?) 
00000290 CashValue dd ? 
00000294 SavingsValue dd ? 
00000298 Reserved2 db 28 dup(?) 
000002B4 ArrayOfUserCard RICH_USER_CARD_ITEM 8 dup(?) 
00000314 Reserved3 dd ? 
00000318 Reserved4 dd ? 
0000031C Reserved5 dd ? 
00000320 CountOfUserCard dd ? 
00000324 CouponValue dd ? 
00000328 RICH_USER_INFO ends 
 
00000000 RICH_USER_CARD_ITEM struc ; (sizeof=0xC) 
00000000 CardValue dd ? 
00000004 Reserved1 dd ? 
00000008 Reserved2 dd ? 
0000000C RICH_USER_CARD_ITEM ends

 

在定义的数据结构中,由于我只需要修改它的现金、储蓄、点券和卡片,所以就只定义了几个我需要的变量,其余的那些如贷款、

状态什么的我就没有去看它对应着哪个,不过估计就在我定义的那些预留变量中。

 

(头好酸啊,一个晚上就这样过去了...

快乐8大数据分析网站 快乐8分析yuce_数据结构_02