Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

WORK IN PROGRESS

This page is currently being created or overworked. Please don't treat it as a finished document.

Some initial words (You can skip this if you only want the facts (wink) )

In this tutorial I want to create a Java Program that is able to access a remote Windows machines Service Registry and for example start and stop services on that.

Access will be done during DCE/RPC calls transported using SMB named pipes. In contrast to allmost all other techniques this has the smallest configuration impact on the targeted machine. All you have to have configured are the "File and Printer Sharing" and have the administrative Shares enabled. In most cases Windows is allready configured correctly.

There seem to be several Java libraries that offer services in the DCE/RPC sector. The one I liked most was JCIFS, which seems to be the base of most, but not all of the other solutions.

While doing the basic stuff with JCIFS was pretty easy, finding out how to do DCE/RPC calls was allmost impossible. Unfortunately JCIFS allready had stuff for a lot of DCE/RPC services, but how to use them was explained nowhere and nither Google nor the Mailinglist was able or willing to explain how to do it. So I had to look how JCIFS did stuff internally. So the source was with me, but it turned out it was more the dark side of the source.

The svcctl service I wanted to acces didn't have any implementation in JCIFS so I had to create my own. Luckily this code can be generated automatically from so-called IDL (http://en.wikipedia.org/wiki/Microsoft_Interface_Definition_Language) files. This interface definition was a well protected secret of Microsoft. The guys at the Samba project did quite a good job of reverse engineering this. Luckily Microsoft had to publish the specification in 2002 in order to avoid major trouble with the European Union (http://www.theregister.co.uk/2002/03/19/why_microsofts_eu_concession/).

