import 'dart:ffi'; import 'dart:io'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // This is the theme of your application. // // Try running your application with "flutter run". You'll see the // application has a blue toolbar. Then, without quitting the app, try // changing the primarySwatch below to Colors.green and then invoke // "hot reload" (press "r" in the console where you ran "flutter run", // or simply save your changes to "hot reload" in a Flutter IDE). // Notice that the counter didn't reset back to zero; the application // is not restarted. primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect // how it looks. // This class is the configuration for the state. It holds the values (in this // case the title) provided by the parent (in this case the App widget) and // used by the build method of the State. Fields in a Widget subclass are // always marked "final". final String title; @override State createState() => _MyHomePageState(); } typedef GetHelloFunc = Pointer Function(); typedef GetHello = Pointer Function(); typedef MmapNative = Pointer Function(Pointer, IntPtr, Int32, Int32, Int32, IntPtr); typedef MmapDart = Pointer Function(Pointer, int, int, int, int, int); final DynamicLibrary libc = DynamicLibrary.open("libc.so"); final dlopen = libc.lookupFunction< Pointer Function(Pointer, Int32), Pointer Function(Pointer, int)>('dlopen'); class _MyHomePageState extends State { int _counter = 0; Future _incrementCounter() async { print("Running on ${Platform.operatingSystem} (${Platform.version}) | IsAndroid: ${Platform.isAndroid}"); ByteData data = await rootBundle.load("assets/files/libnative.so"); var soData = data.buffer.asUint8List(); final int length = soData.length; //this works as quick and dirt way but we want from memory // final String tempLibPath = "${Directory.systemTemp.path}/libnative.so"; // final File libFile = File(tempLibPath); // libFile.createSync(recursive: true); // libFile.writeAsBytesSync(soData, flush: true); // Process.runSync("chmod", ["777", tempLibPath]); // final DynamicLibrary dylib = DynamicLibrary.open(tempLibPath); final MmapDart mmap = libc.lookupFunction("mmap"); const int PROT_READ = 0x1; const int PROT_WRITE = 0x2; const int PROT_EXEC = 0x4; const int MAP_PRIVATE = 0x02; const int MAP_ANONYMOUS = 0x20; final Pointer mem = mmap(nullptr, length, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mem.address == 0) { print("Failed to allocate executable memory!"); return; } final Pointer memBytes = mem.cast(); for (int i = 0; i < length; i++) { memBytes[i] = soData[i]; } Pointer cPath = "/proc/self/mem".toNativeUtf8(); Pointer handle = dlopen(cPath, 2); // RTLD_LAZY malloc.free(cPath); if (handle.address == 0) { print("Failed to load ELF!"); return; } print("Library successfully loaded!"); final DynamicLibrary dylib = DynamicLibrary.process(); final GetHello getHello = dylib.lookupFunction("get_hello"); print(getHello().toDartString()); // Prints "Hello from native C!" setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }