h33p / vmread

A library to read/write memory to Windows on KVM

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Injection example doesn't list processes.

0Slugcat0 opened this issue · comments

Running Windows 10 LTSC,

The external method returns the following:

Using Mode: MODE_EXTERNAL
PML4:	1aa000	| KernelEntry:	fffff80580c27460
Kernel Base:	fffff8058021f000 (1e1f000)
PsInitialSystemProcess:	fffff805806e42e0 (22e42e0)
System (PID 4):	ffff8e056d87b1c0 (0)
NT Version:	1000
Process List:

The injection method simply crashes Qemu.

[New LWP 1081]
[New LWP 1090]
[New LWP 1091]
[New LWP 1094]
[New LWP 1097]
[New LWP 1098]
[New LWP 1101]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f4208df27d6 in __GI_ppoll (fds=0x55a299d9fc00, nfds=13, timeout=<optimized out>, sigmask=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:39
39	../sysdeps/unix/sysv/linux/ppoll.c: No such file or directory.

Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
0x00007f420c2864f1 in ?? () from target:/lib64/ld-linux-x86-64.so.2
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(__dlclose) will be abandoned.
When the function is done executing, GDB will silently stop.
[Inferior 1 (process 1059) detached]

Possibly caused by the wrong offsets?

commented

Must be some null pointer read. Will have to investigate.

commented

Or it could also be wrong offsets. In fact, all reads should be sanitized properly. Could you run the commands of injection manually inside GDB and post the backtrace here once it crashes? Build in debug mode first so that line information is preserved.

(gdb) call $dlclose($library)
[Thread 0x7fc9875ff700 (LWP 4866) exited]

Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
0x00007fc99e0e34f1 in ?? () from target:/lib64/ld-linux-x86-64.so.2
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(__dlclose) will be abandoned.
When the function is done executing, GDB will silently stop.
(gdb) backtrace 
#0  0x00007fc99e0e34f1 in ?? () from target:/lib64/ld-linux-x86-64.so.2
#1  0x00007fc99ac9648f in __GI__dl_catch_exception (exception=exception@entry=0x7fffa4591090, 
    operate=operate@entry=0x7fc99ab35330 <dlclose_doit>, args=args@entry=0x0) at dl-error-skeleton.c:196
#2  0x00007fc99ac9651f in __GI__dl_catch_error (objname=objname@entry=0x55c7da60bfe0, errstring=errstring@entry=0x55c7da60bfe8, 
    mallocedp=mallocedp@entry=0x55c7da60bfd8, operate=operate@entry=0x7fc99ab35330 <dlclose_doit>, args=args@entry=0x0)
    at dl-error-skeleton.c:215
#3  0x00007fc99ab35a25 in _dlerror_run (operate=operate@entry=0x7fc99ab35330 <dlclose_doit>, args=0x0) at dlerror.c:163
#4  0x00007fc99ab35364 in __dlclose (handle=<optimized out>) at dlclose.c:46
#5  <function called from gdb>
#6  0x00007fc99ac487d6 in __GI_ppoll (fds=0x55c7daa88d60, nfds=14, timeout=<optimized out>, sigmask=0x0)
    at ../sysdeps/unix/sysv/linux/ppoll.c:39
#7  0x000055c7d8159011 in qemu_poll_ns ()
#8  0x000055c7d8159eb8 in main_loop_wait ()
#9  0x000055c7d7d3c4dd in main ()
(gdb) c
Continuing.
Couldn't get registers: No such process.
Couldn't get registers: No such process.
(gdb) [Thread 0x7fc9869ff700 (LWP 4867) exited]
[Thread 0x7fc987fff700 (LWP 4864) exited]
[Thread 0x7fc994ce6700 (LWP 4863) exited]
[Thread 0x7fc9954e7700 (LWP 4862) exited]
[Thread 0x7fc995ce8700 (LWP 4859) exited]
[Thread 0x7fc9964e9700 (LWP 4858) exited]
[Thread 0x7fc997d1a700 (LWP 4851) exited]
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.