Based upon this IDL it is possible to generate the code for communicating with MS services automatically. One implementation of such a compiler is MIDLC (http://jcifs.samba.org/src/) which is available from the JCIFS site. It seems that this is actually only used occasionally as I couldn't manage to get it running on a non-liunux machine and it did seem to have quite some quirks. I assume this was used to generate the Stubs used in JCIFS and hasn't been used or implroved for a long time (2006).

Next unfortunate thing is that midlc allready comes with a set of idl files. From having a look at them the svcctl.idl seems to be implemented as far as the Samba guys managed to do this (Not all service functions are available). Unfortunately the ones I was looking for were missing (CreateServiceW, DeleteServiceW, StopServiceW, ...).

So If you now think: "Hey ... so I'll get the official IDL file from Microsoft and use that" (http://msdn.microsoft.com/en-us/library/cc245860(v=prot.10).aspx) ... well what should I say ... the formats are different and therefore you can't use them out of the box. But they will be helpfull in extending the existing file.

So far, so good ... Now I used midlc on a linux machine to generate the stubs for communicating with SVCCTL. I used the default files first because I had to learn how do do these calls first and I didn't want to get stuck, just because my IDL was wrong.

Unfortunateley I wasted about 5 full days on this and finally abandoned my quest ... at least for a little while. Finding out how to do stuff with a completely undocumented API based upon untested code following an undocumented protocol by looking a binary WireShark dumps of a tool called PSExec (http://technet.microsoft.com/de-de/sysinternals/bb897553) was a total nightmare and I was on the brink of going insane on it.

I even offered JCIFS professionals a bounty (not the chocolate bar) for providing me with a solution, but not a single "professional" even answered (sad) ... "Hail to the glory of open-source!!!!".

Luckily my employee stumbled over Alfresco JLan (http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/root/projects/alfresco-jlan/) and discovered that it could do a lot more than simply copy files from one server to another. It actually came with a full implementation of the SVCCTL Service. This allowed to build the Software I was trying to build for so long. It took quite some time to figgure out how to build such an application with the library that was available from Alfresco, but we managed to get it going. 

The general functionality was implemented pretty fast after that and we sarted implementing some logic that sent quite large amounts of data over Named Pipes using JLan. This is where we began having trouble. Errors complaning about the size of data packets keept on occuring, ruining the data-transfer. We avoided this by breaking everything down to really small pieces of data, but I was never really satisfied with this and it didn't look as if there was any support available from anywhere (sad). So this was only a Proof-Of-Concept solution.

Then one day I had the Idea to use a stripped-down minimal implementation of a JLan program and to have a look at what it does in WireShark and to try to replicate what JLan did in JCIFS. With this approach it took another two days and I was actually able to start a Windows service on a remote machine using JCIFS.

The rest of this tutorial will explain in detail the steps needed to do this. Hopefully this document might save the sanity of the one or the other developer also going nuts on something similar (wink)

Generating the Stub

Ok ... so before we can start coding we have to generate the stub code, that we will use do encode/decode the DCE/RPC requests. This is done using the midlc tool (http://jcifs.samba.org/src/midlc-0.6.1.tar.gz). Download it to a linux machine and build it:

Building midlc
$ cd libmba-0.9.1
$ make ar
$ cd ..
$ make

You can leave away the "make install" documented in the README.txt as this failled to execute and isn't really needed.

Now you have the "midlc" binary and can start generating code. So if you simply execute the "midlc" binary, it will present you the options:

Building midlc
cdutz@ubuntu:~/Test/midlc-0.6.1$ ./midlc 
usage: ./midlc [-v|-d] [-s <symtab>] [-t jcifs|java|samba|c] [-o outfile] [-Dmacro=defn] <filename>

Of course everybody knows what the "-v", "-d", "-s" and "-Dmacro" oprions are (wink) ... well ... I couln't really find any form of documentation that explained them. But it seemed that I didn't need them in order to get my code generated. 

The most important option is "-t", which controls for which library the code is generated for:

  • "jcifs": Generates code to be used with jcifs ... who would have thought about that!
  • "java": Generates code to be used with jarapac (http://sourceforge.net/projects/jarapac/) which seems to be yet another dead project in the DCE/RPC area.
  • "samba": Generates "segmentation faults" (wink)
  • "c": Generates an whole bunch of C code and header files. As we will be using Java I didn't really waste a second on this.

The other option that seemed obvious was "-o" this controls the name of the output file. So if you set that option to "myCoolFile", then midlc will generate myCoolFile.java" (or an unnamed segmentation fault (wink) or "myCoolFile.c" and "myCoolFile.h").

Inside the midlc package there is a directory "idl" which contains several IDL files for different Windows services. The one I was interested in was "idl/svcctl.idl", so I used that one to generate the initial stub:

Building midlc
cdutz@ubuntu:~/Test/midlc-0.6.1$ ./midlc -t jcifs -o svcctl idl/svcctl.idl

The resulting "svcctl.java" was now the starting point of my JCIFS-based SVCCTL client.

Making DCE/RPC calls from Java using JCIFS

So if you generated the code as described in the previous chapter and you use this unmodified, all you will get are "DCERPC_FAULT_PROTO_ERROR" errors. As I had absolutely no clue on how to use the Stub classes and perform DCE/RPC requests, these Errors were driving me nuts. Expecially when comparing my WireShark dumps with those of PSExec. But when I started comparing them to my JLan POC I was able to reverse enginer what to do in JCIFS. I did this by looing at what packets were sent by JLan and then to search through the JCIFS source to find something that looked similar and somehow figgured out how to use them. 

One thing I found relatively anoying was the wide usage of packet-level accessability. So I had to implement the code for accessing SVCCTL in a class in the "jcifs.smb" package to be able to access some important methods of the SmbTransport class.

After setting up the initial handshake stuff identical to the way JLan did it, I wanted to actually perfom my first SVCCTL call. The result was my ever-so-beloved "DCERPC_FAULT_PROTO_ERROR". But this time comparing the bytes generated by JLan and JCIFS showed me that the packet-type "ptype" being sent was set to "0xff" (-1) instead of "0" (Request). So I modified the generated classes so manually set "ptype" to "0" and was actually able to perform my calls without any problems (smile)

So now comes the code for starting the "Remoteregistry" service on a remote Windows system using JCIFS:

package jcifs.smb;
import de.cware.utils.libPsExec.msrpc.svcctl;
import jcifs.UniAddress;
import jcifs.dcerpc.DcerpcBinding;
import jcifs.dcerpc.DcerpcHandle;
import jcifs.dcerpc.rpc;
import jcifs.netbios.NbtAddress;
import jcifs.netbios.NbtException;
import jcifs.netbios.NbtSocket;
/**
 * Created with IntelliJ IDEA.
 * User: cdutz
 * Date: 08.03.12
 * Time: 10:32
 */
public class Test {
    // Without registering our class at DcerpcBinding we are not able to use the pipe "\pipe\svcctl".
    static {
        DcerpcBinding.addInterface("svcctl", svcctl.getSyntax());
    }
    public static void main(String[] args) throws Exception {
        // Create a NbtSocket to the remote machine.
        // It seems the socket itself isn't needed at all, it's just
        // for testing if the nbtAddress was generated correctly.
        //
        // The structure of this code was strongly inspired by JLan
        final String host = "192.168.178.35";
        NbtAddress nbtAddress = NbtAddress.getByName(host);
        NbtSocket nbtSocket;
        try {
            nbtSocket = new NbtSocket(nbtAddress, 139);
        } catch (NbtException nbte) {
            if ((nbte.errorClass == 2) && (nbte.errorCode == 130)) {
                final NbtAddress[] nbtAddresses = NbtAddress.getAllByAddress(host);
                nbtAddress = nbtAddresses[0];
                nbtSocket = new NbtSocket(nbtAddress, 139);
            } else {
                throw nbte;
            }
        }
        nbtSocket.close();
        // Don't know what this is needed for, but JLan did it.
        SmbTransport smbTransport = SmbTransport.getSmbTransport(new UniAddress(nbtAddress), 445);
        smbTransport.connect();
        SmbSession smbSession = smbTransport.getSmbSession(getNtlmAuthentication());
        SmbTree ipcShare = smbSession.getSmbTree("IPC$", null);
        ipcShare.treeConnect(null, null);

        try {
            // Open the SCManager on the remote machine and get a handle
            // for that open instance (scManagerHandle).
            rpc.policy_handle scManagerHandle = new rpc.policy_handle();
            svcctl.OpenSCManager openSCManagerRpc = new svcctl.OpenSCManager("\\\\" + host, null,
                    (0x000F0000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020), scManagerHandle);
            DcerpcHandle handle = DcerpcHandle.getHandle(
                    "ncacn_np:" + host + "[\\pipe\\svcctl]", getNtlmAuthentication());
            handle.sendrecv(openSCManagerRpc);
            if (openSCManagerRpc.retval != 0) {
                throw new SmbException(openSCManagerRpc.retval, true);
            }
            // Get a handle of the "Remoteregistry" service (svcHandle).
            rpc.policy_handle svcHandle = new rpc.policy_handle();
            svcctl.OpenService openServiceRpc = new svcctl.OpenService(
                    scManagerHandle, "Remoteregistry", 0x0F01FF, svcHandle);
            handle.sendrecv(openServiceRpc);
            if(openServiceRpc.retval != 0) {
                throw new SmbException(openServiceRpc.retval, true);
            }
            // Use the service handle and tell the remote machine to start the service.
            svcctl.StartService startServiceRpc = new svcctl.StartService(svcHandle, 0, new String[0]);
            handle.sendrecv(startServiceRpc);
            if(startServiceRpc.retval != 0) {
                throw new SmbException(startServiceRpc.retval, true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static NtlmPasswordAuthentication getNtlmAuthentication() {
        return new NtlmPasswordAuthentication("localhost", "testadmin", "testpass");
    }
}

For the numeric codes used in the example, please have a look at Microsofts specification document at: http://msdn.microsoft.com/en-us/library/cc245832(v=prot.13).aspx

As I mentioned, I simply replicated the calles done by JLan ... after having a running POC I had another look at the code and noticed that there seemed to be no relation at all between the smbTransport and the DCE/RPC requests. I assumed this "simply has to be done". But now I simply commented parts out, that I assumed to be obsolete. The following code conatins all code needed to perform the actions I wanted:

package my.other.cool.package;
import de.cware.utils.libPsExec.msrpc.svcctl;
import jcifs.dcerpc.DcerpcBinding;
import jcifs.dcerpc.DcerpcHandle;
import jcifs.dcerpc.rpc;
/**
 * Created with IntelliJ IDEA.
 * User: cdutz
 * Date: 08.03.12
 * Time: 10:32
 */
public class Test {
    // Without registering our class at DcerpcBinding we are not able to use the pipe "\pipe\svcctl".
    static {
        DcerpcBinding.addInterface("svcctl", svcctl.getSyntax());
    }
    public static void main(String[] args) throws Exception {
        final String host = "192.168.178.35";
        try {
            // Open the SCManager on the remote machine and get a handle
            // for that open instance (scManagerHandle).
            rpc.policy_handle scManagerHandle = new rpc.policy_handle();
            svcctl.OpenSCManager openSCManagerRpc = new svcctl.OpenSCManager("\\\\" + host, null,
                    (0x000F0000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020), scManagerHandle);
            DcerpcHandle handle = DcerpcHandle.getHandle(
                    "ncacn_np:" + host + "[\\pipe\\svcctl]", getNtlmAuthentication());
            handle.sendrecv(openSCManagerRpc);
            if (openSCManagerRpc.retval != 0) {
                throw new SmbException(openSCManagerRpc.retval, true);
            }


            // Get a handle of the "Remoteregistry" service (svcHandle).
            rpc.policy_handle svcHandle = new rpc.policy_handle();
            svcctl.OpenService openServiceRpc = new svcctl.OpenService(
                    scManagerHandle, "Remoteregistry", 0x0F01FF, svcHandle);
            handle.sendrecv(openServiceRpc);
            if(openServiceRpc.retval != 0) {
                throw new SmbException(openServiceRpc.retval, true);
            }

            // Use the service handle and tell the remote machine to start the service.
            // This will fail if the service is already running. 
            svcctl.StartService startServiceRpc = new svcctl.StartService(svcHandle, 0, new String[0]);
            handle.sendrecv(startServiceRpc);
            if(startServiceRpc.retval != 0) {
                throw new SmbException(startServiceRpc.retval, true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static NtlmPasswordAuthentication getNtlmAuthentication() {
        return new NtlmPasswordAuthentication("localhost", "testadmin", "testpass");
    }
}

As now all references to NbtAddress were eliminated, my code is now also free to run in any package I like (smile)

Extending the default svcctl.idl

The Samba guys could only reverse engineer service calls that were actually used by official tools. I found the Book: "DCE/RPC over SMB - Samba and Windows NT Domain Internals" from Luke Kenneth Casson Leighton (Yes ... that's only one guy (wink)) to describe this process of reverse-engineering the SMB protocol very nicely. It seems that there were no tools available that made use of the ability to remotely create or delete services therefore they could never get network captures of this and therefore never integrate these calls into their IDL.

But now we have the official IDL from Microsoft and we can use this to complete the idl-definition and use this to complete the Stub for the SVCCTL DCE/RPC stub.

To be continued ...

  • No labels