6 ms·
Writing a Self-Mutating x86_64 C Program (2013)
- belter 1y agoI guess in OpenBSD because of W ^ X this would not work?
- akdas 1y agoI was thinking the same thing. Usually, you'd want to write the new code to a page that you mark as read and write, then switch that page to read and execute. This becomes tricky if the code that's doing the modifying is in the same page as the code being modified.
- timewizard 1y agoThe way it's coded it wouldn't; however, you can map the same shared memory twice. Once with R|W and a second time with R|X. Then you can write into one region and execute out of it's mirrored mapping.
- rkeene2 1y agoIn Linux it also needs mprotect() to change the permissions on the page so it can write it. The OpenBSD man page[0] indicate that it supports this as well, though notes that not all implementations are guaranteed to allow it, but my guess is it would generally work. [0] https://man.openbsd.org/mprotect.2 https://man.openbsd.org/mprotect.2
- Retr0id 1y agoIt's not required on linux, if the ELF headers are set up such that the page is mapped rwx to begin with. (but rwx mappings are generally frowned upon from a security perspective)
- mananaysiempre 1y agoNot as is, but I think OpenBSD permits you to map the same memory twice, once as W and once as X (which would be a reasonable hoop to jump through for JITs etc., except there’s no portable way to do it). ARM64 MacOS doesn’t even permit that, and you need to use OS-specific incantations[1] that essentially prohibit two JITs coexisting in the same process. [1] https://developer.apple.com/documentation/apple-silicon/porting-just-in-time-compilers-to-apple-silicon https://developer.apple.com/documentation/apple-silicon/port...
- saagarjha 1y agoNo, the protection is per-thread. You can run the JITs in different threads
- alcover 1y agoI often think this could maybe allow fantastic runtime optimisations. I realise this would be hardly debuggable but still..
- Retr0id 1y agoIt already does, in the form of JIT compilation.
- alcover 1y agoOK but I meant in already native code, like in a C program - no bytecode.
- Retr0id 1y agoI mean that, too.
- connicpu 1y agoLuaJIT has a wonderful dynamic code generation system in the form of the DynASM[1] library. You can use it separately from LuaJIT for dynamic runtime code generation to create machine code optimized for a particular problem. [1]: https://luajit.org/dynasm.html https://luajit.org/dynasm.html
- lmm 1y agoIf you are generating or modifying code at runtime then how is that different from bytecode? Standardised bytecodes and JITs are just an organised way of doing the same thing.
- vbezhenar 1y agoI used GNU lightning library once for such optimisation. I think it was ICFPC 2006 task. I had to write an interpreter for virtual machine. Naive approach worked but was slow, so I decided to speed it up a bit using JIT. It wasn't a 100% JIT, I think I just implemented it for loops but it was enough to tremendously speed it up.
- 1y ago
- oxcabe 1y agoIt's impressive how well laid out the content in this article is. The spacing, tables, and code segments all look pristine to me, which is especially helpful given how dense and technical the content is.
- AStonesThrow 1y agoIt was designed by Elves on Christmas Island where Dwarves run the servers and Hobbits operate the power plant
- f1shy 1y agoI have the suspicion that there is a high correlation between how organized the content is, and how organized and clear the mind of the writer is.
- ivanjermakov 1y agoI had a great experience writing self modified programs is a single instruction programming game SIC-1: https://store.steampowered.com/app/2124440/SIC1/ https://store.steampowered.com/app/2124440/SIC1/
- ycombinatrix 1y agoCool recommendation, will give it a try.
- fadfsdfaes 1y ago[dead]
- Someone 1y agoFun article, but the resulting code is extremely brittle: - assumes x86_64 - makes the invalid assumption that functions get compiled into a contiguous range of bytes (I’m not aware of any compiler that violates that, but especially with profile-guided optimization or compilers that try to minimize program size, that may not be true, and there is nothing in the standard that guarantees it) - assumes (as the article acknowledges) that “to determine the length of foo(), we added an empty function, bar(), that immediately follows foo(). By subtracting the address of bar() from foo() we can determine the length in bytes of foo().”. Even simple “all functions align at cache lines” slightly violates that, and I can see a compiler or a linker move the otherwise unused bar away from foo for various reasons. - makes assumptions about the OS it is running on. - makes assumptions about the instructions that its source code gets compiled into. For example, in the original example, a sufficiently smart compiler could compile void foo(void) { int i=0; i++; printf("i: %d\n", i); } as void foo(void) { printf("1\n"); } or maybe even void foo(void) { puts("1"); } Changing compiler flags can already break this program. Also, why does this example work without flushing the instruction cache after modifying the code?
- nekitamo 1y agoFor the mainstream OSes (Windows, OSX, Linux Android) You don't need to flush the instruction cache on most x86 CPUs after modifying the code segment dynamically, but you do on ARM and MIPS. This has burned me before while writing a binary packer for Android.
- saagarjha 1y agoThey check all those assumptions by disassembling the code.
- Cloudef 1y ago> self-modifying code > brittle I mean that is to be very much expected, unless someone comes up with a programming language that fully embraces the concept.
- znpy 1y agoThe author clearly explained that the whole article is more a demonstration for illustrative purposes than anything else. > Changing compiler flags can already break this program. That's not the point of the article.
- xixixao 1y agoI’ve been thinking a lot about this topic lately, even studying how executables look on arm macOS. My motivation was exploring truly fast incremental compilation for native code. The only way to do this now on macOS is remapping whole pages as JIT. This makes it quite a challenge but still it might work…
- Cloudef 1y agoKaze Emanuar's "Optimizing with Bad Code" video also goes briefly go through self-modifying code https://www.youtube.com/watch?v=4LiP39gJuqE https://www.youtube.com/watch?v=4LiP39gJuqE
- pfdietz 1y agoA program that can generate, compile, and execute new code is nothing special in the Common Lisp world. One can build lambda expressions, invoke the compile function on them, and call the resulting compiled functions. One can even assign these functions to the symbol-function slot of symbols, allowing them to be called from pre-existing code that had been making calls to that function named by that symbol.
- BenjiWiebe 1y agoI know that no other language can match Lisp, but many languages can generate and execute new code, if they're interpreted. Compile, too, if they're JITted. They all require quite a bit of runtime support though.
- sfdoiaojfias 1y ago[flagged]
- fdsafkas 1y ago[dead]
- DrZhvago 1y agoSomeone correct me if I am wrong, but self-mutating code is not as uncommon as the author portrays it. I thought the whole idea of hotspot optimization in a compiler is essentially self-mutating code. Also, I spent a moderately successful internship at Microsoft working on dynamic assemblies. I never got deep enough into that to fully understand when and how customers where actually using it. https://learn.microsoft.com/en-us/dotnet/fundamentals/reflection/emitting-dynamic-methods-and-assemblies https://learn.microsoft.com/en-us/dotnet/fundamentals/reflec...
- iamcreasy 1y agoIs it possible to mutate the text segment by another process? For example, injecting something malicious instead of exec-ing a shell?