大富翁8分析

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

一、用户内存数据

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

下面是我找到的

image

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

二、反汇编相应部分代码

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

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

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

     v30 = CardValueCalcFromIndex(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

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

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

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

原文地址:https://www.cnblogs.com/Quincy/p/1936754.html