I'm not sure line information was preserved here.
You think qemu thread crashes when dlopen calls the __attribute__((constructor))?

commented

Okay, so apparently injecting the library does work just fine. It is the unloading part that is not properly working.

Which version of Windows 10 LTSC are you running. Could you, please, post the OS version and build information here (through winver or settings)? That will help me either get my hands on the windows version or at least look for correct structures.

screenshot_20190224_054937
Will this suffice?

commented

Okay, I will take a look. Thank you.

In addition, what is your qemu set up? Are you using q35 chipset? In case you are using the other (default) platform chipset, it could actually be the main issue.

Okay, I will take a look. Thank you.

In addition, what is your qemu set up? Are you using q35 chipset? In case you are using the other (default) platform chipset, it could actually be the main issue.

I took a look and I was not using the q32 chipset I changed it from "pc-i440fx-2.1" to "pc-q35-2.12" in my VM XML file.
However it still crashes with the same backtrace:

(gdb) call $dlclose($library)

Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
0x00007ff2a501f4f1 in ?? () from target:/lib64/ld-linux-x86-64.so.2
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(__dlclose) will be abandoned.
When the function is done executing, GDB will silently stop.
(gdb) backtrace 
#0  0x00007ff2a501f4f1 in ?? () from target:/lib64/ld-linux-x86-64.so.2
#1  0x00007ff2a1bd248f in __GI__dl_catch_exception (exception=exception@entry=0x7fff4e769960, operate=operate@entry=0x7ff2a1a71330 <dlclose_doit>, 
    args=args@entry=0x0) at dl-error-skeleton.c:196
#2  0x00007ff2a1bd251f in __GI__dl_catch_error (objname=objname@entry=0x562d9db3d720, errstring=errstring@entry=0x562d9db3d728, 
    mallocedp=mallocedp@entry=0x562d9db3d718, operate=operate@entry=0x7ff2a1a71330 <dlclose_doit>, args=args@entry=0x0) at dl-error-skeleton.c:215
#3  0x00007ff2a1a71a25 in _dlerror_run (operate=operate@entry=0x7ff2a1a71330 <dlclose_doit>, args=0x0) at dlerror.c:163
#4  0x00007ff2a1a71364 in __dlclose (handle=<optimized out>) at dlclose.c:46
#5  <function called from gdb>
#6  0x00007ff2a1b847d6 in __GI_ppoll (fds=0x562d9f01e420, nfds=14, timeout=<optimized out>, sigmask=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:39
#7  0x0000562d9b523011 in qemu_poll_ns ()
#8  0x0000562d9b523eb8 in main_loop_wait ()
#9  0x0000562d9b1064dd in main ()
(gdb) 

Lol my curiousity peaked at the standalone example you had. It works.

commented

So the non-injecting code works fine? Then it seems that once again, unloading doesn't properly work, but the output file should be in tact inside the /tmp folder.

Anyways, I will have to figure out what is causing the unload problem and then also add a note regarding the chipset, as it seems to be a recurring problem.

but the output file should be in tact inside the /tmp folder.

I ran the injector again to double check, but there was no "testr.txt" in /tmp.
Wouldn't that mean It fails to call the init() function?

commented

Does running the ./inject normally work? I believe the issue could be the fact that relative (CWD) paths do not work on dlopen. I have just tested ./inject and it ran just fine for me.

Does running the ./inject normally work?

No.

I believe the issue could be the fact that relative (CWD) paths do not work on dlopen. I have just tested ./inject and it ran just fine for me.

Doesn't the injection script use a absolute path?
-ex "set \$library = \$dlopen(\"$(pwd)/build/libexample.so\", 1)" \

commented

The injection script does use absolute path but it is expanded by shell. I do not know whether you did expand the path or not.

commented

The fact that injection does not work properly is rather worrying, because I am unable to reproduce it and yet the library does not load for you (assuming $library is null after dlopen. Otherwise it should either work or crash).

I suppose it would be wise to check with dlerror what load error was that, however, it is rather finicky to execute it through GDB.

And I did just that, dlopen returns a nullptr, so when dlclose is called it dereferences a null pointer

