Skip to content

Latest commit

 

History

History
311 lines (232 loc) · 13 KB

LUAHELP.md

File metadata and controls

311 lines (232 loc) · 13 KB

Examples of LUA / LUAJ with XPL-EX

-- To Start the Hook you either need to Specify a "before" or "after" Function with params "(hook, param)"
-- If what you are Hooking is a Field then the Function will have to be a "after" Hook do not use a "before" Hook
-- To Specify Strings you can use double quotes or Single quotes
-- Lets say for example we are Hooking a "toString" Function

-- First define the function , either as a "before" or "after" with its params 

-- We use a "before" Hook if we want to Modify the Target Function Behaviour BEFORE it Executes the Original Function Code
function before(hook, param)
	log("Hello World, the before hook for toString has been Invoked")
	-- add code here
end


-- We use an "after" Hook when we want to Modify the Target Function Behaviour AFTER it Executes
-- Use this if you want to get the Result of the Return value of the Hook Function if it Has a return Value
function after(hook, param) 
	log("Hello World, the after hook for toString has been Invoked")
	--add code here
end

BASICS

-- To Log we simply use the "log" function
-- Do note LUA if a Fast Scripting language , bit faster than Java ;)
-- The version of LUA Is 5.2 (Version right behind the one that supports BIG INT / LONG)
-- With that we get to use the LUA Basic Functions such as "math", "tostring" , "tonumber"
-- If we want to interact with the JAVA Environment we simply use "luajava", following with either "bindClass" or "newInstance"

function before(hook, param)
	log("Before Hook is Invoked")

	-- lets create a Java "File" object
	-- First lets create the LUA Object that points to that Class
	local fileClass = luajava.bindClass("java.io.File")
	-- We specify the Full length to the class including class name
	-- From here we can interact with the class obj using ":"
	-- Lets create a TEMP File

	log("Creating Temp File!")

	local tempFile = fileClass:createTempFile("temp", null)
	-- 'null' can be used but in the context of Java
	-- 'nil' is used in the Context of LUA

	local path = tempFile:getPath();
	log("Temp File was Created, Path is: " .. path)

	-- To Concatinate strings in lua you use ".."
	-- As you see to Interact with the Return value of 'java.io.File' object 'tempFile' we use ":" , "tempFile:getPath()"
	-- If we want to access a JAVA Field we dont need to use ":" instead we can go back to using "."
	-- If Java "java.io.File" Class has a Public Field called 'filePath' we can then do "local path = tempFile.filePath"
	-- In LUA you dont end code with a semi colon ";"
end

PARAM Usage

-- Now we got the Basics out the way how LUA works / LUAJ / Java with LUA on a Basic Level
-- Lets get into the parameters/ arguments that each "before" and "after" Function is defined with (hook, param)
-- These two Params will be our Bridge between LUA and our JAVA API to give eas of access to functions at your need
-- "hook" Param will store just information on the actual hook definition it self
-- "param" Param will store all the cool utils to use, and as well as the Return Value of the Hooked Function as well as Argument Values inputed into the Hook Function
-- Lets set the return Value of a "toString" Function Hook, if its defined properly within the UI the LUA Code can look something like the following

function after(hook, param) 
	log("The toString Before Hook has been Invoked!")
	-- lets get the result of the 'toString' Function return

	local res = param:getResult()
	-- using the 'param' arg we have a function in the API that we can use called 'getResult'

	if res == nil then 
		return false
	end

	log("Original Return result=" .. res)

	-- We did a few things, since the object has been lifted to LUA layer to check if its NULL we use NIL
	-- Its best to always check what you are using if its NIL, good code practicies avoid crashing when you try to access a 'NULL'/'NIL' Object

	-- Logging is not needed but is a good practice to understand what is going on if things do not work
	-- Lets now actually Modify the return value
	param:setResult("Fake to string return, Hahaha Hello World, Spoofed!")

	-- using 'param' API we get to set the Result of the Hook function using 'setResult'
	-- Make sure the Value your setting it to is the same return value of the Hooked Function
	-- Once we do that we now have to Notify the Actual Hook Instance itself that the return value and or function was modified
	-- To do so we return 'true' else if no modifications were made to the function either 'result' or changing the hooked function 'argument' values then return false
	-- Since we modifed the Return Value/Result we return from this LUA Function with 'true'
	-- With '== nil' check above we return 'false' since result is NULL we will not modify the function there for return 'false'
	return true
