Link to home
Start Free TrialLog in
Avatar of krakatoa
krakatoaFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Registry access via Java.

It would be fair to say that I have around zero idea whether I am on the right track with this one or not, which is to get into the Windows Registry with Java. I need to read a value or two from there. Don't need to amend or write any new values.

So I borrowed a piece of code from elsewhere on the 'net, the narrative of which says that it can access the registry for such read write ops. It's reliant on Preferences, and the code itself seems to be for static access only, which means that I simply compile it, and supply my own tiny class to call the one method that looks like it should deliver the entry I am looking for.  (I'm fishing for the KV pair for a USB memory stick, which is held by HKEY_LOCAL_MACHINE somewhere).

All the code compiles and seems to run, but I can't see past this error, nor why (fundamentally) it occurs, and even less do I know what to do about it :
Nov 08, 2018 12:06:44 AM java.util.prefs.WindowsPreferences <init>
WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0
x80000002. Windows RegCreateKeyEx(...) returned error code 5.

Open in new window


If anything I've said makes sense and there's a suggestion what should come next,, I'd be pleased to hear.
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

I was struck by the incongruence of the error/warning with your declared objective (a read operation). The message seems to be oriented to writing.

I think the explanation is to be found HERE
I can't say that i can confirm the validity of the above from experience, but i'm guessing it's quite correct. In that case, you would need to read the registry in a different way, perhaps by a Runtime.exec of the Window's  command line reg utility
Avatar of krakatoa

ASKER

Interesting, thanks. Yes, on the surface RegCreateKey . .  if that is what you are referring to . . . does appear to suggest writing. I'm reluctant to speculate on how accurate the narrative is in that post however (it has zero points for one thing, which rarely bodes well for comments over there), but in addition the lit. itself seems clear what Preferences are able to tackle :

Typical implementations include flat files, OS-specific registries, directory servers and SQL databases. [. . . ]

so I am not sure. If it would be of interest / help, I can post the code I'm using. Please let me know about that.

Runtime.exec was suggested in another source I was looking at, true. I may have to go that route, but it would be far 'nicer' to keep it all really inside Java rather than stoop to relying on the OS. If I can tell you anything I've missed that might be further help, plmk. :)

Edit :

I should have mentioned that I am not happy with the params I'm passing to the method though. One in particular, which is String 'key' is a mystery. Reason being the next param is String 'valueName'. I'm not sure at all that I've differentiated / specified these in the right way.
Yes, it's probably a good idea to post the code.
You'll probably find someone who's tried to read a value and knows what to do from experience
Right you are, I’ll get it together.
The code to ostensibly access the Windows Registry :

/**
 * Pure Java Windows Registry access.
 * Modified by petrucio@stackoverflow(828681) to add support for
 * reading (and writing but not creating/deleting keys) the 32-bits
 * registry view from a 64-bits JVM (KEY_WOW64_32KEY)
 * and 64-bits view from a 32-bits JVM (KEY_WOW64_64KEY).
 *****************************************************************************/

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.Preferences;

public class WinRegistry {
  public static final int HKEY_CURRENT_USER = 0x80000001;
  public static final int HKEY_LOCAL_MACHINE = 0x80000002;
  public static final int REG_SUCCESS = 0;
  public static final int REG_NOTFOUND = 2;
  public static final int REG_ACCESSDENIED = 5;

  public static final int KEY_WOW64_32KEY = 0x0200;
  public static final int KEY_WOW64_64KEY = 0x0100;

  private static final int KEY_ALL_ACCESS = 0xf003f;
  private static final int KEY_READ = 0x20019;
  private static Preferences userRoot = Preferences.userRoot();
  private static Preferences systemRoot = Preferences.systemRoot();
  private static Class<? extends Preferences> userClass = userRoot.getClass();
  private static Method regOpenKey = null;
  private static Method regCloseKey = null;
  private static Method regQueryValueEx = null;
  private static Method regEnumValue = null;
  private static Method regQueryInfoKey = null;
  private static Method regEnumKeyEx = null;
  private static Method regCreateKeyEx = null;
  private static Method regSetValueEx = null;
  private static Method regDeleteKey = null;
  private static Method regDeleteValue = null;

  static {
    try {
      regOpenKey     = userClass.getDeclaredMethod("WindowsRegOpenKey",     new Class[] { int.class, byte[].class, int.class });
      regOpenKey.setAccessible(true);
      regCloseKey    = userClass.getDeclaredMethod("WindowsRegCloseKey",    new Class[] { int.class });
      regCloseKey.setAccessible(true);
      regQueryValueEx= userClass.getDeclaredMethod("WindowsRegQueryValueEx",new Class[] { int.class, byte[].class });
      regQueryValueEx.setAccessible(true);
      regEnumValue   = userClass.getDeclaredMethod("WindowsRegEnumValue",   new Class[] { int.class, int.class, int.class });
      regEnumValue.setAccessible(true);
      regQueryInfoKey=userClass.getDeclaredMethod("WindowsRegQueryInfoKey1",new Class[] { int.class });
      regQueryInfoKey.setAccessible(true);
      regEnumKeyEx   = userClass.getDeclaredMethod("WindowsRegEnumKeyEx",   new Class[] { int.class, int.class, int.class });  
      regEnumKeyEx.setAccessible(true);
      regCreateKeyEx = userClass.getDeclaredMethod("WindowsRegCreateKeyEx", new Class[] { int.class, byte[].class });
      regCreateKeyEx.setAccessible(true);  
      regSetValueEx  = userClass.getDeclaredMethod("WindowsRegSetValueEx",  new Class[] { int.class, byte[].class, byte[].class });  
      regSetValueEx.setAccessible(true); 
      regDeleteValue = userClass.getDeclaredMethod("WindowsRegDeleteValue", new Class[] { int.class, byte[].class });  
      regDeleteValue.setAccessible(true); 
      regDeleteKey   = userClass.getDeclaredMethod("WindowsRegDeleteKey",   new Class[] { int.class, byte[].class });  
      regDeleteKey.setAccessible(true); 
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  private WinRegistry() {  }

  /**
   * Read a value from key and value name
   * @param hkey   HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
   * @param key
   * @param valueName
   * @param wow64  0 for standard registry access (32-bits for 32-bit app, 64-bits for 64-bits app)
   *               or KEY_WOW64_32KEY to force access to 32-bit registry view,
   *               or KEY_WOW64_64KEY to force access to 64-bit registry view
   * @return the value
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static String readString(int hkey, String key, String valueName, int wow64) 
    throws IllegalArgumentException, IllegalAccessException,
    InvocationTargetException 
  {
    if (hkey == HKEY_LOCAL_MACHINE) {
      return readString(systemRoot, hkey, key, valueName, wow64);
    }
    else if (hkey == HKEY_CURRENT_USER) {
      return readString(userRoot, hkey, key, valueName, wow64);
    }
    else {
      throw new IllegalArgumentException("hkey=" + hkey);
    }
  }

  /**
   * Read value(s) and value name(s) form given key 
   * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
   * @param key
   * @param wow64  0 for standard registry access (32-bits for 32-bit app, 64-bits for 64-bits app)
   *               or KEY_WOW64_32KEY to force access to 32-bit registry view,
   *               or KEY_WOW64_64KEY to force access to 64-bit registry view
   * @return the value name(s) plus the value(s)
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static Map<String, String> readStringValues(int hkey, String key, int wow64) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    if (hkey == HKEY_LOCAL_MACHINE) {
      return readStringValues(systemRoot, hkey, key, wow64);
    }
    else if (hkey == HKEY_CURRENT_USER) {
      return readStringValues(userRoot, hkey, key, wow64);
    }
    else {
      throw new IllegalArgumentException("hkey=" + hkey);
    }
  }

  /**
   * Read the value name(s) from a given key
   * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
   * @param key
   * @param wow64  0 for standard registry access (32-bits for 32-bit app, 64-bits for 64-bits app)
   *               or KEY_WOW64_32KEY to force access to 32-bit registry view,
   *               or KEY_WOW64_64KEY to force access to 64-bit registry view
   * @return the value name(s)
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static List<String> readStringSubKeys(int hkey, String key, int wow64) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    if (hkey == HKEY_LOCAL_MACHINE) {
      return readStringSubKeys(systemRoot, hkey, key, wow64);
    }
    else if (hkey == HKEY_CURRENT_USER) {
      return readStringSubKeys(userRoot, hkey, key, wow64);
    }
    else {
      throw new IllegalArgumentException("hkey=" + hkey);
    }
  }

  /**
   * Create a key
   * @param hkey  HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE
   * @param key
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static void createKey(int hkey, String key) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int [] ret;
    if (hkey == HKEY_LOCAL_MACHINE) {
      ret = createKey(systemRoot, hkey, key);
      regCloseKey.invoke(systemRoot, new Object[] { new Integer(ret[0]) });
    }
    else if (hkey == HKEY_CURRENT_USER) {
      ret = createKey(userRoot, hkey, key);
      regCloseKey.invoke(userRoot, new Object[] { new Integer(ret[0]) });
    }
    else {
      throw new IllegalArgumentException("hkey=" + hkey);
    }
    if (ret[1] != REG_SUCCESS) {
      throw new IllegalArgumentException("rc=" + ret[1] + "  key=" + key);
    }
  }

  /**
   * Write a value in a given key/value name
   * @param hkey
   * @param key
   * @param valueName
   * @param value
   * @param wow64  0 for standard registry access (32-bits for 32-bit app, 64-bits for 64-bits app)
   *               or KEY_WOW64_32KEY to force access to 32-bit registry view,
   *               or KEY_WOW64_64KEY to force access to 64-bit registry view
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static void writeStringValue
    (int hkey, String key, String valueName, String value, int wow64) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    if (hkey == HKEY_LOCAL_MACHINE) {
      writeStringValue(systemRoot, hkey, key, valueName, value, wow64);
    }
    else if (hkey == HKEY_CURRENT_USER) {
      writeStringValue(userRoot, hkey, key, valueName, value, wow64);
    }
    else {
      throw new IllegalArgumentException("hkey=" + hkey);
    }
  }

  /**
   * Delete a given key
   * @param hkey
   * @param key
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static void deleteKey(int hkey, String key) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int rc = -1;
    if (hkey == HKEY_LOCAL_MACHINE) {
      rc = deleteKey(systemRoot, hkey, key);
    }
    else if (hkey == HKEY_CURRENT_USER) {
      rc = deleteKey(userRoot, hkey, key);
    }
    if (rc != REG_SUCCESS) {
      throw new IllegalArgumentException("rc=" + rc + "  key=" + key);
    }
  }

  /**
   * delete a value from a given key/value name
   * @param hkey
   * @param key
   * @param value
   * @param wow64  0 for standard registry access (32-bits for 32-bit app, 64-bits for 64-bits app)
   *               or KEY_WOW64_32KEY to force access to 32-bit registry view,
   *               or KEY_WOW64_64KEY to force access to 64-bit registry view
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public static void deleteValue(int hkey, String key, String value, int wow64) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int rc = -1;
    if (hkey == HKEY_LOCAL_MACHINE) {
      rc = deleteValue(systemRoot, hkey, key, value, wow64);
    }
    else if (hkey == HKEY_CURRENT_USER) {
      rc = deleteValue(userRoot, hkey, key, value, wow64);
    }
    if (rc != REG_SUCCESS) {
      throw new IllegalArgumentException("rc=" + rc + "  key=" + key + "  value=" + value);
    }
  }

  //========================================================================
  private static int deleteValue(Preferences root, int hkey, String key, String value, int wow64)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS | wow64)
    });
    if (handles[1] != REG_SUCCESS) {
      return handles[1];  // can be REG_NOTFOUND, REG_ACCESSDENIED
    }
    int rc =((Integer) regDeleteValue.invoke(root, new Object[] { 
          new Integer(handles[0]), toCstr(value) 
          })).intValue();
    regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
    return rc;
  }

  //========================================================================
  private static int deleteKey(Preferences root, int hkey, String key) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int rc =((Integer) regDeleteKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key)
    })).intValue();
    return rc;  // can REG_NOTFOUND, REG_ACCESSDENIED, REG_SUCCESS
  }

  //========================================================================
  private static String readString(Preferences root, int hkey, String key, String value, int wow64)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key), new Integer(KEY_READ | wow64)
    });
    if (handles[1] != REG_SUCCESS) {
      return null; 
    }
    byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] {
        new Integer(handles[0]), toCstr(value)
    });
    regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
    return (valb != null ? new String(valb).trim() : null);
  }

  //========================================================================
  private static Map<String,String> readStringValues(Preferences root, int hkey, String key, int wow64)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    HashMap<String, String> results = new HashMap<String,String>();
    int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key), new Integer(KEY_READ | wow64)
    });
    if (handles[1] != REG_SUCCESS) {
      return null;
    }
    int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] {
        new Integer(handles[0])
    });

    int count  = info[2]; // count  
    int maxlen = info[3]; // value length max
    for(int index=0; index<count; index++)  {
      byte[] name = (byte[]) regEnumValue.invoke(root, new Object[] {
          new Integer(handles[0]), new Integer(index), new Integer(maxlen + 1)
      });
      String value = readString(hkey, key, new String(name), wow64);
      results.put(new String(name).trim(), value);
    }
    regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
    return results;
  }

  //========================================================================
  private static List<String> readStringSubKeys(Preferences root, int hkey, String key, int wow64)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    List<String> results = new ArrayList<String>();
    int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key), new Integer(KEY_READ | wow64) 
        });
    if (handles[1] != REG_SUCCESS) {
      return null;
    }
    int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] {
        new Integer(handles[0])
    });

    int count  = info[0]; // Fix: info[2] was being used here with wrong results. Suggested by davenpcj, confirmed by Petrucio
    int maxlen = info[3]; // value length max
    for(int index=0; index<count; index++)  {
      byte[] name = (byte[]) regEnumKeyEx.invoke(root, new Object[] {
          new Integer(handles[0]), new Integer(index), new Integer(maxlen + 1)
          });
      results.add(new String(name).trim());
    }
    regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
    return results;
  }

  //========================================================================
  private static int [] createKey(Preferences root, int hkey, String key)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    return (int[]) regCreateKeyEx.invoke(root, new Object[] {
      new Integer(hkey), toCstr(key)
    });
  }

  //========================================================================
  private static void writeStringValue(Preferences root, int hkey, String key, String valueName, String value, int wow64)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key), new Integer(KEY_ALL_ACCESS | wow64)
    });
    regSetValueEx.invoke(root, new Object[] { 
          new Integer(handles[0]), toCstr(valueName), toCstr(value) 
          }); 
    regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
  }

  //========================================================================
  // utility
  private static byte[] toCstr(String str) {
    byte[] result = new byte[str.length() + 1];

    for (int i = 0; i < str.length(); i++) {
      result[i] = (byte) str.charAt(i);
    }
    result[str.length()] = 0;
    return result;
  }
}

Open in new window


Code being used to call one of its methods :

try{
System.out.println(WinRegistry.readStringValues(WinRegistry.HKEY_LOCAL_MACHINE, "WindowsRegQueryInfoKey1"/*"USBSTOR\\Disk&Ven_SanDisk&Prod_Cruzer_Blade&Rev_2.01\\8d530310010217111315&0 "*/,WinRegistry.KEY_WOW64_64KEY));
}catch(Exception bombed){bombed.printStackTrace();}

Open in new window


As I mentioned earlier, my calling args are highly suspect.
Could it be that the app needs to run with Administrator privileges in order to access the registry?
That sounds highly plausible . . . but I don't know how to (if it's even possible) to award those privileges through Java code. I'm assuming that this being my own machine, that I'm logged in with those privileges with my user account.

