A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #25001  by Microwave89
 Mon Jan 19, 2015 5:46 pm
Hey Kernelmode.info board!

(Just for your information, the following question/issue is mainly just for fun or sparetime.)
I simply want to turn off and on the backlight of my monitor since I was planning to (mis)use the large glowing area as a small software controlled LED strobe light.
As the built in backlight isn't made of a fluorescent light tube but rather a LED array (if referring to my notebook stickers), judged on the hardware my notebook should theoretically able to support quite fast turn off and turn on cycles.
Remember that LEDs can be pulsed up to MHz...

Obviously, for this little project I wouldn't need to run my display at MHz frequencies, however, I would appreciate to control the display on a say, 20ms granularity, which would be just little above the thread switching time.

I ended up trying it by leveraging a device driver since I couldn't find any suitable high level API that lets me just turn off and turn on the backlight:
I tried to scrutinize the working principle of the Windows power settings and learned that I can alter the screen brightness within a enough short timespan but I couldn't make the screen go off, entirely.
After further research, I found a small program called "nircmd" which finally let me turn off my screen. I tracked then down the used API command "SendMessageA(INVALID_HANDLE_VALUE, 0x112, 0xF170, 2)" until this command vanished in the deep, undocumented darkness of win32k.sys.
There were two major issues with this command:

1.) The transition of turning off the screen was incredibly long. (100ms?)
2.) I did't find a complementary API letting me turn on the screen again.


