0

My goal is far from nefarious. After several polite attempts to try and get Lenovo to implement a proper keyboard backlight control, I've had no response. They do have a Windows Store app which adds that control, however, it's not tied to keyboard or mouse input (idle user time). I can implement that, with AutoHotKey, if I manage to call their methods.

Screenshots for reference: https://i.stack.imgur.com/pGBre.jpg

Using JetBrains I can see a quite readable code, written in .NET (I assume), my main interest is in Keyboard_Core.dll. There I can see the methods for getting and setting the keyboard backlight, but I don't understand how they work.

public unsafe uint SetKeyboardBackLightStatus(int nStatus)
{
  CKeyboardLight* ckeyboardLightPtr1 = (CKeyboardLight*) <Module>.@new(4U);
  CKeyboardLight* ckeyboardLightPtr2;
  // ISSUE: fault handler
  try
  {
    ckeyboardLightPtr2 = (IntPtr) ckeyboardLightPtr1 == IntPtr.Zero ? (CKeyboardLight*) 0 : <Module>.ThinkPad.CKeyboardLight.{ctor}(ckeyboardLightPtr1);
  }
  __fault
  {
    <Module>.delete((void*) ckeyboardLightPtr1);
  }
  <Module>.ThinkPad.CKeyboardLight.SetStatus(ckeyboardLightPtr2, (KBDLIGHT_STATUS) nStatus);
  if ((IntPtr) ckeyboardLightPtr2 != IntPtr.Zero)
  {
    CKeyboardLight* ckeyboardLightPtr3 = ckeyboardLightPtr2;
    int num = 1;
    // ISSUE: cast to a function pointer type
    // ISSUE: function pointer call
    void* voidPtr = __calli((__FnPtr<void* (IntPtr, uint)>) *(int*) *(int*) ckeyboardLightPtr2)((uint) ckeyboardLightPtr3, (IntPtr) num);
  }
  return 0;
}

I don't understand where @new(4U) points to. In the decompiled declaration it says it's [StructLayout(LayoutKind.Sequential, Size = 4)] - so I can probably work with that without any structure description. Is it later pointed to some real memory address? I cannot figure that out, it seems that both ckeyboardLightPtr1 and ckeyboardLightPtr2 are new objects until the end of the function.

I don't know how to call a random address (function) with some prepared parameters (stack) - should I stick to AHK (then it would be a different question), try Python (another layer), PowerShell (never used it) or create a small C program?

Either way, it looks that I have to set 4 bytes somewhere in memory. Perhaps I'm chasing the wrong end and these functions just set a temporary/proxy object that is later handled by a monitor thread.

https://pastebin.com/vyfiTTcr

Ollydbg doesn't let me do anything with the DLL.

2 Answers 2

1

@new(4U) points to an external function that this DLL uses via P/Invoke. I don't have Jetbrains dotPeek installed right now but if you do it in dnSpy - you can click on the @new and you will be taken to the method.

enter image description here

Is it later pointed to some real memory address?

I would assume that's it's just allocates the memory and returns the pointer that's assigned to ckeyboardLightPtr1 and then later it is used in the call to <Module>.ThinkPad.CKeyboardLight.{ctor}.

I don't know how to call a random address (function) with some prepared parameters (stack) - should I stick to AHK (then it would be a different question), try Python (another layer), PowerShell (never used it) or create a small C program?

Not sure to what random function you are referring to but I guess you can choose what's best for you. I would use .NET ;)

Either way, it looks that I have to set 4 bytes somewhere in memory.

yes, it does look like that.

0

It was actually pretty easy to do in Visual Studio with a bit of basic research. I created a console application, the main functionality is contained within these lines:

            Assembly myAssembly;
            //full path to dll
            myAssembly = Assembly.LoadFile(args[0]);

            object obj;
            Type myType = myAssembly.GetType("Keyboard_Core.KeyboardControl");
            obj = Activator.CreateInstance(myType);
            IEnumerable list = myType.GetMethods();
            MethodInfo setKeyboardBackLightStatusInfo = GetRuntimeMethodsExt(myType, "SetKeyboardBackLightStatus");


            object[] arguments = new object[] { Int32.Parse(args[1]) };
            UInt32 output = (UInt32)setKeyboardBackLightStatusInfo.Invoke(obj, arguments);

The "list" parameter is useless for normal usage but useful when debugging. With that, it's easy to see what the available functions are and their signature.

args[0] points to the absolute path of the DLL, including its name.

args[1] is the int parameter to be given to the function.

I used a modified version of the answer linked below, to avoid having to manually give Type parameters: https://stackoverflow.com/questions/21307845/runtimereflectionextensions-getruntimemethod-does-not-work-as-expected

The modified (unsafe) version is basically this:

    static MethodInfo GetRuntimeMethodsExt(this Type type, string name)
    {
        var potentials = (from ele in type.GetMethods()
                          where ele.Name.Equals(name)
                          select ele);

        // Maybe check if we have more than 1? Or not?
        return potentials.FirstOrDefault();
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.