首页
社区
课程
招聘
[翻译]通过破解游戏学习Frida基础知识
2018-8-13 19:44 10782

[翻译]通过破解游戏学习Frida基础知识

2018-8-13 19:44
10782

通过破解游戏学习Frida基础知识


最近我看到LiveOverflow开始播放一系列关于如何“破解“一款游戏的视频,破解该游戏是2015年Ghost in the Shellcode CTF比赛中的一项挑战。在观看了前两、三个视频之后,我决定使用同一款游戏来做一些关于Frida的介绍,并且向你展示这个了不起的项目是可以助你一臂之力的。

因此,在本文中,我们要构思一个可以帮助我们破解游戏的作弊方法。读者要注意的如下:
  • 使用Frida hook函数
  • 读内存
  • 写内存
  • 在我们的hook中调用二进制函数
  • 处理类和结构
首先,请参考此链接配置服务器。如果你已准备好服务器和客户端,那我们就开玩!:)

0x00第一步:信息收集!

第一步是启动客户端,注册新玩家,并探索游戏世界。在你花了几分钟在地图上移动并查看HUD(比如魔法值,生命值,物品等)之后,现在是时候继续下一步,在终端上做一些小动作了。游戏运行时,执行ps-aux并检查客户端二进制文件的名称:PwnAdventure3-Linux-Shipping。它是一个带符号的动态链接二进制文件(使用file查看),因此很有可能我们关心的“核心”位于一个共享对象中。 我们可以使用ldd列出该二进制文件使用的所有共享对象:
mothra@kaiju:~/holydays|⇒  ldd $(locate PwnAdventure3-Linux-Shipping)
        linux-vdso.so.1 (0x00007fff5e6c2000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f5af4631000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5af442d000)
        libGameLogic.so => /home/mothra/PwnAdventure3_Data/PwnAdventure3/PwnAdventure3/Binaries/Linux/libGameLogic.so (0x00007f5af3f61000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f5af3d59000)
        libopenal.so.1 => /home/mothra/PwnAdventure3_Data/PwnAdventure3/PwnAdventure3/Binaries/Linux/../../../Engine/Binaries/Linux/libopenal.so.1 (0x00007f5af3b02000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5af3801000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5af34f6000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5af32e0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5af2f35000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f5af484e000)
        libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f5af2cd4000)
        libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f5af28d7000)

看起来“libGameLogic.so”是我们的目标。由于游戏是用C++编写的,我们必须处理name mangling(名字修饰)。为了转储所有导出函数并做名字解析,我们执行一个使用Frida和cxxfilt的小脚本:
# Extract exports & demangle it

import frida
import cxxfilt


session = frida.attach("PwnAdventure3-Linux-Shipping")
script = session.create_script("""
    var exports = Module.enumerateExportsSync("libGameLogic.so");
    for (i = 0; i < exports.length; i++) {
        send(exports[i].name);
    }
        """);

def on_message(message, data):
    print message["payload"] + " - " + cxxfilt.demangle(message["payload"])

script.on('message', on_message)
script.load()

我们在做什么?我们将自己附加到正在运行的游戏进程(PwnAdventure3-Linux-Shipping),然后创建一个包含主逻辑的脚本(JavaScript)。从该JavaScript代码段我们可以访问Frida API,所有magic都会实现:):使用Module.enumerateExportsSync(libname)我们检索一个包含所有导出函数的数组,然后遍历数组并使用send()传递信息给Python脚本。在Python脚本中,我们只需调用cxxfilt.demangle()来解析名字。

现在我们有了一堆有用的信息,可以从中搜索。例如,让我们搜索与速度(speed)相关的方法:
mothra@kaiju:~/holydays|⇒  python demangle-exports.py > demangled.txt
mothra@kaiju:~/holydays|⇒  cat demangled.txt | grep -i speed
_ZN6Player12GetJumpSpeedEv - Player::GetJumpSpeed()
_ZThn168_N6Player12GetJumpSpeedEv - non-virtual thunk to Player::GetJumpSpeed()
_ZN6Player15GetWalkingSpeedEv - Player::GetWalkingSpeed()
_ZThn168_N6Player15GetWalkingSpeedEv - non-virtual thunk to Player::GetWalkingSpeed()

