zhangyuang / node-ffi-rs

Implement ffi in Node.js by Rust and NAPI
MIT License
191 stars 7 forks source link

Help with creating and testing NULL pointers #79

Closed btsimonh closed 1 week ago

btsimonh commented 1 month ago

Current ffi-rs version

Print current Node.js info with the following code

"version": "1.0.96",

$ ls node_modules/@yuuang node_modules\@yuuang\ffi-rs-win32-x64-msvc + i164

Current Node.js arch

22.10.0

Print current Node.js info with the following code

$ node -e "console.log(process.arch, process.platform)" x64 win32

Descibe your problem in detail

How do I create a null pointer? Howe do I compare a pointer to null?

What's your expect result

I expect to be able to understand how to pass null pointers to API functions, and to detect null pointers.

The reproduction repo address

Full Example:

Here we are getting certificates from the windows certificate store. The CertOpenSystemStoreA(hProv, Storename) call's first parameter should be a null pointer. I have forced this with an I64 = 0???

context = CertEnumCertificatesInStore( handle, context ) must be passed context as null first call, then the returned context in the next, until it returns zero. I have spoofed this for the first call to use I64, and External in subsequent calls.

But I don't know how to test if context becomes null...

I'm finding it very difficult to understand how to create a null pointer, or check if a pointer is null?

var ffi_rs = require('ffi-rs');
var util = require('util');

let lib = ffi_rs.open({
  library: "Crypt32.dll",
  path: "Crypt32.dll",
});
console.log(lib);

const Crypto = {
    CertOpenSystemStoreA: function( hProv, szSubsystemProtocol){
      // hProv should always be 0 - it's a handle.
      // szSubsystemProtocol is a 8 bit string
      console.log(hProv, szSubsystemProtocol);
      let handle = ffi_rs.load({
        library: "Crypt32.dll",
        funcName: "CertOpenSystemStoreA",
        retType: ffi_rs.DataType.External,
        paramsType: [ffi_rs.DataType.I64, ffi_rs.DataType.String],
        paramsValue: [hProv, szSubsystemProtocol],
      });
      return handle;
    },
    CertEnumCertificatesInStore: //[ PCERT_CONTEXT, ['void *', PCERT_CONTEXT]],
    function( handle, context ){
      // hProv should always be 0 - it's a handle.
      // szSubsystemProtocol is a 8 bit string
      let nextcontext;
      if (context){
        nextcontext = ffi_rs.load({
          library: "Crypt32.dll",
          funcName: "CertEnumCertificatesInStore",
          retType: ffi_rs.DataType.External,
          paramsType: [ffi_rs.DataType.External, ffi_rs.DataType.External],
          paramsValue: [handle, context],
        });
      } else {
        nextcontext = ffi_rs.load({
          library: "Crypt32.dll",
          funcName: "CertEnumCertificatesInStore",
          retType: ffi_rs.DataType.External,
          paramsType: [ffi_rs.DataType.External, ffi_rs.DataType.I64],
          paramsValue: [handle, context],
        });
      }
      console.log(nextcontext);
      return nextcontext;
    },      

    CertFreeCertificateContext: //[ 'bool', [PCERT_CONTEXT] ],*/
    function( context ){
      // hProv should always be 0 - it's a handle.
      // szSubsystemProtocol is a 8 bit string
      let res = ffi_rs.load({
        library: "Crypt32.dll",
        funcName: "CertFreeCertificateContext",
        retType: ffi_rs.DataType.Boolean,
        paramsType: [ffi_rs.DataType.External],
        paramsValue: [context],
      });
      console.log(res);
      return res;
    },      

    CertCloseStore: // [ 'bool', ['void *', 'void *']],
    function( handle, other ){
      // hProv should always be 0 - it's a handle.
      // szSubsystemProtocol is a 8 bit string
      let res = ffi_rs.load({
        library: "Crypt32.dll",
        funcName: "CertCloseStore",
        retType: ffi_rs.DataType.Boolean,
        paramsType: [ffi_rs.DataType.External, ffi_rs.DataType.I64],
        paramsValue: [handle, other],
      });
      console.log(res);
      return res;
    },      
};

var hStoreHandle = null;

// How can I indicate a variable of type External, but value null?
var pCertContext = 0;   

// What can I pass in here to indicate a variable of type External, but value null?
// (I have hacked it to use 0 and IA64 - so that I can dump the handle)
hStoreHandle = Crypto.CertOpenSystemStoreA(0, 'ROOT');

for (let i = 0; i < 3; i++){ // iterate first three certs.
  console.log('CertEnumCertificatesInStore hStoreHandle '+util.inspect(hStoreHandle)+' pCertContext '+util.inspect(pCertContext));
  pCertContext = Crypto.CertEnumCertificatesInStore(hStoreHandle, pCertContext);
}

// How can I detect if pCertContext is null or not? (it will be null when no more crets can be found).
console.log("pCertContext "+util.inspect(pCertContext));

if (pCertContext) {
  Crypto.CertFreeCertificateContext(pCertContext);
  pCertContext = null;
}

if (!Crypto.CertCloseStore(hStoreHandle, 0)) {
  console.log("Failed CertCloseStore");
}
zhangyuang commented 1 month ago

void* is not null pointer but a generic pointer.

Create a pointer with specify type by ffi-rs.createPointer

Check a pointer whether is null pointer refer https://github.com/zhangyuang/node-ffi-rs/issues/64#issuecomment-2260414751

btsimonh commented 1 month ago

thankyou for the quick response. It would be really useful to have a function like ref.isNull(pntrvar) inside ffi-rs. I don't want to write a new DLL and .so just to get this feature :(. Also, to have ffi-rs.createPointer(null) create a null pointer?

zhangyuang commented 1 month ago

Support ffi-rs.isNullPointer in 1.0.97

Get a null pointer from return value or createPointer

const nullPointer = load({
    library: "libsum",
    funcName: "returnNullPointer",
    retType: DataType.External,
    paramsType: [],
    paramsValue: [],
  })
  equal(isNullPointer(nullPointer), true)
  const rsNullPointer = createPointer({
    paramsType: [DataType.Void],
    paramsValue: [undefined]
  })
  equal(isNullPointer(unwrapPointer(rsNullPointer)[0]), true)
  logGreen('test null pointer success')

CreatePointer will create a pointer point to specify data type which is null pointer in this scene.So we should use unwrapPointer to get the wrapped pointer in the package.

If you get a null pointer type by ffi method return value, use it directly.

If you create a null pointer type by createPointer type and use it to call another ffi function. Use unwrapPointer for it,