riru官网

https://github.com/RikkaApps/Riru

 

1.riru原理

    Riru的原理是通过替换会被Zygote加载的libmemtrack.so从而实现Zygote注入,而安卓应用进程都是从Zygote fork的,注入了Zygote也就等同于注入了接下来会启动的游戏,也就可以轻松实现修改了。然后hook掉Zygote.nativeForkAndSpecialize函数监听app启动。

2.riru 安装

    Riru是Magisk的模块,所以首先要安装Magisk,不过现在手机root应该都是选择Magisk了,然后去Riru的Github的Releases页面下载最新的riru-core包,在magisk里安装,安装好Riru后就可以动手写自己的修改模块了。

 

3.开发riru模块

去Riru的Github上下载所有代码,根据官方README,先给自己的模块取个名字,比如perfare,然后修改riru-module-template/jni/main/Android.mk中的模块名字

1LOCAL_MODULE := libriru_perfare

 

接着修改riru-module-template/build.gradle中的模块信息

1

def moduleName = “perfare”

这两个地方的模块名字一定要一样,其他模块信息自己看着修改就好,修改好后就开始写代码吧,打开riru-module-template/jni/main/main.cpp

在main.cpp中本身已经写好了一些函数,我们只需要关注两个函数
nativeForkAndSpecializePre 通过解析参数appDataDir从而得到当前启动的进程包名,判断是不是我们要修改的游戏
nativeForkAndSpecializePost 在这里创建新线程进行修改

首先写个函数判断包名

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

static int enable_hack;

static const char* game_name = “com.aniplex.fategrandorder”;

 

int isGame(JNIEnv *env, jstring appDataDir) {

    if (!appDataDir)

        return 0;

 

    const char *app_data_dir = env->GetStringUTFChars(appDataDir, NULL);

 

    int user = 0;

    static char package_name[256];

    if (sscanf(app_data_dir, “/data/%*[^/]/%d/%s”, &user, package_name) != 2) {

        if (sscanf(app_data_dir, “/data/%*[^/]/%s”, package_name) != 1) {

            package_name[0] = ‘\0’;

            LOGW(“can’t parse %s”, app_data_dir);

            return 0;

        }

    }

    env->ReleaseStringUTFChars(appDataDir, app_data_dir);

    if (strcmp(package_name, game_name) == 0) {

        LOGD(“detect game: %s”, package_name);

        return 1;

    }

    else {

        return 0;

    }

}

然后在nativeForkAndSpecializePre里调用这个函数

1

2

3

4

5

6

7

8

9

void nativeForkAndSpecializePre(

        JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, jint *runtime_flags,

        jobjectArray *rlimits, jint *_mount_external, jstring *se_info, jstring *se_name,

        jintArray *fdsToClose, jintArray *fdsToIgnore, jboolean *is_child_zygote,

        jstring *instructionSet, jstring *appDataDir, jstring *packageName,

        jobjectArray *packagesForUID, jstring *sandboxId) {

    // packageName, packagesForUID, sandboxId exists from Android Q

    enable_hack = isGame(env, *appDataDir);

}

之后就可以在nativeForkAndSpecializePost里根据enable_hack来修改了,这里选择启动一个新线程来修改

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

int nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) {

    if (res == 0) {

        // in app process

        if (enable_hack) {

            int ret;

            pthread_t ntid;

            if ((ret = pthread_create(&ntid, NULL, hack_thread, NULL))) {

                LOGE(“can’t create thread: %s\n”, strerror(ret));

            }

        }

    } else {

        // in zygote process, res is child pid

        // don’t print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66

    }

    return 0;

}

至于hack_thread里要怎么修改游戏就看你自己了,这里随便示范一个简单的libil2cpp.so修改,通过不断读取/proc/self/maps确定libil2cpp.so的载入和获取基址,然后直接通过指针修改text段代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

unsigned long get_module_base(const char* module_name)

{

    FILE *fp;

    unsigned long addr = 0;

    char *pch;

    char filename[32];

    char line[1024];

 

    snprintf(filename, sizeof(filename), “/proc/self/maps”);

 

    fp = fopen(filename, “r”);

 

    if (fp != NULL) {

        while (fgets(line, sizeof(line), fp)) {

            if (strstr(line, module_name)) {

                pch = strtok(line, “-“);

                addr = strtoul(pch, NULL, 16);

                if (addr == 0x8000)

                    addr = 0;

                break;

            }

        }

        fclose(fp);

    }

    return addr;

}

 

void *hack_thread(void *arg)

{

    LOGD(“hack thread :%d”, gettid());

    unsigned long base_addr;

    while (true)

    {

        base_addr = get_module_base(“libil2cpp.so”);

        if (base_addr != 0) {

            break;

        }

    }

    LOGD(“detect libil2cpp.so %lx”, base_addr);

    LOGD(“hack game begin”);

 

    unsigned long hack_addr = base_addr + 偏移;

 

    //设置属性可写</p快3导师群2

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

unsigned long get_module_base(const char* module_name)

{

    FILE *fp;

    unsigned long addr = 0;

    char *pch;

    char filename[32];

    char line[1024];

 

    snprintf(filename, sizeof(filename), “/proc/self/maps”);

 

    fp = fopen(filename, “r”);

 

    if (fp != NULL) {

        while (fgets(line, sizeof(line), fp)) {

            if (strstr(line, module_name)) {

                pch = strtok(line, “-“);

                addr = strtoul(pch, NULL, 16);

                if (addr == 0x8000)

                    addr = 0;

                break;

            }

        }

        fclose(fp);

    }

    return addr;

}

 

void *hack_thread(void *arg)

{

    LOGD(“hack thread :%d”, gettid());

    unsigned long base_addr;

    while (true)

    {

        base_addr = get_module_base(“libil2cpp.so”);

        if (base_addr != 0) {

            break;

        }

    }

    LOGD(“detect libil2cpp.so %lx”, base_addr);

    LOGD(“hack game begin”);

 

    unsigned long hack_addr = base_addr + 偏移;

 

    //设置属性可写

    void* page_start = (void*)(hack_addr – hack_addr % PAGE_SIZE);

    if (-1 == mprotect(page_start, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC)) {

        LOGE(“mprotect failed(%d)”, errno);

        return NULL;

    }

 

    unsigned char* tmp = (unsigned char*)(void*)hack_addr;

    tmp[0] = 0x00;

    tmp[1] = 0x00;

    tmp[2] = 0x00;

    tmp[3] = 0x00;

 

    LOGD(“hack game finish”);

    return NULL;

}

到这里代码就写完了,可以使用gradlew.bat assembleMagiskRelease命令直接编译或者用Android Studio,在release文件夹下就会生成zip包,在Magisk安装即可