0x01 作弊

通常我们不需要一直作弊。也许我们只想增加我们的移动速度以便做长距离移动,但在建筑物内我们想要正常的速度,或者将自己传送到另一个位置我们则需要传递坐标参数。最好的解决方法是使用游戏的聊天功能。 在我们导出的数据中搜索“chat”字样:
mothra@kaiju:~/holydays|⇒  cat demangled.txt | grep -i chat
_ZN6Player11ReceiveChatEPS_RKSs - Player::ReceiveChat(Player*, std::string const&)
_ZN11ClientWorld4ChatEP6PlayerRKSs - ClientWorld::Chat(Player*, std::string const&)
_ZN10LocalWorld13SendChatEventEP6PlayerRKSs - LocalWorld::SendChatEvent(Player*, std::string const&)
_ZN20GameServerConnection11OnChatEventEP6Player - GameServerConnection::OnChatEvent(Player*)
_ZN11ServerWorld4ChatEP6PlayerRKSs - ServerWorld::Chat(Player*, std::string const&)
_ZN11ServerWorld13SendChatEventEP6PlayerRKSs - ServerWorld::SendChatEvent(Player*, std::string const&)
_ZN13ClientHandler4ChatEv - ClientHandler::Chat()
_ZN11ClientWorld13SendChatEventEP6PlayerRKSs - ClientWorld::SendChatEvent(Player*, std::string const&)
_ZN6Player4ChatEPKc - Player::Chat(char const*)
_ZN20GameServerConnection4ChatERKSs - GameServerConnection::Chat(std::string const&)
_ZN6Player11PerformChatERKSs - Player::PerformChat(std::string const&)
_ZThn168_N6Player4ChatEPKc - non-virtual thunk to Player::Chat(char const*)
_ZN10LocalWorld4ChatEP6PlayerRKSs - LocalWorld::Chat(Player*, std::string const&)

那个Player::Chat(char const*)看起来很有趣,因为它收到一个指向字符串的指针(也许是我们的聊天消息?)。为了确认,我们hook它并将该字符串的内容输出为log:
 # Log chat
 import frida
 import sys

 session = frida.attach("PwnAdventure3-Linux-Shipping")
 script = session.create_script("""
         //Find "Player::Chat"
         var chat = Module.findExportByName("libGameLogic.so", "_ZN6Player4ChatEPKc");
         console.log("Player::Chat() at  address: " + chat);

         Interceptor.attach(chat, {
             onEnter: function (args) { // 0 => this; 1 => cont char* (our text)
                var chatMsg = Memory.readCString(args[1]);
                console.log("[Chat]: " + chatMsg);
             }

         });
 """)

 script.load()
 sys.stdin.read()

通过Module.findExportByName(libname, function),我们得到Player::Chat的地址,然后我们将该地址传递给Interceptor,以便“附加”我们的hook。现在我们可以控制两个事件:onEnter和onLeave(按字面意思理解就行)。在onEnter里面我们可以看到参数(记住第一个参数是this,所以第二个参数是我们指向字符串的指针)。最后我们只需要用Memory.readCString(pointer)读取内存来获取字符串。执行之并输入聊天内容:
mothra@kaiju:~/holydays|⇒  python log-chat.py
Player::Chat() at  address: 0x7f4ca4d4d850
[Chat]: This Works

此时,我们可以在游戏聊天中键入并解析命令以触发我们的作弊行为。噢,等等,什么行为呢?继续阅读!

0x02 速度!

我们要做的第一件事就是加快移动速度。如前所述,二进制文件有符号。让我们转储与Player类相关的符号:
gdb -p  $(pidof PwnAdventure3-Linux-Shipping) --batch -ex "ptype Player" -ex "quit" > Player.class

搜索speed:
mothra@kaiju:~/holydays|⇒  cat Player.class | grep -i speed
    float m_walkingSpeed;
    float m_jumpSpeed;
    virtual float GetWalkingSpeed();
    virtual float GetJumpSpeed();