\O/
How are you running the app? If it's from the command line, try opening the Command Prompt as Administrator, then running the app.
Ok, so that "worked" (i.e. no Errors or Exceptions), *but it returns (well. prints to the console) just 'null'. So it seems like the params are bad somehow, wouldn't you say?

Or . . . as CEHJ's link suggests, access is somehow limited to certain parts or keys.
I had thought about that (privileges) myself but of course in a sense that is academic in terms of general software usage. You can't require (in most cases) that your user have admin privileges.
For reading from the registry, would code such as this work for you?

Preferences.userRoot().node(nodePath).get(key, defaultValue);

Preferences.userRoot().node(nodePath).put(key, value);

Open in new window


I'm not sure that all that other stuff is necessary?
@CEHJ. Right. Correct in practice naturally. But it would still be very desirable to see if it can be done at all, and how.

@ Jan Louwerens

I'll give it a go - presumably the params in what I posted are then passable ? What do your 'key', 'value' and 'defaultName' params represent actually please ?
nodePath = the path to the the registry key that you're trying to access

key = the name of the key you're trying to access
defaultValue = the value to be returned in the event that this node has no value associated with key


Note that userRoot() most likely refers to HKEY_CURRENT_USER, while systemRoot() most likely refers to HKEY_LOCAL_MACHINE
Right now, the attempt is with :

