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.

Screen:



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 http://www.erikyyy.de/tempest/) 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.



Controls:
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.

Code:

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
 
 
Private Type BITMAPINFOHEADER
    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
 
Private Type BITMAPINFO
    bmiHeader As BITMAPINFOHEADER
    bmiColors(255) As RGBQUAD
End Type
 
 
Dim PicW As Long
Dim PicH As Long
Dim Pix() As Byte
Dim BMI As BITMAPINFO
 
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
    Refresh
    Sleep Song(n).Duration
    DoEvents
    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
        Stop
End Select
End Function
 
 
Private Sub Timer1_Timer()
If (GetAsyncKeyState(vbKeyEscape) And 1) = 1 Then
    ProgQuit = True
    Unload Me
End If
End Sub


Wednesday, March 31, 2021

Calculating hashes: MD2, MD4, MD5, SHA1, SHA2-256, SHA2-384, and SHA2-512

It works with MD2, MD4, MD5, SHA1, SHA2-256, SHA2-384, and SHA2-512. Put the below code in a module (BAS file). It does everything that CAPICOM does regarding hashes, but without using any ActiveX DLL files. It depends entirely on the standard cryptographic API DLL files, using declare statements. There are several publicly accessible functions. These are
HashBytes
HashStringA
HashStringU
HashArbitraryData
BytesToHex
BytesToB64

HashBytes computes a hash of a 1D byte array, who's lower bound is 0.

HashStringA computes the hash of an Ascii/Ansi (1 byte per character) string. As VB6 strings are actually Unicode (2 bytes per character), and due to the fact that this function is intended to calculate the hash of the Ascii version of the string, the function first converts VB6's unicode characters to true Ascii characters via VB6's StrConv function. However, because characters with an Ascii value above 127 will differ between locales, the LocaleID is needed to be known for this conversion. As such, LocaleID is a parameter for this function. By default, the LocaleID used by the program is the LocaleID of the PC that the program is running on. This should be used in most situations, as this will generate a hash that will match the output of most other programs that generate a hash (such as the program called Easy Hash).