有趣的是,我们找到了m_walkingSpeed(float),看起来像是移动动作的基准速度,还找到一个名为“GetWalkingSpeed()”的方法(如果我们用解析数据做交叉检查)对应于_ZN6Player15GetWalkingSpeedEv - Player::GetWalkingSpeed()。我们可以hook GetWalkingSpeed所以每次调用时m_walkingSpeed的值都会被我们想要的速度值覆盖。

为了获得m_walkingSpeed的内存地址,我在调用GetWalkingSpeed()时用this中的一个偏移量做计算(我在FuzzySecurity的一篇文章中看到了这个方法——Application Introspection & Hooking With Frida)。使用GDB很容易做到:
mothra@kaiju:~/holydays|⇒  gdb -p  $(pidof PwnAdventure3-Linux-Shipping) --batch 
\ -ex "b _ZN6Player15GetWalkingSpeedEv" --ex "c" --ex "print &this->m_walkingSpeed" 
\ -ex "print this" -ex "print (int)\$1-(int)\$2" -ex "quit" 2>/dev/null | awk '/\$3/ {print $3 }'
736

所以我们的步骤是:1、hook ___ZN6Player15GetWalkingSpeedEv;2、获取onEnter的this指针并添加736(偏移量)来获取m_walkingSpeed的内存地址; 3、用我们想要的速度值覆盖该浮点数(不大于9999)。
// Find Player::GetWalkingSpeed()
         var walkSpeed = Module.findExportByName("libGameLogic.so", "_ZN6Player15GetWalkingSpeedEv");
         console.log("Player::GetWalkingSpeed() at address: " + walkSpeed);

         // Check Speed
         Interceptor.attach(walkSpeed,
             {
                 // Get Player * this location
                 onEnter: function (args) {
                     console.log("Player at address: " + args[0]);
                     this.walkingSpeedAddr = ptr(args[0]).add(736) // Offset m_walkingSpeed
                     console.log("WalkingSpeed at address: " + this.walkingSpeedAddr);
                 },

留意我们是如何获取walkingSpeedAddr中m_walkingSpeed的内存地址的,以便我们在onLeave事件中访问此值:
                 // Get the return value and write the new value
                 onLeave: function (retval) {
                     console.log("Walking Speed: " + Memory.readFloat(this.walkingSpeedAddr));
                     Memory.writeFloat(this.walkingSpeedAddr, 9999);

                 }
             });

正如我们之前对readCString所做的那样,现在我们使用Memory.readFloat来读取原始速度值(200)并将其记录在命令行终端中。最后,我们将移动速度设为浮点数(9999)。运行游戏并在地图上移动。疯狂的速度!

由于我们有获取聊天消息的例行程序,我们可以配合使用 !wspeed_on NUMBER和!wspeed_off来调节速度:
script = session.create_script("""
        // Global Values
        var Player = {
            m_walkingSpeed : 200,
        };

        // Cheat status
        var cheatStatus = {
            walkingSpeed : 0,
        };

        // Chat Helper
        function chatHelper(msg) {
            var token = msg.split(" ");
            if (token[0] === "!wspeed_on") {
                Player.m_walkingSpeed = parseInt(token[1]);
                cheatStatus.walkingSpeed = 1;
                console.log("[CHEAT]: Walking Speed Enabled (" + token[1] + ")");
            }
            if (token[0] === "!wspeed_off") {
                Player.m_walkingSpeed = 200;
                cheatStatus.walkingSpeed = 0;
                console.log("[CHEAT]: Walking Speed Disabled (200)");
            }
        }


        //Find "Player::Chat"
        var chat = Module.findExportByName("libGameLogic.so", "_ZN6Player4ChatEPKc");
        console.log("Player::Chat() at  address: " + chat);

        // Add our logger
        Interceptor.attach(chat, {
            onEnter: function (args) { // 0 => this; 1 => cont char* (our text)
               var chatMsg = Memory.readCString(args[1]);
               console.log("[Chat]: " + chatMsg);
               chatHelper(chatMsg);
            }

        });

        // Find Player::GetWalkingSpeed()
        var walkSpeed = Module.findExportByName("libGameLogic.so", "_ZN6Player15GetWalkingSpeedEv");
        console.log("Player::GetWalkingSpeed() at address: " + walkSpeed);

        // Check Speed
        Interceptor.attach(walkSpeed,
            {
                // Get Player * this location
                onEnter: function (args) {
                    //console.log("Player at address: " + args[0]);
                    this.walkingSpeedAddr = ptr(args[0]).add(736) // Offset m_walkingSpeed
                    //console.log("WalkingSpeed at address: " + this.walkingSpeedAddr);
                },

                // Get the return value and write the new speed
                onLeave: function (retval) {
                    if (Memory.readFloat(this.walkingSpeedAddr) != Player.m_walkingSpeed && cheatStatus.walkingSpeed == 0) {
                        Memory.writeFloat(this.walkingSpeedAddr, 200);
                    }
                    if (cheatStatus.walkingSpeed == 1) {
                        Memory.writeFloat(this.walkingSpeedAddr, Player.m_walkingSpeed);
                    }
                }
            });

""")