try{//\\SYSTEM\\CurrentControlSet\\Enum\\USBSTOR
System.out.println(java.util.prefs.Preferences.systemRoot().node("HKEY_LOCAL_MACHINE").get("HardwareID", "No Value"));
}catch(Exception bombed){bombed.printStackTrace();}

Open in new window


. . . which compiles but produces "No Value", (as coded), accompanied by :  

Nov 08, 2018 11:31:40 PM java.util.prefs.WindowsPreferences <init>
WARNING: Could not create windows registry node Software\JavaSoft\Prefs\/H/K/E/Y
_/L/O/C/A/L_/M/A/C/H/I/N/E at root 0x80000002. Windows RegCreateKeyEx(...) retur
ned error code 5.
Nov 08, 2018 11:31:40 PM java.util.prefs.WindowsPreferences WindowsRegOpenKey1
WARNING: Trying to recreate Windows registry node Software\JavaSoft\Prefs\/H/K/E
/Y_/L/O/C/A/L_/M/A/C/H/I/N/E at root 0x80000002.
Nov 08, 2018 11:31:40 PM java.util.prefs.WindowsPreferences openKey
WARNING: Could not open windows registry node Software\JavaSoft\Prefs\/H/K/E/Y_/
L/O/C/A/L_/M/A/C/H/I/N/E at root 0x80000002. Windows RegOpenKey(...) returned er
ror code 2.
No Value

