Thursday, April 1, 2021

Tempest Test for Windows

First a bit of background on the subject:
Tempest is the concept of being able to retrieve usable information about what data is being processed by a computer or other electronic device, entirely from the "electronic noise" that is given off by that device. These RF emissions usually cause trouble if they cause interference with radio receivers like an AM radio that you are trying to listen to. The FCC has standards for reducing this interference to an acceptable level, but even so, if you are TRYING to pick up this signal it is usually possible.


Under the right conditions, this interference isn't just unintelligible noise, but can actually convey data that is being processed by the computer at that time. This can cause a privacy risk if the data being processed that causes these RF emissions contains information is confidential. This could be the case if for example you are looking at a document on your PC that has confidential information, but your monitor's RF emissions allow the screen image to be received by an adversary with a radio receiver.

To demonstrate the ability of a monitor to transmit intelligible information, Erik Thiele created a program called "Tempest for Eliza" (which you can read about here which transmits Beethoven's song Fur Elise. It depends on the ability of a CRT monitor to send one pixel at a time to the screen with an electron gun, so that the signal going to the electron gun gets radiated as RF. So to send a tone, the brightness of a pixel is based on both the frequency of the audible tone and the RF frequency that you want to have to tune your AM radio to to receive it.

Unfortunately, this program has several problems. One is that it requires being compiled (no binaries can be downloaded). Another is that it only runs on Linux. And lastly, it is based on a CRT monitor which sends one pixel at a time. The last of these is a problem because modern LCD monitors process data one line at a time. While vertically, each line of the display is set in sequence, within each line, all of the pixels are set simultaneously. There is no "pixel clock" in an LCD monitor, just a line clock and a data clock. The data clock runs very fast like a CPU processor (probably at 10s or 100s of megahertz at least) and handles the image data very fast for that particular line. Depending on the monitor's microcontroller clock speed (which can be pretty much anything, and not predictable like the pixel clock of a CRT monitor), you will have the carrierwave signal based on that clock speed. Depending on what that frequency is, you may need to tune around your radio to find it or one of its harmonics (sometimes these can be lower than the clock frequency in the form of a lower side band). There's not much than can be done about this, except tune your AM radio (preferably a shortwave receiver so you get more frequencies to search through) to the strongest signal for your particular LCD monitor. However, since you don't need to worry about the pixels horizontally, that means that every pixel on a given line can be lit up at maximum brightness, and I have found that this actually makes the signal stronger. You only need to worry about modulating the brightness vertically.

And here's the solution I've found:
Of course, there's a pretty simple solution to fixing these things at once. To fix the first 2 things, just write your own version of this software in a language you are familiar with and which is designed to compile for Windows (VB6 in this case). And the last thing is to make it so that every pixel is lit up on a given line, which naturally is easy to do when you are writing it yourself (you just write it to do that). So below, there are 2 links to my VB6 version of this guy's program, designed from the ground up to work with LCD monitors (sorry if you want to use it with a CRT monitor, it won't work, as I've made this based on the fact that nearly everyone uses LCD not CRT monitors nowadays). The first link fixes problem 2 and 3 (it is made for Windows, not Linux, and it is optimized for LCD screens, but still requires compiling). The second link fixes all 3 problems. It has the source code, just as with the first link, but it also has a compiled EXE file (in case you don't want to go through the hassle of compiling it yourself, or if you don't have a copy of VB6 yourself). If you are really paranoid about viruses and stuff, you can use the first link, but as it is not a virus (I have no desire to hack anybody's PC) I would highly recommend the second link, which has all the source code (just as with the first link) and also has the compiled EXE file.

The name of the program is "Tempest Test for Windows". With it, you can determine how much RF signal is coming from your monitor that actually conveys information about what's on your computer screen, with the idea that if you are running a business that has confidential info on your computer, and you find that you can hear the music from this program playing on a nearby radio, you should consider Faraday shielding your PC or the room that the PC is in. As with the original "Tempest for Eliza" (which was created by Erik Thiele), it plays Fur Elise. The notes data are in the "song.txt" file, which can be edited to make it play any musical piece that you want.

There's only one control, the Esc key. Press it to close the program before the song has finished playing. If the song is allowed to continue playing, the program will close when the song ends.

Format of the "song.txt" file:
It is case-insensitive. Each note is specified by note letter, a modifier symbol ("#" for sharp, or "b" for flat, and yes that is a lowercase "B", but uppercase works as well, as the program is completely case insensitive), and an octave number (from 0 to 8), in that order. In the case of it not being sharp or flat, you leave out the modifier. For example, D sharp in octave 4 is D#4 (or d#4), while B normal in octave 7 is B7 (or b7), and B flat in octave 2 is Bb2 (or bb2, or bB2, or BB2). Each note or special symbol is separated from each other by a space. There are 2 special symbols ("." and "-"). The "." represents no tone transmitted for the period of one half of a note. The dash represents holding the previous note for a period of one note. Any other text in a given entry, or a blank entry (such as formed by an extra space at the start or end of the text file, or by 2 consecutive spaces in the middle of the file) will trigger the error "Stop statement encountered". This is because I left a stop statement in it while debugging it, prior to compiling it. That stopped the code is designed to stop it so that you can check one of the variables that holds the string for that note or special symbol, to see why it didn't match what the program was expecting (so you could go search for the specific bad string in the song.txt file and correct it). It's not nearly as useful with the EXE file, as it alerts you to the fact that there is something wrong with the file, but you'll need to manually look through the text file to see what's wrong. But I left it in anyway so that you could see if there is in fact something wrong with the text file, should you decide to edit it and put in your own song.


Private Declare Sub FillMemory Lib "kernel32.dll" Alias "RtlFillMemory" (ByRef Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
Private Declare Function SetDIBits Lib "gdi32.dll" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, ByRef lpBits As Any, ByRef lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Private Declare Function SetWindowPos Lib "user32.dll" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Long) As Integer
    biSize As Long
    biWidth As Long
    biHeight As Long
    biPlanes As Integer
    biBitCount As Integer
    biCompression As Long
    biSizeImage As Long
    biXPelsPerMeter As Long
    biYPelsPerMeter As Long
    biClrUsed As Long
    biClrImportant As Long
End Type
Private Type RGBQUAD
    rgbBlue As Byte
    rgbGreen As Byte
    rgbRed As Byte
    rgbReserved As Byte
End Type
    bmiColors(255) As RGBQUAD
End Type
Dim PicW As Long
Dim PicH As Long
Dim Pix() As Byte
Private Type Note
    Freqency As Single
    Duration As Long
End Type
Dim Song() As Note
Dim ProgQuit As Boolean
Private Sub Form_Activate()
Dim y As Long
Dim n As Long
Dim z As Byte
Dim Freq As Single
Dim FrameRate As Long
Dim FrameDuration As Single
Dim LineDuration As Single
Dim LineRate As Single
FrameRate = 60
FrameDuration = 1 / FrameRate
LineDuration = FrameDuration / PicH
LineRate = 1 / LineDuration
SetWindowPos Me.hWnd, -1, 0, 0, 0, 0, &H201
For n = 0 To UBound(Song)
    Freq = Song(n).Freqency
    For y = 0 To PicH - 1
        FillMemory Pix(0, y), PicW, Sgn(Sin(2 * 3.14159 * Freq * y / LineRate)) * 127.5 + 127.5
    Next y
    SetDIBits Me.hDC, Me.Image.Handle, 0, PicH, Pix(0, 0), BMI, 0
    Sleep Song(n).Duration
    If ProgQuit Then Exit Sub
Next n
Unload Me
End Sub
Private Sub Form_Load()
Dim i As Long
Dim NotesStr As String
ChDir App.Path
PicW = Screen.Width / 15
PicH = Screen.Height / 15
ReDim Pix(PicW - 1, PicH - 1)
With BMI
    With .bmiHeader
        .biSize = 40
        .biPlanes = 1
        .biBitCount = 8
        .biClrUsed = 256
        .biClrImportant = 256
        .biWidth = PicW
        .biHeight = -PicH
    End With
    For i = 0 To 255
        With .bmiColors(i)
            .rgbRed = i
            .rgbGreen = i
            .rgbBlue = i
        End With
    Next i
End With
Open "song.txt" For Binary Access Read As #1
NotesStr = String$(LOF(1), vbNullChar)
Get #1, 1, NotesStr
Close #1
Song() = String2Notes(NotesStr, 300)
GetAsyncKeyState vbKeyEscape
Timer1.Enabled = True
End Sub
Private Function String2Notes(ByVal NotesStr As String, ByVal Duration As Long) As Note()
Dim NoteStrings() As String
Dim Notes() As Note
Dim n As Long
NoteStrings() = Split(NotesStr, " ")
ReDim Notes(UBound(NoteStrings))
For n = 0 To UBound(Notes)
    With Notes(n)
        Select Case NoteStrings(n)
            Case "."
                .Freqency = 0
                .Duration = Duration / 2
            Case "-"
                .Freqency = Notes(n - 1).Freqency
                .Duration = Duration
            Case Else
                .Freqency = Note2Freq(NoteStrings(n))
                .Duration = Duration
        End Select
    End With
Next n
String2Notes = Notes()
End Function
Private Function Note2Freq(ByVal NoteName As String) As Single
Select Case LCase$(NoteName)
    Case "c0"
        Note2Freq = 16.35
    Case "c#0", "db0"
        Note2Freq = 17.32
    Case "d0"
        Note2Freq = 18.35
    Case "d#0", "eb0"
        Note2Freq = 19.45
    Case "e0"
        Note2Freq = 20.6
    Case "f0"
        Note2Freq = 21.83
    Case "f#0", "gb0"
        Note2Freq = 23.12
    Case "g0"
        Note2Freq = 24.5
    Case "g#0", "ab0"
        Note2Freq = 25.96
    Case "a0"
        Note2Freq = 27.5
    Case "a#0", "bb0"
        Note2Freq = 29.14
    Case "b0"
        Note2Freq = 30.87
    Case "c1"
        Note2Freq = 32.7
    Case "c#1", "db1"
        Note2Freq = 34.65
    Case "d1"
        Note2Freq = 36.71
    Case "d#1", "eb1"
        Note2Freq = 38.89
    Case "e1"
        Note2Freq = 41.2
    Case "f1"
        Note2Freq = 43.65
    Case "f#1", "gb1"
        Note2Freq = 46.25
    Case "g1"
        Note2Freq = 49
    Case "g#1", "ab1"
        Note2Freq = 51.91
    Case "a1"
        Note2Freq = 55
    Case "a#1", "bb1"
        Note2Freq = 58.27
    Case "b1"
        Note2Freq = 61.74
    Case "c2"
        Note2Freq = 65.41
    Case "c#2", "db2"
        Note2Freq = 69.3
    Case "d2"
        Note2Freq = 73.42
    Case "d#2", "eb2"
        Note2Freq = 77.78
    Case "e2"
        Note2Freq = 82.41
    Case "f2"
        Note2Freq = 87.31
    Case "f#2", "gb2"
        Note2Freq = 92.5
    Case "g2"
        Note2Freq = 98
    Case "g#2", "ab2"
        Note2Freq = 103.83
    Case "a2"
        Note2Freq = 110
    Case "a#2", "bb2"
        Note2Freq = 116.54
    Case "b2"
        Note2Freq = 123.47
    Case "c3"
        Note2Freq = 130.81
    Case "c#3", "db3"
        Note2Freq = 138.59
    Case "d3"
        Note2Freq = 146.83
    Case "d#3", "eb3"
        Note2Freq = 155.56
    Case "e3"
        Note2Freq = 164.81
    Case "f3"
        Note2Freq = 174.61
    Case "f#3", "gb3"
        Note2Freq = 185
    Case "g3"
        Note2Freq = 196
    Case "g#3", "ab3"
        Note2Freq = 207.65
    Case "a3"
        Note2Freq = 220
    Case "a#3", "bb3"
        Note2Freq = 233.08
    Case "b3"
        Note2Freq = 246.94
    Case "c4"
        Note2Freq = 261.63
    Case "c#4", "db4"
        Note2Freq = 277.18
    Case "d4"
        Note2Freq = 293.66
    Case "d#4", "eb4"
        Note2Freq = 311.13
    Case "e4"
        Note2Freq = 329.63
    Case "f4"
        Note2Freq = 349.23
    Case "f#4", "gb4"
        Note2Freq = 369.99
    Case "g4"
        Note2Freq = 392
    Case "g#4", "ab4"
        Note2Freq = 415.3
    Case "a4"
        Note2Freq = 440
    Case "a#4", "bb4"
        Note2Freq = 466.16
    Case "b4"
        Note2Freq = 493.88
    Case "c5"
        Note2Freq = 523.25
    Case "c#5", "db5"
        Note2Freq = 554.37
    Case "d5"
        Note2Freq = 587.33
    Case "d#5", "eb5"
        Note2Freq = 622.25
    Case "e5"
        Note2Freq = 659.25
    Case "f5"
        Note2Freq = 698.46
    Case "f#5", "gb5"
        Note2Freq = 739.99
    Case "g5"
        Note2Freq = 783.99
    Case "g#5", "ab5"
        Note2Freq = 830.61
    Case "a5"
        Note2Freq = 880
    Case "a#5", "bb5"
        Note2Freq = 932.33
    Case "b5"
        Note2Freq = 987.77
    Case "c6"
        Note2Freq = 1046.5
    Case "c#6", "db6"
        Note2Freq = 1108.73
    Case "d6"
        Note2Freq = 1174.66
    Case "d#6", "eb6"
        Note2Freq = 1244.51
    Case "e6"
        Note2Freq = 1318.51
    Case "f6"
        Note2Freq = 1396.91
    Case "f#6", "gb6"
        Note2Freq = 1479.98
    Case "g6"
        Note2Freq = 1567.98
    Case "g#6", "ab6"
        Note2Freq = 1661.22
    Case "a6"
        Note2Freq = 1760
    Case "a#6", "bb6"
        Note2Freq = 1864.66
    Case "b6"
        Note2Freq = 1975.53
    Case "c7"
        Note2Freq = 2093
    Case "c#7", "db7"
        Note2Freq = 2217.46
    Case "d7"
        Note2Freq = 2349.32
    Case "d#7", "eb7"
        Note2Freq = 2489.02
    Case "e7"
        Note2Freq = 2637.02
    Case "f7"
        Note2Freq = 2793.83
    Case "f#7", "gb7"
        Note2Freq = 2959.96
    Case "g7"
        Note2Freq = 3135.96
    Case "g#7", "ab7"
        Note2Freq = 3322.44
    Case "a7"
        Note2Freq = 3520
    Case "a#7", "bb7"
        Note2Freq = 3729.31
    Case "b7"
        Note2Freq = 3951.07
    Case "c8"
        Note2Freq = 4186.01
    Case "c#8", "db8"
        Note2Freq = 4434.92
    Case "d8"
        Note2Freq = 4698.63
    Case "d#8", "eb8"
        Note2Freq = 4978.03
    Case "e8"
        Note2Freq = 5274.04
    Case "f8"
        Note2Freq = 5587.65
    Case "f#8", "gb8"
        Note2Freq = 5919.91
    Case "g8"
        Note2Freq = 6271.93
    Case "g#8", "ab8"
        Note2Freq = 6644.88
    Case "a8"
        Note2Freq = 7040
    Case "a#8", "bb8"
        Note2Freq = 7458.62
    Case "b8"
        Note2Freq = 7902.13
    Case Else
End Select
End Function
Private Sub Timer1_Timer()
If (GetAsyncKeyState(vbKeyEscape) And 1) = 1 Then
    ProgQuit = True
    Unload Me
End If
End Sub

No comments:

Post a Comment