0x03 传送

快速移动有助于探索更大的地图区域,但在地图的其他地点闪现的能力更酷。再次搜索我们的demangled函数:
mothra@kaiju:~/holydays|⇒  cat demangled.txt| grep -i position
_ZN11ServerWorld23SendActorPositionEventsEP6Player - ServerWorld::SendActorPositionEvents(Player*)
_ZN6Player25ShouldSendPositionUpdatesEv - Player::ShouldSendPositionUpdates()
_ZN5Actor28ShouldReceivePositionUpdatesEv - Actor::ShouldReceivePositionUpdates()
_ZN7AIActor25ShouldSendPositionUpdatesEv - AIActor::ShouldSendPositionUpdates()
_ZN5Actor28SetRemotePositionAndRotationERK7Vector3RK8Rotation - Actor::SetRemotePositionAndRotation(Vector3 const&, Rotation const&)
_ZN20GameServerConnection26OnPositionAndVelocityEventEP6Player - GameServerConnection::OnPositionAndVelocityEvent(Player*)
_ZN5Actor11GetPositionEv - Actor::GetPosition()
_ZN4Drop25ShouldSendPositionUpdatesEv - Drop::ShouldSendPositionUpdates()
_ZN6Player28ShouldReceivePositionUpdatesEv - Player::ShouldReceivePositionUpdates()
_ZN10LocalWorld23SendActorPositionEventsEP6Player - LocalWorld::SendActorPositionEvents(Player*)
_ZN6Player15GetLookPositionEv - Player::GetLookPosition()
_ZN20GameServerConnection15OnPositionEventEP6Player - GameServerConnection::OnPositionEvent(Player*)
_ZN5Actor15GetLookPositionEv - Actor::GetLookPosition()
_ZN11ClientWorld23SendActorPositionEventsEP6Player - ClientWorld::SendActorPositionEvents(Player*)
_ZN20GameServerConnection21OnPlayerPositionEventEP6Player - GameServerConnection::OnPlayerPositionEvent(Player*)
_ZN5Actor21GetProjectilePositionEv - Actor::GetProjectilePosition()
_ZN10Projectile25ShouldSendPositionUpdatesEv - Projectile::ShouldSendPositionUpdates()
_ZN5Actor25ShouldSendPositionUpdatesEv - Actor::ShouldSendPositionUpdates()
_ZN5Actor25InterpolateRemotePositionEf - Actor::InterpolateRemotePosition(float)
_ZN7AIActor28ShouldReceivePositionUpdatesEv - AIActor::ShouldReceivePositionUpdates()
_ZN5Actor11SetPositionERK7Vector3 - Actor::SetPosition(Vector3 const&)

一个令人激动的SetPosition出现了!其中有一个Vector3参数,是x,y和z轴的坐标,所以这个SetPosition是我们传送的关键。

在Frida中,我们可以通过NativeFunction调用二进制文件内的函数。我们需要知道:
  • 想要调用的函数的地址
  • 返回类型
  • 参数和类型