(. . .  with the same effect when userRoot is substituted for systemRoot).
It looks like Java automatically starts at the node "Software\JavaSoft\Prefs" as the root node. (This was mentioned in the link CEHJ posted.)

Perhaps that's what all that other registry stuff you posted was for. To be able to access nodes outside of that "root" from within Java.

I looked at that attached library, but without any examples, I'm not sure the proper way to use it.
OK. It's now time to get at the goal. Is the goal to detect the presence of a usb storage medium for IO purposes?
Yes, that’s correct. If I read the registry correctly, devices which are not present but have been, retain a registry entry. (The entry on the Enum subkey however (afaics the only other one relating to the device), IS live). I’d be happy capturing only the first one (i tbink it’s one of the Hardware keys).
In that case, there are probably better ways.
A technique i've used to check a device for its ability to do IO is to scan for a sentinel file i've placed on it. File.listRoots() (cross-platform) should include your stick if it's present
OK, well I got this out of that call :

C:\
D:\
E:\
F:\

. . .  now how would I ascertain which was the drive letter in play on a foreign machine fir the memory stick, and secondly, how to get the actual files listed on, say, the F:\ in the above scenario? Or would it be that all the drives have to be searched, using the name of the sentinel file as the param ?
Or would it be that all the drives have to be searched, using the name of the sentinel file as the param ?

