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