我们需要将指针传递给this和我们的Vector3。第一点要求很容易解决:当我们调用“!tp”命令时,只需从聊天的hook中将其取出。为了解决第二个要求,我们用Frida分配一个小缓冲区,其中我们将用x,y和z的信息写入浮点数,然后将指针和此缓冲区传递给SetPosition。
//Teleport
        var setPositionAddr = Module.findExportByName("libGameLogic.so", "_ZN5Actor11SetPositionERK7Vector3");
        var setPosition = new NativeFunction(setPositionAddr, 'void', ['pointer', 'pointer']);
        var Vector3 = Memory.alloc(16);

        function teleport(thisReference, x, y, z) {
            Memory.writeFloat(Vector3, x);
            Memory.writeFloat(ptr(Vector3).add(4), y);
            Memory.writeFloat(ptr(Vector3).add(8), z);
            setPosition(thisReference, Vector3);
        }

通过调用Memory.alloc(SIZE)可以很容易分配缓冲区。 然后通过Memory.writeFloat,我们输入所需坐标(x, y, z)的值,最后我们调用该函数。整个脚本,包括聊天解析,应该如下所示:
 // Global Values
        var Player = {
            m_walkingSpeed : 200,
        };

        // Cheat status
        var cheatStatus = {
            walkingSpeed : 0,
        };

        //Teleport
        var setPositionAddr = Module.findExportByName("libGameLogic.so", "_ZN5Actor11SetPositionERK7Vector3");
        var setPosition = new NativeFunction(setPositionAddr, 'void', ['pointer', 'pointer']);
        var Vector3 = Memory.alloc(16);

        function teleport(thisReference, x, y, z) {
            Memory.writeFloat(Vector3, x);
            Memory.writeFloat(ptr(Vector3).add(4), y);
            Memory.writeFloat(ptr(Vector3).add(8), z);
            setPosition(thisReference, Vector3);
        }


        // Chat Helper
        function chatHelper(msg, thisReference) {
            var token = msg.split(" ");
            if (token[0] === "!wspeed_on") {
                Player.m_walkingSpeed = parseInt(token[1]);
                cheatStatus.walkingSpeed = 1;
                console.log("[CHEAT]: Walking Speed Enabled (" + token[1] + ")");
            }
            if (token[0] === "!wspeed_off") {
                Player.m_walkingSpeed = 200;
                cheatStatus.walkingSpeed = 0;
                console.log("[CHEAT]: Walking Speed Disabled (200)");
            }
            if (token[0] === "!tp") {
                console.log("[CHEAT]: Teleporting to " + token[1] + " " + token[2] + " "+ token[3]);
                teleport(thisReference, parseInt(token[1]), parseInt(token[2]), parseInt(token[3]));
         }
        }


        //Find "Player::Chat"
        var chat = Module.findExportByName("libGameLogic.so", "_ZN6Player4ChatEPKc");
        console.log("Player::Chat() at  address: " + chat);

        // Add our logger
        Interceptor.attach(chat, {
            onEnter: function (args) { // 0 => this; 1 => cont char* (our text)
               var chatMsg = Memory.readCString(args[1]);
               console.log("[Chat]: " + chatMsg);
               chatHelper(chatMsg, args[0]);
            }

        });

        // Find Player::GetWalkingSpeed()
        var walkSpeed = Module.findExportByName("libGameLogic.so", "_ZN6Player15GetWalkingSpeedEv");
        console.log("Player::GetWalkingSpeed() at address: " + walkSpeed);

        // Check Speed
        Interceptor.attach(walkSpeed,
            {
                // Get Player * this location
                onEnter: function (args) {
                    //console.log("Player at address: " + args[0]);
                    this.walkingSpeedAddr = ptr(args[0]).add(736) // Offset m_walkingSpeed
                    //console.log("WalkingSpeed at address: " + this.walkingSpeedAddr);
                },

                // Get the return value
                onLeave: function (retval) {
                    if (Memory.readFloat(this.walkingSpeedAddr) != Player.m_walkingSpeed && cheatStatus.walkingSpeed == 0) {
                        Memory.writeFloat(this.walkingSpeedAddr, 200);
                    }
                    if (cheatStatus.walkingSpeed == 1) {
                        Memory.writeFloat(this.walkingSpeedAddr, Player.m_walkingSpeed);
                    }
                }
            });

