├── inc ├── scanmem.h ├── defs.h ├── base.h ├── pvz.h ├── utils.h ├── pvz_offset.h └── cheater.h ├── .gitignore ├── images ├── pvz.png ├── effect.png ├── cheater_failure.png └── cheater_success.png ├── tools ├── a.py ├── zombiesList.py └── findRange.py ├── README.md ├── Makefile ├── scanmem ├── scanmem.h └── ptrace.c ├── doc └── data.txt └── src ├── cheater.c └── utils.c /inc/scanmem.h: -------------------------------------------------------------------------------- 1 | ../scanmem/scanmem.h -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cheater 2 | *.gch 3 | lib 4 | /old_cheater 5 | /release 6 | -------------------------------------------------------------------------------- /images/pvz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ze00/pvz/HEAD/images/pvz.png -------------------------------------------------------------------------------- /images/effect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ze00/pvz/HEAD/images/effect.png -------------------------------------------------------------------------------- /images/cheater_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ze00/pvz/HEAD/images/cheater_failure.png -------------------------------------------------------------------------------- /images/cheater_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ze00/pvz/HEAD/images/cheater_success.png -------------------------------------------------------------------------------- /tools/a.py: -------------------------------------------------------------------------------- 1 | #!/data/data/com.termux/files/usr/bin/python 2 | #-*- coding: utf-8 -*- 3 | """ 4 | File : a.py 5 | Project : 6 | Author : ze00 7 | Email : zerozakiGeek@gmail.com 8 | Date : 2018-03-10 9 | """ 10 | import os 11 | import sys 12 | for i in open('/sdcard/Notes/com.popcap.pvz_na.txt'): 13 | res = i.split('|') 14 | if len(res) >= 2: 15 | addr = res[1] 16 | os.system("findRange.py {} {}".format(sys.argv[1], addr)) 17 | -------------------------------------------------------------------------------- /tools/zombiesList.py: -------------------------------------------------------------------------------- 1 | #!/data/data/com.termux/files/usr/bin/python 2 | #-*- coding: utf-8 -*- 3 | """ 4 | File : zombiesList.py 5 | Project : PVZ 6 | Author : ze00 7 | Email : zerozakiGeek@gmail.com 8 | Date : 2018-01-18 9 | """ 10 | import sys 11 | if len(sys.argv[1:]) != 2: 12 | print("need 2 argument") 13 | sys.exit(-1) 14 | dat = open(sys.argv[1],"rb").read() 15 | b = dat[0x1504:] 16 | p = dat[:0x564] 17 | open(sys.argv[2],"wb").write(p + b'\x15\x00\x00\x00' * 1000 + b) 18 | -------------------------------------------------------------------------------- /inc/defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : defs.h 3 | * Project : PVZ 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2017-08-17 7 | * Module : 8 | * License : MIT 9 | */ 10 | #ifndef __DEFS__H 11 | #define __DEFS__H 12 | #include 13 | #define BUFSIZE 255 14 | #define IN_RANGE(obj, min, max) (obj >= min && obj <= max) 15 | #define SETJMP_RET 0xff 16 | #define WAIT_USECONDS (500000) 17 | typedef char BufferType[BUFSIZE]; 18 | typedef char Path[PATH_MAX]; 19 | #endif //__DEFS__H 20 | -------------------------------------------------------------------------------- /inc/base.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : base.h 3 | * Project : PVZ 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2017-08-15 7 | * Module : 8 | * License : MIT 9 | */ 10 | #ifndef __BASE__H 11 | #define __BASE__H 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "defs.h" 17 | #include "utils.h" 18 | struct { 19 | char *base; 20 | char *bss; 21 | int32_t val; 22 | pid_t pid; 23 | __task *task; 24 | __images *images; 25 | } baseInfo; 26 | void initBase() { 27 | baseInfo.base = NULL; 28 | baseInfo.bss = NULL; 29 | baseInfo.task = NULL; 30 | baseInfo.images = NULL; 31 | } 32 | #endif //__BASE__H 33 | -------------------------------------------------------------------------------- /inc/pvz.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : pvz.h 3 | * Project : PVZ 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2017-08-15 7 | * Module : 8 | * License : MIT 9 | */ 10 | #ifndef __PVZ__H 11 | #define __PVZ__H 12 | #include 13 | #include 14 | #include 15 | #ifndef SPECIFIC_PACKAGE 16 | #define SPECIFIC_PACKAGE "com.popcap.pvz_na" 17 | #endif 18 | #ifndef SPECIFIC_DYNAMIC_LIBRARIES 19 | #define SPECIFIC_DYNAMIC_LIBRARIES "libpvz.so" 20 | #endif 21 | #define PUMPKIN_CODE (30) 22 | #define LILYPAD_CODE (16) 23 | struct Hp { 24 | uint32_t curHp; 25 | uint32_t totalHp; 26 | uint32_t armor; 27 | }; 28 | jmp_buf env; 29 | #endif //__PVZ__H 30 | -------------------------------------------------------------------------------- /tools/findRange.py: -------------------------------------------------------------------------------- 1 | #!/data/data/com.termux/files/usr/bin/python 2 | #-*- coding: utf-8 -*- 3 | """ 4 | File : findRange.py 5 | Project : PVZ 6 | Author : ze00 7 | Email : zerozakiGeek@gmail.com 8 | Date : 2018-01-19 9 | """ 10 | import sys 11 | import re 12 | try: 13 | fhandle = open("/proc/{}/maps".format(sys.argv[1])) 14 | addr = int(sys.argv[2],16) 15 | except IndexError: 16 | print("provide me 2 argument(pid and address you want to query)") 17 | exit(-1) 18 | pat = re.compile(r"\A([0-9a-fA-F]{8})-([0-9a-fA-F]{8})") 19 | for i in fhandle: 20 | res = pat.match(i) 21 | if res: 22 | start,end = [x for x in res.groups()] 23 | if int(start,16) <= addr and int(end,16) >= addr: 24 | print(i) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PVZ小工具 2 | * 本人在aarch64/arm的Android 7.12中测试通过 3 | ![pvz](images/pvz.png) 4 | * 效果 5 | ![effect](images/effect.png) 6 | * 需要Root权限 7 | ## 使用步骤 8 | * **获取一个北美版的植物大战僵尸** 9 | 10 | 目前必须要使用我提供的pvz主程序,否则不保证功能能够正常使用 11 | 12 | 本人所使用的北美版:链接: https://pan.baidu.com/s/1d1T0K2 密码: y98i 13 | 14 | 本文档第一张图即是详细的版本信息,感谢植物大战僵尸吧@囧丫乙 15 | 16 | 具体安装教程:http://lonelystar.org/ResDownload/1_Android_Na.htm 17 | 18 | * 1.**使用直装版的修改器** 19 | 20 | 链接: https://pan.baidu.com/s/1kWUH3Kj 密码: 4hby 21 | 22 | 基于NeoTerm(https://github.com/NeoTerm/NeoTerm) 23 | 24 | 感谢@imkiva 25 | 26 | 打开修改器,会请求您给予ROOT权限,允许即可 27 | 28 | 如果您已经启动了植物大战僵尸,则应该是 29 | ![cheater_suceess](images/cheater_success.png) 30 | 否则,将会是 31 | ![cheater_failure](images/cheater_failure.png) 32 | 33 | REF:https://github.com/scanmem/scanmem 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE := \ 2 | cheater 3 | cheater_src := scanmem/ptrace.c src/cheater.c src/utils.c 4 | inc := $(shell find inc) 5 | CC_FLAG := -Iinc -Wall -std=c99 6 | CC_FLAG += -DHAVE_PROCMEM 7 | ifeq ($(NDK_BUILD),true) 8 | NDK ?= $(HOME)/android-ndk-r14b 9 | ifeq ($(ARM64),true) 10 | NDK_STANDALONE ?= $(HOME)/ndk/arm64 11 | NDK_TOOCHAIN ?= $(NDK_STANDALONE)/bin/aarch64-linux-android 12 | NDK_SYSROOT ?= $(NDK)/platforms/android-24/arch-arm64 13 | else 14 | NDK_STANDALONE ?= $(HOME)/ndk/arm 15 | NDK_TOOCHAIN ?= $(NDK_STANDALONE)/bin/arm-linux-androideabi 16 | NDK_SYSROOT ?= $(NDK)/platforms/android-24/arch-arm 17 | endif 18 | CC := $(NDK_TOOCHAIN)-gcc 19 | STRIP := $(NDK_TOOCHAIN)-strip 20 | CC_FLAG += -fPIC -pie --sysroot=$(NDK_SYSROOT) 21 | else 22 | CC := gcc 23 | STRIP := strip 24 | endif 25 | all:$(MODULE) 26 | $(foreach m,$(MODULE),$(eval TARGET := $(m))$(eval DEP := $($(m)_src) $(inc))$(eval include build/reg_rule.mk)) 27 | define make_release 28 | make NDK_BUILD=true --no-print-directory $(1) 29 | @ mkdir -p release/$(dir $(2)) 30 | @ cp cheater release/$(2) 31 | @ make clean 32 | endef 33 | .PHONY:release 34 | release: 35 | $(call make_release,,arm/cheater) 36 | $(call make_release,ARM64=true,aarch64/cheater) 37 | .PHONY:clean 38 | clean: 39 | -@ rm -rf $(MODULE) 40 | -------------------------------------------------------------------------------- /scanmem/scanmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | Provide interfaces for front-ends. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009-2013 WANG Lu 7 | Copyright (C) 2016 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #ifndef SCANMEM_H 26 | #define SCANMEM_H 27 | #include 28 | bool sm_read_array(pid_t target, const void *addr, char *buf, int len); 29 | bool sm_write_array(pid_t target, void *addr, const void *data, int len); 30 | 31 | #endif /* SCANMEM_H */ 32 | -------------------------------------------------------------------------------- /inc/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : inc/utils.h 3 | * Project : 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2018-01-19 7 | * Module : 8 | */ 9 | #ifndef __UTILS__H 10 | #define __UTILS__H 11 | #include 12 | #include 13 | #include "defs.h" 14 | #define PANIC \ 15 | do { \ 16 | printf("无效输入\n"); \ 17 | raise(SIGINT); \ 18 | } while (0) 19 | typedef struct __list { 20 | void *next; 21 | void *real; 22 | } __list; 23 | typedef struct __task { 24 | __list list; 25 | int32_t row; 26 | int32_t col; 27 | } __task; 28 | typedef struct __images { 29 | __list list; 30 | int32_t value; 31 | void *remote; 32 | } __images; 33 | #define next(x) ((x)->list.next) 34 | #define real(x) ((x)->list.real) 35 | #define back(x) ((__typeof__((x)))(x)->list.real) 36 | extern void pop(__task **); 37 | extern int has(__task *, int, int); 38 | extern void insert_images(__images **, int, void *); 39 | extern void recover_images(__images *); 40 | extern void destroy(__list **, void (*)(void *)); 41 | extern void *insert(__list **, size_t); 42 | extern void parseRowAndCol(const char *, __task **); 43 | extern void checkRootState(); 44 | extern const char *readline(FILE *); 45 | extern pid_t findPVZProcess(); 46 | extern FILE *openProcFile(pid_t, const char *); 47 | #endif //__UTILS__H 48 | -------------------------------------------------------------------------------- /inc/pvz_offset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : pvz_offset.h 3 | * Project : PVZ 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2017-08-15 7 | * Module : 8 | * License : MIT 9 | */ 10 | #ifndef __PVZ_OFFSET__H 11 | #define __PVZ_OFFSET__H 12 | #include 13 | #include 14 | #define ZOM_HP_OFF 0xd4 15 | #define PLAN_HP_OFF 0x4c 16 | #define PLAN_ATT_TOTAL_OFF 0x68 17 | struct pvz_offset { 18 | const char *name; 19 | off_t offset; 20 | }; 21 | struct pvz_offset pvz_off_tbl[] = { 22 | {"sun", 0x56bc}, 23 | // 卷心菜头手的射速 24 | {"cabbage", 0xc44460}, 25 | // 炮的冷却偏移 26 | {"cannon", 0xc4467c}, 27 | // 僵尸类型代码 28 | {"zombies_type", 0x30}, 29 | // 僵尸x坐标 30 | // 相对于格子的坐标 31 | {"zombies_pos_x", 0x38}, 32 | // 僵尸y坐标 33 | {"zombies_pos_y", 0x3c}, 34 | // 僵尸的行 35 | {"zombies_row", 0x1c}, 36 | // 僵尸雪亮 37 | {"zombies_hp", 0xd4}, 38 | // 植物的列 39 | {"plants_col", 0x34}, 40 | // 植物是否可见 41 | {"plants_vis", 0x18}, 42 | // 植物的行 43 | {"plants_row", 0x2c}, 44 | // 植物类型代码 45 | {"plants_type", 0x30}, 46 | // 植物血量 47 | {"plants_hp", 0x4c}, 48 | // 植物攻击力 49 | {"plants_attack", 0x54}, 50 | // bss + base 51 | {"base", 0x5138c8}, 52 | // *base + 0x2dfa0 53 | {"field", 0x2dfa0}, 54 | // field + 0x7c8 55 | {"status", 0x7c8}, 56 | // status + ... 57 | {"zombies_entry", 0xb4}, 58 | {"zombies_count", 0xc4}, 59 | {"zombies_list", 0x810}, 60 | {"plants_entry", 0xd0}, 61 | {"plants_count", 0xe0}, 62 | {"pass", 0x5760}, 63 | {"flags_helper", 0x294}, 64 | // *flags_helper + 0x6c 65 | {"flags", 0x6c}, 66 | }; 67 | off_t getOffset(const char *name) { 68 | off_t off = -1; 69 | struct pvz_offset *pf; 70 | for (size_t i = 0; i < sizeof(pvz_off_tbl) / sizeof(pvz_off_tbl[0]); ++i) { 71 | pf = &pvz_off_tbl[i]; 72 | if (strcmp(name, pf->name) == 0) { 73 | off = pf->offset; 74 | break; 75 | } 76 | } 77 | if (off == -1) 78 | printf("offset of '%s' not found!\n", name); 79 | return off; 80 | } 81 | #endif //__PVZ_OFFSET__H 82 | -------------------------------------------------------------------------------- /doc/data.txt: -------------------------------------------------------------------------------- 1 | ## 一部分数据(来源于http://tieba.baidu.com/p/2843347257) 2 | * 植物内存布局 3 | │├┬AC//植物对象序列,+14C下一个 4 | ││├—8//植物X坐标 5 | ││├—C//植物Y坐标 6 | ││├—18//(Byte)为0时隐形 7 | ││├—1C//植物行数(0-5) 8 | ││├—20//与图像遮盖关系有关 9 | ││├—24//植物特性种类 10 | ││├—28//植物列数(0-8) 11 | ││├—3C//植物状态(?) 12 | ││├—40//当前血值 13 | ││├—44//血值上限 14 | ││├—48//是否开火(1为开火) 15 | ││├—4C//植物消失倒计时(?) 16 | ││├—50//灰烬植物、寒冰菇、三叶草生效倒计时 17 | ││├—54//各种倒计时 18 | ││├—58//发射子弹/产生物品倒计时,为0时开始子弹发射动作/产生物品 19 | ││├—5C//固定子弹发射/产生物品时间 20 | ││├—90//子弹发射动作倒计时,为1时子弹产生,为0时植物恢复为子弹发射前的状态 21 | ││├—B0//眨眼倒计时(为0时眨眼) 22 | ││├—B8//植物亮度(倒计时,为0时不发亮) 23 | ││├—130//阳光蘑菇长大倒计时,蘑菇睡醒倒计时 24 | ││├—141//(Byte)不为0时植物消失 25 | ││├—142//(Byte)不为0时植物为被压扁状态 26 | ││├—143//(Byte)不为0时植物为睡着状态 27 | ││└—145//(Byte)为0时不发亮,否则发亮(如当铲子移到这个植物上时,咖啡豆移到睡觉的蘑菇上时) 28 | * 僵尸内存布局 29 | │├┬90//僵尸对象序列,+15C下一个 30 | ││├—8//x坐标(图像位置) 31 | ││├—C//y坐标(图像位置) 32 | ││├—18//(Byte)为0时隐形 33 | ││├—1C//所在行数 34 | ││├—20//与图像遮盖关系有关 35 | ││├—24//僵尸类型(0为普僵) 36 | ││├—28//僵尸状态 37 | ││├—2C//(Float)x坐标 38 | ││├—30//(Float)y坐标 39 | ││├—34//(Float)下一cs +2C减少的量 40 | ││├—51//(Byte)啃食时为1,否则为0(修改会发生奇特的效果) 41 | ││├—60//僵尸出生至当前的时间差(正数) 42 | ││├—64//换道 1为下 2为上 43 | ││├—68//相关倒计时 44 | ││├—74//正数时为透明度、消失倒计时,为0时不可见(有影子),为负数时正常状态 45 | ││├—84//(Float)y坐标减小值 46 | ││├—AC//剩余减速时间 47 | ││├—B0//剩余黄油固定时间 48 | ││├—B4//剩余冻结时间 49 | ││├—B8//(Byte)不等于0时被魅惑 50 | ││├—B9//(Byte)不等于0时以10像素/cs的速度向右运动(如被吹飞的气球僵尸) 51 | ││├—BA//(Byte)为0时为濒死状态 52 | ││├—BC//(Byte)不为0时为(巨人)拥有小鬼僵尸,(雪人)左行 53 | ││├—BD//(Byte)不为0时为水中僵尸 54 | ││├—C8//当前血值 55 | ││├—CC//血值上限 56 | ││├—D0//I类饰品当前HP(?) 57 | ││├—D4//I类饰品HP上限(?) 58 | ││├—DC//II类饰品当前HP 59 | ││├—E0//II类饰品HP上限 60 | ││├—EC//(Byte)不为0时消失 61 | ││└—11C//僵尸大小 62 | * 卡槽内存布局 63 | │├┬144//卡槽,+28以前为整个卡槽的属性,以后为每格的属性,+50下一格 64 | ││├—8//卡槽X坐标 65 | ││├—C//卡槽Y坐标 66 | ││├—10//卡槽横向判定范围 67 | ││├—18//(Byte)为0时卡槽隐形 68 | ││├—24//卡槽格数 69 | ││├—30//卡槽中的卡牌的X坐标 70 | ││├—34//卡槽中的卡牌的Y坐标 71 | ││├—38//卡牌判定宽度 72 | ││├—3C//卡牌判定高度 73 | ││├—40//(Byte)为0时隐形 74 | ││├—4C//已冷却时间 75 | ││├—50//总冷却时间 76 | ││├—54//卡槽序号(这是第几格) 77 | ││├—58//传送带中滑动倒计时,卡牌实际坐标为这里与+30的值的和 78 | ││├—5C//卡槽内容 79 | ││├—60//模仿者模仿内容 80 | ││├—64//Slot Machine中老虎机停止倒计时,为0时该格停止变化 81 | ││├—68//Slot Machine中该格接下来转出的内容 82 | ││├—70//(Byte)为0时为冷却中(或被鼠标点中但未被种植),否则为可用 83 | ││└—71//(Bool)为0时为鼠标点中但未被种植 84 | * 杂项 85 | │├┬14C//LS的Start Onslaught按钮 86 | ││├—8//X坐标 87 | ││├—C//Y坐标 88 | ││├—14//文字纵向位置偏移 89 | ││├—18//(Byte)鼠标是否悬停在按钮上,0为否 90 | ││├—19//(Byte)鼠标是否按下按钮,0为否 91 | ││└—84//(String[15])按钮内容 92 | │├—164//(Byte)不为0时游戏暂停 93 | │├—168~23C//场景每格类型 94 | │├—554~5C0//雾的浓度(每4字节只用首个字节,另3个字节值为0) 95 | │├—5D8~5EC//每行出怪类型 96 | │├—60C~620//每行冰道最左坐标(最右坐标为800) 97 | │├—624~638//每行冰道消失倒计时,小于10时冰道有逐渐变浅的过程 98 | │├—554C//场景类型 99 | │├—5550//(冒险模式)当前关卡 100 | │├—5558//鼠标X坐标 101 | │├—555C//鼠标Y坐标 102 | │├—5560//阳光 103 | │├—557C//已刷新波数(当前选卡) 104 | │├—559C//下一波僵尸刷新时间(倒计时) 105 | │└—561C//出怪种子码 106 | ├—7F8//当前模式(同存档编号) 107 | └—7FC//当前游戏状态 108 | ## 关卡模式代码 109 | 01:白天生存模式【5面旗帜】 110 | 02:黑夜生存模式【5面旗帜】 111 | 03:泳池生存模式【5面旗帜】 112 | 04:浓雾生存模式【5面旗帜】 113 | 05:屋顶生存模式【5面旗帜】 114 | 06:白天生存模式【10面旗帜】 115 | 07:黑夜生存模式【10面旗帜】 116 | 08:泳池生存模式【10面旗帜】 117 | 09:浓雾生存模式【10面旗帜】 118 | 10:屋顶生存模式【10面旗帜】 119 | 11:白天无尽 120 | 12:黑夜无尽 121 | 13:泳池无尽 122 | 14:浓雾无尽 123 | 15:屋顶无尽 124 | * 南瓜头会在血量<=1332时变成破损状态 125 | * 植物僵尸的场景特效代码是16,老虎机的场景特效代码是18 126 | ## 基址的计算方式 127 | x -> 基址 128 | y -> 包含基址的映射首地址 129 | z -> 储存y的地址 130 | bss -> bss段首地址 131 | 132 | z = bss + 0x5138c8 133 | y = *z 134 | x = y + 0x2dfa0 135 | -------------------------------------------------------------------------------- /src/cheater.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : cheater.c 3 | * Project : PVZ 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2017-08-15 7 | * Module : 8 | * License : MIT 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "defs.h" 16 | #include "utils.h" 17 | #include "base.h" 18 | #include "pvz.h" 19 | #include "cheater.h" 20 | void collect() { 21 | checkRootState(); 22 | detectPVZ(); 23 | baseInfo.base = getDynamicBase(); 24 | getBssBase(); 25 | printf("基址:%p\n", getField()); 26 | } 27 | int main(int argc, char **argv) { 28 | collect(); 29 | int option; 30 | BufferType buf; 31 | registeSigHandle(); 32 | if (setjmp(env) == SETJMP_RET) { 33 | puts(""); 34 | } 35 | while (1) { 36 | puts("1.改阳光"); 37 | puts("2.僵尸秒杀"); 38 | puts("3.卷心菜投手加强"); 39 | puts("4.无冷却"); 40 | puts("5.黄油糊脸"); 41 | puts("6.僵尸加强二倍"); 42 | puts("7.输出植物地址"); 43 | puts("8.输出僵尸地址"); 44 | puts("9.植物血量增加二倍"); 45 | puts("10.植物攻速增加二倍"); 46 | puts("11.搭梯"); 47 | puts("12.炸荷叶烂南瓜"); 48 | puts("13.所有植物不攻击"); 49 | puts("14.恢复攻击"); 50 | puts("15.输出调试信息"); 51 | puts("16.过关"); 52 | puts("17.修改当前无尽轮数"); 53 | puts("18.只出梯子僵尸"); 54 | puts("19.阵型压力测试"); 55 | puts("20.退出"); 56 | 57 | #define GETOPT(mess, opt) \ 58 | printf(mess); \ 59 | if (scanf("%d", &opt) != 1) { \ 60 | PANIC; \ 61 | } 62 | GETOPT("请输入:", option); 63 | switch (option) { 64 | case 1: 65 | GETOPT("更改为?", baseInfo.val); 66 | setSun(); 67 | break; 68 | case 2: 69 | forEachZombies(letZombiesFragile); 70 | break; 71 | case 3: 72 | increaseCabbagePult(); 73 | break; 74 | case 4: 75 | removeColdDown(); 76 | break; 77 | case 5: 78 | forEachZombies(coverZombies); 79 | break; 80 | case 6: 81 | forEachZombies(increaseZombies); 82 | break; 83 | case 7: 84 | forEachPlants(reportPlants); 85 | break; 86 | case 8: 87 | forEachZombies(reportZombies); 88 | break; 89 | case 9: 90 | forEachPlants(increasePlants); 91 | break; 92 | case 10: 93 | forEachPlants(increasePlantsAttack); 94 | break; 95 | case 11: { 96 | printf("要将梯子僵尸放于何列?\n例如:1.2,1.3,(行与列以英文句号分隔," 97 | "多个行列以英文逗号分隔)"); 98 | setbuf(stdin, NULL); 99 | if (fgets(buf, sizeof(buf), stdin) == NULL) 100 | PANIC; 101 | parseRowAndCol(buf, &baseInfo.task); 102 | while (baseInfo.task != NULL) { 103 | forEachZombies(putLadder); 104 | usleep(WAIT_USECONDS); 105 | } 106 | } break; 107 | case 12: { 108 | printf("要去除何处的莲叶或破坏何处的南瓜?(行与列以英文句号分隔," 109 | "多个行列以英文逗号分隔"); 110 | setbuf(stdin, NULL); 111 | if (fgets(buf, sizeof(buf), stdin) == NULL) 112 | PANIC; 113 | parseRowAndCol(buf, &baseInfo.task); 114 | forEachPlants(fuck_LilyPad_Pumpkin); 115 | destroy((__list **)&baseInfo.task, NULL); 116 | } break; 117 | case 13: 118 | forEachPlants(plants_freeze); 119 | break; 120 | case 14: 121 | forEachPlants(plants_attack); 122 | break; 123 | case 15: 124 | printf("状态与信息:%p 基址:%p\n", getStatus(), getField()); 125 | break; 126 | case 16: 127 | pass(); 128 | break; 129 | case 17: 130 | GETOPT("更改为?", baseInfo.val); 131 | setFlags(); 132 | break; 133 | case 18: 134 | callLadder(); 135 | break; 136 | case 19: 137 | doLimits(); 138 | break; 139 | case 20: 140 | return 0; 141 | default: 142 | printf("输入错误...\n"); 143 | } 144 | } 145 | return 0; 146 | } 147 | #undef GETOPT 148 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : src/utils.c 3 | * Project : 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2018-01-19 7 | * Module : 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "defs.h" 16 | #include "scanmem.h" 17 | #include "pvz.h" 18 | #include "utils.h" 19 | void *insert(__list **target, size_t len) { 20 | __list *node = malloc(len); 21 | node->next = NULL; 22 | if (*target == NULL) { 23 | *target = node; 24 | } else { 25 | // real是当前链表的尾节点 26 | ((__list *)(*target)->real)->next = node; 27 | } 28 | (*target)->real = node; 29 | return node; 30 | } 31 | void destroy(__list **node, void (*op)(void *)) { 32 | __list *helper; 33 | while (*node != NULL) { 34 | helper = (*node)->next; 35 | if (op != NULL) 36 | op(*node); 37 | free(*node); 38 | *node = helper; 39 | } 40 | *node = NULL; 41 | } 42 | void insert_task(__task **target, int row, int col) { 43 | __task *node = insert((__list **)target, sizeof(__task)); 44 | node->row = row; 45 | node->col = col; 46 | } 47 | void pop(__task **target) { 48 | __task *helper = next(*target); 49 | free(*target); 50 | *target = helper; 51 | } 52 | int has(__task *target, int row, int col) { 53 | while (target != NULL) { 54 | if (target->row == row && target->col == col) 55 | return 1; 56 | target = next(target); 57 | } 58 | return 0; 59 | } 60 | void insert_images(__images **target, int value, void *remote) { 61 | __images *node = insert((__list **)target, sizeof(__images)); 62 | node->value = value; 63 | node->remote = remote; 64 | } 65 | void recover_images(__images *node) { 66 | while (node != NULL) { 67 | extern void pvz_write(void *, void *, size_t); 68 | pvz_write(node->remote, &node->value, sizeof(node->value)); 69 | node = next(node); 70 | } 71 | } 72 | void parseRowAndCol(const char *buf, __task **task) { 73 | const char *val = buf; 74 | int row, col; 75 | enum statusMachine { 76 | NEED_DOT, 77 | NEED_COMMA, 78 | NEED_ROW, 79 | NEED_COL, 80 | } status = NEED_ROW; 81 | parse: 82 | #define CHECK(stmt) \ 83 | if (!(stmt)) { \ 84 | printf("%s", buf); \ 85 | printf("%*s\n", (int)((val - buf) + 1), "^"); \ 86 | goto panic; \ 87 | } 88 | #define DIGIT() (*val - '0') 89 | if (*val == '\n') { 90 | if (status != NEED_COMMA) { 91 | --val; 92 | CHECK(0); 93 | } 94 | goto out; 95 | } 96 | while (isspace(*val)) 97 | ++val; 98 | switch (status) { 99 | case NEED_ROW: 100 | CHECK(isdigit(*val) && IN_RANGE(DIGIT(), 1, 6)); 101 | row = DIGIT(); 102 | status = NEED_DOT; 103 | val++; 104 | goto parse; 105 | case NEED_COL: 106 | CHECK(isdigit(*val) && IN_RANGE(DIGIT(), 1, 9)); 107 | col = DIGIT(); 108 | status = NEED_COMMA; 109 | val++; 110 | // 不允许重复 111 | if (!has(*task, row, col)) 112 | insert_task(task, row, col); 113 | goto parse; 114 | case NEED_DOT: 115 | CHECK('.' == *val); 116 | status = NEED_COL; 117 | val++; 118 | goto parse; 119 | case NEED_COMMA: 120 | CHECK(',' == *val); 121 | status = NEED_ROW; 122 | val++; 123 | goto parse; 124 | } 125 | panic: 126 | PANIC; 127 | out: 128 | return; 129 | } 130 | #undef CHECK 131 | #undef DIGIT 132 | void checkRootState() { 133 | if (getuid() != 0 || getgid() != 0) { 134 | printf("must run me under root mode\n"); 135 | exit(-1); 136 | } 137 | } 138 | const char *readline(FILE *fp) { 139 | static BufferType buf; 140 | if (fp == NULL) 141 | return ""; 142 | if (fgets(buf, BUFSIZE, fp) == NULL) { 143 | fclose(fp); 144 | return ""; 145 | } 146 | fclose(fp); 147 | return buf; 148 | } 149 | FILE *openProcFile(pid_t pid, const char *file) { 150 | static Path path; 151 | sprintf(path, "/proc/%d/%s", pid, file); 152 | return fopen(path, "r"); 153 | } 154 | FILE *openCmdline(const char *pid) { 155 | return openProcFile(atoi(pid), "cmdline"); 156 | } 157 | const char *getPackageName(const char *pid) { 158 | return readline(openCmdline(pid)); 159 | } 160 | pid_t findPVZProcess() { 161 | DIR *dp = opendir("/proc"); 162 | struct dirent *dirHandle; 163 | pid_t pid = -1; 164 | while ((dirHandle = readdir(dp))) { 165 | // 文件名第一个字符是数字 166 | if (dirHandle->d_type & DT_DIR && isdigit(*dirHandle->d_name)) { 167 | if (strcmp(getPackageName(dirHandle->d_name), SPECIFIC_PACKAGE) == 0) { 168 | pid = atoi(dirHandle->d_name); 169 | break; 170 | } 171 | } 172 | } 173 | closedir(dp); 174 | return pid; 175 | } 176 | -------------------------------------------------------------------------------- /scanmem/ptrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | Functions to access the memory of the target process. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009,2010 WANG Lu 7 | Copyright (C) 2015 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | /* for pread */ 26 | #ifdef _XOPEN_SOURCE 27 | #undef _XOPEN_SOURCE 28 | #endif 29 | #define _XOPEN_SOURCE 500 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifdef __GNUC__ 47 | #define EXPECT(x, y) __builtin_expect(x, y) 48 | #else 49 | #define EXPECT(x, y) x 50 | #endif 51 | 52 | // dirty hack for FreeBSD 53 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 54 | #define PTRACE_ATTACH PT_ATTACH 55 | #define PTRACE_DETACH PT_DETACH 56 | #define PTRACE_PEEKDATA PT_READ_D 57 | #define PTRACE_POKEDATA PT_WRITE_D 58 | #endif 59 | 60 | #include "scanmem.h" 61 | /* progress handling */ 62 | #define NUM_DOTS (10) 63 | #define NUM_SAMPLES (100) 64 | #define MAX_PROGRESS (1.0) /* 100% */ 65 | #if (!NUM_DOTS || !NUM_SAMPLES || NUM_SAMPLES % NUM_DOTS != 0) 66 | #error Invalid NUM_DOTS to NUM_SAMPLES proportion! 67 | #endif 68 | #define SAMPLES_PER_DOT (NUM_SAMPLES / NUM_DOTS) 69 | #define PROGRESS_PER_SAMPLE (MAX_PROGRESS / NUM_SAMPLES) 70 | 71 | /* ptrace peek buffer, used by peekdata() */ 72 | /* make it larger in order to reduce shift */ 73 | /* #define MAX_PEEKBUF_SIZE (4*sizeof(int64_t)) */ 74 | #define MAX_PEEKBUF_SIZE (1024) 75 | static struct { 76 | uint8_t cache[MAX_PEEKBUF_SIZE]; /* read from ptrace() */ 77 | unsigned size; /* number of entries (in bytes) */ 78 | const char *base; /* base address of cached region */ 79 | pid_t pid; /* what pid this applies to */ 80 | } peekbuf; 81 | 82 | #define show_error printf 83 | #define show_info printf 84 | bool sm_attach(pid_t target) { 85 | int status; 86 | 87 | /* attach to the target application, which should cause a SIGSTOP */ 88 | if (ptrace(PTRACE_ATTACH, target, NULL, NULL) == -1L) { 89 | show_error("failed to attach to %d, %s\n", target, strerror(errno)); 90 | return false; 91 | } 92 | 93 | /* wait for the SIGSTOP to take place. */ 94 | if (waitpid(target, &status, 0) == -1 || !WIFSTOPPED(status)) { 95 | show_error("there was an error waiting for the target to stop.\n"); 96 | show_info("%s\n", strerror(errno)); 97 | return false; 98 | } 99 | 100 | /* flush the peek buffer */ 101 | memset(&peekbuf, 0x00, sizeof(peekbuf)); 102 | 103 | /* everything looks okay */ 104 | return true; 105 | } 106 | 107 | bool sm_detach(pid_t target) { 108 | // addr is ignored on Linux, but should be 1 on FreeBSD in order to let the 109 | // child process continue execution where it had been interrupted 110 | return ptrace(PTRACE_DETACH, target, 1, 0) == 0; 111 | } 112 | 113 | /* read region using /proc/pid/mem */ 114 | ssize_t readregion(pid_t target, void *buf, size_t count, 115 | unsigned long offset) { 116 | char mem[32]; 117 | int fd; 118 | ssize_t len; 119 | 120 | /* print the path to mem file */ 121 | snprintf(mem, sizeof(mem), "/proc/%d/mem", target); 122 | 123 | /* attempt to open the file */ 124 | if ((fd = open(mem, O_RDONLY)) == -1) { 125 | show_error("unable to open %s.\n", mem); 126 | return -1; 127 | } 128 | 129 | /* try to honor the request */ 130 | len = pread64(fd, buf, count, offset); 131 | 132 | /* clean up */ 133 | close(fd); 134 | 135 | return len; 136 | } 137 | 138 | bool sm_read_array(pid_t target, const void *addr, char *buf, int len) { 139 | if (sm_attach(target) == false) { 140 | return false; 141 | } 142 | 143 | #if HAVE_PROCMEM 144 | unsigned nread = 0; 145 | ssize_t tmpl; 146 | while (nread < len) { 147 | if ((tmpl = readregion(target, buf + nread, len - nread, 148 | (unsigned long)(addr + nread))) == -1) { 149 | /* no, continue with whatever data was read */ 150 | break; 151 | } else { 152 | /* some data was read */ 153 | nread += tmpl; 154 | } 155 | } 156 | 157 | if (nread < len) { 158 | sm_detach(target); 159 | return false; 160 | } 161 | 162 | return sm_detach(target); 163 | #else 164 | int i; 165 | /* here we just read long by long, this should be ok for most of time */ 166 | /* partial hit is not handled */ 167 | for (i = 0; i < len; i += sizeof(long)) { 168 | errno = 0; 169 | *((long *)(buf + i)) = ptrace(PTRACE_PEEKDATA, target, addr + i, NULL); 170 | if (EXPECT((*((long *)(buf + i)) == -1L) && (errno != 0), false)) { 171 | sm_detach(target); 172 | return false; 173 | } 174 | } 175 | return sm_detach(target); 176 | #endif 177 | } 178 | 179 | /* TODO: may use /proc//mem here */ 180 | bool sm_write_array(pid_t target, void *addr, const void *data, int len) { 181 | int i, j; 182 | long peek_value; 183 | 184 | if (sm_attach(target) == false) { 185 | return false; 186 | } 187 | 188 | for (i = 0; i + sizeof(long) < len; i += sizeof(long)) { 189 | if (ptrace(PTRACE_POKEDATA, target, addr + i, *(long *)(data + i)) == -1L) { 190 | return false; 191 | } 192 | } 193 | 194 | if (len - i > 0) /* something left (shorter than a long) */ 195 | { 196 | if (len > sizeof(long)) /* rewrite last sizeof(long) bytes of the buffer */ 197 | { 198 | if (ptrace(PTRACE_POKEDATA, target, addr + len - sizeof(long), 199 | *(long *)(data + len - sizeof(long))) == -1L) { 200 | return false; 201 | } 202 | } else /* we have to play with bits... */ 203 | { 204 | /* try all possible shifting read and write */ 205 | for (j = 0; j <= sizeof(long) - (len - i); ++j) { 206 | errno = 0; 207 | if (((peek_value = ptrace(PTRACE_PEEKDATA, target, addr - j, NULL)) == 208 | -1L) && 209 | (errno != 0)) { 210 | if (errno == EIO || errno == EFAULT) /* may try next shift */ 211 | continue; 212 | else { 213 | show_error("%s failed.\n", __func__); 214 | return false; 215 | } 216 | } else /* peek success */ 217 | { 218 | /* write back */ 219 | memcpy(((int8_t *)&peek_value) + j, data + i, len - i); 220 | 221 | if (ptrace(PTRACE_POKEDATA, target, addr - j, peek_value) == -1L) { 222 | show_error("%s failed.\n", __func__); 223 | return false; 224 | } 225 | 226 | break; 227 | } 228 | } 229 | } 230 | } 231 | 232 | return sm_detach(target); 233 | } 234 | -------------------------------------------------------------------------------- /inc/cheater.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : cheater.h 3 | * Project : PVZ 4 | * Author : ze00 5 | * Email : zerozakiGeek@gmail.com 6 | * Date : 2017-08-15 7 | * Module : 8 | * License : MIT 9 | */ 10 | #ifndef __CHEATER__H 11 | #define __CHEATER__H 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "pvz_offset.h" 18 | #include "base.h" 19 | #include "scanmem.h" 20 | void pvz_write(void *rp, void *buf, size_t len) { 21 | if (!sm_write_array(baseInfo.pid, rp, buf, len)) { 22 | printf("cannot write memory at %p,'%s' has died?\n", rp, SPECIFIC_PACKAGE); 23 | exit(-1); 24 | } 25 | } 26 | void pvz_read(void *rp, void *buf, size_t len) { 27 | if (!sm_read_array(baseInfo.pid, rp, buf, len)) { 28 | printf("cannot read memory at %p,'%s' has died?\n", rp, SPECIFIC_PACKAGE); 29 | exit(-1); 30 | } 31 | } 32 | int32_t getI32(void *rp) { 33 | static int32_t val; 34 | pvz_read(rp, &val, sizeof(val)); 35 | return val; 36 | } 37 | void *getP32(void *rp) { 38 | static void *val; 39 | pvz_read(rp, &val, sizeof(uint32_t)); 40 | return val; 41 | } 42 | float getF32(void *rp) { 43 | static float val; 44 | pvz_read(rp, &val, sizeof(val)); 45 | return val; 46 | } 47 | void setI32(void *rp, int32_t v) { pvz_write(rp, &v, sizeof(v)); } 48 | void setF32(void *rp, float v) { pvz_write(rp, &v, sizeof(v)); } 49 | void *getBase(const char *spec, int findFirst, void (*action)(void *, void *), 50 | void **end) { 51 | void *base; 52 | FILE *maps = openProcFile(baseInfo.pid, "maps"); 53 | BufferType buf; 54 | while (fgets(buf, BUFSIZE, maps) != NULL) { 55 | if (strstr(buf, spec)) { 56 | sscanf(buf, "%p-%p", &base, end); 57 | if (action != NULL) 58 | action(base, *end); 59 | if (findFirst) 60 | break; 61 | } 62 | } 63 | fclose(maps); 64 | return base; 65 | } 66 | void detectPVZ() { 67 | pid_t pid = findPVZProcess(); 68 | if (pid == -1) { 69 | printf("cannot locate '%s'\n", SPECIFIC_PACKAGE); 70 | exit(-1); 71 | } 72 | printf("Found %s:%d\n", SPECIFIC_PACKAGE, pid); 73 | baseInfo.pid = pid; 74 | } 75 | void *getDynamicBase() { 76 | void *v; 77 | return getBase(SPECIFIC_DYNAMIC_LIBRARIES, 1, NULL, (void **)&v); 78 | } 79 | void getBssBase() { 80 | getBase(SPECIFIC_DYNAMIC_LIBRARIES, 0, NULL, (void **)&baseInfo.bss); 81 | } 82 | 83 | void removeColdDown() { 84 | char *base = baseInfo.base; 85 | int32_t *p = (int32_t *)(base + getOffset("cannon")); 86 | for (size_t i = 0; i < 48; ++i) { 87 | setI32(p, 0); 88 | p -= 9; 89 | } 90 | } 91 | void letZombiesFragile(void *rp) { 92 | struct Hp hp = { 93 | .curHp = 10, 94 | .totalHp = 10, 95 | .armor = 0, 96 | }; 97 | pvz_write(rp + ZOM_HP_OFF, &hp, sizeof(hp)); 98 | } 99 | void coverZombies(void *rp) { setI32(rp + 0xbc, 5000); } 100 | void increaseZombies(void *rp) { 101 | setI32(rp + ZOM_HP_OFF, getI32(rp + ZOM_HP_OFF) * 2); 102 | } 103 | void increaseCabbagePult() { 104 | char *p = baseInfo.base + getOffset("cabbage"); 105 | setI32(p + 8, 45); 106 | } 107 | 108 | void *getField() { 109 | void *helper = getP32(baseInfo.bss + getOffset("base")); 110 | return getOffset("field") + helper; 111 | } 112 | void *getStatus() { 113 | void *status = getP32(getField() + getOffset("status")); 114 | if (status == NULL) { 115 | printf("请先进入游戏\n"); 116 | longjmp(env, SETJMP_RET); 117 | } 118 | return status; 119 | } 120 | void forEachPlants(void (*op)(void *)) { 121 | size_t pcnt = getI32(getStatus() + getOffset("plants_count")); 122 | int32_t *entry = getP32(getStatus() + getOffset("plants_entry")); 123 | void *pp; 124 | for (size_t idx = 0; idx < pcnt;) { 125 | pp = getP32(entry); 126 | if (pp > (void *)0x10000000) { 127 | op(pp); 128 | entry++; 129 | idx++; 130 | } 131 | entry++; 132 | } 133 | } 134 | void forEachZombies(void (*op)(void *)) { 135 | size_t zcnt = getI32(getStatus() + getOffset("zombies_count")); 136 | int32_t *entry = getP32(getStatus() + getOffset("zombies_entry")); 137 | void *zp; 138 | for (size_t idx = 0; idx < zcnt;) { 139 | // 在僵尸地址前 140 | // 有一些小的数据 141 | // 不知道干嘛的 142 | zp = getP32(entry); 143 | if (zp > (void *)0x10000000) { 144 | op(zp); 145 | idx++; 146 | // 僵尸地址后面有一个指针 147 | // 同不知道干嘛的 148 | entry++; 149 | } 150 | entry++; 151 | } 152 | } 153 | #define ROW(lp) (getI32(lp + getOffset("zombies_row")) + 1) 154 | #define COL(lp) (getF32(lp + getOffset("zombies_pos_y"))) 155 | #define HP(lp) (getI32(lp + ZOM_HP_OFF)) 156 | #define CODE(lp) (getI32(lp + getOffset("zombies_type"))) 157 | void reportZombies(void *rp) { 158 | printf("Found at %p(row@%d x pos_y@%f)(hp:%d code:%d)\n", rp, ROW(rp), 159 | COL(rp), HP(rp), CODE(rp)); 160 | } 161 | #undef ROW 162 | #undef COL 163 | #undef HP 164 | #undef CODE 165 | void increasePlants(void *remote) { 166 | setI32(remote + PLAN_HP_OFF, getI32(remote + PLAN_HP_OFF) * 2); 167 | } 168 | void increasePlantsAttack(void *remote) { 169 | setI32(remote + PLAN_ATT_TOTAL_OFF, getI32(remote + PLAN_ATT_TOTAL_OFF) / 2); 170 | } 171 | void putLadder(void *remote) { 172 | 173 | if (baseInfo.task != NULL) { 174 | int32_t type = getI32(remote + getOffset("zombies_type")); 175 | if (type == 21) { 176 | float f = baseInfo.task->col * 100; 177 | int32_t row = baseInfo.task->row - 1; 178 | if (f > getF32(remote + getOffset("zombies_pos_x"))) 179 | return; 180 | setI32(remote + getOffset("zombies_row"), row); 181 | setF32(remote + getOffset("zombies_pos_x"), f); 182 | setF32(remote + getOffset("zombies_pos_y"), f); 183 | printf("put ladder on %d:%d\n", baseInfo.task->row, baseInfo.task->col); 184 | pop(&baseInfo.task); 185 | } 186 | } 187 | } 188 | #define ROW(lp) (getI32(lp + getOffset("plants_row")) + 1) 189 | #define COL(lp) (getI32(lp + getOffset("plants_col")) + 1) 190 | #define HP(lp) (getI32(lp + PLAN_HP_OFF)) 191 | #define CODE(lp) (getI32(lp + getOffset("plants_type"))) 192 | #define ATTACK(lp) (getI32(lp + getOffset("plants_attack"))) 193 | void reportPlants(void *remote) { 194 | printf("Found at %p (row@%d x col@%d)(hp:%d code:%d)\n", remote, ROW(remote), 195 | COL(remote), HP(remote), CODE(remote)); 196 | } 197 | void fuck_LilyPad_Pumpkin(void *remote) { 198 | if (has(baseInfo.task, ROW(remote), COL(remote))) { 199 | switch (CODE(remote)) { 200 | case LILYPAD_CODE: 201 | setI32(remote + getOffset("plants_vis"), 0); 202 | break; 203 | case PUMPKIN_CODE: 204 | setI32(remote + getOffset("plants_hp"), 1332); 205 | } 206 | } 207 | } 208 | void plants_freeze(void *remote) { 209 | if (ATTACK(remote) == 0) 210 | return; 211 | insert_images(&baseInfo.images, ATTACK(remote), 212 | remote + getOffset("plants_attack")); 213 | setI32(remote + getOffset("plants_attack"), 0); 214 | } 215 | void plants_attack(void *remote) { 216 | recover_images(baseInfo.images); 217 | destroy((__list **)&baseInfo.images, NULL); 218 | } 219 | void setSun() { setI32(getStatus() + getOffset("sun"), baseInfo.val); } 220 | void pass() { setI32(getStatus() + getOffset("pass"), 1); } 221 | void setFlags() { 222 | setI32(getP32(getStatus() + getOffset("flags_helper")) + getOffset("flags"), 223 | baseInfo.val); 224 | } 225 | void doLimits() { 226 | uint32_t *zom = getStatus() + getOffset("zombies_list"); 227 | // 普僵 红眼 小丑 气球 冰车 舞王 海豚 228 | static uint32_t candidate[] = {0, 0x20, 0x10, 0xf, 0xc, 0x8, 0xe}; 229 | static uint32_t which; 230 | srand(time(NULL)); 231 | for (size_t iidx = 0; iidx < 20; ++iidx) { 232 | for (size_t jidx = 0; jidx < 50; ++jidx) { 233 | which = rand() % 7; 234 | setI32(zom, candidate[which]); 235 | ++zom; 236 | } 237 | } 238 | } 239 | void callLadder() { 240 | uint32_t *zom = getStatus() + getOffset("zombies_list"); 241 | for (size_t idx = 0; idx < 2000; ++idx) { 242 | setI32(zom, 0x15); 243 | ++zom; 244 | } 245 | } 246 | #undef ROW 247 | #undef COL 248 | #undef HP 249 | #undef CODE 250 | #undef ATTACK 251 | void catchSIGINT() { 252 | fflush(stdout); 253 | setbuf(stdin, NULL); 254 | destroy((__list **)&baseInfo.task, NULL); 255 | longjmp(env, SETJMP_RET); 256 | } 257 | void registeSigHandle() { signal(SIGINT, catchSIGINT); } 258 | #endif //__CHEATER__H 259 | --------------------------------------------------------------------------------