Jump to content




Advanced function replacement


59 replies to this topic

#1 ElvishJerricco

  • Members
  • 803 posts

Posted 01 August 2012 - 12:11 PM

This tutorial will explain an advanced technique that should not be used by most programs except alternate operating systems that don't want to mess with the source code of bios.lua or the other built in APIs.

I've always wanted to create my own Computercraft OS without having to modify bios.lua and other files. The problem with that though, is that then I can't change any of the default behavior. The solution however, turned out to be fairly simple, for those of us with a better understanding of how lua works.

function test()
	print("testing!")
end

test()

The code above will simply print "testing!" correct? But what's actually happening in the code here?
  • We declare the function test()
  • We put a body in test()
  • We end, and thus finish, test()
  • We call test
There's something interesting about step 4 though. We're not simply calling a function. We're giving the location of the function with the "test" token, and telling lua to run the function stored there by including the brackets. What this means is that we can reassign the value of test to another function. Take a look at this

function test()
	print("testing 1!")
end

function testAgain()
	print("testing 2!")
end

test = testAgain
test()

Traditionally, you might expect to get an error on the "test = testAgain" line. But in fact, you're simply telling lua that the value of test is now the value of testAgain. So when you call test, you're pointing to testAgain, and testAgain gets run instead, outputting "testing 2!"

So now we can get ourselves into some advanced, dangerous trickery. Let's try messing with the systems APIs.

function test(side)
	print("I've stolen a function! Sorry " .. side .. " side, I'm not gonna read you")
	return false
end

redstone.getInput = test
truth = redstone.getInput("left")


Here we've chosen redstone.getInput as our "victim." We've replaced it with test! So when you call redstone.getInput("left"), you get the return of false and the output "I've stolen a function! Sorry left side, I'm not gonna read you". Now why exactly is this dangerous? Well it turns out, after your program exits, redstone.getInput keeps pointing to your function. So the next guy's program who tries to call redstone.getInput() is going to be out of luck. He's only going to get false, no matter what, because your function always returns false.

But there's some good in this technique, I swear! Let's imagine I were writing an OS without rewriting any of dan200's files. I've come across a problem that people's programs are printing on the screen when I don't want them to be able to print. I can fix this with function replacement!

function myPrint(msg)
	-- calculate condition
	condition = conditionFindingFunction()
	
	-- compare to see if printing is allowed
	if condition then
		--call the backup of print() that get's made before this function is called
		printBackup(msg)
	end
end

-- backup print to printBackup so that we don't lose the original print
printBackup = print

-- replace print with myPrint
print = myPrint
print("Testing!")

Here we do a more advanced set of replacements. We've got a function called printBackup. We never even declared it as a function, but we can still set its value to print. This ensures that as long as we don't lose or change printBackup(), we won't lose the original print(). Then we set print's value to our own custom function myPrint(). In here we calculate the condition to see if printing is currently allowed, throw it in an if statement, and call the printBackup that was made before this function was ever called. So now there's a system wide change to the print function.

One more application of this, again pertaining to the print function, would be forcing print to print into the text windows an OS might use. A lot of OSes for computercraft create a box out of text and call it a window. They might also make use of some class system (which uses tables to store functions and variables) to have objects represent those windows. Here, we'll assume both of those things are true. Let's try to force print to have different parameters and print out to the window.

function myPrint(msg, window)
	if window ~= nil then
		window:write(msg)
	end
end

print = myPrint

It's important to note here that we're assuming window is a standard window object, which is just a table full of functions and variables. Tables can store functions and you can access them using standard table.function() notation, but if you use a colon instead of a period (as shown here), it will automatically pass in the table object as an argument to the function, and if the function was declared with the colon notation, you can use the self keyword to reference that table passed in an invisible parameter. Otherwise you'd have to accept the table as a normal parameter.