end

Lifting objects from the JAVA Layer to LUA can sometimes get 'broken' or mis translated, either from JAVA to LUA or LUA back to JAVA To Handle this issue Ive added Functions in the API that can set the result of the Hooked function to a few specific types (ones Ive had issues with translation)

   void setResult(java.lang.Object)
   void setResultString(java.lang.String)
   void setResultByteArray([B or BYTE[])
-- Only needed when your hook gives a expected result/return value error , aka Type Mis Match
-- It will say something like 'got UserData instead of X Type'
-- Lets now Modify the Arguments going into the Hooked Function
-- Example we will use is 'java.io.File(java.lang.String)' Function "File f = new File("/sdcard/filePath/fileName");"
-- We will specifically filter for the "/proc/cupinfo" File

-- Define the LUA Function
function before(hook, param) 
	log("File is being Opened")
	-- to get the paramter passed into the Hook Function we use the API Function 'getArgument(int index)'
	local filePath = param:getArgument(0) -- 0 Since Index of the String File Path Param is (1) aka (0) starting from (0) 
	-- If you dont know why (0) then Google Array Indexes or something :P
	-- Do Note LUA Arrays do start on index (0), but since its out API Function that lays in the JAVA layer it will start at index (0)
	-- Check if its NULL
	if filePath == nil then
		log("Hook Constructor File Path Param is NULL! Skipping....")
		return false
	end

	log("File that is being opened=" .. filePath) -- filePath is a 'java.lang.String' , first Arg in the Constructor of 'java.io.File'

	-- to do a string check you can use '==' or I like to use (works most times) is LUA 'string.match'

	if string.match(filePath, "cpuinfo$") then
		-- In the string check we add at the end a '$' to indicate that the string 'cpuinfo' is at the end of the string your comparing with
		-- Lets now create a Empty file
		local fileClass = luajava.bindClass("java.io.File")
		local tempFile = fileClass:createTempFile("temp", null) -- Since we are passing it down to the JAVA layer we will use 'null' instead of 'nil'

		if tempFile ~= nil then
			log("Replacing File open from: " .. filePath .. " to => " .. tempFile:getPath())
			-- "~=" is not equals or "!="
			-- Instead of replacing the return value this time we will replace the argument of the "File" Function to point to our fake created file
			-- We use the API Function 'setArgument(int index, Object value)'
			-- Index being the Index of the Argument you want to replace

			param:setArgument(0, tempFile:getPath()) -- Out Empty Created File in the temp dir of the App being Hooked
			-- Since we modified the the Function we will return True
			return true
		end
	end

	return false -- Make sure to always return, false if no modifications, true if function was modfied via args or result/return value
end

Just like 'setResult' we have different helper functions for 'setArgument' incase the type is mismatched / didnt translate between the two languages

   void setArgument(java.lang.Object)
   void setArgumentString(java.lang.String)
-- When returning from the Script we can add more return values after the 'true' or 'false'
-- The Extra values getting returned will be printed to the log cat, make sure your returning string values so it can be printed to logcat
-- Example we modify the Arguments again for the "File" Constructor to point to our fake file

-- Remeber returning 'true' will prevent the Target Hook Function from finishing execution assuming its a 'before' Hook
		-- returning 'false' will allow the Target Hook Function to Finish execution assuming its a 'before' Hook

function before(hook, param) 
	log("File Constructor Hook Invoked")
	local filePath = param:getArgument(0)
	if filePath == nil then
		log("File path arg is NULL skipping...")
		return false
	end

	if string.match(filePath, "/sdcard/WhateverFile") then 
		log("Is Target File: " .. filePath .. " Replacing")
		
		local fileClass = luajava.bindClass("java.io.File")
		local fake = fileClass:createTempFile("temp", null)

		log("Fake File path: " .. fake)
		--param:setResult(fake) we can do this replacing the orignal File Object this the Fake File Object
		param:setArgument(0, fake:getPath())

		return true, filePath, fake:getPath()
		-- Doing this first will tell the Hook, yes the Target Hook Function was Modified
		-- Then the following Return Values will be printed out to the log so you can see the changes
		-- This also means for alot of scripts no need to log assuming its simple logic, nothing too complex
	end

	return false
end

LUAJ newInstance

-- The LUAJ Function 'newInstance' is simple, first arg will be the full path to the class including name
-- Then the Following Arguments will be passed down to the object as args to use to create the new Instance
-- Lets create a new instance of a File object , Java defination something like "File(java.lang.String filePath)"
 
 function before(hook, param) 
 	log("I am in some Hooked Function ahahahaha")
 	local fileClass = luajava.bindClass("java.io.File")
 	-- can do 
 	local fakeFile1 = luajava.newInstance(fileClass, "/sdcard/SomeCoolFile")
 	-- or
 	local fakeFile2 = luajava.newInstance("java.io.File", "/sdcard/SomeCoolFile")
 	-- Dont mistake the 'newInstance' function from Java Reflect Array
 	return false
 end

 -- One thing this LUA version does not support is BIG INTs or LONG Values
 -- Once a LONG Value is lifted from the JAVA layer to the LUA layer it will be translated into a DECIMAL
 -- It does not matter how you Handle it, once it touches the LUA Layer it will be converted into a different type
 -- If you try to convert it back to a LONG it will Fail, simply put it LUA does not support LONG
 -- To work with this, we Convert the LONG value into a String BUT before it touches the LUA layer becuase once again it will be converted
 -- Ive made a few Helper Functions to "work" with this issue
 -- Lets work with one structure that has LONG Fields "ActivityManager.MemoryInfo" , Specific Function "ActivityManager.getMemoryInfo(MemoryInfo)"

 -- [MemoryInfo]
 -- long advertisedMem;
 -- long availMem;
 -- long lowMemory;
 -- long threshold;
 -- long totalMem;

 function after(hook, param)
 	local memInfo = param:getArgument(0)
 	if memInfo == nil then
 		return false
 	end

 	local longClass = luajava.bindClass("java.lang.Long")
 	local strgClass = luajava.bindClass("java.lang.String")

 	-- this wont work
 	local bad1 = memInfo.availMem
 	local bad2 = longClass:parseLong(bad1) -- This wont help, once the LONG gets lifted from the JAVA layer to LUA its back to Decimal
 	local bad3 = luajava.newInstance(longClass, 9999999999)
 	local bad4 = strgClass:valueOf(memInfo.availMem) 
 	-- Once again it will have to lift the Field result from the Struct to the LUA layer
 	-- Sure your passing it into a JAVA Function but again has to touch the LUA layer , then messes up the Type

 	-- What you CAN do, using our API in this case we use: getFieldLong(Object objectInstance, String fieldName) returns a String representing a LONG
 	-- public String getFieldLong(Object instance, String fieldName)

 	local good = param:getFieldLong(memInfo, "availMem")
 	-- Now to set the LONG Field
 	-- public void setFieldLong(Object instance, String fieldName, String longValue) 

 	param:setFieldLong(memInfo, "availMem", "99999999999")

 	-- We substitue by using a String, then in the JAVA layer convert the String to a LONG
 	-- We use a Mix of things with Reflection
 	-- Within the Future we should Have more APIs to work around the LONG issue

 end

HOOK USAGE API

-- Just like the 'param' arg that gets passed within the hook you also get 'hook' arg that is passed
-- This just Provides information about the Hook

function before(hook, param)
	-- Name of Hook
	local name = hook:getName()
	log("Hook Name: " .. name)
	
	-- Group Name of Hook
	local groupName = hook:getGroup()
	log("Group Name: " .. groupName)

	-- Author Name for the Hook
	local authorName = hook:getAuthor()
	log("Author Name: " .. authorName)

	return false
end