Currently checking why with dlerror..

(gdb) call dlerror()
$2 = 0x559c9218b750 "/home/User/Libraries/vmread/build/libexample.so: cannot open shared object file: Permission denied"

Note, gdb was was ran as root.

commented

Try checking whether or not libexample.so has executable permissions set on all users. If that fails, try moving it to the /usr/lib directory (however, I doubt that would change anything).

Also check with ldd -r libexample.so to check all the library dependencies. In some cases it could be that requested library versions and the ones used in qemu are mismatching and can not co-exist (can be checked through /pid/$(pidof qemu-system-x86_64)/maps). In that case you would need to figure out a way to make qemu use the latest libraries, or link the example against the newest ones.

I chmod'ed it to 777 (all user all access) - Nope.
Moved it to /usr/lib, - It crashes.

(gdb) call (void*(*)(char*, int)) dlopen("/usr/lib/libexample.so", 1)                    
[Thread 0x7f3ae2ffd700 (LWP 27401) exited]
[Thread 0x7f3ae37fe700 (LWP 27400) exited]
[Thread 0x7f3ae1ffb700 (LWP 27403) exited]
[Thread 0x7f3ae17fa700 (LWP 27404) exited]
[Thread 0x7f3ae0ff9700 (LWP 27405) exited]
[Thread 0x7f3ae27fc700 (LWP 27402) exited]
[Thread 0x7f3ac37fe700 (LWP 27407) exited]
[Thread 0x7f3ac3fff700 (LWP 27406) exited]
[Thread 0x7f3ae3fff700 (LWP 27399) exited]
[Thread 0x7f3ac2bf9700 (LWP 27420) exited]
[Thread 0x7f3b00ff9700 (LWP 27398) exited]
[Thread 0x7f3b017fa700 (LWP 27397) exited]
[Thread 0x7f3b01ffb700 (LWP 27396) exited]
[Thread 0x7f3b027fc700 (LWP 27395) exited]
[Thread 0x7f3b02ffd700 (LWP 27394) exited]
[Thread 0x7f3b037fe700 (LWP 27393) exited]
[Thread 0x7f3b03fff700 (LWP 27392) exited]
[Thread 0x7f3b18da9700 (LWP 27391) exited]
[Thread 0x7f3b195aa700 (LWP 27390) exited]
[Thread 0x7f3b19dab700 (LWP 27389) exited]
[Thread 0x7f3b1a5ac700 (LWP 27388) exited]
[Thread 0x7f3b1adad700 (LWP 27387) exited]
[Thread 0x7f3b35ced700 (LWP 27386) exited]
[Thread 0x7f3b364ee700 (LWP 27385) exited]
[Thread 0x7f3c5d9ff700 (LWP 27361) exited]

Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
_IO_vfprintf_internal (s=0x0, format=0x7f3c60897143 "Using Mode: %s\n", ap=ap@entry=0x7ffe24ba6ff0) at vfprintf.c:1278
1278    vfprintf.c: No such file or directory.
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(__dlopen) will be abandoned.
When the function is done executing, GDB will silently stop.
(gdb) call dlerror()
$1 = 0x0
(gdb) backtrace 
#0  _IO_vfprintf_internal (s=0x0, format=0x7f3c60897143 "Using Mode: %s\n", ap=ap@entry=0x7ffe24ba6ff0) at vfprintf.c:1278
#1  0x00007f3c64d898c4 in __fprintf (stream=<optimized out>, format=<optimized out>) at fprintf.c:32
#2  0x00007f3c608944bc in init () at ../example.cpp:26
#3  0x00007f3c682c898a in ?? () from target:/lib64/ld-linux-x86-64.so.2
#4  0x00007f3c682c8a89 in ?? () from target:/lib64/ld-linux-x86-64.so.2
#5  0x00007f3c682cccdc in ?? () from target:/lib64/ld-linux-x86-64.so.2
#6  0x00007f3c64e8048f in __GI__dl_catch_exception (exception=<optimized out>, operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:196
#7  0x00007f3c682cc2c6 in ?? () from target:/lib64/ld-linux-x86-64.so.2
#8  0x00007f3c64d1f256 in dlopen_doit (a=a@entry=0x7ffe24ba7730) at dlopen.c:66
#9  0x00007f3c64e8048f in __GI__dl_catch_exception (exception=exception@entry=0x7ffe24ba76d0, operate=operate@entry=0x7f3c64d1f200 <dlopen_doit>, 
    args=args@entry=0x7ffe24ba7730) at dl-error-skeleton.c:196
