Advanced function replacement
ElvishJerricco 01 Aug 2012
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
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!
Pharap 06 Aug 2012
I suppose most people think it's blasphemy lol
acters124 06 Aug 2012
I need to learn more then ./>
wilcomega 06 Aug 2012
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 />
Pharap 06 Aug 2012
wilcomega, on 06 August 2012 - 03:45 PM, said:
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
ElvishJerricco 07 Aug 2012
Pharap, on 06 August 2012 - 04:27 PM, said:
wilcomega, on 06 August 2012 - 03:45 PM, said:
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)
ElvishJerricco 07 Aug 2012
acters124, on 06 August 2012 - 09:27 AM, said:
I need to learn more then ./>
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
wilcomega 07 Aug 2012
ElvishJerricco, on 07 August 2012 - 05:48 AM, said:
Pharap, on 06 August 2012 - 04:27 PM, said:
wilcomega, on 06 August 2012 - 03:45 PM, said:
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 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. />
Pharap 08 Aug 2012
ElvishJerricco, on 07 August 2012 - 05:48 AM, said:
Pharap, on 06 August 2012 - 04:27 PM, said:
wilcomega, on 06 August 2012 - 03:45 PM, said:
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.
KaoS 10 Aug 2012
local print=nil
ElvishJerricco 10 Aug 2012
KaoS, on 10 August 2012 - 12:34 PM, said:
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.
KaoS 13 Aug 2012
ElvishJerricco 13 Aug 2012
KaoS, on 13 August 2012 - 01:11 PM, said:
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.
KaoS 16 Aug 2012
ElvishJerricco 16 Aug 2012
KaoS, on 16 August 2012 - 08:07 AM, said:
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.
immibis 17 Aug 2012
function foo() print("Hi!") endand
foo = function() print("Hi!") endare exactly equivalent (in fact, the first gets translated into the second when Lua compiles it)
ElvishJerricco, on 01 August 2012 - 12:11 PM, said:
ElvishJerricco, 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!")
ElvishJerricco, on 01 August 2012 - 12:11 PM, said:
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).
KaoS, on 16 August 2012 - 08:07 AM, said:
local os.pullEvent = os.pullEventRawNot only will that not work, it's a syntax error.