Anyways. So we first check if window is nil. If it is, the programmer was trying to use print the regular way; by using print(msg). There's no specified window so we'll just throw it out and ignore the message entirely. (In a real OS, I wouldn't do this. I'd specify a specific place in which these messages would be logged or printed, but we're not going to spend that much time on this) If the window is not nil, we call the function write(msg) stored in the window object to print the message onto the window however the function specifies it to be printed. Notice that we didn't even back up the old print function. It's practically useless now. Windows are going to be using a custom write function that deals with the term API, and printing is supposed to bring them to our function. So there's no need for the old function.

EDIT: New application I just figured out (with the idea coming from KaoS). What if you want a password protected computer? Well as we all know, ctrl-T will close your password program. Now there's a very well known way around this that prevents the computer from ever terminating a program using ctrl-T.

os.pullEvent = os.pullEventRaw

Most people probably just copied this code into their password programs and forgot about it. But as we now know, it's a function replacement! You're replacing os.pullEvent with os.pullEventRaw. So now whenever ctrl-T is pressed, nothing happens. But This brings up a new problem. Once past your password protection, you can't use ctrl-T to terminate programs that you actually wan s to terminate. The solution to this might be obvious to many by now. But here's my implementation.

pullBackup = os.pullEvent
os.pullEvent = os.pullEventRaw

print("Enter password")
pass = read("*")

if pass ~= "password" then
	os.reboot()
else
	os.pullEvent = pullBackup
end

First, we backup os.pullEvent. Then we replace it with os.pullEventRaw so that any call to os.pullEvent actually calls os.pullEventRaw, which doesn't automatically handle the ctrl-T events. After that we use pretty standard procedure to read a password and reboot if what was read is not the password or finish the program if it is. But we do something a little different. We set os.pullEvent to pullBackup. This replaces os.pullEvent with the old one so that whenever programs call it in the future, it'll call the original os.pullEvent, which allows for ctrl-T to work..



I hope you've learned something here. I was just thinking about how you can set functions in javascript and wondered if it could be done in lua too. Luckily it can. This could become a wildly important feature of some OSes, but unfortunately a wildly dangerous feature in some malicious programs. I won't explain those though, as I want as low a chance of malicious code as possible. Either way, I'm glad you read this whole thing. Have fun with it. Happy crafting!

#2 Pharap

  • Members
  • 816 posts
  • LocationEngland

Posted 06 August 2012 - 08:22 AM

I'm surprised this doesn't have any more replies, this could come in handy.
I suppose most people think it's blasphemy lol

#3 Xhisor

  • New Members
  • 37 posts
  • LocationSweden

Posted 06 August 2012 - 08:27 AM

I lost it after the second code, brb going to learn some more LUA.

#4 acters124

  • Members
  • 21 posts

Posted 06 August 2012 - 09:27 AM

lost me with "condition = conditionFindingFunction()" xD
I need to learn more then .:P/>

#5 wilcomega

  • Members
  • 466 posts
  • LocationHolland

Posted 06 August 2012 - 03:45 PM

nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>

#6 Pharap

  • Members
  • 816 posts
  • LocationEngland

Posted 06 August 2012 - 04:27 PM

View Postwilcomega, on 06 August 2012 - 03:45 PM, said:

nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

#7 ElvishJerricco

  • Members
  • 803 posts

Posted 07 August 2012 - 05:48 AM

View PostPharap, on 06 August 2012 - 04:27 PM, said:

View Postwilcomega, on 06 August 2012 - 03:45 PM, said:

nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

Actually (as i'm pretty sure i mentioned in the post) I was surprised to find that replacing a function in your program replaces it for the entire system until you reboot. I'm pretty sure it's because you're messing with global variables. The reason your functions usually get unloaded is because there's no reference to them in the global table once your program ends, so Lua's garbage collection gets rid of them. But if you replace os.shutdown with your function, the global table now has a reference to your function. And if you backed up the original os.shutdown, your function has a reference to that! So neither get unloaded. Funky stuff, right? Anyways so once something's in the global table (in this case, our custom os.shutdown) it'll remain there until the global table is reloaded. And the only time the global table can be reloaded is when the OS reboots.

So the point is, the os.shutdown hack could theoretically work. You'd just have to write the download code (which shouldn't be too hard anyways)

#8 ElvishJerricco

  • Members
  • 803 posts

Posted 07 August 2012 - 05:55 AM

View Postacters124, on 06 August 2012 - 09:27 AM, said:

lost me with "condition = conditionFindingFunction()" xD
I need to learn more then .:P/>

conditionFindingFunction is just supposed to be some random dummy function that I never coded. It's purpose would have been to determine if printing should be allowed at that time or not. Again, I never coded that function, but it is assumed that in context, that function would have been made

#9 wilcomega

  • Members
  • 466 posts
  • LocationHolland

Posted 07 August 2012 - 02:06 PM

View PostElvishJerricco, on 07 August 2012 - 05:48 AM, said:

View PostPharap, on 06 August 2012 - 04:27 PM, said:

View Postwilcomega, on 06 August 2012 - 03:45 PM, said:

nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

Actually (as i'm pretty sure i mentioned in the post) I was surprised to find that replacing a function in your program replaces it for the entire system until you reboot. I'm pretty sure it's because you're messing with global variables. The reason your functions usually get unloaded is because there's no reference to them in the global table once your program ends, so Lua's garbage collection gets rid of them. But if you replace os.shutdown with your function, the global table now has a reference to your function. And if you backed up the original os.shutdown, your function has a reference to that! So neither get unloaded. Funky stuff, right? Anyways so once something's in the global table (in this case, our custom os.shutdown) it'll remain there until the global table is reloaded. And the only time the global table can be reloaded is when the OS reboots.

So the point is, the os.shutdown hack could theoretically work. You'd just have to write the download code (which shouldn't be too hard anyways)

i guess you are right. lol. its just http.get stuff and then content readall() and just put it there. the only way to detect it is with a costum boot disk that the startup program doesnt get executed. btw i am trying to get 50 posts because i want to be a script kiddie as title. btw i am way better that the title script kiddie discribes. :(/>

#10 Pharap

  • Members
  • 816 posts
  • LocationEngland

Posted 08 August 2012 - 03:05 AM

View PostElvishJerricco, on 07 August 2012 - 05:48 AM, said:

View PostPharap, on 06 August 2012 - 04:27 PM, said:

View Postwilcomega, on 06 August 2012 - 03:45 PM, said:

nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer ;)/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time :(/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

Actually (as i'm pretty sure i mentioned in the post) I was surprised to find that replacing a function in your program replaces it for the entire system until you reboot. I'm pretty sure it's because you're messing with global variables. The reason your functions usually get unloaded is because there's no reference to them in the global table once your program ends, so Lua's garbage collection gets rid of them. But if you replace os.shutdown with your function, the global table now has a reference to your function. And if you backed up the original os.shutdown, your function has a reference to that! So neither get unloaded. Funky stuff, right? Anyways so once something's in the global table (in this case, our custom os.shutdown) it'll remain there until the global table is reloaded. And the only time the global table can be reloaded is when the OS reboots.

So the point is, the os.shutdown hack could theoretically work. You'd just have to write the download code (which shouldn't be too hard anyways)

I stopped reading after the list and just read the code since I thought you were probably just explaining what the code does for those less versed in the programming arts.

#11 KaoS

    Diabolical Coder

  • Members
  • 1,510 posts
  • LocationThat dark shadow under your bed...

Posted 10 August 2012 - 12:34 PM

if you don't want to break other programs just make it a local change

local print=nil


#12 ElvishJerricco

  • Members
  • 803 posts

Posted 10 August 2012 - 09:22 PM

View PostKaoS, on 10 August 2012 - 12:34 PM, said:

if you don't want to break other programs just make it a local change

local print=nil

Well yea but the point of doing it this way is to mess with the whole system. Otherwise you could very easily just create a local print function that called the original print function. But that's no fun and serves little purpose besides a helper function.

#13 KaoS

    Diabolical Coder

  • Members
  • 1,510 posts
  • LocationThat dark shadow under your bed...

Posted 13 August 2012 - 01:11 PM

what I mean is that for example in your password function you swop os.pullEvent with os.PullEventRaw but make it a local change so when your password accepting program ends everything works as normal

#14 ElvishJerricco

  • Members
  • 803 posts

Posted 13 August 2012 - 08:07 PM

View PostKaoS, on 13 August 2012 - 01:11 PM, said:

what I mean is that for example in your password function you swop os.pullEvent with os.PullEventRaw but make it a local change so when your password accepting program ends everything works as normal

Unfortunately that's not actually the case. If you create a local os.pullEvent() in your program, it is not available to anything but your program. This means APIs and standard system functions don't see it as the existing os.pullEvent(). So in fact, they will still call the standard os.pullEvent. There is, however, a way to do what you're describing.

pullBackup = os.pullEvent
os.pullEvent = os.pullEventRaw

print("Enter password")
pass = read("*")

if pass ~= "password" then
	os.reboot()
else
	os.pullEvent = pullBackup
end

What this does should be fairly obvious by now. Backup the original pullEvent, read a password, if it isn't right reboot. Else revert pullEvent to the backup and that's the end of the program.

This is actually a really good application of this technique. I'm gonna put it in my tutorial now.

#15 KaoS

    Diabolical Coder

  • Members
  • 1,510 posts
  • LocationThat dark shadow under your bed...

Posted 16 August 2012 - 08:07 AM

I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure

#16 ElvishJerricco

  • Members
  • 803 posts

Posted 16 August 2012 - 11:29 PM

View PostKaoS, on 16 August 2012 - 08:07 AM, said:

I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure

Local variables cannot be referenced outside their scope. But anything in the same scope as them can reference them. And as long as there's any thing that references any variable (even local ones), that variable stays in memory. And since you're calling a function that's in the same scope as the variable, it will access them and keep them in memory.

#17 immibis

    Lua God

  • Members
  • 1,033 posts
  • LocationWellington, New Zealand

Posted 17 August 2012 - 08:08 AM

You should probably mention that functions are values (technically "first class objects") in Lua. That's why
function foo()
print("Hi!")
end
and
foo = function()
print("Hi!")
end
are exactly equivalent (in fact, the first gets translated into the second when Lua compiles it)

View PostElvishJerricco, on 01 August 2012 - 12:11 PM, said:

There's something interesting about step 4 though. We're not simply calling a function. We're giving the location of the function with the "test" token,
You are reading the value from the variable "test", and calling it. There is nothing special about variables that happen to contain functions.


View PostElvishJerricco, on 01 August 2012 - 12:11 PM, said:

function myPrint(msg)
	-- calculate condition
	condition = conditionFindingFunction()
	
	-- compare to see if printing is allowed
	if condition then
		--call the backup of print() that get's made before this function is called
		printBackup(msg)
	end
end

-- backup print to printBackup so that we don't lose the original print
printBackup = print

-- replace print with myPrint
print = myPrint
print("Testing!")
If printBackup was local (to the file, or some other scope) then people couldn't just call printBackup to get around your restriction.

View PostElvishJerricco, on 01 August 2012 - 12:11 PM, said:

For some reason when calling a function stored in a table, you have to use a colon instead of a period before the key. I think it's because a period would ask for the value (for a function, the location of the function).

Wrong. Because functions are just values, you can get a function from a table the same way you get any other element in the table. window.print(msg) works fine, if the window object is programmed to work that way. However, in Lua it's very common for functions in tables to have the table itself as the first parameter, as there's no implicit "this" parameter like in Java.
window:print("hi") is just a shortcut for writing window.print(window, msg).

View PostKaoS, on 16 August 2012 - 08:07 AM, said:

I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure
I'm guessing you mean something like
local os.pullEvent = os.pullEventRaw
Not only will that not work, it's a syntax error.

#18 KaoS

    Diabolical Coder

  • Members
  • 1,510 posts
  • LocationThat dark shadow under your bed...

Posted 17 August 2012 - 03:56 PM

How is that a syntax error?

#19 Cranium

    Ninja Scripter

  • Moderators
  • 4,031 posts
  • LocationLincoln, Nebraska

Posted 17 August 2012 - 04:11 PM

View PostKaoS, on 17 August 2012 - 03:56 PM, said:

How is that a syntax error?
It might be the lack of () in that line...

#20 KaoS

    Diabolical Coder

  • Members
  • 1,510 posts
  • LocationThat dark shadow under your bed...

Posted 17 August 2012 - 04:18 PM

there should be no () as we are not calling the function, we are changing it





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users