HashStringU computes the hash of a Unicode (2 bytes per character) string. As VB6 strings are actually Unicode, there is no conversion needed, and thus is no need to specify LocaleID. Therefore, this function doesn't have a LocaleID parameter. Because each character is defined by 2 bytes, rather than 1, the output of this hash function will obviously differ from the hash calculated by HashStringA, and thus will differ from the hash calculated by most other hash calculating programs (such as the freeware one that I used for testing called Easy Hash). For example, a string with 3 spaces " " is represented as the byte array (shown in hex) 20 00 20 00 20 00 in Unicode encoding, but as 20 20 20 in Ascii encoding. These are 2 distinctly different byte arrays, and thus will produce 2 completely different hashes.
Side-Note regarding Unicode in VB6: Despite this fact, that internally in VB6 all the strings are Unicode, the implementation of Unicode in VB6 is VERY LIMITED. That is, it won't display any Unicode character that can't also be displayed as an extended ascii character for the computer's current locale. Instead it will show it as a question mark. This won't effect how this function works (or the above function, as it's computing a hash, not displaying anything), but it will effect whether or not a given string will be properly displayed.

HashArbitraryData computes the hash of absolutely anything. It just needs to know where in memory the first byte of data is, and how many bytes long the data is. It will work with multidimensional byte arrays, arrays of other data types, arrays that start with with a lower bound other than zero, user defined types, sections of memory allocated with API functions, etc. There's nothing that it can't compute the hash of. Of course this gives you the added responsibility of needing to know where exactly in memory the data is, and the size of the data in bytes.

BytesToHex. This is a function intended to convert the raw bytes output from a hash function to a displayable hexadecimal string.

BytesToB64. This is a function intended to convert the raw bytes output from a hash function to a displayable base64 string.

Code:

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
 
Private Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As Long, ByVal Algid As Long, ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare Function CryptHashData Lib "advapi32.dll" (ByVal hHash As Long, ByRef pbData As Any, ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptGetHashParam Lib "advapi32.dll" (ByVal hHash As Long, ByVal dwParam As Long, ByRef pByte As Any, ByRef pdwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptDestroyHash Lib "advapi32.dll" (ByVal hHash As Long) As Long
Private Declare Function CryptReleaseContext Lib "advapi32.dll" (ByVal hProv As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptBinaryToString Lib "Crypt32.dll" Alias "CryptBinaryToStringA" (ByRef pbBinary As Any, ByVal cbBinary As Long, ByVal dwFlags As Long, ByVal pszString As String, ByRef pcchString As Long) As Long
 
Private Const PROV_RSA_AES As Long = 24
Private Const CRYPT_VERIFYCONTEXT As Long = &HF0000000
 
Public Enum HashAlgo
    HALG_MD2 = &H8001&
    HALG_MD4 = &H8002&
    HALG_MD5 = &H8003&
    HALG_SHA1 = &H8004&
    HALG_SHA2_256 = &H800C&
    HALG_SHA2_384 = &H800D&
    HALG_SHA2_512 = &H800E&
End Enum
 
Private Const HP_HASHSIZE As Long = &H4&
Private Const HP_HASHVAL As Long = &H2&
 
 
Public Function HashBytes(ByRef Data() As Byte, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim hProv As Long
Dim hHash As Long
Dim Hash() As Byte
Dim HashSize As Long
 
CryptAcquireContext hProv, vbNullString, vbNullString, 24, CRYPT_VERIFYCONTEXT
CryptCreateHash hProv, HashAlgorithm, 0, 0, hHash
CryptHashData hHash, Data(0), UBound(Data) + 1, 0
CryptGetHashParam hHash, HP_HASHSIZE, HashSize, 4, 0
ReDim Hash(HashSize - 1)
CryptGetHashParam hHash, HP_HASHVAL, Hash(0), HashSize, 0
CryptDestroyHash hHash
CryptReleaseContext hProv, 0
 
HashBytes = Hash()
End Function
 
 
 
Public Function HashStringA(ByVal Text As String, Optional ByVal LocaleID As Long, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim Data() As Byte
Data() = StrConv(Text, vbFromUnicode, LocaleID)
HashStringA = HashBytes(Data, HashAlgorithm)
End Function
 
Public Function HashStringU(ByVal Text As String, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim Data() As Byte
Data() = Text
HashStringU = HashBytes(Data, HashAlgorithm)
End Function
 
Public Function HashArbitraryData(ByVal MemAddress As Long, ByVal ByteCount As Long, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim Data() As Byte
ReDim Data(ByteCount - 1)
CopyMemory Data(0), ByVal MemAddress, ByteCount
HashArbitraryData = HashBytes(Data, HashAlgorithm)
End Function
 
 
 
 
Public Function BytesToHex(ByRef Bytes() As Byte) As String
Dim HexStringLen As Long
Dim HexString As String
 
CryptBinaryToString Bytes(0), UBound(Bytes) + 1, 12, vbNullString, HexStringLen
HexString = String$(HexStringLen, vbNullChar)
CryptBinaryToString Bytes(0), UBound(Bytes) + 1, 12, HexString, HexStringLen
 
BytesToHex = UCase$(HexString)
End Function
 
Public Function BytesToB64(ByRef Bytes() As Byte) As String
Dim B64StringLen As Long
Dim B64String As String
 
CryptBinaryToString Bytes(0), UBound(Bytes) + 1, 1, vbNullString, B64StringLen
B64String = String$(B64StringLen, vbNullChar)
CryptBinaryToString Bytes(0), UBound(Bytes) + 1, 1, B64String, B64StringLen
 
BytesToB64 = B64String
End Function

Source:
http://earlier189.rssing.com/browser.php?indx=6373759&item=379

Tuesday, March 30, 2021

How to insert machine code in source code: VB6 & ASM

Just a quick post for documentation sake of using inline asm with VB6 (or at least as close as we can get to it without an external c dll. In terms of development of the asm to put in. You can use your C Compiler to generate it for you..here are the tips.

  • CallwindowsProc has 5 arguments max and can return a long The first argument is already used so your args start at [EBP+0x0C] this means use a dummy int arg first in your prototype to line up args.
  • do not use any sub functions from your code do things in blocks in you have to.
  • once you generate your code, you can extract the opcodes from VC in debug mode viewing mixed mode disasm (develop as an exe usually although you may have to as a dll to use with vb as standard call dll).
  • you need to strip all the function prolog and epilog asm from the compiler generated block (or use naked declspec) your ret should be RETN 10h.
  • keep a couple nops (&H90) in place at start in case you need room to add a breakpoint (&hCC) manually to stop on your asm in a debugger to debug it. yes you will have to use ollydbg to debug it in asm most likley.
  • you can twiddle with the CallWindowProc prototypes more based on what you are using it for..see last example.
Full example here:


Note: All single quotes for comments are stripped by my blog script. Same as default CallWindowProc except param 1 is now "ByRef lpBytes As Any" or you can use the default like this: CallWindowProc(Varptr(asmBytes(0)).


The most simple and direct example of VB6 & ASM:

Private Declare Function CallAsm Lib "user32" 
    Alias "CallWindowProcA" _
    (ByRef lpBytes As Any, 
    ByVal hWnd As Long, 
    ByVal Msg As Long, 
    ByVal wParam As Long, 
    ByVal lParam As Long) As Long

Function Shl(x As Long) As Long
    '8B45 0C        MOV EAX,DWORD PTR SS:[EBP+12]
    'D1E0           SHL EAX,1
    'C2 10 00       RETN 10h
    Dim o() As Byte
    Const sl As String = "8B 45 0C D1 E0 C2 10 00"
    o() = toBytes(sl)
    Shl = CallAsm(o(0), x, 0, 0, 0)
End Function

private Function toBytes(x As String) As Byte()
    Dim tmp() As String
    Dim fx() As Byte
    Dim i As Long
    
    tmp = Split(x, " ")
    ReDim fx(UBound(tmp))
    
    For i = 0 To UBound(tmp)
        fx(i) = CInt("&h" & tmp(i))
    Next
    
    toBytes = fx()

End Function

Another example of working on a byte buffer in your asm:

Private Declare Function CallAsm2 Lib "user32" 
     Alias "CallWindowProcA" _
    (ByRef lpBytes As Any, 
     ByRef chararray As Any, 
     ByVal length As Long, 
     ByVal unused1 As Long, 
     ByVal unused2 As Long) As Long




Const opcodes As String = 
   "909090C745F800000000EB098B4DF88" & _
   "3C101894DF88B55F83B55107D258B45" & _
   "0C0345F88A08884DFC8B45F833D28A5" & _
   "5FC2AD08855FC8B550C0355F88A45FC" & _
   "8802EBCA9090C21000"

fx() = toBytes2(opcodes)
CallAsm2 fx(0), byteBufferToWorkOn(0), UBound(byteBufferToWorkOn), 0, 0

Function toBytes2(x As String, Optional debugit As Boolean = False) As Byte()
    Dim tmp() As String
    Dim fx() As Byte
    Dim i As Long
    Dim y
    
    ReDim fx(Len(x) / 2)
    
    For i = 1 To Len(x) Step 2
        fx(y) = CByte(CLng("&h" & Mid(x, i, 2)))
        y = y + 1
    Next
    
    If debugit Then fx(0) = &HCC
    
    toBytes2 = fx()
    
End Function

The opcodes are for the following C with the prolog and epilog stripped out:

void __stdcall  fnDecode(int dummy, char* b, int len)
{
    char x;
    for(int i=0; i<len; i++){
        x = b[i];
        _asm{
            //do stuff to x here
        }
        b[i] = x; //update vb byte buffer
    }
} 

Source:
http://sandsprite.com/blogs/index.php/index.php?uid=11&pid=43

Wednesday, February 17, 2021

String-style operations by wrapping a Byte array


People seem to get tangled up in their underwear a lot trying to fiddle with binary data in String variables. Often they run into nightmares where they convert Unicode "to Unicode" and then back later, in the vain hope of avoiding data corruption. And then some locale boundary gets crossed and it all falls down. Hard.

From what I've seen the bulk of this comes from the desire to use String operations on binary data. But most of these are fairly trivial to synthesize, especially with the help of CopyMemory.

The Bytarr Class wraps a dynamic Byte array along with several properties and methods to make this easier.

You can use the Class for lots of applications, or when you only need one or two operations it can server as a template for inline code when you don't want the Class.

Bytarr (biter?) is bundled with a test program in the attachment. This also includes my Dump Class, which I find handy for debugging and testing.



Source:

Sunday, February 14, 2021

Working with pointers - VB6 (by Krivous Anatoly Anatolevich)

Often there are situations when you need to get data having only the address (for example, in WndProc, HookProc). Usually, simply copy the data via CopyMemory the structure after changing data and copy it back. If the structure is large, it will be a waste of resources to copy into structure and back. In languages such as C ++ is all done easily with the help of pointers, written something like newpos = (WINDOWPOS *) lparam. Nominally VB6 does not work with pointers, but there are a few workarounds.

Public Declare Function GetMem4 Lib "msvbvm60" (src As Any, Dst As Any) As Long
Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (src() As Any) As Long
For a start I will give the necessary declarations:
Code:

Private Type Vector
    X As Single
    Y As Single
End Type
Private Type TestRec
    Name As String
    Value As Long
    Position As Vector
    Money As Double
End Type
 
Private Sub Form_Load()
    Dim tr As TestRec
    Test tr
End Sub
 
Private Function Test(Pointer As TestRec, Optional ByVal nu As Long)
    Dim q As TestRec, z As TestRec
 
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
 
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
 
    GetMem4 VarPtr(q), ByVal VarPtr(nu) - 4    ' Set pointer to q (Pointer = &q)
 
    PrintRec Pointer
 
    GetMem4 VarPtr(z), ByVal VarPtr(nu) - 4    ' Set pointer to z (Pointer = &z)
 
    PrintRec Pointer
 
End Function
 
Private Sub PrintRec(Pt As TestRec)
    Debug.Print "----------------"
    Debug.Print "Name = " & Pt.Name
    Debug.Print "Value = " & Pt.Value
    Debug.Print "Money = " & Pt.Money
    Debug.Print "Position.X = " & Pt.Position.X
    Debug.Print "Position.Y = " & Pt.Position.Y
End Sub
You can also create a pointer by using arrays. The idea is to create 2 arrays one element each, which will store the address of a variable, and the other will refer to the data. The first will always be Long, a second type of data desired. This is useful for example if you want to pass on lists, etc. It's no secret that the array in VB is simply an SafeArray. In the data structure of this array contains a lot of useful information, and a pointer to the data. What we do, we create two arrays:


  • 1st (with address) refers to a pointer to the second data array. As a result, changing the values in the first array, 2nd automatically refer to the desired data.*
  • 2nd is directly the data pointed to by the first.*


Also, after all the manipulations necessary to return all the pointers back to VB properly clear the memory.* For all manipulations I created auxiliary functions and structure for data recovery.* Address SafeArray is available through Not Not Arr, but IDE after such manipulations are glitches with floating point:
Code:

Public Type PtDat
    Prv1 As Long
    Prv2 As Long
End Type
 
' Create the pointer. 1st param is pointer, 2nd address.
Public Function PtGet(Pointer() As Long, ByVal VarAddr As Long) As PtDat
    Dim i As Long
    i = GetSA(ArrPtr(Pointer)) + &HC
    GetMem4 ByVal i, PtGet.Prv1
    GetMem4 VarAddr + &HC, ByVal i
    PtGet.Prv2 = Pointer(0)
End Function
' Release pointer
Public Sub PtRelease(Pointer() As Long, prev As PtDat)
    Pointer(0) = prev.Prv2
    GetMem4 prev.Prv1, ByVal GetSA(ArrPtr(Pointer)) + &HC
End Sub
' Obtaint address of SafeArray (same Not Not)
Public Function GetSA(ByVal addr As Long) As Long
    GetMem4 ByVal addr, GetSA
End Function
Example of use:
Code:

Private Sub Form_Load()
    Dim pt() As Long, var() As TestRec, prev As PtDat      ' Pointer, references data, release data.
    Dim q As TestRec, z As TestRec                          ' The structures, which we refer
 
    ReDim pt(0): ReDim var(0)
 
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
 
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
 
    prev = PtGet(pt, GetSA(ArrPtr(var)))                    ' Create "pointer"
 
    pt(0) = VarPtr(q)                                      ' Refer to q (pt = &q)
    PrintRec var(0)
    pt(0) = VarPtr(z)                                      ' Refer to z (pt = &z)
    PrintRec var(0)
 
    PtRelease pt, prev                                      ' Release
 
End Sub

Source:
http://earlier189.rssing.com/browser.php?indx=6373759&item=376