Yes. Of course, you can eliminate C: if (you might not be on Windows) that is part of the array. You would place the sentinel file in the root of the root so not much searching would be necessary
The problem wit this approach however is that anyone can copy files from the device and place them on another, or send them over a network. That cannot happen with the HardwareID on a USB stick, afaik, as it's embedded in ROM.  Reproducing that value for use in the registry key of another machine may be possible, but far more difficult than simply copying a file. I wonder if you'd like to comment on that ?

Also, I'm not sure what you mean by the "root of the root" - could you kindly clarify ?
I wonder if you'd like to comment on that ?
Unfortunately i don't understand it. Please try to explain it in terms of a goal

Also, I'm not sure what you mean by the "root of the root" - could you kindly clarify ?
e.g. D:\somefile.txt E:\somefile.txt (D: and E: are 'roots' in terms of the aforementioned Java method) those file are in the root directory of those roots
are in the root directory of those roots
ok. 'In my day', it was just the root, iirc. ;

in terms of a goal

The goal is that a user, in possession of the programme, ought not to be able to use it without some form of permission; permission which resides in and relies on the relative difficulty of obtaining the (and even of having any idea *in the first place*) information concerning the memory stick's ID. The ID can't easily be seen from the outside in any case, without some investigation into the registry, which is already reasonably tricky, whereas  file is very visible. The stick's ID is also only one param of several (or parts thereof) which could be chosen to act as the authentication token, which extends the task facing someone who is trying to 'guess' what the actual token is.
OK, so the goal is DRM. That's a bit different. If you use an NTFS file system on the stick you can hide files though:
https://www.howtogeek.com/howto/windows-vista/stupid-geek-tricks-hide-data-in-a-secret-text-file-compartment/
I . . . I don't want to give the impression that I'm being flippant or dismissive of that idea, but it sounds like something right out of a junior school wet-weather lunchtime filler session. I've no doubt it works, and despite the fact I'd never heard of it (which of course means very little) it is neat in a way, but I think I'd have to rank it as the last choice option at the moment.

