mirror of
https://github.com/frida/frida-python
synced 2026-06-08 14:16:17 +00:00
243cce22e6
Which means: - Pass in data to RPC method. - Return value and data from RPC method. Co-authored-by: Håvard Sørbø <havard@hsorbo.no>
172 lines
4.8 KiB
Python
172 lines
4.8 KiB
Python
import subprocess
|
|
import threading
|
|
import time
|
|
import unittest
|
|
|
|
import frida
|
|
|
|
from .data import target_program
|
|
|
|
|
|
class TestRpc(unittest.TestCase):
|
|
target: subprocess.Popen
|
|
session: frida.core.Session
|
|
|
|
@classmethod
|
|
def setUp(cls):
|
|
cls.target = subprocess.Popen([target_program], stdin=subprocess.PIPE)
|
|
# TODO: improve injectors to handle injection into a process that hasn't yet finished initializing
|
|
time.sleep(0.05)
|
|
cls.session = frida.attach(cls.target.pid)
|
|
|
|
@classmethod
|
|
def tearDown(cls):
|
|
cls.session.detach()
|
|
cls.target.terminate()
|
|
cls.target.stdin.close()
|
|
cls.target.wait()
|
|
|
|
def test_basics(self):
|
|
script = self.session.create_script(
|
|
name="test-rpc",
|
|
source="""\
|
|
rpc.exports = {
|
|
add(a, b) {
|
|
const result = a + b;
|
|
if (result < 0)
|
|
throw new Error("No");
|
|
return result;
|
|
},
|
|
sub(a, b) {
|
|
return a - b;
|
|
},
|
|
speak() {
|
|
const buf = Memory.allocUtf8String("Yo");
|
|
return Memory.readByteArray(buf, 2);
|
|
},
|
|
speakWithMetadata() {
|
|
const buf = Memory.allocUtf8String("Yo");
|
|
return ['soft', Memory.readByteArray(buf, 2)];
|
|
},
|
|
processData(val, data) {
|
|
return { val, dump: hexdump(data, { header: false }) };
|
|
},
|
|
};
|
|
""",
|
|
)
|
|
script.load()
|
|
agent = script.exports_sync
|
|
self.assertEqual(agent.add(2, 3), 5)
|
|
self.assertEqual(agent.sub(5, 3), 2)
|
|
self.assertRaises(Exception, lambda: agent.add(1, -2))
|
|
self.assertEqual(agent.speak(), b"\x59\x6f")
|
|
meta, data = agent.speak_with_metadata()
|
|
self.assertEqual(meta, "soft")
|
|
self.assertEqual(data, b"\x59\x6f")
|
|
result = agent.process_data(1337, b"\x13\x37")
|
|
self.assertEqual(result["val"], 1337)
|
|
self.assertEqual(result["dump"], "00000000 13 37 .7")
|
|
|
|
def test_post_failure(self):
|
|
script = self.session.create_script(
|
|
name="test-rpc",
|
|
source="""\
|
|
rpc.exports = {
|
|
init: function () {
|
|
},
|
|
};
|
|
""",
|
|
)
|
|
script.load()
|
|
agent = script.exports_sync
|
|
|
|
self.session.detach()
|
|
self.assertRaisesScriptDestroyed(lambda: agent.init())
|
|
self.assertEqual(script._pending, {})
|
|
|
|
def test_unload_mid_request(self):
|
|
script = self.session.create_script(
|
|
name="test-rpc",
|
|
source="""\
|
|
rpc.exports = {
|
|
waitForever: function () {
|
|
return new Promise(function () {});
|
|
},
|
|
};
|
|
""",
|
|
)
|
|
script.load()
|
|
agent = script.exports_sync
|
|
|
|
def unload_script_after_100ms():
|
|
time.sleep(0.1)
|
|
script.unload()
|
|
|
|
threading.Thread(target=unload_script_after_100ms).start()
|
|
self.assertRaisesScriptDestroyed(lambda: agent.wait_forever())
|
|
self.assertEqual(script._pending, {})
|
|
|
|
def test_detach_mid_request(self):
|
|
script = self.session.create_script(
|
|
name="test-rpc",
|
|
source="""\
|
|
rpc.exports = {
|
|
waitForever: function () {
|
|
return new Promise(function () {});
|
|
},
|
|
};
|
|
""",
|
|
)
|
|
script.load()
|
|
agent = script.exports_sync
|
|
|
|
def terminate_target_after_100ms():
|
|
time.sleep(0.1)
|
|
self.target.terminate()
|
|
|
|
threading.Thread(target=terminate_target_after_100ms).start()
|
|
self.assertRaisesScriptDestroyed(lambda: agent.wait_forever())
|
|
self.assertEqual(script._pending, {})
|
|
|
|
def test_cancellation_mid_request(self):
|
|
script = self.session.create_script(
|
|
name="test-rpc",
|
|
source="""\
|
|
rpc.exports = {
|
|
waitForever: function () {
|
|
return new Promise(function () {});
|
|
},
|
|
};
|
|
""",
|
|
)
|
|
script.load()
|
|
agent = script.exports_sync
|
|
|
|
def cancel_after_100ms():
|
|
time.sleep(0.1)
|
|
cancellable.cancel()
|
|
|
|
cancellable = frida.Cancellable()
|
|
threading.Thread(target=cancel_after_100ms).start()
|
|
self.assertRaisesOperationCancelled(lambda: agent.wait_forever(cancellable=cancellable))
|
|
self.assertEqual(script._pending, {})
|
|
|
|
def call_wait_forever_with_cancellable():
|
|
with cancellable:
|
|
agent.wait_forever()
|
|
|
|
cancellable = frida.Cancellable()
|
|
threading.Thread(target=cancel_after_100ms).start()
|
|
self.assertRaisesOperationCancelled(call_wait_forever_with_cancellable)
|
|
self.assertEqual(script._pending, {})
|
|
|
|
def assertRaisesScriptDestroyed(self, operation):
|
|
self.assertRaisesRegex(frida.InvalidOperationError, "script has been destroyed", operation)
|
|
|
|
def assertRaisesOperationCancelled(self, operation):
|
|
self.assertRaisesRegex(frida.OperationCancelledError, "operation was cancelled", operation)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|