Next, I developed a small driver which hooks the whole IRP_MJ_XXX array of an arbitrary driver. I hooked the dispatch table of several driver objects including \acpi, \nvlddmkm, \BasicRender, \monitor, \dxgkrnl and so on, all the time looking after a particular IRP being sent clearly corresponding to the turn off command issued.
I even wrote an additional API which let me send a keyboard "turn-on-capslock-LED" IOCTL to i8042prt.sys while at DISPATCH_LEVEL which enabled me to take up to 10ms exact time measurements. I used my smart phone to measure the time the "turn off!"-command was received by my patch driver (capslock indicator lights up) until the screen actually went black.
Interestingly, as long as I didn't hook nvlddmkm.sys, the screen went black way before the keyboard LED turned on! (1s NEGATIVE! delay)
When I hooked nvlddmkm.sys the delay between the screen beginning to darken and my capslock LED turning on decreased to about 60ms.
(However, I wasn't able to receive an IRP before the screen began to darken no matter which driver I patched...)

By provoking a precisely timed BSOD I was able to seemingly get the missing piece of the puzzle a paragraph above and knew what was happening to my SendMessageA call mentioned earlier:

ffffd001`07708578 fffff802`0e20cab8 : 00000000`00000050 ffffffff`fffffffe 00000000`00000001 ffffd001`077087d0 : nt!KeBugCheckEx
ffffd001`07708580 fffff802`0e0e8e78 : 00000000`00000001 ffffe000`7afe8900 ffffd001`077087d0 ffffe000`7932e000 : nt! ?? ::FNODOBFM::`string'+0x29408
ffffd001`07708620 fffff802`0e1dd42f : 00000000`c0000002 ffffffff`ff676980 ffffe000`79370000 ffffd001`077087d0 : nt!MmAccessFault+0x758
ffffd001`077087d0 fffff800`5e4014b5 : fffff800`5e402070 00000000`00000004 00000000`00000065 00000000`00000001 : nt!KiPageFault+0x12f
ffffd001`07708960 fffff960`001edb9d : ffffe000`7afb6910 ffffe000`7dc8acc0 00000000`00000000 ffffe000`79247470 : IRP_HookTst+0x14b5
ffffd001`077089d0 fffff960`00265d6a : fffff901`400b1be0 00000000`00000000 fffff901`400fb2a0 00000000`c0000001 : win32k!GreDeviceIoControlEx+0xd9
ffffd001`07708a70 fffff960`001d6689 : fffff901`44ee3010 00000000`00000004 00000000`00000001 00000000`00000000 : win32k!DrvSetMonitorPowerState+0xfe
ffffd001`07708ad0 fffff960`001d6b25 : 00000000`00000000 00000000`00000001 ffffe000`0000003c fffff901`44ee3010 : win32k!PowerOffMonitor+0xe1
ffffd001`07708b10 fffff960`001d7879 : ffffe000`7fce56b0 00000003`00000100 00000000`0000001f fffff960`0023b8fc : win32k!xxxUserPowerEventCalloutWorker+0x211
ffffd001`07708bb0 fffff960`00172f74 : ffffe000`770ac080 00000000`00000000 00000000`00000000 00000000`00000000 : win32k!xxxUserPowerCalloutWorker+0x89
ffffd001`07708c10 fffff802`0e1de9b3 : ffffe000`770ac080 00000000`00000002 00000000`00000020 ffffd001`07708c40 : win32k!NtUserCallNoParam+0x44
ffffd001`07708c40 00007ffa`a92a161a : 00007ffa`a92a17f3 00000000`00000004 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
000000bb`f26bfc38 00007ffa`a92a17f3 : 00000000`00000004 00000000`00000000 00000000`00000000 00000000`00000004 : winsrv!NtUserCallNoParam+0xa <--here, my SendMessageA command vanished!
000000bb`f26bfc40 00007ffa`abf54411 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : winsrv!NotificationThread+0x1bc
000000bb`f26bff40 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x25

Since I exactly knew the IOCTL requirements nvlddmkm had at this point, I sent the IOCTL "0x23200F" to the first device object obtained from nvlddmkm->DeviceObject using the following code:

PUCHAR pPuffer = ExAllocatePool(NonPagedPool, 8);
pPuffer[0] = 0x4;
PIRP pIrp = IoBuildDeviceIoControlRequest(0x23200F, pPhysicalDrive, pPuffer, 4, NULL, 0, TRUE, NULL, &ioStatusBlock);
if (!pIrp){
DbgPrint("could not allocate irp");
return STATUS_NO_MEMORY;
}
DbgPrint("0x%lX", IofCallDriver(pPhysicalDrive, pIrp));

After calling IofCallDriver the screen went black as expected. Writing 0x1 to pPuffer turned the screen back on.
Note, that those requests were issued from a second, external driver I also wrote.
Unfortunately, I saw in no time that the screen control was extremely slow.
The same was true when I issued the request to monitor.sys. It also worked but it was still insanely slow so no chance to construct a strobe light with this.

Since I also intercepted IRP_MJ_POWER IRPs during my research I leveraged PoRequestPowerIrp to send a power IRP to monitor.sys which led to a WDF_VIOLATION BSOD, as I'm not power policy owner (and also not interested in being so, btw). An issue to nvlddmkm using the same technique returned me a 0xC0000010, INVALID_DEVICE_REQUEST.

Finally, I built a IRP_MJ_INTERNAL_DEVICE_CONTROL IRP, overwrote the major and minor function codes with IRP_MJ_POWER as well as IRP_MN_SET_POWER and crafted the returned IRP so it looked to the monitor.sys driver like a power IRP. I planned to circumvent the power manager as well as the KMDF by doing so.
Unfortunately, my IRP seemed to look a little too much like a real power request, so it kept being processed by the power manager.
That again bluescreened my computer with a WDF_VIOLATION message.


So you can see, I tried my best and researched a lot; however, I didn't succeed in rapidly controlling my notebook display's backlight.

Now onto my questions:

- What other possiblities do you know of (rapidly) controlling the screen backlight?
- Should I reverse monitor.sys and try to call internal APIs of monitor.sys, thus directly controlling the backlight?
- Should I try to use WRITE_PORT_UCHAR() to directly control the internal display port?
- Are there drivers other than monitor.sys or the proprietary nvidia driver I should issue IOCTLs to?
- Why does it take so long to switch the monitor off if directly called with a IOCTL as mentioned above?? (monitor.sys)
- Do you know any other component responsible (lowest level, Bus driver or port driver or whatever) for directly controlling my display hardware?


- Am I even suggested to completely ignore the backlight and just to leverage documented Win32 graphic functions to black and white paint a window area as large as my desktop?

Some more information: As always with me, I do neither bother how undocumented your recommendations are, nor if a particular dirty solution only will work with my current OS with the exactly defined build number.
Preferred OS would be Windows 8.1, x64, Build 9600
Hardware is a ASUS G60J notebook with 4GB RAM, a LED backlight and a NVIDIA GTX260M graphics solution.

I do not bother either if your solution generates large lags, high CPU loads, or hangs as long as the screen is flashing, because everything like these problems can be solved at a later time. ;)