Which leaves me torn between further study of the registry option, and Runtime.exec(). I'd prefer to get the first of those working.
Any thoughts on this :

Link to some Java source code.
I know what you mean. The correct solution would probably be a very different DRM approach

Runtime.exec is no worse than that encapsulation-busting reflection code but what are you going to do for your Mac users (and others)?
Yes.

As for Mac and other users, I am not so focused on that for now. A different proposition can be considered at a later point for that I think.

The advantage of the registry is it ties it to the physical presence of a discrete piece of hardware. That would be highly effective to harness.

This seems like an interesting one.
OK. Try that reflection-based class that you posted
So I had a go at the reflect code . . . using just one of the test statements, once looking for HardwareID and second time for FriendlyName. Evidently, it ran, but although the values are obviously in the registry, it returned null.

>java WindowsRegistry
HKLM\SYSTEM\ControlSet001\Enum\USBSTOR\Disk&Ven_SanDisk&Prod_Cruzer_Blade&Rev_2.
01
  Reading: FriendlyName
    >>null<<

Open in new window

That doesn't show how you called the code. Also it's probably better to post the reflection code here as well as its link
Sure, well all I did was to change the params in the linked-to code to this at the appropriate line :

testKey(HKEY_LOCAL_MACHINE, "SYSTEM\\ControlSet001\\Enum\\USBSTOR\\Disk&Ven_SanDisk&Prod_Cruzer_Blade&Rev_2.01", "FriendlyName");

Open in new window


which returned the null.

Do you say I post the entirety of the 3rd party? I mean I *can, but it is no different to that in the link . . . ?
Well it's just that
a. the link might change in the future
b. it's easier to refer to it here

Can you screenshot the key and value in the registry?
Called code:

package net.infotrek.util;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
 
/**
 * Very evil class to read from the Windows registry by breaking into the WindowsPreference
 * class methods and forcing them to be accessible.
 * <p/>
 * N.B. All access to WindowsPreferences (rather than just Preferences) must be through introspection,
 * as this class only exists on Windows platforms.
 *
 * @author David Croft (<a href="http://www.davidc.net">www.davidc.net</a>)
 * @version $Id: WindowsRegistry.java 285 2009-06-18 17:48:28Z david $
 */
@SuppressWarnings({"HardCodedStringLiteral", "StringConcatenation"})
public class WindowsRegistry
{
  /* Windows hives */
  public static final int HKEY_CURRENT_USER = 0x80000001;
  public static final int HKEY_LOCAL_MACHINE = 0x80000002;
 
  /* Windows security masks */
  private static final int KEY_READ = 0x20019;
 
  /* Constants used to interpret returns of native functions */
  private static final int NATIVE_HANDLE = 0;
  private static final int ERROR_CODE = 1;
 
  /* Windows error codes. */
  private static final int ERROR_SUCCESS = 0;
  private static final int ERROR_FILE_NOT_FOUND = 2;
 
