comeback commit!

This commit is contained in:
2024-12-15 21:21:44 +03:00
parent b093674b6e
commit 947ba44245
30 changed files with 793 additions and 57 deletions
+1
View File
@@ -96,6 +96,7 @@ dependencies {
implementation(libs.ui.graphics)
implementation(libs.ui.tooling.preview)
implementation(libs.material3)
implementation(project(mapOf("path" to ":mpcore")))
testImplementation(libs.junit.v412)
//testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
@@ -24,6 +24,7 @@ import com.ea.EAMIO.StorageDirectory
import com.ea.easp.EASPHandler
import com.ea.games.nfs13_na.BuildConfig
import com.ea.nimble.ApplicationLifecycle
import ru.megboyzz.mpcore.NativeLib
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
@@ -42,6 +43,7 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
init {
System.loadLibrary("nimble")
System.loadLibrary("app")
System.loadLibrary("mpcore")
}
private var splashDelay: Long = 0
Binary file not shown.
@@ -1,6 +1,6 @@
package com.megboyzz.devmenu.domain.entities
enum class GameLang(private val lang: String) {
enum class GameLang(val lang: String) {
System("sys"),
Chinese("cn"),
Dutch("nl"),
@@ -0,0 +1,14 @@
package com.megboyzz.devmenu.domain.usecases.settings
import com.megboyzz.devmenu.domain.entities.GameLang
import com.megboyzz.devmenu.domain.repository.SettingsRepository
import javax.inject.Inject
class GetGameLangUseCase @Inject constructor(
private val settingsRepository: SettingsRepository
) {
operator fun invoke() = settingsRepository.gameLang
}
@@ -0,0 +1,13 @@
package com.megboyzz.devmenu.domain.usecases.settings
import com.megboyzz.devmenu.domain.entities.TrackingData
import com.megboyzz.devmenu.domain.repository.TrackingRepository
import javax.inject.Inject
class GetTrackingStateUseCase @Inject constructor(
private val trackingRepository: TrackingRepository
) {
operator fun invoke() = trackingRepository.trackingData
}
@@ -8,6 +8,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import cafe.adriel.voyager.transitions.SlideTransition
import com.megboyzz.devmenu.ui.components.MainScaffold
import com.megboyzz.devmenu.ui.screens.HomeScreen
@@ -39,7 +40,9 @@ class MainActivity : ComponentActivity() {
BottomSheetNavigator(
sheetShape = RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp)
) {
Navigator(HomeScreen(::runGame))
Navigator(HomeScreen(::runGame)) {
SlideTransition(it)
}
}
}
}
@@ -3,6 +3,8 @@ package com.megboyzz.devmenu.di.components
import android.content.Context
import com.megboyzz.devmenu.di.modules.DatabaseModule
import com.megboyzz.devmenu.di.modules.RepositoryModule
import com.megboyzz.devmenu.domain.usecases.settings.GetGameLangUseCase
import com.megboyzz.devmenu.domain.usecases.settings.GetTrackingStateUseCase
import com.megboyzz.devmenu.domain.usecases.settings.RemoveGameDataUseCase
import com.megboyzz.devmenu.domain.usecases.settings.SetGameLangUseCase
import com.megboyzz.devmenu.domain.usecases.settings.SetTrackingSaveFileUseCase
@@ -24,8 +26,12 @@ interface SettingsUseCaseComponent {
val setGameLangUseCase: SetGameLangUseCase
val getGameLangUseCase: GetGameLangUseCase
val setTrackingSaveFileUseCase: SetTrackingSaveFileUseCase
val getTrackingStateUseCase: GetTrackingStateUseCase
val turnOffDevMenuUseCase: TurnOffDevMenuUseCase
@Component.Builder
@@ -29,7 +29,7 @@ class SettingsRepositoryImpl @Inject constructor(
if(!localeFile.exists()){
localeFile.createNewFile()
localeFile.writeBytes(byteArrayOf(0x02, 0x0) + systemLang.name.toByteArray())
localeFile.writeBytes(byteArrayOf(0x02, 0x0) + systemLang.lang.toByteArray())
return systemLang
}
@@ -40,7 +40,7 @@ class SettingsRepositoryImpl @Inject constructor(
if(!localeFile.exists()) localeFile.createNewFile()
localeFile.writeBytes(byteArrayOf(0x02, 0x0) + value.name.toByteArray())
localeFile.writeBytes(byteArrayOf(0x02, 0x0) + value.lang.toByteArray())
}
@@ -29,7 +29,7 @@ class TrackingRepositoryImpl @Inject constructor(
override var trackingData: TrackingData
get() {
val enabled = sharedPreferences.getBoolean(trackingStateKey, false)
val path = sharedPreferences.getString(trackingStateKey, defaultPathToSave)?: defaultPathToSave
val path = sharedPreferences.getString(trackingSavePath, defaultPathToSave)?: defaultPathToSave
return TrackingData(
enabled = enabled,
@@ -1,14 +1,19 @@
package com.megboyzz.devmenu.ui
import android.annotation.SuppressLint
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import com.megboyzz.devmenu.MainActivity
import java.text.CharacterIterator
import java.text.SimpleDateFormat
import java.text.StringCharacterIterator
@@ -46,7 +51,39 @@ fun humanReadableByteCountSI(bytes: Long): String {
fun formatDate(date: Date) = SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date)
fun Context.getInternalAppDataDirectory() = "/data/data/${packageName}"
fun Context.getExternalAppDataDirectory(): String {
val externalDir = getExternalFilesDir(null)
return externalDir?.absolutePath ?: ""
}
@Composable
fun Context.registerOnBackPressedAction(
action: () -> Unit
) {
val context = this
DisposableEffect(this) {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
action()
}
}
(context as ComponentActivity).onBackPressedDispatcher.addCallback(callback)
// Отменяем обратный вызов при уничтожении DisposableEffect
onDispose {
callback.remove()
}
}
}
/**
* ## Неработает((
*
* Возвращает лямбду того типа которая требуется в Composable контексте<br>
* например есть функция
*
@@ -62,5 +99,4 @@ fun formatDate(date: Date) = SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date
*
* example(a = SomeType().asComposableLambda)
*/
val <T> T.asComposableLambda: @Composable () -> T
get() = { this }
val <T> T.asComposableLambda: @Composable () -> T get() = { this }
@@ -6,6 +6,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.megboyzz.devmenu.ui.models.FsElementModel
@@ -13,7 +14,7 @@ import com.megboyzz.devmenu.ui.models.FsElementModel
fun FsElement(
fsElementEntity: FsElementModel,
onClick: () -> Unit,
onLongPressClick: () -> Unit
onLongPressClick: () -> Unit = {}
) {
Column(
@@ -25,6 +26,9 @@ fun FsElement(
)
) {
FsElementIcon(type = fsElementEntity.type)
Text(text = fsElementEntity.name)
Text(
text = fsElementEntity.name,
textAlign = TextAlign.Center
)
}
}
@@ -1,7 +1,6 @@
package com.megboyzz.devmenu.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
@@ -39,10 +38,20 @@ fun CoreBar(folders: List<String>) {
@Composable
fun StatusBarInCurFolder(
path: String
path: String,
internalPath: String = "",
externalPath: String = ""
) {
if(path.isEmpty()) return
val pathAsArray = path.split("/").filter { it.isNotEmpty() }
var pathProcess = path
if(internalPath.isNotEmpty() && pathProcess.contains(internalPath))
pathProcess = path.replace(internalPath, "Внутренее хранилище")
if(externalPath.isNotEmpty() && pathProcess.contains(externalPath))
pathProcess = path.replace(externalPath, "Внешнее хранилище")
val pathAsArray = pathProcess.split("/").filter { it.isNotEmpty() }
CoreBar(folders = pathAsArray)
}
@@ -4,6 +4,8 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -33,6 +35,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.megboyzz.devmenu.ui.SpacerHeight
import com.megboyzz.devmenu.ui.theme.DevMenuTheme
@OptIn(ExperimentalMaterial3Api::class)
@@ -42,7 +45,7 @@ fun MainScaffold(
searchValue: String,
onSearchValueChanged: (String) -> Unit,
onFloatingButtonClick: () -> Unit,
content: @Composable (PaddingValues) -> Unit
content: @Composable ColumnScope.() -> Unit
) {
var searchValue by remember { mutableStateOf(searchValue) }
@@ -66,11 +69,13 @@ fun MainScaffold(
shape = CircleShape,
content = {Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null)}
)
},
content = content
)
}
){
Column {
SpacerHeight(height = it.calculateTopPadding())
content()
}
}
}
@@ -1,5 +1,7 @@
package com.megboyzz.devmenu.ui.models
import com.megboyzz.devmenu.domain.entities.GameLang
enum class GameLangModel(private val lang: String) {
System("sys"),
Chinese("cn"),
@@ -16,4 +18,8 @@ enum class GameLangModel(private val lang: String) {
override fun toString() = lang
}
fun toDomain() = GameLang.values()[ordinal]
}
fun GameLang.toModel() = GameLangModel.values()[ordinal]
@@ -0,0 +1,125 @@
package com.megboyzz.devmenu.ui.screenmodels
import android.content.Context
import android.util.Log
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.megboyzz.devmenu.ui.components.ElementType
import com.megboyzz.devmenu.ui.getExternalAppDataDirectory
import com.megboyzz.devmenu.ui.getInternalAppDataDirectory
import com.megboyzz.devmenu.ui.models.FsElementModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import java.io.File
import java.util.Date
class HomeScreenModel(
private val context: Context
): ScreenModel {
private val TAG = this::class.simpleName
private val internalRef = FsElementModel(
"Внутренне хранилище",
ElementType.isDir,
0,
Date()
)
private val externalRef = FsElementModel(
"Внешнее хранилище",
ElementType.isDir,
0,
Date()
)
private val rootFs = listOf(
internalRef,
externalRef
)
val currentFolder = MutableStateFlow("")
val folderFiles = MutableStateFlow(listOf(internalRef, externalRef))
private fun isRoot() = currentFolder.value.isEmpty()
private fun getElements(path: String): List<FsElementModel>{
val here = File(path)
return if(here.exists()){
here.listFiles()!!.map {
FsElementModel(
name = it.name,
type = if(it.isFile) ElementType.isFile else ElementType.isDir,
size = it.length(),
date = Date(it.lastModified())
)
}
}else listOf()
}
fun enterRoot(){
screenModelScope.launch(Dispatchers.IO) {
currentFolder.emit("")
folderFiles.emit(rootFs)
}
}
fun enterFolder(elementModel: FsElementModel) {
if(isRoot()){
when(elementModel){
externalRef -> enterTheExternal()
internalRef -> enterTheInternal()
else -> Log.e(TAG, "enterFolder: error path! ${elementModel.name}")
}
}else{
screenModelScope.launch(Dispatchers.IO) {
currentFolder.emit(currentFolder.value + "/" + elementModel.name)
folderFiles.emit(getElements(currentFolder.value))
}
}
}
fun backFolder(){
val path = File(currentFolder.value)
val internal = context.getInternalAppDataDirectory()
val external = context.getExternalAppDataDirectory()
if(path.exists()){
screenModelScope.launch(Dispatchers.IO) {
when(currentFolder.value){
context.getInternalAppDataDirectory(), context.getExternalAppDataDirectory() -> enterRoot()
else -> {
currentFolder.emit(path.parentFile.absolutePath)
folderFiles.emit(getElements(currentFolder.value))
}
}
}
}
}
private fun enterTheInternal(){
screenModelScope.launch(Dispatchers.IO) {
currentFolder.emit(context.getInternalAppDataDirectory())
folderFiles.emit(getElements(currentFolder.value))
}
}
private fun enterTheExternal(){
screenModelScope.launch(Dispatchers.IO) {
currentFolder.emit(context.getExternalAppDataDirectory())
folderFiles.emit(getElements(currentFolder.value))
}
}
}
@@ -0,0 +1,62 @@
package com.megboyzz.devmenu.ui.screenmodels
import android.content.Context
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.megboyzz.devmenu.App
import com.megboyzz.devmenu.MainActivity
import com.megboyzz.devmenu.ui.models.GameLangModel
import com.megboyzz.devmenu.ui.models.toModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class SettingsScreenModel(
context: Context
): ScreenModel {
private val settingsUseCaseComponent = (((context as MainActivity).application) as App).settingsUseCaseComponent
val gameLangModel = MutableStateFlow(settingsUseCaseComponent.getGameLangUseCase().toModel())
val trackingIsEnabled = MutableStateFlow(false)
init {
screenModelScope.launch(Dispatchers.IO) {
update()
}
}
private suspend fun update() {
gameLangModel.emit(settingsUseCaseComponent.getGameLangUseCase().toModel())
trackingIsEnabled.emit(settingsUseCaseComponent.getTrackingStateUseCase().enabled)
}
fun changeLang(gameLangModel: GameLangModel){
screenModelScope.launch(Dispatchers.IO) {
settingsUseCaseComponent.setGameLangUseCase(gameLangModel.toDomain())
update()
}
}
fun changeTrackingState(state: Boolean) {
screenModelScope.launch(Dispatchers.IO) {
val trackingData = settingsUseCaseComponent.getTrackingStateUseCase()
trackingData.copy(enabled = state)
settingsUseCaseComponent.setTrackingSaveFileUseCase(trackingData.enabled, trackingData.pathToSave)
update()
}
}
fun changeTrackingPath(path: String) {
screenModelScope.launch(Dispatchers.IO) {
val state = settingsUseCaseComponent.getTrackingStateUseCase()
state.copy(pathToSave = path)
settingsUseCaseComponent.setTrackingSaveFileUseCase(state.enabled, state.pathToSave)
update()
}
}
}
@@ -2,17 +2,19 @@ package com.megboyzz.devmenu.ui.screens
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen
import com.megboyzz.devmenu.ui.components.ElementType
import com.megboyzz.devmenu.ui.components.FileContextMenu
import com.megboyzz.devmenu.ui.models.FsElementModel
class FileContextMenuScreen(
private val fsElementModel: FsElementModel,
private val onRemoveFile: () -> Unit,
private val onUpdateType: (ElementType) -> ElementType?
) : Screen {
@Composable
override fun Content() = FileContextMenu(
fsElementEntity = fsElementModel,
onRemoveElement = onRemoveFile,
onUpdateType = {null}
onUpdateType = onUpdateType
)
}
@@ -1,61 +1,115 @@
package com.megboyzz.devmenu.ui.screens
import android.annotation.SuppressLint
import androidx.activity.OnBackPressedCallback
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.megboyzz.devmenu.MainActivity
import com.megboyzz.devmenu.ui.SpacerHeight
import com.megboyzz.devmenu.ui.components.ElementType
import com.megboyzz.devmenu.ui.components.FsElement
import com.megboyzz.devmenu.ui.components.MainScaffold
import com.megboyzz.devmenu.ui.components.StatusBarInCurFolder
import com.megboyzz.devmenu.ui.getExternalAppDataDirectory
import com.megboyzz.devmenu.ui.getInternalAppDataDirectory
import com.megboyzz.devmenu.ui.models.FsElementModel
import com.megboyzz.devmenu.ui.registerOnBackPressedAction
import com.megboyzz.devmenu.ui.screenmodels.HomeScreenModel
import java.util.Date
class HomeScreen(
private val onFloatingButtonClick: () -> Unit
): Screen {
@SuppressLint("StateFlowValueCalledInComposition")
@Composable
override fun Content() {
val context = LocalContext.current
val bottomSheetNavigator = LocalBottomSheetNavigator.current
var searchValue by remember{ mutableStateOf("") }
val navigator = LocalNavigator.current
val fsElementModel = FsElementModel(
name = "my_txt.txt",
type = ElementType.isHidden,
size = 123,
date = Date()
)
var searchValue by remember { mutableStateOf("") }
val homeScreenModel = rememberScreenModel { HomeScreenModel(context) }
val fsState by homeScreenModel.folderFiles.collectAsState()
val currentFolder by homeScreenModel.currentFolder.collectAsState()
context.registerOnBackPressedAction {
homeScreenModel.backFolder()
}
MainScaffold(
onSettingsClick = { /*TODO Переход к настройкам */ },
onSettingsClick = { navigator?.push(SettingsScreen()) },
onSearchValueChanged = { searchValue = it },
searchValue = searchValue,
onFloatingButtonClick = onFloatingButtonClick
) {
Column {
SpacerHeight(height = it.calculateTopPadding())
FsElement(
fsElementEntity = fsElementModel,
onClick = { /*TODO*/ },
onLongPressClick = {
bottomSheetNavigator.show(
FileContextMenuScreen(
fsElementModel = fsElementModel,
onRemoveFile = {}
)
)
}
Column(
modifier = Modifier.padding(horizontal = 20.dp)
) {
StatusBarInCurFolder(
path = currentFolder,
externalPath = context.getExternalAppDataDirectory(),
internalPath = context.getInternalAppDataDirectory()
)
}
val filteredList by remember(searchValue, fsState) {
derivedStateOf {
fsState.filter { it.name.contains(searchValue, ignoreCase = true) }
}
}
LazyVerticalGrid(
columns = GridCells.Adaptive(80.dp),
horizontalArrangement = Arrangement.spacedBy(40.dp),
verticalArrangement = Arrangement.spacedBy(40.dp),
contentPadding = PaddingValues(20.dp)
){
items(filteredList){
FsElement(
fsElementEntity = it,
onClick = { homeScreenModel.enterFolder(it) },
onLongPressClick = {
bottomSheetNavigator.show(FileContextMenuScreen(
fsElementModel = it,
onRemoveFile = {},
onUpdateType = {null}
))
}
)
}
}
}
}
@@ -1,11 +1,41 @@
package com.megboyzz.devmenu.ui.screens
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import com.megboyzz.devmenu.ui.components.SettingsScreenContent
import com.megboyzz.devmenu.ui.models.GameLangModel
import com.megboyzz.devmenu.ui.screenmodels.SettingsScreenModel
class SettingsScreen : Screen {
@Composable
override fun Content() {
TODO("Not yet implemented")
val navigator = LocalNavigator.current
val context = LocalContext.current
val settingsScreenModel = rememberScreenModel { SettingsScreenModel(context) }
val curLang by settingsScreenModel.gameLangModel.collectAsState()
val trackingEnabled by settingsScreenModel.trackingIsEnabled.collectAsState()
SettingsScreenContent(
onBackClick = { navigator?.pop() },
langModel = curLang,
onUpdateLanguage = { settingsScreenModel.changeLang(it) },
isTrackingSaveFile = trackingEnabled,
onUpdateTrackingSaveFileState = { settingsScreenModel.changeTrackingState(it) },
onChooseTrackingPathToSave = { },
onUploadSaveFile = { /*TODO*/ },
onDownloadSaveFile = { /*TODO*/ },
onDevMenuOff = {
})
}
}
+5 -1
View File
@@ -6,11 +6,15 @@ plugins {
android {
namespace = "ru.megboyzz.mpcore"
compileSdk = 33
compileSdk = 34
defaultConfig {
minSdk = 21
ndk {
abiFilters.add("armeabi-v7a")
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
externalNativeBuild {
+3 -1
View File
@@ -3,7 +3,9 @@ cmake_minimum_required(VERSION 3.22.1)
project("mpcore")
add_library(${CMAKE_PROJECT_NAME} SHARED
mpcore.cpp)
main.cpp
util/util.cpp
util/armhook.cpp)
target_link_libraries(${CMAKE_PROJECT_NAME}
android
+73
View File
@@ -0,0 +1,73 @@
#include <cstring>
#include <cstdio>
#include <android/log.h>
#include <jni.h>
#include <thread>
#include <chrono>
#include "main.h"
#include "util/util.h"
#include "util/armhook.h"
uintptr_t libapp = 0;
int (*print_internal_log)(int a1, const char *a2, ...);
void pending_thread(){
Log("Hello from thread!");
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
int unk = APP_ADDR(0x00E4B8EC);
print_internal_log(unk, "Hello!");
unProtect(APP_ADDR(0x00E4A738));
}
jint JNI_OnLoad(JavaVM *vm, void *reserved){
libapp = FindLibrary("libapp.so");
Log("Hello! my base addr is 0x%08x", libapp);
//InitHookStuff();
//unProtect(APP_ADDR(0x00A522C4));
//*(int *)APP_ADDR(0x00A522C4) = 0xE320F000;
//WriteMemory(APP_ADDR(0x00E4BA94), (uintptr_t)"my_tag", 6);
raise(SIGSTOP);
return JNI_VERSION_1_6;
}
/*
Log("Hello! my base addr is 0x%08x", libapp);
Log("Address i wanna to get 0x%08x", APP_ADDR(0xC8C9D8));
const char* str = (char *)APP_ADDR(0xC8C9D8);
Log("i found it! %s", str);
unProtect(APP_ADDR(0xC8C9D8));
WriteMemory(APP_ADDR(0xC8C9D8), (uintptr_t)"APPLICATION_NO", 14);
str = (char *)APP_ADDR(0xC8C9D8);
Log("i change it! %s", str);
//makeNOP(APP_ADDR(0x00652EB4), 2);
//makeNOP(APP_ADDR(0x00652E3C), 2);
unProtect(APP_ADDR(0x00652EB4));
*(char *)APP_ADDR(0x00652EB4) = 0xE320F000;
unProtect(APP_ADDR(0x00652E3C));
*(char *)APP_ADDR(0x00652E3C) = 0xE320F000;
*/
+6
View File
@@ -0,0 +1,6 @@
//Основная ссылка на начало основного процесса - libapp.so
extern uintptr_t libapp;
//Макрос для ссылок внутри памяти процесса
#define APP_ADDR(addr) (libapp + (addr))
-10
View File
@@ -1,10 +0,0 @@
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_ru_megboyzz_mpcore_NativeLib_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
+198
View File
@@ -0,0 +1,198 @@
#include <unistd.h> // system api
#include <assert.h> // assert()
#include <dlfcn.h> // dlopen
#include <cerrno>
#include <cstring>
#include <exception>
#include "armhook.h"
#include "util.h"
#include "../main.h"
uintptr_t mmap_start = 0;
uintptr_t mmap_end = 0;
uintptr_t memlib_start = 0;
uintptr_t memlib_end = 0;
//#define PAGE_SIZE 0x8124
#define HOOK_PROC "\x01\xB4\x01\xB4\x01\x48\x01\x90\x01\xBD\x00\xBF\x00\x00\x00\x00"
void unProtect(uintptr_t ptr, size_t dwSize)
{
if(mprotect(reinterpret_cast<void*>(ptr & 0xFFFFF000), PAGE_SIZE, PROT_WRITE | PROT_READ | PROT_EXEC) == -1)
{
const auto err = errno;
if(err == EACCES)
{
mprotect(reinterpret_cast<void*>(ptr & 0xFFFFF000), PAGE_SIZE, PROT_WRITE | PROT_READ);
return;
}
}
}
void makeNOP(uintptr_t addr, unsigned int count)
{
Log("Hello from makeNOP!");
unProtect(addr);
for(uintptr_t ptr = addr; ptr != (addr+(count * 2)); ptr += 2)
{
Log("Hello from loop makeNOP!");
*(char*)ptr = 0x00;
*(char*)(ptr+1) = 0x46;
}
cacheflush(addr, (uintptr_t)(addr + count * 2), 0);
}
void WriteMemory(uintptr_t dest, uintptr_t src, size_t size)
{
unProtect(dest);
memcpy((void*)dest, (void*)src, size);
cacheflush(dest, dest+size, 0);
}
void WriteMemory(uintptr_t dest, void* src, size_t size)
{
unProtect(dest);
memcpy((void*)dest, src, size);
cacheflush(dest, dest+size, 0);
}
void SetPointer(uintptr_t addr, void* value)
{
WriteMemory(addr, &value, sizeof(value));
}
void ReadMemory(uintptr_t dest, uintptr_t src, size_t size)
{
unProtect(src);
memcpy((void*)dest, (void*)src, size);
}
void InitHookStuff()
{
memlib_start = APP_ADDR(0x000A1B44);
memlib_end = memlib_start + 0x1A36;
mmap_start = (uintptr_t)mmap(0, PAGE_SIZE, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
int code = mprotect((void*)(mmap_start & 0xFFFFF000), PAGE_SIZE, PROT_READ | PROT_EXEC | PROT_WRITE);
mmap_end = (mmap_start + PAGE_SIZE);
/*const auto err = errno;
Log("Status is %d && %d", err, code);
Log("mmap_stat = 0x%08x", mmap_start);
Log("mmap_end = 0x%08x", mmap_end);
Log("memlib_start = 0x%08x", memlib_start);
Log("memlib_end = 0x%08x", memlib_end);*/
//unProtect(APP_ADDR(0x1800AC));
}
void makeJMP(uintptr_t func, uintptr_t addr)
{
uint32_t code = ((addr - func - 4) >> 12) & 0x7FF | 0xF000 | ((((addr - func - 4) >> 1) & 0x7FF | 0xB800) << 16);
WriteMemory(func, (uintptr_t)&code, 4);
}
void makeBLX(uintptr_t func, uintptr_t addr)
{
uint32_t code = ((addr - func - 4) >> 12) & 0x7FF | ((addr - func - 4) << 15) | 0xF800F000;
WriteMemory(func, (uintptr_t)&code, 4);
}
void WriteHookProc(uintptr_t addr, uintptr_t func)
{
char code[16];
memcpy(code, HOOK_PROC, 16);
*(uint32_t*)&code[12] = (func | 1);
WriteMemory(addr, (uintptr_t)code, 16);
}
void InstallBLXHook(uintptr_t addr, uintptr_t func)
{
// memlib_start = 0
// memlib_end = 10
if(memlib_end < (memlib_start + 0x10) || mmap_end < (mmap_start + 0x20))
{
std::terminate();
}
makeBLX(addr, func);
WriteHookProc(addr, func);
memlib_start += 16;
}
void InstallJMPHook(uintptr_t addr, uintptr_t func)
{
if(memlib_end < (memlib_start + 0x10) || mmap_end < (mmap_start + 0x20))
{
std::terminate();
}
makeJMP(addr, func);
WriteHookProc(addr, func);
memlib_start += 16;
}
void InstallHook(uintptr_t addr, uintptr_t func, uintptr_t *orig)
{
if(memlib_end < (memlib_start + 0x10) || mmap_end < (mmap_start + 0x20))
{
Log("FAILED!");
std::terminate();
}
ReadMemory(mmap_start, addr, 4);
WriteHookProc(mmap_start+4, addr+4);
*orig = mmap_start+1;
mmap_start += 32;
makeJMP(addr, memlib_start);
WriteHookProc(memlib_start, func);
memlib_start += 16;
}
void RedirectCall(uintptr_t addr, uintptr_t func)
{
makeJMP(addr, memlib_start);
WriteHookProc(memlib_start, func);
memlib_start += 16;
}
void InstallMethodHook(uintptr_t addr, uintptr_t func)
{
unProtect(addr);
*(uintptr_t*)addr = func;
}
void CodeInject(uintptr_t addr, uintptr_t func, int reg)
{
char injectCode[12];
injectCode[0] = 0x01;
injectCode[1] = 0xA0 + reg;
injectCode[2] = (0x08 * reg) + reg;
injectCode[3] = 0x68;
injectCode[4] = 0x87 + (0x08 * reg);
injectCode[5] = 0x46;
injectCode[6] = injectCode[4];
injectCode[7] = injectCode[5];
*(uintptr_t*)&injectCode[8] = func;
WriteMemory(addr, (uintptr_t)injectCode, 12);
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <sys/mman.h>
extern uintptr_t mmap_start;
extern uintptr_t mmap_end;
extern uintptr_t memlib_start;
extern uintptr_t memlib_end;
void unProtect(uintptr_t ptr, size_t dwSize = 100);
void WriteMemory(uintptr_t dest, uintptr_t src, size_t size);
void ReadMemory(uintptr_t dest, uintptr_t src, size_t size);
void makeNOP(uintptr_t addr, unsigned int count);
void makeJMP(uintptr_t func, uintptr_t addr);
void makeBLX(uintptr_t func, uintptr_t addr);
void InitHookStuff();
void InstallHook(uintptr_t addr, uintptr_t func, uintptr_t *orig);
void InstallMethodHook(uintptr_t addr, uintptr_t func);
void InstallJMPHook(uintptr_t addr, uintptr_t func);
void InstallBLXHook(uintptr_t addr, uintptr_t func);
void CodeInject(uintptr_t addr, uintptr_t func, int reg);
void WriteMemory(uintptr_t dest, void* src, size_t size);
void SetPointer(uintptr_t addr, void* value);
template <typename Ret, typename... Args>
static Ret CallFunction(unsigned int address, Args... args)
{
return reinterpret_cast<Ret(__cdecl *)(Args...)>(address)(args...);
}
+52
View File
@@ -0,0 +1,52 @@
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <android/log.h>
#include "util.h"
uintptr_t FindLibrary(const char *lib_name) {
char filename[0xFF] = {0},
buffer[2048] = {0};
FILE *fp = 0;
uintptr_t address = 0;
sprintf(filename, "/proc/%d/maps", getpid());
fp = fopen(filename, "rt");
if (fp == nullptr) {
Log("ERROR: can't open file %s", filename);
goto done;
}
while (fgets(buffer, sizeof(buffer), fp)) {
if (strstr(buffer, lib_name)) {
address = (uintptr_t) strtoul(buffer, 0, 16);
break;
}
}
done:
if (fp)
fclose(fp);
return address;
}
void Log(const char *fmt, ...) {
char buffer[0xFF];
static FILE *flLog = nullptr;
memset(buffer, 0, sizeof(buffer));
va_list arg;
va_start(arg, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, arg);
va_end(arg);
__android_log_write(ANDROID_LOG_INFO, "mpcore_log", buffer);
if (flLog == nullptr) return;
fprintf(flLog, "%s\n", buffer);
fflush(flLog);
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
uintptr_t FindLibrary(const char* lib_name);
void Log(const char *fmt, ...);
@@ -2,11 +2,10 @@ package ru.megboyzz.mpcore
class NativeLib {
external fun stringFromJNI(): String
companion object {
init {
System.loadLibrary("mpcore")
//System.loadLibrary("mpcore")
}
}
}