If you don't see a particular rapid solution just tell everything, including slow solutions. This helps me to better understand the internals and maybe to come up with a rapid solution on my own.
And if nothing helps I will go for writing real mode boot code turning the screen off and on, since after all, there MUST be some command(s) controlling the backlight..but on Windows 8.1 it's just too good hidden from me...

Any help really appreciated, thanks in advance!


Best Regards

Microwave

P.S. With my hooks I have also analyzed what happens if I alter the screen brightness.
There are IOCTLs sent to monitor.sys/nvlddmkm.sys ranging from 0x0 to 0x64 according to 0...100% brightness setting. So no need to send 0x0, as 0x0 already is being sent but doesn't mean "screen off".
 #25008  by feryno
 Tue Jan 20, 2015 2:53 pm
Heh, I see, that you attempted really hard and spent a lot of time to achieve your dream.
You remembered me times which were about 20 years ago, when I used computer for fun, not yet to earn money. Times which were are lost.
Today a lot of people has very high skills and knowledge in OS and there is lack of people with skills and knowledge in hardware (that includes not only periferials, but also CPU).
If your graphic card supports VGA modes (it should support if you can boot via legacy BIOS emulation, not only via UEFI), the easiest solution for you is - as you already wrote - in real mode. Turning on/off screen in VGA mode is task for only one bit of one port (port 3C4h, port-index 1, bit 5.).
I would be very surprised if this VGA port works in modern graphic modes with thousands of pixels resolutions and millions of colors per pixel - I didn't try it here, maybe you'll have success.
The attached sample is MS DOS executable which I made for you quickly. It switches to VGA mode 320x200 pixels with 256 colors per pixel, fills the whole screen with white color and performs toggles of the "magic" VGA bit on and off periodically 256 times. A delay is obtained again using VGA ports (vertical retrace start and end). Have a fun.
By my opinion, the best graphic effect I've ever seen in viruses was implemented in kaczor, under ms dos it was really impressive and poor victims were probably very scared. When infected win95 booted, the graphic effect was slowed down a lot so it was observable what was it doing.
Attachments
(649 Bytes) Downloaded 34 times
 #25116  by Brock
 Sat Jan 31, 2015 9:46 am
Good old DOS. I remember doing DOS TSR programming and software interrupt hooking when real-mode was "real" and protected mode wasn't publicly available. DOS memory extenders were magical back then as well :D but prior to this I recall trash 80 and CoCo 8-bit asm dev as my real start. DOS is very nostalgic =)

Best Regards,
Brock
 #25123  by Microwave89
 Sat Jan 31, 2015 3:44 pm
I also want to reply officially:

Thank you very much, feryno for your share.
I will try to set up an old DOS again. Long time ago I used to have a small thumb drive containing New DOS (or something like that). At the moment it is screwed, however.
Does (should) your program also turn off the backlight completely? (My screen has a very bad "black" when its backlight is glowing...)

Sorry for late reply, was kinda busy with exams...


Best Regard

Microwave89