  public static String getKeySz(int hive, String keyName, String valueName)
          throws BackingStoreException
  {
    if (hive != HKEY_CURRENT_USER && hive != HKEY_LOCAL_MACHINE) {
      throw new IllegalArgumentException("Invalid hive " + hive);
    }
 
    final Class clazz = Preferences.userRoot().getClass();
 
    try {
      final Method openKeyMethod = clazz.getDeclaredMethod("WindowsRegOpenKey",
              int.class, byte[].class, int.class);
      openKeyMethod.setAccessible(true);
 
      final Method closeKeyMethod = clazz.getDeclaredMethod("WindowsRegCloseKey",
              int.class);
      closeKeyMethod.setAccessible(true);
 
      final Method queryValueMethod = clazz.getDeclaredMethod("WindowsRegQueryValueEx",
              int.class, byte[].class);
      queryValueMethod.setAccessible(true);
 
      int[] result = (int[]) openKeyMethod.invoke(null, hive, stringToByteArray(keyName), KEY_READ);
      if (result[ERROR_CODE] != ERROR_SUCCESS) {
        if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
          throw new BackingStoreException("Not Found error opening key " + keyName);
        }
        else {
          throw new BackingStoreException("Error " + result[ERROR_CODE] + " opening key " + keyName);
        }
      }
 
      int hKey = result[NATIVE_HANDLE];
 
      byte[] b = (byte[]) queryValueMethod.invoke(null, hKey, stringToByteArray(valueName));
      closeKeyMethod.invoke(null, hKey);
 
      if (b == null)
        return null;
      else
        return byteArrayToString(b);
    }
    catch (InvocationTargetException e) {
      throw new BackingStoreException(e.getCause());
    }
    catch (NoSuchMethodException e) {
      throw new BackingStoreException(e);
    }
    catch (IllegalAccessException e) {
      throw new BackingStoreException(e);
    }
  }
 
  /**
   * Returns this java string as a null-terminated byte array
   *
   * @param str The string to convert
   * @return The resulting null-terminated byte array
   */
  private static byte[] stringToByteArray(String str)
  {
    byte[] result = new byte[str.length() + 1];
    for (int i = 0; i < str.length(); i++) {
      result[i] = (byte) str.charAt(i);
    }
    result[str.length()] = 0;
    return result;
  }
 
  /**
   * Converts a null-terminated byte array to java string
   *
   * @param array The null-terminated byte array to convert
   * @return The resulting string
   */
  private static String byteArrayToString(byte[] array)
  {
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < array.length - 1; i++) {
      result.append((char) array[i]);
    }
    return result.toString();
  }
 
  @SuppressWarnings({"UseOfSystemOutOrSystemErr", "HardcodedFileSeparator"})
  private static void testKey(int hive, String keyName, String valueName)
  {
    String s;
 
    if (hive == HKEY_CURRENT_USER) System.out.print("HKCU\\");
    if (hive == HKEY_LOCAL_MACHINE) System.out.print("HKLM\\");
    System.out.println(keyName);
    System.out.println("  Reading: " + valueName);
 
    try {
      s = getKeySz(hive, keyName, valueName);
      System.out.println("    >>" + s + "<<");
    }
    catch (BackingStoreException e) {
      System.out.println("    !!" + e.getMessage());
    }
  }
 
  @SuppressWarnings({"HardcodedFileSeparator", "DuplicateStringLiteralInspection"})
  public static void main(String args[])
  {
    testKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam", "InstallPath");
    testKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam\\Apps\\15660", "");
    testKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam\\Apps\\22000", "");
    testKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam\\Apps\\22010", "");
  }
}

Open in new window


User generated image
User generated image
and my one amendment to the above code was :

testKey(HKEY_LOCAL_MACHINE, "SYSTEM\\ControlSet001\\Enum\\USBSTOR\\Disk&Ven_SanDisk&Prod_Cruzer_Blade&Rev_2.01", "FriendlyName");

Open in new window


(or HardwareID instead of FriendlyName).
ASKER CERTIFIED SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Right, thanks.

I'm getting a bit tied in knots at the moment - for one thing, I thought that I remembered seeing a smaller set of entries in some location, where there was just a key called diskID, and that every time I inserted or removed the stick, that kv pair appeared / disappeared. Now I can't locate where that was happening, if it was.

I ( also ) seem to have two identical keys and subkeys one under controlset and the other currentcontrolset (which I assume from these names differentiates between historical and current device presence), but whereas previously I am as I said above certain that the entry appeared/ disappeared on connection / removal, this feature seems no longer to be evident, and both locations retain the id for the stick, whether it's in or out. I'd prefer - as you allude to  - to get the current presence or absence of the stick, and that's it.

Anyway, I go down a couple of levels and in this key is the diskId param I mentioned. However this no longer (if it ever did) reacts to the stick being in or out. So I'm encouraged by your last comment, but I can't seem to make it make sense operationally here just yet.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR\Disk&Ven_SanDisk&Prod_Cruzer_Blade&Rev_2.01\4C530310010217111315&0\Device Parameters\Partmgr

I don't know if what I've said it too garbled or unclear, but I'd be happy to explain anything I've missed or misunderstood.
I have scant knowledge of how usb storage is meant to behave under Windows, but you're really meant to eject such devices explicitly via the System Tray applet.

