[HN Gopher] You can't copy code with memcpy
       ___________________________________________________________________
        
       You can't copy code with memcpy
        
       Author : hyperrail
       Score  : 396 points
       Date   : 2021-12-29 20:57 UTC (1 days ago)
        
 (HTM) web link (devblogs.microsoft.com)
 (TXT) w3m dump (devblogs.microsoft.com)
        
       | userbinator wrote:
       | I've done stuff like this before; it works very well if you know
       | the limitations, and I'd say that it even gives you a better
       | understanding of how things actually work. Of course, don't
       | bother MS or any other "official" vendor if it doesn't work,
       | because you are on your own in debugging it.
        
         | wyldfire wrote:
         | Well, it should work in some well-controlled cases and in those
         | cases if you're writing this code you are probably an OS
         | vendor. Examples of legit use case: an executable loader or
         | some bootstrapping code/bootloader.
         | 
         | When writing code like this, as Chen says, you are bound to the
         | architectural rules regarding how to appropriately locate code
         | and safely invalidate code caches etc.
         | 
         | So if you follow the rules it doesn't work then typically I
         | figure you'd take it up with the CPU vendor.
         | 
         | While you could potentially do it and have a good reason to in
         | userspace code, it should be heavily scrutinized because it's
         | so unconventional.
        
         | Terry_Roll wrote:
         | >because you are on your own in debugging it.
         | 
         | "The customer reported that this code "worked just fine on
         | 32-bit x86 and 64-bit x86", but it doesn't work on Itanium."
         | 
         | "Actually, I'm surprised that it worked even on x86!"
         | 
         | And that is probably how Spectre and Meltdown were found!
         | 
         | Differences in the cpu's.
        
       | jl2718 wrote:
       | I was competing in the Jump Trading programming competition and
       | thought I had a pretty good implementation in AVX asm, but I was
       | still behind one of their engineers, so I asked him after the
       | competition. Turns out he was a Linux kernel committer and wrote
       | a process to spawn multiple threads by copying itself, modifying
       | the parameters, and then setting the offsets directly in the
       | thread table, avoiding all mallocs and thread startup. So
       | basically, his math code was just basic C loops, but his process
       | was complete before my threads even finished allocation.
       | 
       | Forgive me if I got it wrong, I am definitely not a Linux kernel
       | committer.
        
       | basementcat wrote:
       | The client certainly should have made sure their code was truly
       | position independent.
       | 
       | Also, the client should have embedded their code in the
       | executable file name so they just have to jump to the appropriate
       | offset in argv[0]. This way, future updates just require renaming
       | the file!
        
         | MattPalmer1086 wrote:
         | I imagine they were dynamically building the code to inject, or
         | why bother with the complexity?
        
         | liftm wrote:
         | Why use PIC if you can just write your own relocator?
         | https://fasterthanli.me/series/making-our-own-executable-pac...
        
       | ouid wrote:
       | "No, the opposite of a virus, an anti-virus". Thereby
       | demonstrating that a virus is its own anti-particle.
        
         | watersb wrote:
         | When I read this quote from the customer in the story, I wasn't
         | surprised.
         | 
         | I figured it was likely that code that aggressively scans and
         | modifies other running executables would be written as a
         | kludge, an unorthodox way of abusing the compiler-loader-
         | runtime chain.
        
           | anothernewdude wrote:
           | Kaspersky actually has been known to travel networks.
        
       | fork-bomber wrote:
       | Memcpying and executing code could also surface micro-
       | architectural realities of the underlying CPU and memory
       | subsystem micro-architecture that may need attention from the
       | programmer.
       | 
       | For example:
       | 
       | - On most RISCy Arm CPUs with Harvard style split instruction and
       | data caches special architecture specific actions would need to
       | be taken to ensure that after the memcpy any code still lingering
       | in the data cache was cleaned/pushed out to the intended
       | destination memory immediately (instead of at the next cache line
       | eviction).
       | 
       | - Any stale code that happened to be cached from the destination
       | (either by design or coincidence) needs to be invalidated in the
       | instruction cache.
       | 
       | - Depending on the CPU micro architecture, programmer unknown
       | speculative prefetching into caches as a result of the previous
       | two actions may also need attention.
        
         | oshiar53-0 wrote:
         | Is there anything else that should be done except flushing
         | D-cache and invalidating I-cache? I'm genuinely curious.
        
           | jlokier wrote:
           | If you are asking about general cases:
           | 
           | If using paging you may need to invalidate the TLB entry
           | which contains execute permission for the page.
           | 
           | On x86 if using segments, after changing segment attributes
           | you need to reload the segment selectors.
           | 
           | The execution pipeline may need to be flushed, using a
           | serialising instruction.
           | 
           | When modifying code in place that may be being executed by
           | another thread on another core at the same time, some
           | modifications may trigger CPU errata.
           | 
           | On particular CPUs there may be other kinds of caches or
           | state invalidation required, but hopefully the OS provides a
           | "flush I-cache" function that covers all of them.
        
       | Jyaif wrote:
       | Don't publicly make fun of your customers... unless they are an
       | anti-virus company.
       | 
       | What boggles my mind is how they went on to ask MS for help
       | fixing their obviously wrong vulnerability-and-crash-introducing
       | software.
        
         | Arnavion wrote:
         | Raymond Chen makes fun of customers all the time. He just
         | doesn't name them.
         | 
         | Also, in this particular case the case is old enough that it's
         | not necessarily any AV that's still around anyway.
        
       | [deleted]
        
       | leni536 wrote:
       | Of course all of this is very much undefined behavior in standard
       | C and C++. Some programmers really need to learn that they
       | program the "abstract machine" when they write C or C++.
        
         | plorkyeran wrote:
         | That is probably the smallest and least interesting of the
         | problems with this. Any method for injecting code into another
         | process is inherently going to be platform-specific and outside
         | the bounds of the C++ abstract machine.
        
         | woodruffw wrote:
         | I don't think most of this is actually UB. The cast from a
         | function pointer to `BYTE *` is, but the rest is "sound"
         | (AFAICT) from the C abstract machine's perspective. The reason
         | it fails is basically orthogonal to C.
        
           | Kranar wrote:
           | The reason it fails is due to UB. There's not much point in
           | saying that most of it isn't UB since the part that is UB is
           | the part that causes the failure.
        
             | woodruffw wrote:
             | The cast isn't the part that causes the failure (C says
             | that function pointers don't have to be safely
             | representable within `void *` or any other fundamental
             | pointer type, but they are on both x86(-64) and
             | Itanium[1]).
             | 
             | The failure happens because the programmer assumed that the
             | code is "self contained" and position-independent, both of
             | which are concepts outside of the C abstract machine.
             | 
             | [1]: Function pointers on Itanium are actually fat, but
             | IIRC most compilers hide this by making the "function
             | pointer" point to some kind of thunk instead.
        
               | Kranar wrote:
               | This is the type of reasoning that leads people to write
               | insecure code, by trying to outguess the compiler with
               | implementation details that are entirely inapplicable. It
               | suggests that if the code was self-contained or position
               | independent or satisfied any property whatsoever, that
               | using memcpy would be a perfectly fine way to copy it,
               | that is simply untrue, it would never be safe to use
               | memcpy on pointers to functions under any circumstance.
               | It's irrelevant whether you are on x86, Itanium, or any
               | other platform, undefined behavior WILL result in
               | incorrect and invalid program semantics and can not be
               | relied upon to produce consistent results even within the
               | same execution of the same program.
               | 
               | There's literally decades worth of people trying to use
               | undefined behavior in clever ways and ultimately failing
               | and yet here we are...
        
               | woodruffw wrote:
               | I think you're reading too far into my reasoning. I'm not
               | a huge fan of UB, and I mostly write Rust these days to
               | get away from cheekiness like this.
               | 
               | My only point was that, for better or worse, UB is _not_
               | the culprit in this code. C _could_ have well-defined
               | abstract semantics for copying functions or aliasing
               | function pointers through datatype pointers, and this
               | code would _still_ be platform dependent and would
               | _still_ break on different hosts.
        
               | Kranar wrote:
               | This is also untrue, if the standard did mandate that
               | pointers to functions could participate in a memcpy and
               | the behavior was as if the instructions of that function
               | were treated as an array of chars, then C++ compilers
               | would be forced to accommodate that implementation
               | regardless of what platform it ran on, Itanium, x86, even
               | a PDP-11 it wouldn't matter. For example it might mean
               | that implementations must tag or otherwise store a
               | dictionary to keep track of addresses to functions so as
               | to differentiate pointers to functions from pointers to
               | objects and produce whatever appropriate behavior is
               | needed to copy said instructions. An implementation could
               | keep an intermediate representation of any function that
               | can potentially be memcpy'd and then translate that
               | representation at runtime when it participates in a
               | memcpy. Whatever the case may be, if the standard
               | mandates certain behavior then an implementation is
               | required to respect it. While not entirely the same, C++
               | does do something along these lines in certain situations
               | involving pointers to virtual member functions involved
               | in a diamond inheritance structure.
               | 
               | Saying that C++ (the code in the article is not C and the
               | two languages differ about the treatment of pointers to
               | functions) could have well defined semantics to handle
               | this is entirely moot... it's about as relevant as saying
               | that a C++ program could run on top of the JVM with a
               | garbage collector [1] and hence eliminate all types of
               | memory errors. Even if a C++ program ran on a platform
               | that had guaranteed garbage collection the fact would
               | still remain that C++ as a language does not have well
               | defined semantics for what happens to a dangling
               | reference and as such a C++ compiler is free to exploit
               | that to make very strong assumptions about the runtime
               | behavior of the program for the sake of generating
               | efficient code.
               | 
               | The fact that this is undefined behavior gives a compiler
               | the freedom to perform optimizations under the assumption
               | that runtime behavior will never engender said behavior.
               | Raymond makes use of this property when he discusses
               | COMDAT folding which is a common optimization to elide
               | multiple copies of the same function or to even produce
               | multiple versions of a single function optimized for
               | different scenarios. From the article:
               | 
               | "Even without Profile-Guided Optimization, compile-time
               | optimization may inline some or all of a function, so a
               | single function might have multiple copies in memory,
               | each of which has been optimized for its specific call
               | site."
               | 
               | This property has nothing to do with x86, or Itanium or
               | PDP-11, it's a purely logical optimization permissible
               | only because of the various forms of undefined behavior
               | in C++ with respect to the treatment of function
               | pointers.
               | 
               | [1] http://nestedvm.ibex.org/
        
           | leni536 wrote:
           | From the C abstract machine's perspective it is undefined:
           | 
           | 1. To substract the two function pointers after casting to
           | BYTE*.
           | 
           | 2. To read the bytes through those pointers.
           | 
           | 3. To cast the copied bytes back to function and invoke it.
           | 
           | The article only focuses on 3, and only about how it's
           | undefined because the compiler is not required to generate
           | position independent code. But an optimizing compiler in
           | theory can just optimize the whole function away from looking
           | at the very first undefined line.
           | 
           | The message is that yes, you need to step out of the standard
           | to do stuff like this, but you may have to consult a lot more
           | about your implementation defined behavior than you
           | originally signed up for.
        
         | 10000truths wrote:
         | The standard is pointless when what you're trying to implement
         | is non-portable or architecture-specific by nature. At that
         | point, your compiler implementation and target architecture are
         | what matters.
        
           | alisonkisk wrote:
           | compiler-defined is extremely different from undefined.
        
             | 10000truths wrote:
             | That's the thing, though. What is considered undefined by
             | the standard is NOT necessarily undefined by the compiler
             | or target architecture. Type punning via union is UB in the
             | standard, but well defined in `gcc`. Signed overflow is UB
             | in the standard, but well defined in `gcc` with `-fwrapv`.
             | Casting a void pointer to a function pointer is UB in the
             | standard, but well defined for pretty much every non-MCU
             | target arch (dlsym relies on it, after all!). Dereferencing
             | a runtime-known NULL pointer is UB in the standard, but is
             | well defined to trigger a segfault in pretty much every
             | arch with an MMU. Etcetera, etcetera, etcetera.
        
               | pjmlp wrote:
               | The fun starts when there is a new compiler version in
               | town and that changes.
               | 
               | Even Linus got to enjoy that fun.
        
       | WalterBright wrote:
       | > This code is such a bad idea, I've intentionally introduced
       | errors so it won't even compile.
       | 
       | No problem, I just fixed the compiler to compile it!
        
       | seligman99 wrote:
       | I wrote a "cd" replacement for cmd[1] a _long_ time ago (I only
       | recently uploaded it to Github).
       | 
       | It uses exactly this technique to run a thread in cmd's process
       | to actually change the directory. It's kept working from XP on up
       | to Windows 11 now. I am always amazed it works, I fully expect it
       | to go boom some day, probably with an error along the lines of
       | "Don't do that, please".
       | 
       | [1]
       | https://github.com/seligman/ccd/blob/master/RemoteThread.cpp...
        
         | rightbyte wrote:
         | Terrible hacks is an art form that is hugely underrated today
         | in the name of overengineered best practice complexity
         | monsters. Sometimes, just doing it the stupid way is simpler
         | than doing it properly. Especially when working with
         | propertiary systems ...
        
       | 0xdky wrote:
       | I froze for a moment seeing this article after having worked at a
       | major anti-virus company long time back and used some low level
       | Win32 APIs.
       | 
       | Fortunately, I followed some of the techniques from "Programming
       | Applications for Microsoft Windows" book and Detours project to
       | intercept and execute custom code mostly based on loading custom
       | DLL in target remote process and using DllMain() to execute.
        
       | ggm wrote:
       | Yet, copy-on-write works well in Unix fork/exec() models and
       | helps reduce memory pressure. Presumably, the kernel has a
       | mechanism which presents as logistically simple "copy" but takes
       | care of page/pointer/vm necessity.
        
         | bzbarsky wrote:
         | Position-dependent code can be copied in _physical_ memory as
         | long as the _virtual_ addresses (which is what you observe)
         | don't change.
         | 
         | So yes, the mechanism you posit exists, and its the virtual
         | memory manager.
        
       | mrlonglong wrote:
       | Yes, you can copy code with memcpy as long as it's position
       | independent code!
        
       | SeanLuke wrote:
       | When I took a compiler class back in the early '90s, the project
       | was to write the compiled machine code into an array, then cast
       | the array into a function, and execute it. I and another student
       | were doing it on 68040 NeXT workstations. One other student was
       | doing it on a Mac, one on a VAX, and the rest on PCs (the PC
       | students largely failed!). We were mystified why, when we tried
       | to execute our code, it was as if it wasn't there. Took us a
       | while to realize that the 68040 had separate instruction and data
       | caches, and even more time (and emailing people at NeXT) to
       | determine what the cache flush procedure was.
        
         | rkeene2 wrote:
         | I once saw code like:
         | 
         | unsigned short main[] = {0104525, 0xb8e5, 32, 0, 0xC35D};
         | 
         | and I made a tool to generate it automatically from a function
         | [0].
         | 
         | [0] https://rkeene.org/viewer/tmp/ret32-maker.c.htm
        
           | foota wrote:
           | You may be thinking of
           | https://jroweboy.github.io/c/asm/2015/01/26/when-is-main-
           | not...
        
             | rkeene2 wrote:
             | I think what I saw was older since "ret32-maker.c" was last
             | updated in 2008.
        
               | yuubi wrote:
               | IOCCC 1984, mullender.c ?
        
       | rkeene2 wrote:
       | This reminds me of the time I wanted to run binaries compiled for
       | SSE3 on a system that lacked SSE3. I started writing a tool to
       | emulate this [0], and one thing it could do is rewrite the
       | executable pages with replacement instructions if there was
       | something that would fit (using memcpy(2), naturally).
       | 
       | This harkens back to the days when you could "download" a math
       | coprocessor for your SX system, which was a TSR which likely did
       | the same catching and handling of illegal instructions.
       | 
       | [0] https://github.com/rkeene/sse3-emu/blob/master/libsse3.c
        
         | tjalfi wrote:
         | Older versions of Windows used a similar technique to replace
         | the PREFETCHW instruction with NOPs on processors that didn't
         | support it.
        
       | WalterBright wrote:
       | In my 1980s version of Empire, all the global variables were kept
       | contiguously in one source file. To save/restore the game, it
       | just took the address of the first one, the address of the last
       | one, and blitted it to a disk file, and blitted it back.
       | 
       | Very fast & easy.
       | 
       | Of course, it broke when COMDATs were introduced.
       | 
       | I did a similar thing with my text editor. The colors were
       | configurable. The usual way was to have a configuration file,
       | which the editor would read upon startup. But floppy disk systems
       | were unbearably slow. So what I did was take the address of the
       | configuration data in the data segment. I'd work backwards to
       | where those bytes were in the EXE file, and patch the EXE file.
       | This worked great!
       | 
       | Until the advent of virus scanners, which broke that. Virus
       | scanners hated self-modifying EXE files.
        
         | halt40 wrote:
         | I love these little "factoid" comments that you hand out from
         | time to time. What would be even better is a book with all the
         | nitty-gritty details of the software you've written, along with
         | all the misadventures in doing so.
         | 
         | You can name it: 'Bright Moments'
         | 
         | I'd be happy to place an advanced order if it leans you in that
         | direction!
        
           | philbo wrote:
           | If we're bikeshedding the book title, surely "Bright Ideas"
           | warrants a mention.
        
             | Cullinet wrote:
             | Something using Bright Lines would be my first thought.
        
         | __david__ wrote:
         | See also: Emacs's old unexec() function. How do you speed up a
         | bunch standard library lisp loading and initialization? Just do
         | it once then write a new exe out with all your state pre-
         | computed. Genius.
        
           | WalterBright wrote:
           | I stole that idea from the 1970's FORTRAN program ADVENT, the
           | precursor to Zork.
           | 
           | I was amazed that I never ran across another DOS program that
           | used the technique.
        
           | weinzierl wrote:
           | Yup, and early TeX did it too. You could run a memory dump of
           | a previously initialized version to improve start-up times.
           | 
           | For a modern take on similar idea see also this discussion
           | from a while ago:
           | 
           |  _" Improving startup time in Atom with V8 snapshots"_
           | 
           | https://news.ycombinator.com/item?id=14140421
        
         | withinrafael wrote:
         | How did it break? COMDAT usage is optional today. (Perhaps it
         | wasn't when first created?)
        
           | WalterBright wrote:
           | COMDATs aren't placed sequentially by the linker. Also, zero
           | initialized data got placed in a separate section.
        
         | floor_ wrote:
         | I've pre-packed game asset data to the end of EXE files using
         | this trick and it seems that newer neural network virus
         | scanners seem to the the only type of scanner to pick it up. So
         | we may even lose the ability to do that soon once these type of
         | scanner become more popular/prevalent.
        
       | wmu wrote:
       | - It looks like a virus... - But it's the antivirus!
       | 
       | :)
        
         | jayd16 wrote:
         | But Doctor, I Am Pagliacci
        
       | badrabbit wrote:
       | Malware authors disagree.
        
       | justicz wrote:
       | "This code is such a bad idea, I've intentionally introduced
       | errors so it won't even compile."
       | 
       | I am stealing this line.
        
         | qahaz wrote:
         | This was common back in the day with ezines. They would include
         | codes to exploit security bugs in common software but they
         | would have intentional errors so script kiddies couldn't
         | compile them.
        
           | koofdoof wrote:
           | Are there any good ezines with content like this you could
           | reccomend to read?
        
           | commandlinefan wrote:
           | Seems like running it through a compiler and fixing the
           | errors would address that for you, though...
        
             | pgwhalen wrote:
             | The point is that script kiddies do not have the ability to
             | do that.
        
             | behringer wrote:
             | Script kiddies can't debug.
        
               | t-3 wrote:
               | Really? Compiler error messages are downright friendly
               | compared to networking tools and concepts. When I was 12,
               | I certainly found them to be much easier, even though I
               | completely failed to learn C, Perl, C++, or any of the
               | other popular languages of the time.
        
               | DoctorDabadedoo wrote:
               | What is this mysterious art you speak of? /s
        
             | thecupisblue wrote:
             | Exactly the idea. If you're a skiddie, you'll copy/paste,
             | look for a minute, say it doesn't work. If you dive in, you
             | gotta learn about the language, debugging it, the exploit
             | itself and suddenly you're not a script kiddy anymore. It's
             | like a leap of faith.
        
         | jiggawatts wrote:
         | I once replied to a flippant comment about the inherent
         | reliability of the cloud with a two-line PowerShell script that
         | would _end_ any 100% cloud-hosted business if executed in the
         | context of an admin account. As in: pack up everything, turn
         | the lights off, lock the doors, and go home because the game is
         | over.
         | 
         | I deliberately obfuscated the script by replacing characters
         | with various Unicode confusables, both the letters and the
         | symbols.
         | 
         | I still felt bad and ended up deleting the post.
         | 
         | Hopefully nobody tried to type it in manually instead of cut &
         | pasting just to see what would happen...
        
           | stavros wrote:
           | What, like rm -rf?
        
             | justsomehnguy wrote:
             | https://docs.aws.amazon.com/powershell/latest/reference/ite
             | m...
             | 
             | # this would wipe all EC2 instances, assuming you are
             | authenticated and there is nothing else need to get a list
             | of them
             | 
             | Get-EC2Instance | Remove-EC2Instance -Force
             | 
             | If there is a way to enumerate objects without explicitly
             | specifying some properties then PS makes it very easy to
             | pipe that output to remove or disable cmdlets.
             | 
             | I never worked with AWS but if I needed to write such
             | killer script I would kill instances (as in above example),
             | remove all S3 objects (quite similar, Get-S3Object |
             | Remove-S3Object) and finally wipe out all IAM roles and
             | users (similar Get/Remove-IAMGroup, ..IAMUser etc). And
             | thesubscription/account things, as @jiggawatts says, I'm
             | just not familiar with the AWS lingo. And secrets/creds, of
             | course.
             | 
             | By the time a human starts to investigate why the
             | monitoring gone mad (assuming it wasn't hosted with all
             | other infra on same account, lol) there would be nothing to
             | do, too many thing are gone already, too many things are on
             | the way to be purged and even if they could be restored
             | there would be too many things to tie it again to something
             | resembling a functioning system.
        
               | chikipowpow wrote:
        
               | stavros wrote:
               | Oh huh, for some reason that didn't even come to mind.
               | Good thing all our infrastructure is applied by CI and
               | our local tokens are read-only!
        
             | jiggawatts wrote:
             | There are cloud-era equivalents that will do a heck of a
             | lot more damage. Think bulk account/subscription deletion
             | with a sprinkling of -force and -purge.
             | 
             | Delete locks and the like will stop this... unless you bulk
             | delete them first.
             | 
             | The admins will get warning emails that they will finish
             | reading in abject terror some time after their entire cloud
             | tenant has gone to heaven.
             | 
             | The modern equivalent of "Operating system not found" is
             | "Click the guides below to get started with your new cloud
             | account."
        
         | raverbashing wrote:
         | It will be interesting if this snippet lands on GitHub
         | Autopilot (though it has important parts missing, even with the
         | minor errors added)
        
         | howdydoo wrote:
         | Raymond Chen is a great writer. He writes about the driest
         | topics on earth, but still manages to make it entertaining.
         | Definitely one of my favorite blogs.
        
           | rightbyte wrote:
           | And he seems to be one of the few who bothers to keep up
           | writing at MS devblogs.
           | 
           | I wonder how it looks internally. Like, can anyone write
           | there. Apart from a university I worked at management would
           | never let me write any "blogs" about work related stuff.
        
           | shaklee3 wrote:
           | he really is one of the best technical bloggers out there.
           | I've learned a ton from his c++ articles.
        
       | rwmj wrote:
       | I think the even crazier thing is Windows having a function to
       | allocate memory in another process
       | (https://docs.microsoft.com/en-
       | us/windows/win32/api/memoryapi...). That seems like a potential
       | source for all kinds of impossible to track bugs.
       | 
       |  _> The customer is a major anti-virus software vendor! The
       | customer has important functionality in their product that that
       | they have built based on this technique of remote code injection,
       | and they cannot afford to give it up at this point._
       | 
       | Oh now it makes sense.
        
         | CodeArtisan wrote:
         | Code injection is common in video gaming, it is used by the
         | Steam overlay, OBS Studio, anticheats, profilers, ...
        
         | Diggsey wrote:
         | Injecting code into another process is well supported :P
         | 
         | 1) VirtualAllocEx some memory to hold the path to a DLL you
         | want to inject.
         | 
         | 2) WriteProcessMemory to write the path to that memory.
         | 
         | 3) CreateRemoteThread passing in the address of `LoadLibrary`
         | and the pointer to the previously allocated memory.
         | 
         | It's no coincidence that `LoadLibrary` has a signature which
         | can directly be passed to `CreateRemoteThread`!
        
           | asveikau wrote:
           | LoadLibrary is not guaranteed to have the same address across
           | processes. As of Win7 you might also have a process link to
           | kernelbase and not kernel32, so it's not even guaranteed to
           | be in the same DLL.
           | 
           | However it should be possible to use GetModuleHandleEx to
           | find the DLL base addresss in a remote process, then
           | ReadProcessMemory to implement your own remote
           | GetProcAddress.
        
             | NtGuy25 wrote:
             | You do end with modules having the same address in every
             | process if untampered though. This is due to Copy on Write
             | windows implements internally with DLL's to save space. So
             | while not guaranteed. On X64 you can be certain that
             | because of COW the module will have the same address.
        
           | codedokode wrote:
           | Isn't this a way to introduce bugs? For example:
           | 
           | - injected DLL can change some process-related settings and
           | break main program
           | 
           | - injected DLL can call non-reenterable function from a
           | system library simultaneously with main program
        
             | bmm6o wrote:
             | No need to go that far. If you allow an untrusted process
             | to write to your memory, you've already lost. One thing I
             | haven't seen called out yet is that there is security
             | associated with this API call - ordinary users can only
             | call this on processes that they own.
             | 
             | (And as far as the terminology, these aren't "bugs" since
             | they aren't defects in the software.)
        
         | watermelon0 wrote:
         | What would be the use case of AV software needing to perform
         | remote code injection?
        
           | rwmj wrote:
           | There's barely a case for AV software existing at all. It's
           | the root cause for all kinds of impossible to track bugs and
           | performance regressions.
        
             | [deleted]
        
             | sebazzz wrote:
             | _Cries in Symantec Endpoint Protection with "file access
             | scans" turned on, compiling a fairly average .NET solution_
        
             | DarkWiiPlayer wrote:
             | The best kind of antivirus is the one that stays quiet
             | unless you specifically ask it to scan a file. Did someone
             | say Clam AV just now?
        
               | Darvon wrote:
               | Why did you download an executable that you don't trust?
               | Don't even scan it, just delete it and go find one you do
               | trust.
        
               | fennecfoxen wrote:
               | Trust, but verify.
        
       | dark-star wrote:
       | I actually did something like that on Windows x86, and it worked
       | fine. Even I was surprised by that fact :)
       | 
       | I used it to copy out a (forgotten) password from a password
       | inputfield in another program, which you cannot read remotely
       | (for security reasons). Worked fine for that one use-case, and I
       | haven't used this trick it anywhere else ever again :)
        
         | gavinray wrote:
         | How did this work exactly?
         | 
         | Suppose you are able to inject and execute remote code into the
         | process containing your password in a text field
         | 
         | How did you read the password out of it? Did you know that the
         | password variable was stored at some particular memory
         | location?
        
           | stevekemp wrote:
           | That reminds me of the "Shatter Attack" - which basically
           | involved posting messages to the event-loop/window of a
           | different process:
           | 
           | https://en.wikipedia.org/wiki/Shatter_attack
           | 
           | You could do that to call WM_GETTEXT, against an input-
           | control, for example.
        
           | kelnos wrote:
           | I don't know much about Windows GUI programming, but in other
           | GUI toolkits I've used, there's usually some sort of
           | text_field.get_current_value() function you can call.
           | Presumably the parent injected some code that repurposes the
           | callback of a button or something so that when you click it,
           | it calls get_current_value() and then dumps it to console or
           | a log file or something.
        
           | dark-star wrote:
           | I don't think I have that code around anymore, at least I
           | can't find it now. And it's been a while. Here is what I
           | remember:
           | 
           | Basically you use VirtualAllocEx() to allocate some memory in
           | the remote thread. The returned pointers are in the context
           | of the target process.
           | 
           | You can access that remote memory with ReadProcessMemory()
           | and WriteProcessMemory(), which uses those "remote" pointers
           | to copy data to/from your process.
           | 
           | You can then use these memory areas to pass global handles
           | and other stuff around.
           | 
           | For accessing the actual password field data, you use
           | standard Window-Messages with SendMessage() etc.
        
             | unnouinceput wrote:
             | This technique is a classic one. I remember learning about
             | it in late 90's after I got infected with a malware that
             | hid itself from Task Manager. That made me also write my
             | own Task Manager.
             | 
             | To this day (Win10/Win11) you can hide your program from
             | Task Manager using this technique and any malware that
             | respect itself does it.
        
             | mwcampbell wrote:
             | Some Windows screen readers used, and maybe still use, the
             | same technique to get data out of the SysListView32 common
             | control, since the parameters and results of that control's
             | window messages aren't marshaled by the OS.
             | 
             | Edit: Looks like NVDA still does. https://github.com/nvacce
             | ss/nvda/blob/master/source/NVDAObje...
        
               | heinrich5991 wrote:
               | Permanent link (press 'y' anywhere on Github): https://gi
               | thub.com/nvaccess/nvda/blob/b5f82f878f344ab26004f3....
        
       | SystemOut wrote:
       | This reminded me of the old days working in Windows 3.1 and my
       | first professional project was to write a SOCKS client that could
       | be loaded up and intercept all calls to Winsock's connect()
       | function. It needed to do this without modifying the other
       | programs and it had to happen at the DLL level and not the VxD
       | layer where our IP stack ran.
       | 
       | Turns out there was an undocumented Windows API function along
       | the lines of "AliasCsToDsRegister" or something like that - I've
       | tried to find a reference to it but I can't find it. It allowed
       | me write into the code segment (the CS was global and read only -
       | as it was shared among all processes) and replace the first few
       | bytes of the connect function call with a jump to my code which
       | would then put it back, make the call to the socks server, do
       | some other magic, put my jump hook back in and the return to the
       | caller. Good times!
       | 
       | Kind of surprised I remember this and more so that it actually
       | worked.
        
         | adontz wrote:
         | https://www.microsoft.com/en-us/research/project/detours/
        
         | oshiar53-0 wrote:
         | It's AllocDStoCSAlias.
        
           | SystemOut wrote:
           | Thank you! I thought the word Alloc was in there but couldn't
           | remember the right combination of Alias/Alloc/etc.
        
         | pacaro wrote:
         | Yeah win16 had AllocCStoDS and AllocDStoCS, one was documented
         | the other wasn't. They also had the fabulously named
         | PrestoChangoSelector which toggled the code/data bit in the
         | descriptor table
        
           | throwaway2037 wrote:
           | Cool. I did some Google searching about PrestoChangoSelector
           | and I found this:
           | 
           | KnowledgeBase Archive
           | 
           | An Archive of Early Microsoft KnowledgeBase Articles
           | 
           | Q89560: Creating Dynamic Code Segments Using
           | PrestoChangoSelector
           | 
           | https://jeffpar.github.io/kbarchive/kb/089/Q89560/
        
           | bonzini wrote:
           | IIRC there was a documented ChangeSelector function that was
           | actually not implemented, so you had to use the undocumented
           | PrestoChangoSelector instead. Visual Basic used it to
           | implement a direct threaded code interpreter.
           | 
           | Some other functions had similarly great names:
           | 
           | >>Anyway, I am not sure what type of person writes a function
           | called "BozosLiveHere" and puts it into USER.EXE
           | 
           | >This started out life with a non-bozo name as an
           | undocumented function in Windows 3.0.
           | 
           | >Windows 3.1 removed the undocumented function, but we found
           | that some programs were using the undocumented function and
           | started crashing.
           | 
           | >So we reluctantly put the function back, but changed its
           | name to "BozosLiveHere" so that nobody else would use it in
           | the future.
           | 
           | >A similar story exists for "TabTheTextOutForWimps".
        
       | donkarma wrote:
       | Does anyone else remember reading an Old New Thing article like
       | this one before?
        
         | donkarma wrote:
         | Found it,
         | https://devblogs.microsoft.com/oldnewthing/20180615-00/?p=99...
        
       | oshiar53-0 wrote:
       | Related:
       | https://devblogs.microsoft.com/oldnewthing/20190902-00/?p=10...
        
       | jnwatson wrote:
       | At least one additional step which is required on some
       | architectures is you must flush the data cache and invalidate the
       | instruction cache at the location of the new code.
       | 
       | Dynamically loading code is indistinguishable from self-modifying
       | code, and each architecture has special steps you must take in
       | order for it to work.
        
       | weinzierl wrote:
       | Isn't this whole idea of injecting code on modern systems doomed
       | because of write xor execute (W^X, NX), which is hopefully
       | enabled?
        
         | liftm wrote:
         | If you can allocate memory in a foreign process, I'd guess you
         | could also change the permissions on that memory... So write
         | first, then change to executable.
         | 
         | (VirtualProtectEx looks like it would do that. Never used
         | winapi, not sure.)
        
       | phendrenad2 wrote:
       | As long as the code is truly position-independent, I.E. compiled
       | with -fPIC in GCC/LLVM, then there will be no problem.
        
         | skim_milk wrote:
         | But the code being memcpy'ed is using the parent process's
         | specific symbol relocations. When a library with PIC is loaded
         | the executable code is copied from the file into RAM to a
         | random offset and all references to structures in the ASM are
         | updated to match their now random offset in memory
         | (simplifying). Say function XYZ in library libfoo is in
         | offsetXYZ, parent process loads libfoo at offset 0xDEADBEEF,
         | injectee process loads libfoo at offset 0xDEC0DE. In Windows
         | the call to function XYZ in the parent process uses the address
         | offsetXYZ+0xDEADBEEF, but the call in the injectee process uses
         | offsetXYZ+0xDEC0DE, causing any reference to the parent
         | process's function to fail. GNU/Linux is very similar but
         | library symbols are found based on an offset to a structure in
         | memory that contains the library symbol metadata, that changes
         | every time the program is loaded.
         | 
         | So actually the opposite is true, if the code _wasn 't_
         | position-independent and was statically located, the assembly
         | code offsets wouldn't need to be updated and you might be able
         | to call a memcpy'd function. Position-independent code could
         | only possibly work if you updated the reference to the symbol
         | metadata structure in the ASM after the memcpy, but at that
         | point you're re-implementing libdl and no longer just using
         | memcpy.
        
           | phendrenad2 wrote:
           | Yes, the example Raymond uses isn't PIC.
        
           | ssklash wrote:
           | This depends on the libraries/DLLs being used. Windows loads
           | system DLLs at the same location in every process's address
           | space, so you can use process-local offsets in a remote
           | process. For custom libraries of course this wouldn't work.
           | Or if the required system library hasn't been loaded in the
           | remote process.
        
       | truekonrads wrote:
       | Much of red-team/pentest/malware code works like this and is
       | surprisingly reliable.
        
         | beaconstudios wrote:
         | really? I've not written much shellcode at all, but what I did
         | write wasn't generically-compiled C++ - it was always either C
         | or ASM, specifically because you get to avoid all the platform
         | and position-dependent stuff (except in return-to-libc
         | payloads).
        
           | ssklash wrote:
           | You can definitely use C++, but you need to use specific
           | compiler flags and avoid things like the STL or exceptions.
           | Strings need to be created on the stack, a few other tricks.
           | Then you can extract the .text section assembly of the
           | resulting binary and inject and run it.
        
             | beaconstudios wrote:
             | That makes sense, but then a C++ without exceptions,
             | without vtables, without the STL - might as well be C,
             | right? There's not much going on there beyond syntax sugar!
        
               | ssklash wrote:
               | Yeah it's not super helpful. Maybe a bit smaller code,
               | stricter type checking, a little faster compilation time
               | possibly But not really a huge benefit over C. Sure beats
               | writing it all in hand-coded assembly though!
        
               | NtGuy25 wrote:
               | You want to use C anyhow as you want to make sure you
               | have control over the code that is output.
               | 
               | For example the following code you know what the assembly
               | is going to be.
               | 
               | strcmp(char* a, char* b);
               | 
               | strcmp(str1,str2);
               | 
               | If you do the above as a template you can run into some
               | weird issues that you may not be expecting. So while
               | tedious, you would need to make your own wscmp. You also
               | have to be very careful so that you don't pull in ANY
               | libraries. Since your code needs to be 100 % independent
               | and do the loading itself.
               | 
               | C++ exceptions are implemented at the OS level in
               | windows. C++ exceptions using SEH, while there's also VEH
               | and unhandled exceptions. You can easily use SEH for your
               | shell code, it's just not documented well. But sadly you
               | have to manually set this up by having something like
               | 
               | SetExceptionHandler(curAddr,Handler) // Where curaddr can
               | be found by doing something like call $+5 so you remain
               | position independent.
        
               | pjmlp wrote:
               | It still has stronger type safety, that is why C89 is
               | mostly C++ compatible, not all of it is.
               | 
               | Then you also get namespaces, modules, compile time
               | execution, type safe macros (aka templates).
               | 
               | It is a bit more than syntax sugar.
        
         | josephcsible wrote:
         | The "such a bad idea" part of this is that the code being
         | injected is written in C++ rather than assembly. The injector
         | itself is perfectly reasonable.
        
         | mox1 wrote:
         | Uhh yea, this is Running shellcode 101, works very well. My Red
         | Team stuff at work all starts with a simple loader like this
         | (with some encryption / obfuscation sprinkled in).
         | 
         | When I was first shown this I was like 'What non virus use case
         | does this have!?!?'
        
           | [deleted]
        
           | ssklash wrote:
           | Red teamer here too, and this was my exact thought. There's
           | lots of legit uses for DLL injection, but straight up
           | shellcode injection? Shady as hell. So of course it was an AV
           | vendor...
        
           | tptacek wrote:
           | Instrumenting and debugging live processes is the big one.
        
             | dotancohen wrote:
             | Which is not an end-user advantage but rather a tool to
             | better understand end-user software - equally useful for
             | improving said software or attacking it. Assuming GP meant
             | "virus" as in "malware" then actually this supports his
             | point.
        
       | RcouF1uZ4gsC wrote:
       | > I pointed out to the customer liaison that what the customer is
       | trying to do is very suspicious and looks like a virus. The
       | customer liaison explained that it's quite the opposite: The
       | customer is a major anti-virus software vendor! The customer has
       | important functionality in their product that that they have
       | built based on this technique of remote code injection, and they
       | cannot afford to give it up at this point.
       | 
       | As an aside, whenever I set up a Windows PC for me or a family
       | member, the first thing I do is uninstall any third-party
       | antivirus that may have come with the computer. I have found that
       | anti-virus software likely makes my computer more insecure by
       | having a big attack surface, not to mention slowing it down.
        
         | AshamedCaptain wrote:
         | It's not as if the first party antivirus inspires any
         | confidence, either. It still loses in performance to some 3rd
         | party ones, and has had its own list of security issues.
         | 
         | For those who remember, its early versions (before the MS
         | acquisition) required the Visual Basic runtime.
        
           | ssklash wrote:
           | The current Windows Defender is not the crappy old MS AV it
           | used to be. It's gotten drastically better, and for normal
           | home users I always recommend it over installing any 3rd
           | party AV.
           | 
           | I'm a pentester and red teamer, and yes, bypassing Defender
           | isn't all that hard, but neither are most 3rd party AVs, and
           | Defender does not bring in all the instability and additional
           | attack surface. Also I think it can take advantage of newer
           | kernel APIs that non-MS AV can't.
        
             | floatboth wrote:
             | Defender still introduces performance issues, sometimes
             | it's better to disable Defender too and have no scanning
             | crap whatsoever.
        
         | kmeisthax wrote:
         | Actual Chromium developers have similar opinions w.r.t.
         | antivirus vendors.
         | 
         | It's almost like the extensibility necessary to make third-
         | party security products work requires creating entirely new
         | attack surface for those products to work.
        
         | Arnavion wrote:
         | You shouldn't trust that uninstalling the AV reliably gets rid
         | of whatever kernel drivers and other detritus it came with. I
         | don't know of any AV examples but plenty of video game anti-
         | cheat software has that problem.
         | 
         | Just wipe and reinstall Windows.
        
       | skim_milk wrote:
       | I remember doing exactly this to get code injection working on
       | GNU/Linux systems! I made a library injection library in college
       | for some coursework, which involved copying a C function into a
       | code cave on a remote process and getting the remote process to
       | execute it and return. It only works because its so bare-bones,
       | it doesn't use try/catch and calls to other functions are
       | possible because the function pointers are passed in through the
       | registers and the compiled code is small enough to fit in a
       | single page.
       | 
       | An example function I memcpy and run:
       | 
       | https://github.com/skimmilk/liblibinject/blob/master/src/lib...
        
         | tomcam wrote:
         | Wait does C now have try/catch?
        
           | skim_milk wrote:
           | Technically the code being uploaded is compiled from C++ as
           | the rest of the project is written in C++. Kind of similar to
           | the article but in my case all I'm doing is calling a bunch
           | of C library functions from the shellcode.
        
           | basementcat wrote:
           | It's called setjmp() and longjmp().
           | 
           | https://web.archive.org/web/20091104065428/http://www.di.uni.
           | ..
        
           | tptacek wrote:
           | It has setjmp and longjmp, so yeah.
        
             | kragen wrote:
             | As you are well aware, calling setjmp and longjmp doesn't
             | make code non-position-independent in the way that Raymond
             | is talking about, because setjmp saves the return address
             | with which it was actually called. It doesn't rely on PC
             | tables in the executable the way that (a common
             | implementation of) C++ exception handling does. I mention
             | this not to inform you of it (you already understand it
             | better than I do) but to keep anyone else reading the
             | thread from being misled.
        
       ___________________________________________________________________
       (page generated 2021-12-30 23:02 UTC)