Becoming NT SERVICE\TrustedInstaller

I just recently had two reasons to act as TrustedInstaller. I wanted to test to change the executable that is started when you press the <Ctrl>-<+> shortcut that usually starts the magnifier accessibility tool. I wanted to make it start a command window instead. It would be started as the logged in user if you press the shortcut while logged in on the desktop, but as NT AUTHORITY\SYSTEM if pressed on the secure desktop (“Do you want to allow this app to make changes to your device?”)(“Run as Administrator…”) or the login screen. Neither useful nor secure but sort of interesting.

I also found a bug in the PowerShell DeliveryOptimization module (build 19041.84 (“C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DeliveryOptimization\DeliveryOptimizationStatus.psm1“), The command Get-DOPercentageMaxBackgroundBandwidth defined its output type as the non-existent [uint8], so that function could not be used nor the module imported by the command browser in e.g. PowerShell ISE (what was why I wanted the import to work, the commands I wanted to execute worked fine without explicitly import the module) . The module folder/files were also owned and writable only by TrustedInstaller. The function Get-DOPercentageMinBackgroundBandwidth defined in the same module file had its output type set to [uint32] though so the fix was trivial. Accessing the file was not.

One of the easiest solutions is to use a program like Process Hacker 2, find a process that has TI access, select it and “Run as this user…”. An easy way to to find such a process is to start the TrustedInstaller/”Windows Module Installer”-service and TrustedInstaller.exe will run long enough for us to have time to find it in the process list and to impersonate it or use it as a parent process for say a new powershell.exe instance. 

I read this excellent article (one among many on that site) from which I’ve pretty much stolen the rest of this post.

The Art of Becoming TrustedInstaller

You’ll need his excellent NtObjectManager powershell module. Then you can for example start a new powershell process with TrustedInstaller as parent;

Start-Service TrustedInstaller
$p = Get-NtProcess -Name TrustedInstaller.exe -FilterScript { -not $_.IsDeleting } | Sort-Object ProcessId -Descending
$proc = New-Win32Process powershell.exe -CreationFlags NewConsole -ParentProcess $p[0]

Or change the current thread to impersonate it (not opening a new powershell console window):

Start-Service TrustedInstaller
$p = Get-NtProcess -Name TrustedInstaller.exe -FilterScript { -not $_.IsDeleting }  | Sort-Object ProcessId -Descending
$th = $p[0].GetFirstThread()
$current = Get-NtThread -Current -PseudoHandle
$imper = $current.ImpersonateThread($th)

Now we can change those files and registry keys without first having to take ownership of them and give our user write permissions, and then try to restore the original permissions and owner (a bit tricky as some special permissions doesn’t seem to to have arithmetically combined human-readable aliases but only have numerical representations).

An example of restoring the magnifier accessibility start executable after trying it as cmd.exe for a while. First just using an administrative shell, then after impersonating a TrustedInstaller.exe thread.

Magnify.exe

# BEFORE
# PS C:\WINDOWS\system32> (Get-NtToken -Effective).User 
# 
# Name                           Attributes                    
# ----                           ----------                    
# PERSEPHONEWL\mignon            None                          
#
# 
# PS C:\WINDOWS\system32> (Get-NtToken -Effective).Groups | ft -AutoSize
# 
# Name                                                          Attributes                                   
# ----                                                          ----------                                   
# Mandatory Label\High Mandatory Level                          Integrity, IntegrityEnabled                  
# Everyone                                                      Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\Local account and member of Administrators group Mandatory, EnabledByDefault, Enabled         
# PERSEPHONEWL\Ssh Users                                        Mandatory, EnabledByDefault, Enabled         
# BUILTIN\Administrators                                        Mandatory, EnabledByDefault, Enabled, Owner  
# BUILTIN\Performance Log Users                                 Mandatory, EnabledByDefault, Enabled         
# BUILTIN\Users                                                 Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\INTERACTIVE                                      Mandatory, EnabledByDefault, Enabled         
# CONSOLE LOGON                                                 Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\Authenticated Users                              Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\This Organization                                Mandatory, EnabledByDefault, Enabled         
# MicrosoftAccount\xxxxxxxxxxxxxxxxx                            Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\Local account                                    Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\LogonSessionId_0_467797                          Mandatory, EnabledByDefault, Enabled, LogonId
# LOCAL                                                         Mandatory, EnabledByDefault, Enabled         
# NT AUTHORITY\Cloud Account Authentication                     Mandatory, EnabledByDefault, Enabled 

# AFTER
# PS C:\Users\mignon> (Get-NtToken -Effective).User | ft -AutoSize
# 
# Name                           Attributes
# ----                           ----------
# NT AUTHORITY\SYSTEM            None      
#  
#
# PS C:\Users\mignon> (Get-NtToken -Effective).EnabledGroups | ft -AutoSize
# 
# Name                                   Attributes                                          
# ----                                   ----------                                          
# Everyone                               Mandatory, EnabledByDefault, Enabled                
# BUILTIN\Users                          Mandatory, EnabledByDefault, Enabled                
# NT AUTHORITY\SERVICE                   Mandatory, EnabledByDefault, Enabled                
# CONSOLE LOGON                          Mandatory, EnabledByDefault, Enabled                
# NT AUTHORITY\Authenticated Users       Mandatory, EnabledByDefault, Enabled                
# NT AUTHORITY\This Organization         Mandatory, EnabledByDefault, Enabled                
# NT SERVICE\TrustedInstaller            EnabledByDefault, Enabled, Owner                      <-- Should be "Enabled"                   
# NT AUTHORITY\LogonSessionId_0_64740669 Mandatory, EnabledByDefault, Enabled, Owner, LogonId
# LOCAL                                  Mandatory, EnabledByDefault, Enabled                
# BUILTIN\Administrators                 EnabledByDefault, Enabled, Owner          

[HACK]Facebook Wall Post and Comments Formatting with UNICODE

This was news for me (very seldomly post anything to Facebook, hardly ever read it any longer). But now I wanted to preserve indentation and use a monospace font in a Facebook wall post/post comment, like <pre> or “`. I was virtually certain that such functionality/markup existed. What about if you want to post a code snippet or a piece of structured data that you would like to remain human-readable (picture I suppose (wrongly most likely)). But the only way that I found that could provide some customization was this surprising UNICODE “hack”.

Firstly, when you have finished editing your post/comment, the editor will eat all leading white-space: ordinary spaces, no-break spaces, tabs, etc.. But if you use e.g. Unicode Character (U+3000) ‘Ideographic Space’ (dunno if other less ASCII:y space-like characters work as well), they will be kept.

Then; UNICODE has a ‘Mathematical Alphanumeric Symbols’ block containing e.g. ‘A’ (U+1D670) ‘Mathematical Monospace Capital A’ and so on. Found a web page, https://yaytext.com/monospace/,  that could convert between characters and basic symbols from the ASCII character set to characters from that block. So, could paste the JSON result and get something mono-spacey out. Neat.  Do that and replace leading whitespace with ‘Ideographic Space’s (also zeroes to 0+1D6F7 as the function on the linked to page does not convert them) and we’ve got … something,.. close enough to what we set out for for it to feel kindof like a success.

I don’t feel that this is the robustestest of solutions though and that it might change at any time. A screen-shot of an editor would have been easier, more reliable and more flexible. Syntax highlighting? Typeface? Highlighting? etc.. So, not very useful at all really. But learned a little about FB wall posts and UNICODE. Aligns well with the blog’s subtitle

UNICODE FB Comment
UNICODE FB Comment

Limiting the output of the ‘man’ command to a max number of columns with an alias

It is hard to read text if the columns are too wide. If i have a wide terminal window open I don’t want e.g. man pages text to fill the entire width of the window (I also use a “readability” greasemonkey/tampermonkey-script to apply custom formatting to Wikipedia pages (slightly customized version of https://userstyles.org/styles/56739/wikipedia-readable-hyphenation that fixes ToC numbering)).

I thought that I could create an alias that set the max number of columns for man to 120 pretty easily.

The environment variable MANWIDTH can be used to control the number of columns manwill format its output to. But one cannot simply set this to 120 and be done with it because then it will format to 120 columns also when the terminal window is narrower, which usually makes terminals wrap at the right edge and then again at a total of 120 char width:

So, my first solution was to first get the width of the terminal window in columns, then use that if the window was narrower than 120 else 120. Something that I found a lot trickier than expected (I hate shell scripting, always seem to only get those parenthesis and quotation marks right by dumb luck after lots of trial and error).

But when revisiting it a good while later I simplified it a tad by just not setting MANWIDTH if width was lower than 120 (duh). The result:

# Attempt 1
alias man='MANWIDTH=$(((x=$(tput cols))) && [ $x -gt 120 ] && echo 120 || echo $x)

# Simplified, duh
alias man='MANWIDTH=$([ $(tput cols) -lt 120 ] || echo 120) man'

tput colswill get current terminal width reliably.

Results:

Modifying Samsung SSG-5100GB Active 3D Glasses for Dual Play

Based on the guide on https://youtu.be/hLf0DTl6o-I. Great instructions on how to get the glasses open, and description of the method.

I followed the guide with a couple of modifications. The looping of the driver connectors for the disconnected lens did not seem necessary (and seemed quite illogical to me). And the wires could comfortably be made quite a bit shorter for a better fit. No need for the complicated twisting procedure leaving long wires that needed to be tucked away. Just connect the innermost connectors to each other and the outermost to each other with wires a bit longer than the distance between the connection points, after desoldering one of the lensess (the left in one pair, right in the other). Putting some electrical tape on the circuit board where they used to connect to prevent those other driver connection points to make contact. Fits right back in its case.

Looped
Looped (yes, ugly soldering =)…)

Desoldered
Desoldered

Samsung Series 7 Keyboard Backlight Controls Investigations

Background

I did a clean install of Windows 10 on my Samsung Series 7 (740U3E) laptop when I found out about Microsoft’s clean install tool which restores the OS to a pristine state, free from preinstalled vendor bloat-/software, but ended up not using the tool but completely reinstalling from an ISO instead (btw, if you visit the cleaning tool’s webpage while using Linux it will redirect to a page where you can download clean Windows 10 Home/Pro install ISO’s instead of the tool…).

Everything seemed to work fine after the reinstall with just drivers from Windows Update. Everything but some of the hotkeys, and the keyboard backlight that seemed to have a life of its own.

In order not to have to install Samsung Update and Samsung Settings and all services and things that that brings I thought that there must exist some kind of standard method to control the LEDs and enable the hotkeys and tools to do it with. And that was the start of way to many hours of analyzing and testing and getting to learn a little (more) about (U)EFI,  ACPI, disassembly, and debugging, memory layout and management… etc..

I couldn’t find any general tools or even information about how the LED’s were controlled. Besides the linux kernel’s samsung-laptop-module source code which hinted at something called SABI (Samsung Advanced BIOS Interface(?)) which could be used for controlling stuff like the ones one can control with the Samsung Settings application: USB-power in sleep mode, battery life saving mode, silent mode, WiFi and Bluetooth state and keyboard backlight level and eventual timeout. It seemed like a good place to start. I never could figure out how to make SABI calls when EFI-booting though, or if it is even possible, so it was a pretty time consuming detour, I’ve put my notes about it at the end.

So I had to install Samsung Settings anyways in order to get it to work (which was no trivial task in itself as Samsung does not seem to provide any software on their product support pages, but finally found a link to Samsung Update on Samsung’s american site in an old news post, through which other Samsung software and drivers could be installed.).

Attempt at debugging

This didn’t satisfy my curiosity though, so a couple of months later I thought that this question would easily be answered by debugging the controlling Samsung software.

Which led to many frustrating hours with the Windows Debugger (WinDbg) and IDA PRO disassembler. I expected to be able to kindof (relatively) quickly find a simple write to some memory address(es) that triggered the changes, and then be able to repeat them. But I didn’t.

Don’t know if it was because of intentional obfuscation reasons or something else, but for some reason the 32-bit EasySettingsCmdServer.exe through a stack of calls though SUS.dll (SetKeyboardBacklitLevel())and WSABI.dll (CallBIOSInterface())

makes a call to 64-bit code through ntdll!Wow64Transition/ntdll!Wow64SystemServiceCall, where 32-bit debuggers/disassemblers cannot follow. My very limited experience and understanding of these topics put a stop to  my investigations here after attempting to debug with WinDbg Preview which seemed to handle the switch (but I’m not sure) but led too deep into debuggers and assembly and windows internals for me to follow, one example trace of an SUS.dll!SetKeyboardBacklitLevel invocation:

Tracing SUS!SetKeyboardBacklitLevel to return address 739bc345
    7     0 [  0] SUS!SetKeyboardBacklitLevel
    4     0 [  1]   SUS!SetMaxPerformanceStatus
    3     0 [  2]     SUS!SetMaxPerformanceStatus
   17     0 [  3]       SUS!SetMaxPerformanceStatus
   14    17 [  2]     SUS!SetMaxPerformanceStatus
   10     0 [  3]       SUS!SetMaxPerformanceStatus
   16     0 [  4]         ntdll!RtlEnterCriticalSection
   17    16 [  3]       SUS!SetMaxPerformanceStatus
    5     0 [  4]         KERNEL32!TlsGetValueStub
   11     0 [  4]         KERNELBASE!TlsGetValue
   25    32 [  3]       SUS!SetMaxPerformanceStatus
   23     0 [  4]         ntdll!RtlLeaveCriticalSection
   32    55 [  3]       SUS!SetMaxPerformanceStatus
   19   104 [  2]     SUS!SetMaxPerformanceStatus
   11     0 [  3]       SUS!SetMaxPerformanceStatus
   20   115 [  2]     SUS!SetMaxPerformanceStatus
   12   135 [  1]   SUS!SetMaxPerformanceStatus
    3     0 [  2]     SUS!SetMaxPerformanceStatus
   18     0 [  3]       SUS!SetMaxPerformanceStatus
    9    18 [  2]     SUS!SetMaxPerformanceStatus
   11     0 [  3]       SUS!SetMaxPerformanceStatus
   10    29 [  2]     SUS!SetMaxPerformanceStatus
   15   174 [  1]   SUS!SetMaxPerformanceStatus
   10   189 [  0] SUS!SetKeyboardBacklitLevel
    6     0 [  1]   SUS!SetMaxPerformanceStatus
    3     0 [  2]     SUS!SetMaxPerformanceStatus
    3     0 [  3]       SUS!SetMaxPerformanceStatus
   17     0 [  4]         SUS!SetMaxPerformanceStatus
   14    17 [  3]       SUS!SetMaxPerformanceStatus
   10     0 [  4]         SUS!SetMaxPerformanceStatus
   16     0 [  5]           ntdll!RtlEnterCriticalSection
   17    16 [  4]         SUS!SetMaxPerformanceStatus
    5     0 [  5]           KERNEL32!TlsGetValueStub
   11     0 [  5]           KERNELBASE!TlsGetValue
   25    32 [  4]         SUS!SetMaxPerformanceStatus
   23     0 [  5]           ntdll!RtlLeaveCriticalSection
   32    55 [  4]         SUS!SetMaxPerformanceStatus
   19   104 [  3]       SUS!SetMaxPerformanceStatus
   11     0 [  4]         SUS!SetMaxPerformanceStatus
   20   115 [  3]       SUS!SetMaxPerformanceStatus
   11   135 [  2]     SUS!SetMaxPerformanceStatus
    3     0 [  3]       SUS!SetMaxPerformanceStatus
   18     0 [  4]         SUS!SetMaxPerformanceStatus
    9    18 [  3]       SUS!SetMaxPerformanceStatus
   11     0 [  4]         SUS!SetMaxPerformanceStatus
   10    29 [  3]       SUS!SetMaxPerformanceStatus
   14   174 [  2]     SUS!SetMaxPerformanceStatus
   15   188 [  1]   SUS!SetMaxPerformanceStatus
    5     0 [  2]     KERNEL32!ActivateActCtxStub
    4     0 [  2]     KERNELBASE!ActivateActCtx
   11     0 [  3]       KERNELBASE!IsActivateActCtxWorkerPresent
    8    11 [  2]     KERNELBASE!ActivateActCtx
    9     0 [  2]     KERNEL32!ActivateActCtxWorker
   17     0 [  3]       ntdll!RtlActivateActivationContext
   29     0 [  4]         ntdll!RtlActivateActivationContextEx
   52     0 [  5]           ntdll!RtlpAllocateActivationContextStackFrame
    3     0 [  6]             ntdll!__security_check_cookie
   55     3 [  5]           ntdll!RtlpAllocateActivationContextStackFrame
   79    58 [  4]         ntdll!RtlActivateActivationContextEx
   22   137 [  3]       ntdll!RtlActivateActivationContext
   15   159 [  2]     KERNEL32!ActivateActCtxWorker
   21   386 [  1]   SUS!SetMaxPerformanceStatus
   15   596 [  0] SUS!SetKeyboardBacklitLevel
   17     0 [  1]   SUS
    5     0 [  2]     advapi32!RegOpenKeyExWStub
   11     0 [  2]     KERNELBASE!RegOpenKeyExW
   54     0 [  3]       KERNELBASE!RegOpenKeyExInternalW
   27     0 [  4]         KERNELBASE!MapPredefinedHandleInternal
   32     0 [  5]           ntdll!RtlGetCurrentTransaction
   30    32 [  4]         KERNELBASE!MapPredefinedHandleInternal
   34     0 [  5]           ntdll!RtlSetCurrentTransaction
   32    66 [  4]         KERNELBASE!MapPredefinedHandleInternal
   16     0 [  5]           ntdll!RtlAcquireSRWLockExclusive
   49    82 [  4]         KERNELBASE!MapPredefinedHandleInternal
   14     0 [  5]           ntdll!RtlReleaseSRWLockExclusive
   52    96 [  4]         KERNELBASE!MapPredefinedHandleInternal
   34     0 [  5]           ntdll!RtlSetCurrentTransaction
   67   130 [  4]         KERNELBASE!MapPredefinedHandleInternal
    3     0 [  5]           KERNELBASE!__security_check_cookie
   70   133 [  4]         KERNELBASE!MapPredefinedHandleInternal
   62   203 [  3]       KERNELBASE!RegOpenKeyExInternalW
  201     0 [  4]         ntdll!RtlInitUnicodeStringEx
   77   404 [  3]       KERNELBASE!RegOpenKeyExInternalW
   73     0 [  4]         KERNELBASE!LocalBaseRegOpenKey
    3     0 [  5]           ntdll!NtOpenKeyEx
    1     0 [  6]             ntdll!Wow64SystemServiceCall
    1     0 [  6]             0x65117000
KBD_backlit_Auto On

And after reading articles like these I realized that it was probably nothing that I quickly could learn to a sufficient degree without significant additional time investment:

https://reverseengineering.stackexchange.com/questions/16200/how-to-investigate-windows-32-64bit-wow64-transition

It also seems to make WMI-calls and do something related to ACPI0008 (driver name: Light Sensor), but I’m too unskilled with the debugger to tell which are related and not.

ACPI

Instead, while trying to find out how to make a SABI call I found out about this tool FWTS (“Firmware Test Suite (FWTS) is a test suite that performs sanity checks on firmware. It is intended to identify BIOS, UEFI, ACPI and many other errors and if appropriate it will try to explain the errors and give advice to help workaround or fix firmware bugs. It is primarily intended to be a Linux-centric firmware troubleshooting tool.”). Running it provided an wealth of interesting hardware information.

Somehow this led me to the ACPI tables, the decompilation of them from “ACPI Machine Language” to “ACPI Source Language”) (fwts dumpacpi, iasl -d), and while reading through the DSDT (Differentiated System Description Table) table’s decompiled source code I happened upon some interestingly named variables (See e.g. Arch Linux DSDT wiki article for more information):

OperationRegion (SNVS, SystemMemory, 0xC9F67D98, 0x0100)
Field (SNVS, AnyAcc, Lock, Preserve)
{
    OSTP,   16, 
    SMIS,   8,     // Offset 0x02
    DB00,   8, 
    DW00,   16, 
    /.../ 
    KBTO,   8,     // Offset 0x77
    Offset (0x7C), 
    KBST,   8,     // Offset 0x7C
    KBLL,   8,     // Offset 0x7D
    /.../
}

Which gave me a memory address (0xC9F67D98) and offsets to KBTO, KBST and KBLL, which I guessed was KeyBoard TimeOut, STatus and LightLevel or something like that.

I wrote getter and setter shell scripts that reads/writes from/to these addresses though /dev/mem:

#!/bin/bash
for i in KBLL,$((0xC9F67D98 + 0x7d)) \
        KBST,$((0xC9F67D98 + 0x7c)) \
        KBTO,$((0xC9F67D98 + 0x77))
do
    IFS=',' read k v <<< "${i}"
    echo -n "${k}: "
    dd if=/dev/mem skip=${v} bs=1 count=1 2>/dev/null | hexdump -e '"%x"'
    echo
done
#!/bin/bash
if [[ $# -ne 2 ]]; then
    echo "Usage setkbd {LL,ST,TO} hexval."
    exit
fi

case "${1^^}" in
 KBLL | LL)
    addr=$((0xC9F67D98 + 0x7d))
    ;;
 KBST | ST)
    addr=$((0xC9F67D98 + 0x7c))
    ;;
 KBTO | TO)
    addr=$((0xC9F67D98 + 0x77))
    ;;
 *) 
    echo "Unknown argument $1"
    echo "Usage setkbd {LL,ST,TO} hexval."
    exit
    ;;
esac

printf "\x${2}" | dd of=/dev/mem seek=$addr bs=1

Just writing to the addresses had no effect though. I had however noticed that the backlight turned on when the computer awoke from sleep (sometimes, or always since I taped over my ambient light sensor). Only from sleep though, not at power on, so the lights would remain off until the computer first was put to sleep and awoken and then they would turn on. So i hypothesized that maybe that could be used as a trigger. Which it could.

So I could set KBLL to a value 0 – 4 (printf "\x04" | dd of=/dev/mem seek=$((0xC9F67D98 + 0x7D)) bs=1)and then put the computer to sleep (fwts s3 -, shutting the lid)and when it woke up it would use that brightness level.  At least I now got a (somewhat cumbersome) way to set the light level!

I booted Windows and had a peek at the addresses (using RW Everything) while I was changing timeout settings and light level using Samsung Settings and found that only KBLL seemed to change from 0 to 4 depending on level chosen. KBTO seemed to have nothing to do with timeout, KBLL just changed from a value > 0 to 0 at timeout and back to whatever it was again upon next user input. I’m guessing that the timeout stuff and hotkeys (that reports ordinary scancodes like ordinary keys) are handled by software under windows.

In the DSDT table there fortunately existed a method (it is a pretty strange and almost superfluous one and without it I would not have come to this solution) that is executed when the computer wakes from sleep that seemingly reads from the ambient light sensor and turn on the keyboard backlight if the reading is below a certain value and turn it off if above another value (unverified) if KBTO <> 0 (verified). This is the only place in the table that the KBxx values seemed to be referenced so searches for those symbols led there:

Method (_Q70, 0, NotSerialized)  // _Qxx: EC Query
{
    If (((ALSE == 0x02) && IGDS))
    {
        Local0 = ^ALSD._ALI ()
        ^^^GFX0.AINT (Zero, Local0)
        Notify (ALSD, 0x80) // Status Change
    }

    Local0 = LUXH /* \_SB_.PCI0.LPCB.H_EC.LUXH */
    Local0 = ((Local0 << 0x08) | LUXL) /* \_SB_.PCI0.LPCB.H_EC.LUXL */
    Local0 = ^ALSD.CLUX (Local0)
    If ((KBTO == Zero))
    {
        If (((Local0 <= 0x14) && (KBST != One))) 
        { 
            SECS (0x9A) 
            KBST = One 
        } 
        ElseIf (((Local0 >= 0x32) && (KBST != Zero)))
        {
            SECS (0x9B)
            KBST = Zero
        }
    }
}

(one annoying this is that most ACPI symbols are four letters long so it is not immediately obvious what they stand for… so by guessing CLUX(LUXH/LUXL) = some kind of normalized measurement from the light sensor (high/low byte)).

When status/state(?) is set to 1 the SECS() function is called with argument 0x9A, when to 0 with 0x9B. So maybe we could simulate those SECS()-calls to trigger a change. Jumping to the definition…

Method (SECS, 1, Serialized)
{
    Acquire (MSEC, 0xFFFF)
    Store (Arg0, SMIS) /* \SMIS */
    Store (SWCD, TRPS) /* \TRPS */
    Release (MSEC)
}

(SECS, guessing something about the EC (Embedded Controller), SMIS reminds of SwSmi, TRPS maybe TRigger something (Power State? 0xB2 (but now we’re getting ahead of ourselves) is sometimes refered to as the APM port?)? SWCD also reminicent of SwSmi…).

We can here see that the argument to SECS, 0x9A or 0x9B, is stored in SMIS and SWCD is written to TRPS.

Moving on to MSEC mutex revealed TRPS/SECR and led on onto SSMI:

Mutex (MSEC, 0x00)
OperationRegion (SECR, SystemIO, SSMI, 0x02)
Field (SECR, ByteAcc, NoLock, Preserve)
{
    TRPS,   8
}

SWCD and SSMI were constants:

Name (SSMI, 0xB2)
Name (SWCD, 0xA8)

And the SMIS variable was to be found in the same SNVS region as the KBxx ones at 0xC9F67D98, offset 0x02:

OperationRegion (SNVS, SystemMemory, 0xC9F67D98, 0x0100)
Field (SNVS, AnyAcc, Lock, Preserve)
{
    OSTP,   16, 
    SMIS,   8,     // Offset 0x02
    DB00,   8, 
    DW00,   16, 
    /.../ 
    KBTO,   8,     // Offset 0x77
    Offset (0x7C), 
    KBST,   8,     // Offset 0x7C
    KBLL,   8,     // Offset 0x7D
    /.../
}

Now I had all the information needed to make the calls by emulating this by writing  to /dev/mem and /dev/port:

# Write "command" 0x9A to enable or 0x9B to disable to SMIS (0xC9F67D98 + 0x02)
printf "\x9A" | dd of=/dev/mem seek=$((0xC9F67D98 + 0x02)) bs=1

# Write desired brightness level 0-4 (8 on some models) to KBLL (0xC9F67D98 + 0x7D)
printf "\x04" | dd of=/dev/mem seek=$((0xC9F67D98 + 0x7D)) bs=1

# Write "SWCD" (0xA8), to SMI Command IO Port "SSMI" (0xB2) to trigger change
printf "\xA8" | dd of=/dev/port seek=$((0xB2)) bs=1

And (to my surprise) it works. Writing 0x9A to SMIS will indeed enable the backlight and 0x9B will disable it. Writing 0 – 4 to KBLL will set light level when enabled. Writing A8 to port B2 is what will trigger the change.

This doesn’t align too well with the values of the commands and arguments as the BIOS SABI calls as per the kernel code. But works well enough. Now I had a way to control keyboard backlight without having to put the computer to sleep to trigger the change. Mission accomplished!

Looking through the (U)EFI driver names (given by UEFItool) one gets the impression that there are two ways of changing settings, the SABI way and the SMM (System Management Mode, google “SSM B2”) way (the existence of an EFI variable named LegacySabiBuffPtr also supports this). (However, SABI also make use of port 0xB2):

SABI
/*
 * This driver is needed because a number of Samsung laptops do not hook
 * their control settings through ACPI.  So we have to poke around in the
 * BIOS to do things like brightness values, and "special" key controls.
 */

/*
 * We have 0 - 8 as valid brightness levels.  The specs say that level 0 should
 * be reserved by the BIOS (which really doesn't make much sense), we tell
 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
 */
#define MAX_BRIGHT	0x07

The kernel module source disclosed that SABI calls were made by writing commands and arguments to a specific memory addresses and output to a port.

The first thing that the module does is to check if we’re EFI-booting. If so initialization will fail and the module not load. The reason for this is that some Samsung models will brick themselves it EFI non-volatile variable storage memory becomes more than 50% full so the kernel devs did not think it safe to peek/poke around in memory then:

static int __init samsung_init(void)
{
    struct samsung_laptop *samsung;
    int ret;

    if (efi_enabled(EFI_BOOT))
        return -ENODEV;
/.../
}

The port, the values to output to the port and the pointer to the SABI call memory address is found by searching for the string “SwSmi@” in the memory segment where BIOS is mapped, 64kb under 1Mb, 0xf0000 to 0xfffff. The bytes following that string then gives us:

.header_offsets = {
    .port = 0x00,
    .iface_func = 0x02,
    .en_mem = 0x03,
    .re_mem = 0x04,
    .data_offset = 0x05,
    .data_segment = 0x07,
}

SwSmi@[port:2][iface:1][en_mem:1][re_mem:1][data_offset:2][data_segment:2]

In a hexdump from that memory segment from my system I found:

“SwSmi@” b2 00 80 81 82 00 f0 90 d2

data_offset and data_segment makes up a pointer to SABI call interface memory area and is given given by the expression (data_segment << 4 | data_offset).

That makes port=0x00B2, iface=0x80, en_mem=0x81, re_mem=0x82 and pointer = (0xf000 << 4 | 0xd290) = 0x000fd290.

The source code for sabi_command() and some defines tells us how a SABI call is made:

#define SABI_IFACE_MAIN			0x00
#define SABI_IFACE_SUB			0x02
#define SABI_IFACE_COMPLETE		0x04
#define SABI_IFACE_DATA			0x05

/.../

/* enable memory to be able to write to it */
outb(readb(samsung->sabi + config->header_offsets.en_mem), port);

/* write out the command */
writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
if (in) {
    writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
    writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
    writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
    writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
}
outb(readb(samsung->sabi + config->header_offsets.iface_func), port);

/* write protect memory to make it safe */
outb(readb(samsung->sabi + config->header_offsets.re_mem), port);

/* see if the command actually succeeded */
complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);

This means the call is made by writing following to in my case 0x000fd290:

[main_funtion:2][command:2][complete:1][d0:4][d1:4][d2:2][d3:1]

Main function is always 0x5843, the command for changing the backlight 0x0078, and first parameter d0 = (0x82 | level << 8). The backlight first has to be enabled by issuing command 0xaabb though, all given by:

/* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
u16 kbd_backlight;
/.../
kbd_backlight = 0x78

So we should be able to control the backlight by something like:

# Enable the bl
printf "\x81" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x43\x58\x78\x00\x00\xbb\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | dd of=/dev/mem seek=$((0xfd290)) bs=1 2>/dev/null
printf "\x80" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x82" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
sleep 1
hexdump -C -s $((0xfd290)) -n 20 /dev/mem
# complete, written 0x00, should read back 0xaa, on error d0 low byte should read 0xff
# d0, written 0xaabb, should now be read back 0xccdd

sleep 10

# Set light level
printf "\x81" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x43\x58\x78\x00\x00\x82\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | dd of=/dev/mem seek=$((0xfd290)) bs=1 2>/dev/null
printf "\x80" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x82" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
sleep 1
hexdump -C -s $((0xfd290)) -n 20 /dev/mem
# complete, written 0x00, should read back 0xaa, on error d0 low byte should read 0xff

(If you’re noticing that the order of bytes written seems a bit of fit is because of little-endianess. Where we have writeb() we can just printf the byte. Where we have writew() we have to write the word least-signifiant-byte-first, so main-function 0x5843 becomes printf “\x43\x58”. Same with writel(), 0x0000aabb becomes printf “\xbb\xaa\x00\x00”.)

This method does not work however. Nothing happens.

The “SwSmi@” string seems to me to be nowhere to be found in memory (where I’ve peeked) unless having the CSM (“The Compatibility Support Module (CSM) is a component of the UEFI firmware that provides legacy BIOS compatibility by emulating a BIOS environment, allowing legacy operating systems and some option ROMs that do not support UEFI to still be used.”…) enabled. And I’d prefer to not enable that. And even if I did the kernel module won’t initialize if EFI booting anyways, a check for this is made at startup and the module is not initialized (could easily be changed though).

(The reason for not wanting to enable the CSM is that it had certain unpredictable side effects on my stationary computers. Firstly, the option that enable/disable it is there named “Windows 8 Mode”. Not entirely obvious that enabling that option means disabling CSM. Having CSM enabled will in its turn limit the EFI framebuffer resolution to something like 1024×768 though, another not-too-obvious connection. It doesn’t matter too much on my media-server which has an Intel video card that has framebuffer driver support that can take over after the EFI framebuffer a bit later in the boot sequence and up the resolution to my TV’s 1920×1080. It was an annoyance on my desktop computer that has an NVidia 1060 card… The proprietary NVidia drivers does not have framebuffer support that can take over after the EFI one which meant that I was left with ugly low-res consoles instead of nice 2560×1440 ones. Or so I thought. Enabling “Windows 8 Mode”, disabling CSM, enabled much higher EFI-framebuffer resolutions. So now even grub is in monitor-native res. Took many years (and three NVidia cards) before I figured that one out.)

There exists at least two EFI variables which seems to be related. KBDBacklitLvl and LegacySabiBuffPtr. Writing to the Kbd-variable does nothing however and the the LegacySabiBuffPtr holds the same memory address that can be found by searching for “SwSmi@” with the CSM on, 0x000fd270.

Tumblr default theme, auto load external images

Tumblr’s default theme doesn’t support external images any longer. It neither auto-loads them nor give you a “Load external image”-link for click-to-load like it used to and like it still does on the dashboard. Only the plain text placeholder “External image” is displayed where the image used to load.

One can restore the old auto-load functionality by editing the theme and adding the following little code snippet somewhere, e.g. right before the </body> tag.

<script type="text/javascript">
    (function($) {
        $(window).scroll(function() {
            $('div.external-image-wrapper').each(function() {
                $(this).replaceWith("<img src='" + $(this).attr("data-src") + "' />")
            });
        });
    })(window.jQuery);
</script>

 

Batman: Arkham Knight – City Heat TT AR

My best City Heat TT time, placing me on the 55th position on the world ranking list. Not to bad for someone who’d only borrowed a PS4 to play through the game and otherwise almost newer plays on consoles or with a gamepad. The batmobile handling and timing rules (drifting slows timer and recharges turbo boost) takes a bit of getting used to though so it is seldom the best/faster looking races that yields the best times… Placed 64th and 120th on the game’s other two TT tracks.

SSH: iptables recent, pknock; google-authenticator; kerberos

Basic SSH

The only port that I have open in my firewall is 22, SSH. Through one can access all other services by means of ssh tunnels, SFTP, scp etc..  I used to leave it at that, just disallowing root logins (from IP-addresses outside my LAN) as most of the time the attacker tried to use username root and I thought that at least that would void those brute force attempts.

But the SSH port gets hammered relentlessly around the clock and the log files grow to be humongus so I thought I’d try to do something at least try to limit the attacks.

iptables recent

So I started by using the iptables recent module to blacklist IP-addresses that tries to connect more than 4 times within 10 minutes to port 22 for 1 hour (i think). But there were still quite a lot of hammering going on as many different source IP’s were used, and, most annoyingly you could (and I did on multiple occasions) lock yourself out by starting more that 4 ssh sessions within 10mins (FileZilla for example quickly does this for you  if you let it use multiple parallel connections (the default) when downloading files). But for a long time this worked well enough I thought.

The iptables commands to set this up is somewhat cryptic:

iptables -A INPUT -m recent --update --seconds 600 --hitcount 4 --name blacklist --mask 255.255.255.255 --rsource -j DROP
iptables -A INPUT -m recent --set --name blacklist --mask 255.255.255.255 --rsource -j ACCEPT
Google-Authenticator-LibPAM

Later, after having turned on TOTP two-factor-authentication on all of my online accounts with that option I wanted to enable the same for SSH logins to my machines and found that this was easily realized by utilizing google-authenticator-libpam.

All one had to do was to build and install the PAM-module and add one line to /etc/pam.d/system-remote-login, requiring pam_google_authenticator.so (nullok makes it so that if a user haven’t setup google-authenticator for his account the login is allowed).

auth        required    pam_tally2.so onerr=succeed
auth        required    pam_shells.so
auth        required    pam_nologin.so
auth        required    pam_env.so
auth        required    pam_unix.so try_first_pass
auth        required    pam_google_authenticator.so echo_verification_code debug nullok
auth        optional    pam_permit.so

account     include     system-login
password    include     system-login
session     include     system-login

Then the user runs a google-authenticator program which generates a secret key, provides you with a number of backup keys (as per the norm), and stores information about this in a dotfile in your home directory that the PAM module will look for. The script even displays a QR-code in the terminal window so you easily can scan it into your phone’s TOTP app!

google-authenticator
google-authenticator

iptables pknock

Then I asked myself if port knocking wouldn’t make a lot more sense that rate-limiting. Then the port would be completely hidden and practically no attack attempts would reach it. Port knocking is a technique whereby one make connections to a specific sequence of ports within a given time frame to open a hole in the firewall to the port you want to give access to to the knocking ip. I chose the iptables pknock module to enable this. It is not included in the main kernel tree but available as an external module. Using gentoo one can set XTABLES_ADDONS=”pknock” in make.conf and then emerge xtables. After setting this up i wondered why I had not just done this from the start, so easy and efficient.

A simple iptables rule that says that we have to make tcp connections to ports 1234, 5678 and 5555 in that order and within 10s to grant access to port 22 for 10min (access to setup connections, ssh sessions can last longer than that but that is handled by another conntrack rule that allows any established connections to continue.)

iptables -A INPUT -p tcp -m pknock --knockports 1234,5678,5555 --time 10 --autoclose 10 --name SSH --strict -m tcp --dport 22 -j ACCEPT

A simple knock-script that uses nc (netcat) utility to connect to the defined ports in sequence for 1s 1s apart.

#!/bin/sh

for x in 1234 5678 5555
do
    nc -z -w 1 hades.eleusis.se $x &>/dev/null
    echo $x
    sleep 1
done

A rather annoying thing with this I notice is that if you build a new kernel and forgot to re-emerge (emerge @modules-rebuild) the xtables package, iptables-restore will fail with missing module error on startup and leave the firewall completely open when one boots one’s new kernel. This is not that easily spotted on a headless server machine. I would have thought it would have made more sense to leave it completely closed in such cases. It would be more secure and one would immediately notice that something was wrong and have the chance to timely remedy the situation. Hopefully I won’t forget again (or maybe I should look into the possibility to have iptables DENY instead of ALLOW by default and make the change).

Kerberos

I later added support for kerberos based authentication in my LAN, following this excellent and still surprisingly,  since written 2007, valid guide (imagine if it had been a latest-javascript-lib-guide… the lib would like not even exist in any recognizable shape of form any longer…). I could follow the guide step by step, just enter the commands one by one, and it would work!: [HOWTO] Kerberos for small networks, without LDAP or AD.

Before reading that howto I had not thought kerberos was possible, or feasible, without a directory server. But afterwards I had realized that a setup like one I would have liked to have actually was not only possible, but pretty simply so, and without any questionable hacks.

I wanted to be able to ssh to any machine in the network without providing any passwords if a valid kerberos ticket was held and the kerberos ticket granting server accessible (which should be only from within my LAN). If trying to connect without ticket or access to the server (I’m actually not really sure about this, maybe access to server is not necessary if one only holds a currently valid ticket?) one would be asked for password and TOTP like before.

I solved this by emerging mit-krb5, setting up a most basic /etc/krb5.conf and a /etc/krb5.keytab with the host principal’s key) and emerging the pam_krb5 package and altering system-remote-login as so:

auth        required    pam_tally2.so onerr=succeed
auth        required    pam_shells.so
auth        required    pam_nologin.so
auth        required    pam_env.so
auth        sufficient  pam_krb5.so force_first_pass minimum_uid=1000
auth        required    pam_unix.so try_first_pass
auth        required    pam_google_authenticator.so echo_verification_code debug nullok
auth        optional    pam_krb5.so force_first_pass minimum_uid=1000
auth        optional    pam_permit.so

account     include     system-login
password    include     system-login
session     include     system-login

The first pam_krb5.so row makes it so that if a user holds a valid kerberos ticket that is sufficient for the login to be allowed, force_first_pass makes it not ask for a password but only use one previously provided, if any, in this case that would be a ticket). No unix password nor OTP will be asked for in that case. The second almost identical row which is marked optional makes it so that if we don’t hold a ticket and move on to unix password and TOTP authentication it will try to use the entered unix password to automatically request a new ticket from the granting server, which potentially can save one from another kinit command with accompanying manual password entry, as long as the local unix and kerberos server passwords match. Otherwise one has to update the local password to match the servers (as we are using kerberos without any directory server or such, even if the pam module can keep the passwords synchronized when changing on one host the change of local password will not automatically propagate to other hosts.).

WSL

This works nicely with the Windows Subsystem for Linux. You can install the kerberos packages, copy the /etc/krb5.conf and use kinit to authenticate within your WSL session. Very neat.

Current Status

So now my server support kerberos authentication within the LAN so we can ssh freely between the hosts without providing credentials other than the ticket that is granted at first login on any machine. A very convenient thing is that one can specify which user one would like to authenticate as. The root user has no kerberos principal (only local logins) and is not permitted to log in via SSH anyways. But by executing “kinit mignon” the root user can be granted a ticket which provides SSH access as mignon, kind of neat. So instead of “ssh mignon@hades” and having to give password and OTP one can use “kinit mignon && ssh mignon@hades” and only give the password and keep the phone in the pocket. Access from outside of the LAN requires port knocking before a connection can be made, and then password and OTP like before.