It might be better to reboot and have a fresh start
It seems tricky for sure.

I rebooted, and then used the tray to eject, but the entry remains.
I introduced an SD card that was lying around and never used before, and its entry showed up in the registry; but ejecting and removing it thereafter didn't remove the entry for it.

I think I mentioned earlier that it would be sufficient to see the key and name/value pair in the registry, even if the actuality of the device can't be obtained (at least at this point). I'm still not clear - which is an irritation - why a request for a name such as FriendlyName or HardwareId returns null.
I think it might be time to try Runtime.exec
Do you want to see that handled in another question?
Not necessarily. I think it might be good to have continuity
Before RT.exec . . . . I'm just wondering what actually is going on with .userRoot() and .systemRoot(). There isn't much narrative I can find that tells me what value(s) these calls really return. Are the returned Preferences actually the OS gateways to the Registry, and specifically to HKEY_LOCAL_MACHINE\SYSTEM and below in the case of systemRoot(), and everything in and below HKEY_CURRENT_USER?

And in the 'evil' code posted above, this passage :

final Class clazz = Preferences.userRoot().getClass();
 
    try {
      final Method openKeyMethod = clazz.getDeclaredMethod("WindowsRegOpenKey",
              int.class, byte[].class, int.class);
      openKeyMethod.setAccessible(true);

Open in new window


. . .  I'm "guessing" that clazz is instantiated from a kind of forced native interface call to Windows, and that Method openKeyMethod() - for example - invokes the native 'WindowsRegOpenKey' function, but without JNI? Sounds highly unlikely, but that's my only reading of it at the moment.

int.class, byte[].class are beyond my ken.
As we've mentioned above, Java doesn't allow unfettered access to the registry. What it does do is allow it to be used to store preferences. All that code does is try to prize open further access by surfacing native methods via reflection. It doesn't need JNI because that method is already native.
That's what I was trying to say in effect. It might be evil, but it's pretty neat.

So in theory, "prize open further access " *does or *could provide an in to the lot . . (?)

-------------------------------
I'm tempted now by this : implant the stick ID into the source code, and have client-server pre-SSL authentication done by an exchange on that ID. Then when/if the day comes along that the 'evil' code has been fully evaluated, it can go in, as the IDs won't have changed in any case.
That's what I was trying to say in effect. It might be evil, but it's pretty neat.

As long as it works. According to you though, it doesn't, does it?
It's also dubious if it requires admin privileges
You’re right, and that’s why I think it needs more research. It looks like admin priviliges are not required as the key was already returned but I suppose they might be for the name- value pair - not sure.
Oops, i meant "prise open" ;)
Yes. ; )

I wasn't going to correct you, nor use sic. ; )

Although the homophonicity was quite apposite given the subject. :)
I've made some headway retrieving the values from the name/value pairs of the stick's key, which is more than I had before. As they are static calls, it's quite straightforward getting them.
OK. What made the difference?
Lol - getting the call right helped enormously. ;)

I think I was getting the key part mixed up with the names.
Calling this :

//========================================================================
  public static Map<String,String> readStringValues(Preferences root, int hkey, String key, int wow64)
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
  {
    HashMap<String, String> results = new HashMap<String,String>();
    int[] handles = (int[]) regOpenKey.invoke(root, new Object[] {
        new Integer(hkey), toCstr(key), new Integer(KEY_READ | wow64)
    });
    if (handles[1] != REG_SUCCESS) {
      return null;
    }
    int[] info = (int[]) regQueryInfoKey.invoke(root, new Object[] {
        new Integer(handles[0])
    });

    int count  = info[2]; // count  
    int maxlen = info[3]; // value length max
    for(int index=0; index<count; index++)  {
      byte[] name = (byte[]) regEnumValue.invoke(root, new Object[] {
          new Integer(handles[0]), new Integer(index), new Integer(maxlen + 1)
      });
      String value = readString(hkey, key, new String(name), wow64);
      results.put(new String(name).trim(), value);
    }
    regCloseKey.invoke(root, new Object[] { new Integer(handles[0]) });
    return results;
  }

  //========================================================================

Open in new window

Calling this :
Where did that come from - did you write it?
No I didn’t write it. It’s some adaptation of the original one I posted.
:)
:)