以下地址是以1.0.0.26.(221715) (10/07/2016)版本为例。所有地址和偏移都是十六进制。 核心看GameCore_Base_FinalRelease.dll。在ida中一般以180001000作为起始地址,而实际运行时会改变所以动态跟踪要做地址映射。 例如某次跟踪得到GameCore_Base_FinalRelease.dll的base = 21170000 新版本支持方法: 1. PlayerAddress:导向玩家列表 1. 搜索lGetTreasury或任意下面“玩家相关”一节列出的关键字 2. 得到一个lua函数,进入后第一个函数进入几层就会得到这个常量 2. ConstTextAddress:导向单位、城市的名称 1. 搜索lGetExperience,并找到同函数注册的另一个函数lGetName 1. 得到一个lua函数,第2个函数会有"Unknown Unit"的字样 1. 参考下面lGetName的反汇编结构找到我标注了“重要”的那一行 3. 通常还有若干偏移会改变,只好一一回归测试一下 lua技巧: 1. 函数名都是l开头,尤其是hks_pushnamedcclosure注册的函数 2. lGetXXX一般返回userdata,返回之前会有一个函数包含hksi_lua_setfield说明返回类型,hksi_lua_setfield的下面一句会注册成员函数 3. 成员函数一般先从lua_state调用一个函数用于解开userdata,其返回值必然用于实际地址 4. 以下针对的是Player、PlayerCities等类,注意游戏的lua对应的其实是CachePlayer、CachePlayerCities等,这些类 也可以修改而且更加简单可惜是只读的(即游戏中数值修改后Cache*的值会跟着改变,反过来不行。没有找到同步的方 法)。 C++技巧: 1. 这里有个入口 sub_18008AD00 这里给每个类注册了构造和析构,只看构造。一般GetPlayer之后紧接着的函数是init,里面会注册成员变量并赋初值 Unit sub_1801E9180 sub_1801A29F0 sub_1801D9830 Formation sub_1801E9110 City::Instance sub_180061820 Trade::Edge sub_1801CFF90 Control::Contract sub_1802D4010 District::Instance sub_180061890 AI::CityBuild sub_1802AF1D0 BehaviorTree sub_1803D8280 AI::Operation sub_18035E6E0 DynamicModifier sub_18052F050 2. 找到成员声明后,代码可以和lua代码结合看,例如Unit与Unit对比时m_eTypeIndex与lua的lGetType结合看, memmove(??, "m_eTypeIndex", 0xC); ... *(_QWORD *)(v3 + 0xB0) = &off_180628F10; *(_DWORD *)(v3 + 0xD0) = 0; 在连续赋初值中,这个与lua一致,所以猜测其他变量也是如此 *(_QWORD *)(v3 + 0xD8) = v3; 注意个别变量没有上面那句,但是这句会有,这时可以自己-8推出缺失的赋值 *(_DWORD *)(v3 + 0xE0) = 0; 玩家相关 CachePlayer刚才说改了没用,这里只是记录一下 有若干list不确定什么意思 1. ? = [180728970] + 37748 + 4 * index 2. ? = [180728970] + 37618 + 4 * index 3. cachePlayer = [180728970] + D88 * index + 8 目前实测要用3 类型CachePlayer,成员函数在这里注册 sub_180489E80 此后cachePlayerTreasury = cachePlayer + 7D0 返回类型CachePlayerTreasury参考 sub_180494A70 注册的 lGetGoldBalance = DWORD[cachePlayerTreasury + 88] 但是CachePlayerTreasury不像PlayerTreasury那样,这里没有SetGoldBalance。间接证明CachePlayerTreasury是没办法写修改器的。 类型Player,成员函数在这里注册 sub_18029CFF0 lGetCities ok lGetUnits ok lGetTechs ok lGetTrade ok lGetTreasury ok lGetCulture 不太好下手,包含文化值的提供方、文化开销、是否解锁政府、是否解锁政策 lGetDiplomacy 不太好下手,包含是否与他国有战争、是否遇见、是否可见 lGetReligion ok lGetResources ok,要在交易页面看到结果或者等到下一回合 lGetGreatPeoplePoints 没必要,可以买来 lGetInfluence ok lGetEras ok 金币 64bits。乘以256的定点小数 lGetTreasury playerTreasury = [[1807185D8] + 8 * index + 210] + 7D60 返回类型PlayerTreasury参考 sub_1802A46B0 注册的lGetGoldBalance goldBalance = [playerTreasury + A0] 信仰 64bits。乘以256的定点小数 lGetReligion playerReligion = [[1807185D8] + 8 * index + 210] + 6A88 返回类型PlayerReligion参考 sub_1802A2D70 注册的lGetFaithBalance faithBalance = [playerReligion + A8] 影响力 64bits。乘以256的定点小数 lGetInfluence playerInfluence = [[1807185D8] + 8 * index + 210] + 7520 返回类型PlayerInfluence参考 sub_1802A11A0 注册的 lGetPointsEarned = [playerInfluence + D0] lGetPointsPerTurn lGetTokensPerThreshold 矿产资源 32bits lGetResources playerResources = [[1807185D8] + 8 * index + 210] + 51C8 返回类型PlayerResources参考 sub_1802A3360 注册的 lHasResource lGetResourceAmount 这两个函数其实是一样的,has就等于amount == 0 其中resourceIndex参考GameInfo.Resources()的每个元素的.Index Base/Assets/Gameplay/Data/Improvments.xml 获取index的函数是 sub_1800A7C20 v2 = sub_180251A00(); [[180717920] + 20] + 0xA68 return sub_1800A6B30(v2, state) [v2 + 0]、[v2 + 8]是列表开始、结束位置,此后 [列表项 + 0]、[列表项 + 8]分别是2个什么数字 returnDWORD [v6 + 4] 实测发现就是0、1、2、...、0x30,应该是字符串到index的映射。也就是v3 = index,由于有顺序所以我们不用管sub_1800A7C20这个函数 获得index之后 sub_180185530 DWORD[[[v4 + 198] + 18 * index] + 4 * 0]; 伟人点数 lGetGreatPeoplePoints playerGreatPeoplePoints = [[1807185D8] + 8 * index + 210] + 7290 返回类型PlayerGreatPeoplePoints参考 sub_1802A0E00 注册的 lGetPointsTotal 没继续看 时代 32bits lGetEras playerEras = [[1807185D8] + 8 * index + 210] + 71D8 返回类型PlayerEras参考 sub_1802A0A60 注册的 GetEra = DWORD[playerEras + A0] 自然科学 byte lGetTechs playerTechs = [[1807185D8] + 8 * index + 210] + 66E8 返回类型PlayerTechs参考 sub_1802A36C0 注册的 CanResearch HasTech = BYTE[[playerTechs + 1E0] + index] lGetScienceYield * 256 = 以下各项目相加 LOC_PLAYER_YIELD_SCIENCE_FROM_CITIES ? 城市科研也是来自多个渠道,所以比较麻烦 LOC_PLAYER_YIELD_SCIENCE_FROM_TOKENS [playerTechs + 78] + 7520 => sub_180166840 没看下去 LOC_PLAYER_YIELD_SCIENCE_FROM_TRIBUTARIES [playerTechs + 78] + 7520 => sub_1801668B0 没看下去 LOC_PLAYER_YIELD_SCIENCE_FROM_OTHER DWORD[playerTechs + 1A8] 也就这个简单一点 LOC_PLAYER_YIELD_SCIENCE_MODIFIER_FROM_TRIBUTARIES [playerTechs + 78] + 7520 => sub_180166920 没看下去 社会科学 byte lGetCulture playerCulture = [[1807185D8] + 8 * index + 210] + 4338 返回类型PlayerCulture参考 sub_18029F8E0 注册的 lGetCultureCost lIsPolicyUnlocked lIsGovernmentUnlocked lHasCivic = BYTE[[playerCulture + 4D8] + index] lGetCultureYield * 256 = 以下各项目相加 sub_1801244E0 LOC_PLAYER_YIELD_CULTURE_FROM_CITY LOC_PLAYER_YIELD_CULTURE_FROM_BELIEFS LOC_PLAYER_YIELD_CULTURE_FROM_TOKENS LOC_PLAYER_YIELD_CULTURE_FROM_TRIBUTARIES LOC_PLAYER_YIELD_CULTURE_FROM_OTHER DWORD[playerCulture + 6E8] 最简单 LOC_PLAYER_YIELD_CULTURE_MODIFIER_FROM_TRIBUTARIES 贸易 32bits lGetTrade playerTrade = [[1807185D8] + 8 * index + 210] + A28 返回类型PlayerTrade参考 sub_1802A4430 注册的 CountOutgoingRoutes GetOutgoingRouteCapacity = DWORD[playerTrade + 98] 城市列表 lGetCities playerCities = [[1807185D8] + 8 * index + 210] + 1738 返回类型PlayerCities参考 sub_18029E360 注册的 lGetCount = DWORD[playerCities + A0] lMembersAux 没有人用到 lMembers返回迭代器,看不太懂 lGetCapitalCity next = [playerCities + B8] while next city = *next if [city + 220] 是首都 next = BYTE[next + 10] 每个member类型City参考 sub_1802701B0 注册的 lGetName = C_STRING[city + 718] lGetPopulation = DWORD[city + 1E8] lGetCitizens cityCitizens = city + E48 + 20 返回类型CityCitizens参考 sub_1802706F0 这是普通table没有注册函数 lGetGrowth sub_180270770 growth = city + 970 + 20 返回类型CityGrowth参考 sub_180273240 注册的 lGetTurnsUntilGrowth lGetFoodSurplus * 256 = sub_180057230 production_base = [growth] LOC_CITY_YIELD_FROM_TILES_SUMMARY_TOOLTIP LOC_CITY_YIELD_FROM_BUILDINGS_SUMMARY_TOOLTIP LOC_CITY_YIELD_FROM_POPULATION_TOOLTIP LOC_CITY_YIELD_FROM_DISTRICTS_SUMMARY_TOOLTIP LOC_CITY_YIELD_FROM_OUTGOING_TRADE_ROUTES_TOOLTIP LOC_CITY_YIELD_FROM_INCOMING_TRADE_ROUTES_TOOLTIP LOC_CITY_YIELD_FROM_PASSING_TRADE_ROUTES_TOOLTIP LOC_CITY_YIELD_FROM_GREATWORKS_TOOLTIP LOC_CITY_YIELD_FROM_GAMEEFFECTS_TOOLTIP DWORD[production_base + 4 * v4 + 500] << 8 其中 v4 = 0时表示改造者产出食物 v4 = 1时表示改造者产出生产力 v4 = 2时表示改造者产出金币 v4 = 3时表示改造者产出科技 v4 = 4时表示改造者产出文化 v4 = 5时表示改造者产出信仰 LOC_CITY_YIELD_FROM_PROJECT_TOOLTIP LOC_CITY_YIELD_FROM_UNIT_PRODUCTION_BONUS DWORD[production_base + 3A8] << 8 好像没用 LOC_CITY_YIELD_FROM_BUILDING_PRODUCTION_BONUS DWORD[production_base + 3E0] << 8 LOC_CITY_YIELD_FROM_DISTRICT_PRODUCTION_BONUS DWORD[production_base + 418] << 8 好像没用 LOC_CITY_YIELD_FROM_MODIFIER_GAMEEFFECTS_TOOLTIP DWORD[production_base + 4 * v13 + 450] << 8 v13定义与上面的GAMEEFFECTS一致,单位是百分数 LOC_CITY_YIELD_FROM_RELIGIOUS_BUILDINGS_TOOLTIP LOC_CITY_YIELD_FROM_RELIGIOUS_FOLLOWERS_TOOLTIP LOC_CITY_YIELD_FROM_HAPPINESS_TOOLTIP LOC_CITY_YIELD_FROM_OCCUPATION_TOOLTIP lGetHousing = DWORD[growth + 30] + DWORD[growth + 34] + DWORD[growth + 38] + 一堆看不懂的东西 lGetHousingFromWater lGetHousingFromBuildings lGetHousingFromImprovements lGetHousingFromDistricts lGetHousingFromCivics = DWORD[growth + 30] lGetAmenities = DWORD[growth + 3C] + DWORD[growth + 40] + DWORD[growth + 44] + DWORD[growth + 48] + DWORD[growth + 4C] + DWORD[growth + 50] + 一堆看不懂的东西 但其中[+ 4C]的部分每回合会重算所以不能改 lGetAmenitiesFromLuxuries lGetAmenitiesFromEntertainment lGetAmenitiesFromCivics lGetHappiness lGetBuildings cityBuildings = city + F50 + 20 返回类型CityBuildings参考 sub_180270CB0 注册的 lHasBuilding lSetBuildingLocation lGetBuildingLocation lRemoveBuilding lIsPillaged lSetPillaged 还没想到要改什么 lGetDistricts cityDistricts = city + CA8 + 20 返回类型CityDistricts参考 sub_180272B00 注册的 lGetNumDistricts lGetNumDistrictsOfType lHasDistrict lGetDistrict lGetDistrictByIndex 返回类型District参考 sub_180279A70 注册的 ... lGetDistrictAtLocation 还没想到要改什么 lGetReligion cirtReligion = city + AF0 + 20 返回类型CityReligion参考 sub_180274090 注册的 lGetMajorityReligion lGetActivePantheon lGetNumFollowers 还没想到要改什么 还要参考C++类 City::Instance sub_180052DE0 m_aFollowerYieldModifiers +4A8 +4B0 +4B8 m_kGold +888 m_kGrowth +990 m_kCombat +AC0 +AC8 +AB8 好像也不比lua信息更多了,这与Unit不一样 单位列表 lGetUnits playerUnits = [[1807185D8] + 8 * index + 210] + 10A0 返回类型PlayerUnits参考 sub_1802A4E40 注册的 lGetCount = DWORD[playerCities + A0] lMembers 从构造看跟playerCities是一样的,所以遍历方法也一样 lFindID 如果是类型Unit,参考 sub_1802A7CC0 注册的 lGetX lGetY lGetType DWORD[unit + D0] lGetName sub_1801E0A10 if DWORD[unit + F38] != -1 && DWORD[unit + F3C] != -1 { // 伟人的名字 names = [[[180717920] + 20] + 720] names_head = [names] names_tail = [names + 8] name_id = DWORD[unit + F38] name_1 = [names_head + 10 * name_id + 8] name_2 = [names_head + 10 * name_id + 0] return C_STRING[name_? + 40] } result = C_STRING[unit + E90] // 似乎是用户自定义名称 if result.empty { names = [[[180717920] + 20] + DE0] // 重要:名字集合 result = C_STRING[[names + 8 * 2 * [unit + D0]] + 88] // 重要:单位类型以及在名字集合中的位置 } lGetDamage = DWORD[unit + 528] lGetMaxDamage lGetExperience experience = unit + E58 + 20 返回类型Unit::Experience参考 sub_1802A8670 注册的 lGetExperiencePoints = DWORD[experience + C] lHasPromotion = BYTE[[experience + 28] + index] lGetFortifyTurns lGetUpgradeCost lGetReligion unitReligion = unit + F68 + 20 返回类型Unit::Religion参考 sub_1802A8EE0 注册的 lGetReligiousStrength = DWORD[unitReligion + 8] lGetReligionType = DWORD[unitReligion + C] lGetSpreadCharges = DWORD[unitReligion + 10] lGetNWDeferredCharges = DWORD[unitReligion + 14] lGetInitiationYield = DWORD[unitReligion + 18] lGetInitiationAmount = DWORD[unitReligion + 1C] lGetEvictPercent = DWORD[unitReligion + 20] lGetForeignSpreadModifier = DWORD[unitReligion + 24] lIsConvertsBarbarians = BYTE[unitReligion + 28] == 0 lIsNoForeignSpread = BYTE[unitReligion + 29] == 0 lIsRelicUponDeath = BYTE[unitReligion + 2A] == 0 还要参考C++类 Unit sub_1801D9830 已验证m_eTypeIndex、m_iDamage的地址C++类和lua地址一致。下面是其他: m_aiBonusPointsPerKill +430 m_iFortifyTurns +560 m_xMovesRemaining 移动力*256 = [unit + 608] m_iAttacksRemaining +640 攻击次数? m_iBuildCharges +6B0 剩余劳动力 m_iAirSlotsModifier +6E8 m_bDominantConquer +720 m_iUpgradeDiscount +790 m_iBarbarianTribeIndex +7C8 m_bAttackAfterMoving +800 m_bMoveAfterAttacking +838 m_bEmbarked +870 m_bDelayedDeath +8A8 m_bIgnoreTerrainCost +8E0 m_bIgnoreZOC +918 m_bExertZOC +950 m_bCanSeeThroughFeatures +988 m_bImmobile +9C0 m_bProcessedTurn +9F8 m_kSightModifiers +C58 视距 m_kAttackRangeModifiers +C90 看样子k、i都是uint32,x是uint32/256,b是byte/bool