0x04 从天而降的魔法

更新于2018年7月8日:服务端会做魔法值检测,所以我这里失败了。事与愿违,我们只设置了HUD中的值:(

如果你走向太阳的方向(在海上),你会在某处找到一个有奶牛的岛屿,之后会触发一个任务。在不做手脚的情况下,这个岛上的一个NPC会给你一个武器。 这个武器会消耗魔法,我们不喜欢浪费魔法(即使会快速再生)。我们希望魔法值始终处于最大值!
mothra@kaiju:~/holydays|⇒  cat Player.class| grep -i mana
    int32_t m_mana;
    float m_manaRegenTimer;
    virtual int32_t GetMana();
    virtual bool UseMana(int32_t);
    void PerformSetMana(int32_t);

魔法值与上文的移动速度情况类似,因此我们的操作方式也是相同的。计算m_mana的偏移量,然后hook GetMana将魔法值覆盖为100:
mothra@kaiju:~/holydays|⇒  gdb -p  $(pidof PwnAdventure3-Linux-Shipping) --batch 
\ -ex "set verbose off" -ex "b _ZN6Player15GetWalkingSpeedEv" --ex "c" --ex "print &this->m_mana"
\ -ex "print this" -ex "print (int)\$1-(int)\$2" -ex "quit" 2>/dev/null | awk '/\$3/ {print $3 }'

544

Hook GetMana:
var getMana = Module.findExportByName("libGameLogic.so", "_ZN6Player7GetManaEv");
        console.log("Player::GetMana at address: " + getMana);
        Interceptor.attach(getMana,
        {
            onEnter: function (args) {
                if (cheatStatus.infiniteMana == 1) {
                    m_manaAddr = ptr(args[0]).add(544) // Offset m_mana
                    Memory.writeInt(m_manaAddr, 100);
                }
            }
        }
        );

火力全开,魔法值不再减少。

0x05 写在最后

使用Frida在这个游戏中作弊只是一个有趣的方式来解释有关这个框架的基本概念。如果你知道更好的方法(或如何优化代码),请随时在Twitter上联系我@TheXC3LL。最终的作弊代码参见在我的GitHub中的PwnAdventure3-cheat.py

如果您发现拼写错误或其他错误,请与我联系:P。

原文链接:https://x-c3ll.github.io/posts/Frida-Pwn-Adventure-3/

编译:看雪翻译小组 SpearMint



[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

最后于 2018-8-13 19:49 被SpearMint编辑 ,原因:
收藏
免费 1
打赏
分享
最新回复 (5)
雪    币: 2176
活跃值: (1170)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
SpearMint 2018-8-13 20:02
2
0
可能有的同学不清楚HUD是什么,百度百科的解释是:

HUD,抬头显视设备。这是一个从军事领域起源的技术,可以把一些重要的战术信息显示在正常观察方向的视野范围内,而同时又不会影响对于环境的注意,也不用总是转移视线去专门观察仪表板上的那些指针和数据。游戏借鉴了这个概念,把游戏相关的信息以类似HUD的方式显示在游戏画面上,让玩家可以随时了解那些最重要最直接相关的内容。


最后于 2018-8-13 20:10 被SpearMint编辑 ,原因:
雪    币: 62
活跃值: (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一首凉凉送给 2018-8-14 14:10
3
0
嗯 挺喜欢看雪的 但是感觉现在有点凉
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
开花的水管 2018-8-27 15:27
4
0
感谢分享!
雪    币: 1080
活跃值: (1802)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
茅山小僧 2018-8-29 16:14
5
0
麻烦请教个问题,折腾好久:
frida -U xx后,我想读取和改写固定地址数据:
Memory.readFloat(ptr("0x1040b1cd0")) ,得到值:1
但是当我改写数据时:Memory.writeFloat(ptr("0x1040b1cd0"), 1),一直显示“undefined”,查了好多资料也没找到例子,看LZ这么写的Memory.writeFloat(address, value),好像没问题啊
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Savoor 2018-9-10 10:43
6
0
感谢分享 
游客
登录 | 注册 方可回帖
返回