#10 0x00007f3c64e8051f in __GI__dl_catch_error (objname=objname@entry=0x5651f84cc720, errstring=errstring@entry=0x5651f84cc728, 
    mallocedp=mallocedp@entry=0x5651f84cc718, operate=operate@entry=0x7f3c64d1f200 <dlopen_doit>, args=args@entry=0x7ffe24ba7730) at dl-error-skeleton.c:215
#11 0x00007f3c64d1fa25 in _dlerror_run (operate=operate@entry=0x7f3c64d1f200 <dlopen_doit>, args=args@entry=0x7ffe24ba7730) at dlerror.c:163
#12 0x00007f3c64d1f2e6 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
#13 <function called from gdb>
#14 0x00007f3c64e327d6 in __GI_ppoll (fds=0x5651f8fb82f0, nfds=14, timeout=<optimized out>, sigmask=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:39
#15 0x00005651f6064011 in qemu_poll_ns ()
#16 0x00005651f6064eb8 in main_loop_wait ()
#17 0x00005651f5c474dd in main ()
(gdb) 

Oh yeah, it injects alright, I found "/tmp/testr.txt"

Using Mode: MODE_QEMU_INJECT
Initialization error: 3

I am also confused and amazed that moving the shared lib to /usr/lib actually fixes the issue of injecting. Odd.

commented

Really odd. For starters it fails to create the testr file when being in the build directory (could be due to /tmp permissions). The really odd part is that it fails to locate the kernel base. As you said, the standalone example works. Thus, there is no real reason why this should not work. Try breakpointing here or adding a MSG to print out the pid, mapsStart and mapsSize variables.

commented

Compare it with what happens on the standalone example, see if something odd occurs.

Really odd. For starters it fails to create the testr file when being in the build directory (could be due to /tmp permissions).

Wasn't reason why it didn't create testr.txr when in the build dir. because dlopen failed open the .so because of permission issues so that the init() wasn't called at all?

Compare it with what happens on the standalone example, see if something odd occurs.

Kay.

commented

Yeah my bad. I misread that the crash happened when you set the permissions to 777.

Getting closer, the injector shits itself at:
https://github.com/Heep042/vmread/blob/80a71afa8b1850ed4ed1d9627f250251c7be2d47/example.cpp#L26

Now comparing with standalone as to why.

commented

out file pointer must be null. But it worked once previously, didn't it?

Wow, I was just about to say, yes.
https://github.com/Heep042/vmread/blob/80a71afa8b1850ed4ed1d9627f250251c7be2d47/example.cpp#L16
sets "out" to 0.

Also I think it is (as you said) a permission issue with /tmp.

commented

It could definitely be the case since by default, qemu runs on a highly unprivileged user.

commented

That occurs when setting VM up with virt-manager. You could try making kvm run as your user (this might be needed anyways in case audio does not work really well out of the box), however, it may bring potential security complications (after all, you are executing arbritary code, even though it is rather sandboxed).

commented

Those options are in the /etc/libvirt directory

Alright I pinpointed the issue, if you are using virt-manager it places qemu by default in a security context.
This security context apparently prohibits qemu from opening a file in /tmp/ among other things.
This also caused the error when trying to dlopen the shared object from the build dir:
"the cannot open shared object file: Permission denied"
To fix this you need to edit /etc/libvirt/qemu.conf
and set security_default_confined to 0.
security_default_confined = 0
Reload the config files:

systemctl restart libvirtd.service 
systemctl restart libvirt-guests.service

I learned a lot today. By the way, awesome project @Heep042 and thanks for the help.

commented

You are welcome, and I'm glad to know the problem is solved.