Quantcast
Channel: Visual Basic 6.0 - Superior Source Code
Viewing all 181 articles
Browse latest View live

A database app with exceptional features (advanced - Win10)

$
0
0
Title: A database programme with exceptional features. (You will learn a lot from this. It’s a promise.)
Description: With this simple, but powerful database programme you will mainly learn the followings.
• How to develop a powerful database programme by using ListView, ImageList Microsoft ActiveX Data Objects Library, Microsoft Excel Object Library for a higher portability.
• How to easily use Listview control with filtering and exporting techniques (Exporting data with different combination of fields) to get the output in various aspects.
• How to do file handlings/clipboard related operations.
• How to remove unnecessary characters from the data.
• How to use Microsoft Excel for the reporting purpose of the programme without using Crystal Reports, Data reports.
• How to use a simple database programme in a network environment.
• How to import data from a same structured database while checking the duplicate entries.
• How to include basic system maintenance features of a database programme like creating, deleting users, setting user permissions, changing user passwords, resetting passwords and etc.
• How to easily implement a simple encryption to encode the constants, user passwords and the database password for a higher security.


Download from PSC
Download from ME


• How to use a backdoor (An alternate and secret way of entering a computer system) with a database programme to bypass its password protected access. (Education purpose only.)
• How to manipulate windows registry easily with few lines of code.
• How to do a search in a database programme efficiently and implement an advanced search.
• How to use functions and sub procedures to get the various operations done easily in the programme.
• How to utilize the pictures stored in resource within the programme.
• How to really use a Splash window with a programme.
• How to use a good looking and easily-created progress bar without any additional coding, for the operations of the programme with the database. (Operations include clearing data, importing data, exporting data, formatting data with the database.)
• How to create a well-arranged, good looking and flexible user interface with professional features in ease, in a limited screen area.
• If you are a beginner you will learn how to create an application with professional features in ease with basic programming skills you have without going for a complicated coding style.
This file came from Planet-Source-Code.com...the home millions of lines of source code
You can view comments on this code/and or vote on it at: http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=73908&lngWId=1

The author may have retained certain copyrights to this code...please observe their request and the law by reviewing all copyright conditions at the above URL.









Passwords used in the programme
-------------------------------

====================================================================
= Initial password for Administrator and others: password          =     
= Password of databases: kingsam2009                               =
= Backdoor of the programme: /bypassadmn2009 (Command line switch) =             
====================================================================

What to learn from this programme
---------------------------------

With this simple, but powerful database programme you will mainly learn the followings.

• How to develop a powerful database programme by using ListView, ImageList which includes in Microsoft Windows
  Common Controls, Microsoft ActiveX Data Objects Library, Microsoft Excel Object Library. (Because
  of these common and basic dependencies, the executable file of this programme works on any latest 32-bit
  windows operating system (from XP SP2 to Windows 7) without any installation and its execution does not depend on the
  User Account type, which is Administrator or Limited, which leads for a higher portability.)

• How to easily use Listview control with filtering and exporting techniques (Exporting data with diffrent combination
  of fields) to get the output in various aspects.

• How to do file handlings like saving database data to text files in different ways.

• How to use clipboard related operations with the data in the database.

• How to remove unnecessary characters from the data in the database.

• How to use Microsoft Excel for the reporting purpose of the programme. Hence, it is not required to have additional
  reporting interfaces like Crystal reports, Data reports which may require additional dependencies to run.
  (Here, you should only have Microsoft Excel installed on the computer, which is a definite installation on any PC.)

• How to use a simple database programme in a network environment.(Keep the database in a shared location on a file server.
  Then, establish a connection to it with the programme.)

• How to import data from a same structured database while checking the duplicate entries.
  (Because of this feature data can be entered separately with several computers, and
  finally main database can be updated easily.)

• How to include basic system maintenance features of a database programme like creating users, deleting users,
  setting user permissions, changing user passwords, resetting passwords and etc.

• How to easily implement a simple encryption to encode the required registry location constants, user passwords
  and the database password in the connection string and decrypt them to memory in order to use them in the programme so that
  the actual information can not be seen with Hex/Text editors. (If you think about more security and you do not use an exe
  compressor to compress your executable, this information will be useful to you.)

• How to use a backdoor (An alternate and secret way of entering a computer system) with a database programme to bypass its
  password protected access. (Education purpose only.)

• How to manipulate windows registry to store particular information and utilize them in the programme easily with few lines of code..

• How to do a search in a database programme efficiently and implement an advanced search.

• How to use functions and sub procedures to get the various operations done easily in the programme.

• How to utilize the pictures stored in resource within the programme.

• How to really use a Splash window with a programme.

• How to use a good looking and easily-created progress bar without any additional coding, for the operations of the programme
  with the database. (Operations include clearing data, importing data, exporting data, formatting data with the database.)

• How to create a well-arranged, good looking and flexible user interface with professional features in ease, in a limited screen area.

• If you are a beginner you will learn how to create an application with professional features in ease with basic programmeming skills you have
  without going for a complicated coding style.


What to do initially
--------------------
1. It's better if you can make the executable file of the programme first.
2. Configure the database location as ...\Databases\Main_DB\Main_Std_Info.mdb and log on as Administrator.
3. You will be prompted to the Options screen where you can configure various options. The default file saving
   location will be your application path by default. Just change it if needed.
4. Set the Email Client application if needed.
5. If you wish configure the other settings as well.
6. Go for programme's operations with sample data included.

===========================================================================================================================================================

How to use some of Programme Operations
---------------------------------------

   To Export Data from the database:
   ---------------------------------
   Configure the database location as ...\Databases\Main_DB\Main_Std_Info.mdb and use Export feature from Data View/Export.


   To Format Data in the database:
   -------------------------------
   Clear the database location from options.
   Configure the database location as ...\Databases\Format_DB\Format_std_Info.mdb and use Format Data feature from Data View/Export or Options.
   You will see how unnecessary characters are removed with the Format Data feature.


   To Import Data from a same structured database:
   -----------------------------------------------
   Clear the database location from options.

Import to a blank Database:
---------------------------
Configure the database location as ...\Databases\Blank_DB\Blank_std_Info.mdb and select
...\Databases\Main_DB\Main_Std_Info.mdb from Options and use Import Data feature.

Import to a Database with data:
-------------------------------
Configure the database location as ...\Databases\Import_DB_To\Import_std_Info_To.mdb and select
...\Databases\Import_DB_From\Import_std_Info_From.mdb from Options and use Import Data feature.


   To clear all data in the database
   ---------------------------------
   Go to options and select the relevent option (Student Information or Course Information) from Database Configuration to delete the relevent
   Data.

===========================================================================================================================================================

Note:
-----
* This is a simple database programme which was developed for a particular purpose only. Some of its features may go beyond its scope,
  which I included them as demonstration and education purpose only. If you find them useful you may use them in your own programmes.

* This database programme has been evaluated as good in its use because of its great portability and flexibility. (If this programme is used in a simple
  working environment with network facility, the only thing the system administrator should do is, allocating a seperate place on a file server to keep
  the database, with applicable permisstion to the relevent domain users.)

* If you think that this programme is helpful to you in whatever manner, please let me know your comments, suggestions and questions. I do appreciate
  them. Further, don't forget to vote me only if you think that this should be voted.

**** Happy Coding..... ****

----------------------------------------------------------------------------------------------------------------------------------------------------------

Author: P. G. B. Prasanna (A Software Developer from Sri Lanka)
E-mail: pgbsoft@gmail.com

Kernel mode driver - 32-bit ring-0 kernel mode driver written in VB6 for reading the arbitrary kernel memory (by Krivous Anatoly Anatolevich)

$
0
0
Hello everyone (sorry my English). There was a time, and decided to write something unusual on VB6, namely to try to write a driver. I must say before that I never wrote a driver and have no programming experience in kernel mode. The driver, according to my idea, will have to read the memory is not available in user mode, namely in the range 0x80000000 - 0xffffffff (in default mode, without IMAGE_FILE_LARGE_ADDRESS_AWARE). Immediately give the driver source code which is obtained:

' // modTrickMemReader.bas  - driver module
' // © Krivous Anatoly Anatolevich (The trick), 2014

Option Explicit

Public Enum NT_STATUS
STATUS_SUCCESS =0
STATUS_INVALID_PARAMETER =&HC000000D
End Enum

Public Type UNICODE_STRING
Length AsInteger
MaximumLength AsInteger
lpBuffer AsLong
End Type

Public Type LIST_ENTRY
Flink AsLong
Blink AsLong
End Type

Public Type KDEVICE_QUEUE
Type AsInteger
Size AsInteger
DeviceListHead As LIST_ENTRY
LockAsLong
Busy AsLong
End Type

Public Type KDPC
Type AsByte
Importance AsByte
Number AsInteger
DpcListEntry As LIST_ENTRY
DeferredRoutine AsLong
DeferredContext AsLong
SystemArgument1 AsLong
SystemArgument2 AsLong
DpcData AsLong
End Type

Public Type DISPATCHER_HEADER
LockAsLong
SignalState AsLong
WaitListHead As LIST_ENTRY
End Type

Public Type KEVENT
Header As DISPATCHER_HEADER
End Type

Public Type IO_STATUS_BLOCK
StatusPointer AsLong
Information AsLong
End Type

Public Type Tail
DriverContext(3)AsLong
Thread AsLong
AuxiliaryBuffer AsLong
ListEntry As LIST_ENTRY
lpCurStackLocation AsLong
OriginalFileObject AsLong
End Type

Public Type IRP
Type AsInteger
Size AsInteger
MdlAddress AsLong
Flags AsLong
AssociatedIrp AsLong
ThreadListEntry As LIST_ENTRY
IoStatus As IO_STATUS_BLOCK
RequestorMode AsByte
PendingReturned AsByte
StackCount AsByte
CurrentLocation AsByte
Cancel AsByte
CancelIrql AsByte
ApcEnvironment AsByte
AllocationFlags AsByte
UserIosb AsLong
UserEvent AsLong
Overlay AsCurrency
CancelRoutine AsLong
UserBuffer AsLong
Tail As Tail
End Type

Public Type DEVICEIOCTL
OutputBufferLength AsLong
InputBufferLength AsLong
IoControlCode AsLong
Type3InputBuffer AsLong
End Type

Public Type IO_STACK_LOCATION
MajorFunction AsByte
MinorFunction AsByte
Flags AsByte
Control AsByte
'Поле DeviceIoControl из объединения
DeviceIoControl As DEVICEIOCTL
pDeviceObject AsLong
pFileObject AsLong
pCompletionRoutine AsLong
pContext AsLong
End Type

Public Type DRIVER_OBJECT
Type AsInteger
Size AsInteger
pDeviceObject AsLong
Flags AsLong
DriverStart AsLong
DriverSize AsLong
DriverSection AsLong
DriverExtension AsLong
DriverName As UNICODE_STRING
HardwareDatabase AsLong
FastIoDispatch AsLong
DriverInit AsLong
DriverStartIo AsLong
DriverUnload AsLong
MajorFunction(27)AsLong
End Type

Public Type DEVICE_OBJECT
Type AsInteger
Size AsInteger
ReferenceCount AsLong
DriverObject AsLong
NextDevice AsLong
AttachedDevice AsLong
CurrentIrp AsLong
TimerAsLong
Flags AsLong
Characteristics AsLong
Vpb AsLong
DeviceExtension AsLong
DeviceType AsLong
StackSize AsByte
Queue(39)AsByte
AlignRequirement AsLong
DeviceQueue As KDEVICE_QUEUE
Dpc As KDPC
ActiveThreadCount AsLong
SecurityDescriptor AsLong
DeviceLock As KEVENT
SectorSize AsInteger
Spare1 AsInteger
DeviceObjExtension AsLong
Reserved AsLong
End Type
Private Type BinaryString
D(255)AsInteger
End Type

PublicConst FILE_DEVICE_UNKNOWN AsLong=&H22
PublicConst IO_NO_INCREMENT AsLong=&H0
PublicConst IRP_MJ_CREATE AsLong=&H0
PublicConst IRP_MJ_CLOSE AsLong=&H2
PublicConst IRP_MJ_DEVICE_CONTROL AsLong=&HE
PublicConst FILE_DEVICE_MEMREADER AsLong=&H8000&
PublicConst IOCTL_READ_MEMORY AsLong=&H80002000

Public DeviceName As UNICODE_STRING ' // Device name unicode string
Public DeviceLink As UNICODE_STRING ' // Device link unicode string
Public Device As DEVICE_OBJECT ' // Device object

Dim strName As BinaryString ' // Device name string
Dim strLink As BinaryString ' // Device link string

PublicSub Main()
EndSub

' // If error - false
PublicFunction NT_SUCCESS(_
ByVal Status As NT_STATUS)AsBoolean
NT_SUCCESS = Status >= STATUS_SUCCESS
EndFunction

' // Get pointer to IRP stack
PublicFunction IoGetCurrentIrpStackLocation(_
ByRef pIrp As IRP)AsLong
IoGetCurrentIrpStackLocation = pIrp.Tail.lpCurStackLocation
EndFunction

' // Entry point of driver
PublicFunction DriverEntry(_
ByRef DriverObject As DRIVER_OBJECT,_
ByRef RegistryPath As UNICODE_STRING)As NT_STATUS
Dim Status As NT_STATUS

' // Strings initialization
Status = Init()

' // This checking is not required but i left it because you can improve Init function
IfNot NT_SUCCESS(Status)Then
DriverEntry = Status
ExitFunction
EndIf

' // Create new device
Status = IoCreateDevice(DriverObject,0, DeviceName, FILE_DEVICE_MEMREADER,0,False, Device)

' // Check if device has been created
IfNot NT_SUCCESS(Status)Then
DriverEntry = Status
ExitFunction
EndIf

' // Create link, in order to access to object from user mode
Status = IoCreateSymbolicLink(DeviceLink, DeviceName)

' // Check if link has been created
IfNot NT_SUCCESS(Status)Then
' // If is not created then delete device
IoDeleteDevice Device
DriverEntry = Status
ExitFunction
EndIf

' // Set callback functions
DriverObject.DriverUnload = GetAddr(AddressOf DriverUnload)' // Driver unloading
DriverObject.MajorFunction(IRP_MJ_CREATE)= GetAddr(AddressOf DriverCreateClose)' // When CreateFile is being called
DriverObject.MajorFunction(IRP_MJ_CLOSE)= GetAddr(AddressOf DriverCreateClose)' // When CloseHandle is being called
DriverObject.MajorFunction(IRP_MJ_DEVICE_CONTROL)= GetAddr(AddressOf DriverDeviceControl)' // When DeviceIoControl is being called

' // Everything fine
DriverEntry = STATUS_SUCCESS

EndFunction

' // Unloading driver procedure
PublicSub DriverUnload(_
ByRef DriverObject As DRIVER_OBJECT)

' // Delete link
IoDeleteSymbolicLink DeviceLink

' // Delete device
IoDeleteDevice ByVal DriverObject.pDeviceObject

EndSub

' // This function is being called during opening/closing driver
PublicFunction DriverCreateClose(_
ByRef DeviceObject As DEVICE_OBJECT,_
ByRef pIrp As IRP)As NT_STATUS

pIrp.IoStatus.Information =0
pIrp.IoStatus.StatusPointer = STATUS_SUCCESS

' // Return IRP packet to IO manager
IoCompleteRequest pIrp, IO_NO_INCREMENT

' // Success
DriverCreateClose = STATUS_SUCCESS

EndFunction

' // IOCTL processing procedure
PublicFunction DriverDeviceControl(_
ByRef DeviceObject As DEVICE_OBJECT,_
ByRef pIrp As IRP)As NT_STATUS
Dim lpStack AsLong
Dim ioStack As IO_STACK_LOCATION

' // Get pointer to IRP stack
lpStack = IoGetCurrentIrpStackLocation(pIrp)

' // If valid pointer
If lpStack Then

' // Copy to local variable
memcpy ioStack,ByVal lpStack,Len(ioStack)


' // Check IOCTL and AssociatedIrp union that contains SystemBuffer
' // SystemBuffer contains the buffer passed from DeviceIoControl
If ioStack.DeviceIoControl.IoControlCode = IOCTL_READ_MEMORY And _
pIrp.AssociatedIrp <>0Then

Dim lpPointer AsLong
Dim DataSize AsLong

' // Copy parameters from SystemBuffer
memcpy lpPointer,ByVal pIrp.AssociatedIrp,4
memcpy DataSize,ByVal pIrp.AssociatedIrp +4,4

'П// Check buffer size
If DataSize <= ioStack.DeviceIoControl.OutputBufferLength Then

' // Get the number of allowed pages
Dim lpStart AsLong
Dim pgCount AsLong
Dim pgSize AsLong
Dim pgOfst AsLong

' // Get first address of page
lpStart = lpPointer And &HFFFFF000

' // Get offset at beginning of page
pgOfst = lpPointer And &HFFF&

' // Go thru pages and check PageFault error
DoWhile MmIsAddressValid(ByVal lpStart) And (pgSize - pgOfst < DataSize)

lpStart = lpStart +&H1000
pgCount = pgCount +1
pgSize = pgSize +&H1000

Loop

' // If there are allowed pages
If pgCount Then

' // Get size in bytes
pgSize = pgCount *&H1000- pgOfst

' // Fix size
If DataSize > pgSize Then DataSize = pgSize

' // Return total read bytes
pIrp.IoStatus.Information = DataSize

' // Success to DeviceIoControl
pIrp.IoStatus.StatusPointer = STATUS_SUCCESS

' Copy data to system buffer
memcpy ByVal pIrp.AssociatedIrp,ByVal lpPointer, DataSize

' // Return IRP packet to IO manager
IoCompleteRequest pIrp, IO_NO_INCREMENT

' // Success
DriverDeviceControl = STATUS_SUCCESS

' // Exit
ExitFunction

EndIf

EndIf

EndIf

EndIf

' // Return real size of read bytes
pIrp.IoStatus.Information =0

' // Error to DeviceIoControl
pIrp.IoStatus.StatusPointer = STATUS_INVALID_PARAMETER

' // Return IRP packet to IO manager
IoCompleteRequest pIrp, IO_NO_INCREMENT

' // Error
DriverDeviceControl = STATUS_INVALID_PARAMETER

EndFunction

' // Initialize all strings
PrivateFunction Init()As NT_STATUS

' // Initialize device name "\Device\TrickMemReader"
strName.D(0)=&H5C: strName.D(1)=&H44: strName.D(2)=&H65: strName.D(3)=&H76: strName.D(4)=&H69:
strName.D(5)=&H63: strName.D(6)=&H65: strName.D(7)=&H5C: strName.D(8)=&H54: strName.D(9)=&H72:
strName.D(10)=&H69: strName.D(11)=&H63: strName.D(12)=&H6B: strName.D(13)=&H4D: strName.D(14)=&H65:
strName.D(15)=&H6D: strName.D(16)=&H52: strName.D(17)=&H65: strName.D(18)=&H61: strName.D(19)=&H64:
strName.D(20)=&H65: strName.D(21)=&H72

' // Fill UNICODE_STRING structure
RtlInitUnicodeString DeviceName, strName

' // Initialize device link for user mode "\DosDevices\TrickMemReader"
strLink.D(0)=&H5C: strLink.D(1)=&H44: strLink.D(2)=&H6F: strLink.D(3)=&H73: strLink.D(4)=&H44:
strLink.D(5)=&H65: strLink.D(6)=&H76: strLink.D(7)=&H69: strLink.D(8)=&H63: strLink.D(9)=&H65:
strLink.D(10)=&H73: strLink.D(11)=&H5C: strLink.D(12)=&H54: strLink.D(13)=&H72: strLink.D(14)=&H69:
strLink.D(15)=&H63: strLink.D(16)=&H6B: strLink.D(17)=&H4D: strLink.D(18)=&H65: strLink.D(19)=&H6D:
strLink.D(20)=&H52: strLink.D(21)=&H65: strLink.D(22)=&H61: strLink.D(23)=&H64: strLink.D(24)=&H65
strLink.D(25)=&H72

' // Fill UNICODE_STRING structure
RtlInitUnicodeString DeviceLink, strLink

EndFunction

' // Return passed value
PrivateFunction GetAddr(_
ByVal Value AsLong)AsLong
GetAddr = Value
EndFunction


So, the driver must have an entry point DriverEntry, which causes the controller I/O driver is loaded. In the parameters of a pointer to an object-driver and a pointer to a string containing the name of the registry key corresponding to the loadable driver. In the Init procedure, we create two lines, one with the name of the device, the other with reference to the device name. Because we can not use the runtime kernel mode, it is necessary to create a string in the form of a static array, wrapped in a user-defined type, thereby VB6 allocates memory for the array on the stack. If you use a string that will inevitably be caused by one of the functions for runtime and copy assignment line, and we can not allow that. Then we can call IoCreateDevice, which creates a device object. Device object is the recipient of I/O requests and to him we will access when calling CreateFile function from user mode.

The first parameter is a pointer to an object-driver; the second parameter is 0, then since we do not have the structure of the expansion device, and we do not need to allocate memory; the third parameter we pass the name of the device, it is we need to implement access to the device; fourth parameter passed to the device type (see below). in the fifth, we pass 0 as we have "non-standard device"; in the sixth pass False, because We do not need single-user mode; the last parameter - the output. As the name of the device we have to use a string like \Device\DeviceName (where DeviceName - TrickMemReader), is the name we need to ensure that we can create a link to it, which in turn need to access the device from user mode.
Device type - FILE_DEVICE_MEMREADER. All non-standard devices must be of type or FILE_DEVICE_UNKNOWN, or the number of 0x8000 - 0xffff. I created FILE_DEVICE_MEMREADER constant with a value of 0x8000, which corresponds to the first free number. On success, the device is created and filled structure DEVICE_OBJECT. After the need to create a connection between the device name of the kernel mode and user mode. As the name we use \DosDevices\TrickMemReader, from user mode, we will refer to it via the link '\\.\TrickMemReader". The link is created through IoCreateSymbolicLink. Next we define callback-procedure that will be called when certain events occur:

  1. DriverUnload - deinitialize driver;
  2. DriverCreateClose - when opening and closing device;
  3. DriverDeviceControl - when calling DeviceIoControl.

And So. Now we return STATUS_SUCCESS, which corresponds to the successful implementation.* Now consider the procedure DriverUnload. It's simple - we remove the connection and set up the device. In the processing functions of opening and closing device DriverCreateClose, the status of the request, we return a success, and return the IRP packet I/O manager. Exchange of data between an application and device via the IRP-packets. IRP-package consists of 2 parts: a header and a stack of variable length. Part of the structure represented by the type of IRP. So now we add functionality to our driver function DriverDeviceControl. In this function I/O Manager will send IRP-data packet transmitted from the client application, which we will generate a call to DeviceIoControl. The parameters we pass 2 Long numbers: 1st address, where produce reading, 2nd number of bytes to read. Also one of the parameters passed to IRP-bag, when calling DeviceIoControl, a control code input / output (IOCTL), which represents the structure of the device type, function number, the type of data and the type of access. You can define several such codes for different operations and use them. I defined the code so IOCTL_READ_MEMORY = 0x800020008000 - corresponds to the type of our device (FILE_DEVICE_MEMREADER); function number = 0x800, values below are reserved for user-defined functions allowed values 0x800 - 0xFFF; the type of data transmission - 0x0 (METHOD_BUFFERED), it means that we will receive / transmit data through the buffer that is specified SystemBuffer IRP-package); access type - FILE_ANY_ACCESS. visually:



So, as a function of DriverDeviceControl we get a pointer to the I/O stack IRP-query using the IoGetCurrentIrpStackLocation, which returns the parameter of lpCurStackLocation. When Successes (if non-zero pointer) is copied to the local structure IO_STACK_LOCATION parameters are referenced by the pointer. Now we check the IOCTL-code field AssociatedIrp, which is a union (in VB6 no associations) which stores a pointer to SystemBuffer. Because we have the type of data corresponds METHOD_BUFFERED, in parameter SystemBuffer contains a pointer to the buffer with the parameters (address and size) DeviceIoControl, in this buffer, we can also recover data that is written to the output buffer DeviceIoControl. Now, if we have data contains the correct values (IOCTL and SystemBuffer), then we copy into local variables (lpPointerDataSize). Next, check the size of the buffer. Size of the system I/O buffer is contained in the parameter DeviceIoControl.OutputBufferLength. If the requested number of bytes is not larger than the size of the system buffer, then everything is fine. Now we need to calculate the number of memory pages occupied by the data that we want to copy. To do this, we define the virtual address of the beginning of the page corresponding to pass a pointer, and because page size is a multiple of 4 KB (0x1000) we simply vanish 12-bit pointer. Next, we check in the cycle will not be whether an exception is thrown Page fault using the MmIsAddressValid. If the page is not in memory, the function returns False. Thus we check the number of pages that you want us to take a piece of memory and the number of pages that we can read. Then we calculate the actual size of the data that we will be able to read and, if necessary, adjust the size. Next to the title of IRP-package we copy the data size that we can read and a successful status. IoStatus.Information field matches the value returned by DeviceIoControl parameter lpBytesReturned. Next copy in SystemBuffer right amount of bytes using RtlMoveMemory and return IRP-package I/O manager. Return the status of a successful operation. In all other cases, return error STATUS_INVALID_PARAMETER and zero data size. All the driver code is ready.

Proceed to the compilation. Because we can not use the runtime, all the API-functions, we declare a TLB, so that they fall into the import:

[uuid(0000001F-0000-0000-0000-000000000AAB)]
library ImportFunctionsForTrickMemReaderDriver
{
[dllname("Ntoskrnl.exe")]
module Ntoskrnl
{
[entry("IoCreateDevice")]int IoCreateDevice
(void *DriverObject,
int DeviceExtensionSize,
void *DeviceName,
int DeviceType,
int DeviceCharacteristics,
int Exclusive,
void *DeviceObject);

[entry("IoCreateSymbolicLink")]int IoCreateSymbolicLink
(void *SymbolicLinkName,
void *DeviceName);

[entry("IoDeleteDevice")]void IoDeleteDevice
(void *DeviceObject);

[entry("IoDeleteSymbolicLink")]int IoDeleteSymbolicLink
(void *SymbolicLinkName);

[entry("IoCompleteRequest")]void IoCompleteRequest
(void *pIrp,
unsigned char PriorityBoost);

[entry("RtlInitUnicodeString")]int RtlInitUnicodeString
(void *UnicodeString,
void *StringPtr);

[entry("RtlMoveMemory")]void memcpy
(void *Destination,
void *Source,
int Length);

[entry("MmIsAddressValid")]int MmIsAddressValid
(void *VirtualAddress);

[entry("InterlockedExchange")]int InterlockedExchange
(void *Target,
void *Value);
}

}


PS. InterlockedExchange - I left because first driver had a bit of a different structure, subsequently left the ad in the TLB. In the driver, it does not fall into imports.

To the driver worked to do three things:
  1. In the field Subsystem, structure IMAGE_OPTIONAL_HEADER PE-driver file should be the value that corresponds to IMAGE_SUBSYSTEM_NATIVE kernel-mode driver.
  2. Specify as the entry point of our procedure DriverEntry
  3. Add a relocation section, in order that the driver can be loaded at any address.
  4. Exclude MSVBVM60 of imports.



For the first 3 points are added to the compilation keys vbp-file with the following contents:
Code:
[VBCompiler]
LinkSwitches=/ENTRY:DriverEntry /SUBSYSTEM:NATIVE /FIXED:NO

Compile the project with all the default optimization. To exclude the runtime of the import, I use a utility Patch, I used here. I'm a little modify it, as initially could not start the driver and long puzzled because of what it does, and the reason was the checksum. After exclusion of the import library checksum has changed, and I do not update it. And EXE, DLL, etc. this field is not checked, and the driver checks. To check the watch imports in any viewer PE:


As you can see there is no runtime. What we required.

To test driver I wrote a simple program that loads the driver and works with him.
Code:
' // frmTestTrickVBDriver.frm  - test form for driver
' // © Krivous Anatoly Anatolevich (The trick), 2014

Option Explicit

Private Type SERVICE_STATUS
dwServiceType AsLong
dwCurrentState AsLong
dwControlsAccepted AsLong
dwWin32ExitCode AsLong
dwServiceSpecificExitCode AsLong
dwCheckPoint AsLong
dwWaitHint AsLong
End Type

Private Declare Function ControlService Lib "advapi32.dll"(_
ByVal hService AsLong,_
ByVal dwControl AsLong,_
ByRef lpServiceStatus As SERVICE_STATUS)AsLong
Private Declare Function OpenSCManager Lib "advapi32.dll"_
Alias "OpenSCManagerW"(_
ByVal lpMachineName AsLong,_
ByVal lpDatabaseName AsLong,_
ByVal dwDesiredAccess AsLong)AsLong
Private Declare Function CloseServiceHandle Lib "advapi32.dll"(_
ByVal hSCObject AsLong)AsLong
Private Declare Function OpenService Lib "advapi32.dll"_
Alias "OpenServiceW"(_
ByVal hSCManager AsLong,_
ByVal lpServiceName AsLong,_
ByVal dwDesiredAccess AsLong)AsLong
Private Declare Function CreateService Lib "advapi32.dll"_
Alias "CreateServiceW"(_
ByVal hSCManager AsLong,_
ByVal lpServiceName AsLong,_
ByVal lpDisplayName AsLong,_
ByVal dwDesiredAccess AsLong,_
ByVal dwServiceType AsLong,_
ByVal dwStartType AsLong,_
ByVal dwErrorControl AsLong,_
ByVal lpBinaryPathName AsLong,_
ByVal lpLoadOrderGroup AsString,_
ByRef lpdwTagId AsLong,_
ByVal lpDependencies AsLong,_
ByVal lp AsLong,_
ByVal lpPassword AsLong)AsLong
Private Declare Function StartService Lib "advapi32.dll"_
Alias "StartServiceW"(_
ByVal hService AsLong,_
ByVal dwNumServiceArgs AsLong,_
ByVal lpServiceArgVectors AsLong)AsLong
Private Declare Function DeleteService Lib "advapi32.dll"(_
ByVal hService AsLong)AsLong
Private Declare Function CreateFile Lib "kernel32"_
Alias "CreateFileW"(_
ByVal lpFileName AsLong,_
ByVal dwDesiredAccess AsLong,_
ByVal dwShareMode AsLong,_
ByRef lpSecurityAttributes As Any,_
ByVal dwCreationDisposition AsLong,_
ByVal dwFlagsAndAttributes AsLong,_
ByVal hTemplateFile AsLong)AsLong
Private Declare Function CloseHandle Lib "kernel32"(_
ByVal hObject AsLong)AsLong
Private Declare Function DeviceIoControl Lib "kernel32"(_
ByVal hDevice AsLong,_
ByVal dwIoControlCode AsLong,_
ByRef lpInBuffer As Any,_
ByVal nInBufferSize AsLong,_
ByRef lpOutBuffer As Any,_
ByVal nOutBufferSize AsLong,_
ByRef lpBytesReturned AsLong,_
ByRef lpOverlapped As Any)AsLong

PrivateConst ERROR_SERVICE_ALREADY_RUNNING AsLong=1056&
PrivateConst ERROR_SERVICE_EXISTS AsLong=1073&
PrivateConst SERVICE_CONTROL_STOP AsLong=&H1
PrivateConst SC_MANAGER_ALL_ACCESS AsLong=&HF003F
PrivateConst SERVICE_ALL_ACCESS AsLong=&HF01FF
PrivateConst SERVICE_KERNEL_DRIVER AsLong=&H1
PrivateConst SERVICE_DEMAND_START AsLong=&H3
PrivateConst SERVICE_ERROR_NORMAL AsLong=&H1
PrivateConst GENERIC_READ AsLong=&H80000000
PrivateConst GENERIC_WRITE AsLong=&H40000000
PrivateConst FILE_SHARE_READ AsLong=&H1
PrivateConst FILE_SHARE_WRITE AsLong=&H2
PrivateConst OPEN_EXISTING AsLong=3
PrivateConst FILE_ATTRIBUTE_NORMAL AsLong=&H80
PrivateConst INVALID_HANDLE_VALUE AsLong=-1
PrivateConst IOCTL_READ_MEMORY AsLong=&H80002000

PrivateConst DriverName AsString="TrickMemReader"
PrivateConst NumOfRows AsLong=32

Private DriverFile AsString
Private hMgr AsLong
Private hSrv AsLong
Private hDev AsLong
Private buffer()AsByte
Private bufLen AsLong
Private Address AsLong

' // Read memory from kernel space
PrivateSub cmdRead_Click()
Dim param(1)AsLong

OnErrorGoTo Cancel

Address =CLng("&H"&Trim(txtAddress.Text))

' // Make parameters
param(0)= Address
param(1)=16* NumOfRows

' // Send request
If DeviceIoControl(hDev, IOCTL_READ_MEMORY, param(0),8, buffer(0),UBound(buffer)+1, bufLen,ByVal0&)=0Then
bufLen =0
EndIf

Update

Cancel:

EndSub

PrivateSub Form_Load()
Dim sw AsLong
Dim sh AsLong

' // Allocate buffer
ReDim buffer(16* NumOfRows -1)

' // Get driver file name
DriverFile = App.Path &"\"& DriverName &".sys"

' // Open SC manager database
hMgr = OpenSCManager(0,0, SC_MANAGER_ALL_ACCESS)

If hMgr =0Then
MsgBox"Unable to establish connection with SC manager"
End
EndIf

' // Create servise
hSrv = CreateService(hMgr, StrPtr(DriverName), StrPtr(DriverName), SERVICE_ALL_ACCESS,_
SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, StrPtr(DriverFile),_
0,0,0,0,0)

' // If service already has beend launched
If hSrv =0 And Err.LastDllError = ERROR_SERVICE_EXISTS Then
' // Open existing service
hSrv = OpenService(hMgr, StrPtr(DriverName), SERVICE_ALL_ACCESS)
EndIf

If hSrv =0Then
MsgBox"Unable to create service"
Unload Me
End
EndIf

' // Launch driver
If StartService(hSrv,0,0)=0Then

IfErr.LastDllError <> ERROR_SERVICE_ALREADY_RUNNING Then
MsgBox"Unable to start service"
Unload Me
End
EndIf

EndIf

' // Connect to driver
hDev = CreateFile(StrPtr("\\.\"& DriverName), GENERIC_READ Or FILE_SHARE_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE,ByVal0&,_
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0)

If hDev = INVALID_HANDLE_VALUE Then
MsgBox"Unable to connect to driver"
Unload Me
End
EndIf

' // Determine control size and position
sw = picDump.TextWidth("_")
sh = picDump.TextHeight("_")

picDump.Move 5,5,(sw *77)+(picDump.Width - picDump.ScaleWidth),(sh * NumOfRows)+(picDump.Height - picDump.ScaleHeight)

lblAddress.Top = picDump.Top + picDump.Height +5
txtAddress.Top = lblAddress.Top
cmdRead.Top = txtAddress.Top

Me.Width =(picDump.Width +10-Me.ScaleWidth)* Screen.TwipsPerPixelX +Me.Width
Me.Height =(txtAddress.Top +5+ txtAddress.Height -Me.ScaleHeight)* Screen.TwipsPerPixelY +Me.Height

Update

EndSub

' // Refresh data on window
PrivateSub Update()
Dim col AsLong
Dim row AsLong
Dim ptr AsLong
Dim hxd AsString
Dim asi AsString
Dim adr AsString
Dim out AsString

For row =0To NumOfRows -1

adr =Hex(Address + row *16)
adr =String(8-Len(adr),"0")& adr
asi =""
hxd =""

For col =0To15

If ptr < bufLen Then

hxd = hxd &""&IIf(buffer(ptr)<&H10,"0"&Hex(buffer(ptr)),Hex(buffer(ptr)))
asi = asi &IIf(buffer(ptr)>=32,Chr$(buffer(ptr)),"?")

Else

hxd = hxd &" ??"
asi = asi &"?"

EndIf

ptr = ptr +1

Next

If row Then out = out & vbNewLine

out = out & adr &":"& hxd &" | "& asi

Next

picDump.Cls
picDump.Print out

EndSub

PrivateSub Form_Unload(_
ByRef Cancel AsInteger)
Dim Status As SERVICE_STATUS

' // Disconnect driver
CloseHandle hDev

' // Stop driver
ControlService hSrv, SERVICE_CONTROL_STOP, Status

' // Remove service
DeleteService hSrv

' // Close handles
CloseServiceHandle hSrv
CloseServiceHandle hMgr

EndSub

The driver must be in the same folder as the program. Code is commented, so I will not describe his work.

To debug a driver you want to use the kernel-mode debugger. Debug going on a virtual system (VMware) - Windows XP. As a debugger take Syser, choose our driver and click Load. The system stops and we go to the debugger:


We are in the beginning of the function DriverEntry. The first "CALL" corresponds to a function call Init. If we follow step by step (F8) what's inside, we see how to complete the structure and called RtlInitUnicodeString for the device name and a symbolic link. The second "CALL" corresponds to the function "NT_SUCCESS", look it returns TRUE (in the register EAX) and code jumps after checking (TEST EAX, EAX) zero (False) on:


As can be seen code pushes the stack parameters for the IoCreateDevice from last to first using the instructions "PUSH". We start checking parameters. Check the name of the device (the third parameter - PUSH 0f8a2c010), for example, type "d 0f8a2c010" (which means to view a memory dump at f8a2c010, addresses are valid only for the current debugging) and see the contents:


the first 8 bytes - this is our variable DeviceName. The first two words - respectively the length of the line and the maximum length of the string in bytes. Next double word - a pointer to a string, look (d f8a2c0d8 consider the byte order little-endian):


there Unicode string with the name of the device. If you look at parameter Device (last output parameter - PUSH 0f8a2c020), we can see that it is different from the name on the 0x10 byte. Now look at the declaration of variables, the variable "Device" is declared after the DeviceName and DeviceLink, a total length of 8 + 8 = 0x10 bytes. Ie the order of the variables in the memory corresponds to the order in the ad code. Check the first non-const parameter ESI, in the beginning it is copied to the value at ESP + 0xC. Register ESP - points to the top of the stack. If you walk to the top function DriverEntry, you can see the preservation of the stack of two registers ESI and EDI (by agreement StdCall these registers are saved in the list, ie, the procedure should not change them after the call). DriverObject transmitted in the first variable, i.e. closest to the top of the stack, and after all the settings saved return address - ie DriverObject parameter before executing the first instruction in the DriverEntry function is located at ESP + 4 (the stack grows downward addresses), after two instructions "PUSH" he accordingly shifted by 8 bytes, as a result DriverObject located at ESP + 0C, all right . Correct settings, you can call the function. Hit F10 to not go inside and look IoCreateDevice value of the EAX register after the call, there must be a non-negative integer that indicates that the function worked without error. I have it returned 0 (STATUS_SUCCESS), everything is fine. Next comes the familiar procedure at 0xF8A2B750 - NT_SUCCESS:



If successful, go jump on 0xf8a2b7bf, where there is the pushing of the stack parameters for the function IoCreateSymbolicLink. Parameter DeviceName we have checked, check DeviceLink:


What you need. Hit F10, test EAX, if successful go further if it fails, remove the device and exit with an error. Procedure at 0xf8a2bbb0 - it GetAddr, which simply returns its this value:



Next there is copying of addresses at offsets DriverObject, if you look at the declaration you can see that at offset 0x34 is written address DriverUnload, at offset 0x38 - MajorFunction (0), etc. Recorded values correspond to the address of the function in our driver. Then there is zero EAX (the returned value) and exit from the procedure DriverEntry. Everything works without error, go ahead. So, to track the performance of the driver we will put a breakpoint on the function DriverDeviceControl. Address it is possible to take on the newly written offsets in the structure of DRIVER_OBJECT or find easy viewing and by analyzing the code. In my test, the address is 0xf8a2b870, go to him (. 0xf8a2b870) and press F9, set breakpoints. On the contrary instructions to set a marker:



Now, when this function is called the debugger will stop code execution and enables us to step through the code. Function "DriverCreateClose" and "DriverUnload" I will not describe, because everything is simple. Hit F5, thereby continuing to perform normally. We were immediately transferred back to Windows. Now we run our test application, enter any address (eg 81234567) and click on the button Read. Our challenge intercepts debugger and we can continue to test the function code DriverDeviceControl. Details inside I will not describe the code will focus on the copy:

Immediately look at the stack (register ESP), we see that the correct parameters are passed. In any case, do a dump, then compare:


Press F5 - and return to Windows. We look at the dump is already in our program:


As you can see everything is fine copy. Let's try to copy the data to a page boundary, so that one page was missing. Experimental method was found such a page that's what we get:




As we can see that the data is copied correctly, which did not work there, we displayed a question mark. The output parameter DeviceIoControl we actually returns the number of bytes read, we use it to display a question mark. _________________________________________________________________
As you can see on VB6, you can write a simple driver, and if you use inline assembly can be more serious and write something. Thank you for your attention. Good Luck!



Sources:

http://www.vbforums.com/showthread.php?788179-VB6-Kernel-mode-driver
https://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=75401&lngWId=1










Multithreading in VB6

$
0
0

Module for working with multithreading in VB6

I present the module for working with multithreading in VB6 for Standard EXE projects. This module is based on this solution with some bug fixing and the new functionality is added. The module doesn't require any additional dependencies and type libraries, works as in the IDE (all the functions work in the main thread) as in the compiled form.
To start working with the module, you need to call the Initialize function, which initializes the necessary data (it initializes the critical sections for exclusive access to the heaps of marshalinig and threads, modifies VBHeader (here is description), allocates a TLS slot for passing the parameters to the thread).
The main function of thread creation is vbCreateThread, which is an analog of the CreateThread function.
' // Create a new thread
PublicFunctionvbCreateThread(ByVallpThreadAttributesAsLong,_
ByValdwStackSizeAsLong,_
ByVallpStartAddressAsLong,_
ByVallpParameterAsLong,_
ByValdwCreationFlagsAsLong,_
ByReflpThreadIdAsLong,_
OptionalByValbIDEInSameThreadAsBoolean=True)AsLong
The function creates a thread and calls the function passed in the lpStartAddress parameter with the lpParameter parameter. In the IDE, the call is reduced to a simple call by the pointer implemented through DispCallFunc. In the compiled form, this function works differently. Because a thread requires initialization of project-specific data and initialization of the runtime, the parameters passed to lpStartAddress and lpParameter are temporarily stored into the heap by the PrepareData function, and the thread is created in the ThreadProc function, which immediately deals with the initialization and calling of the user-defined function with the user parameter. This function creates a copy of the VBHeader structure via CreateVBHeaderCopy and changes the public variable placement data in the:
VbPublicObjectDescriptor.lpPublicBytesVbPublicObjectDescriptor.lpStaticBytes structures (BTW it wasn't implemented in the previous version) so that global variables are not affected during initialization. Further, VBDllGetClassObject calls the FakeMain function (whose address is written to the modified VBHeader structure). To transfer user parameters, it uses a TLS slot (since Main function doesn't accept parameters, details here). In FakeMain, parameters are directly extracted from TLS and a user procedure is called. The return value of the function is also passed back through TLS. There is one interesting point related to the copy of the header that wasn't included in the previous version. Because the runtime uses the header after the thread ends (with DLL_THREAD_DETACH), we can't release the header in the ThreadProc procedure, therefore there will be a memory leak. To prevent the memory leaks, the heap of fixed size is used, the headers aren't cleared until there is a free memory in this heap. As soon as the memory ends (and it's allocated in the CreateVBHeaderCopy function), resources are cleared. The first DWORD of header actually stores the ID of the thread which it was created in and the FreeUnusedHeaders function checks all the headers in the heap. If a thread is completed, the memory is freed (although the ID can be repeated, but this doesn't play a special role, since in any case there will be a free memory in the heap and if the header isn't freed in one case, it will be released later). Due to the fact that the cleanup process can be run immediately from several threads, access to the cleanup is shared by the critical section tLockHeap.tWinApiSection and if some thread is already cleaning up the memory the function will return True which means that the calling thread should little bit waits and the memory will be available.
The another feature of the module is the ability to initialize the runtime and the project and call the callback function. This can be useful for callback functions that can be called in the context of an arbitrary thread (for example, InternetStatusCallback). To do this, use the InitCurrentThreadAndCallFunction and InitCurrentThreadAndCallFunctionIDEProc functions. The first one is used in the compiled application and takes the address of the callback function that will be called after the runtime initialization, as well as the parameter to be passed to this function. The address of the first parameter is passed to the callback procedure to refer to it in the user procedure:
' // This function is used in compiled form
PublicFunctionCallbackProc(_
ByVallThreadIdAsLong,_
ByValsKeyAsString,_
ByValfTimeFromLastTickAsSingle)AsLong
' // Init runtime and call CallBackProc_user with VarPtr(lThreadId) parameter
InitCurrentThreadAndCallFunctionAddressOfCallBackProc_user,VarPtr(lThreadId),CallbackProc
EndFunction

' // Callback function is called by runtime/window proc (in IDE)
PublicFunctionCallBackProc_user(_
ByReftParamAstCallbackParams)AsLong

EndFunction
CallBackProc_user will be called with the initialized runtime.
This function doesn't work in the IDE because in the IDE everything works in the main thread. For debugging in the IDE the function InitCurrentThreadAndCallFunctionIDEProc is used which returns the address of the assembler thunk that translates the call to the main thread and calls the user function in the context of the main thread. This function takes the address of the user's callback function and the size of the parameters in bytes. It always passes the address of the first parameter as a parameter of a user-defined function. I'll tell you a little more about the work of this approach in the IDE. To translate a call from the calling thread to the main thread it uses a message-only window. This window is created by calling the InitializeMessageWindow function. The first call creates a WindowProc procedure with the following code:
CMP DWORD [ESP+8], WM_ONCALLBACK
JE SHORT L
JMP DefWindowProcW
L: PUSH DWORD PTR SS:[ESP+10]
CALL DWORD PTR SS:[ESP+10]
RETN 10
As you can see from the code, this procedure "listens" to the WM_ONCALLBACK message which contains the parameter wParam - the function address, and in the lParam parameters. Upon receiving this message it calls this procedure with this parameter, the remaining messages are ignored. This message is sent just by the assembler thunk from the caller thread. Futher, a window is created and the handle of this window and the code heap are stored into the data of the window class. This is used to avoid a memory leak in the IDE because if the window class is registered once, then these parameters can be obtained in any debugging session. The callback function is generated in InitCurrentThreadAndCallFunctionIDEProc, but first it's checked whether the same callback procedure has already been created (in order to don't create the same thunk). The thunk has the following code:
LEAEAX,[ESP+4]
PUSHEAX
PUSH pfnCallback
PUSH WM_ONCALLBACK
PUSH hMsgWindow
Call SendMessageW
RETN lParametersSize
As you can see from the code, during calling a callback function, the call is transmitted via SendMessage to the main thread. The lParametersSize parameter is used to correctly restore the stack.
The next feature of the module is the creation of objects in a separate thread, and you can create them as private objects (the method is based on the code of the NameBasedObjectFactory by firehacker module) as public ones. 

To create the project classes use the CreatePrivateObjectByNameInNewThread function and for ActiveX-public classes: 
CreateActiveXObjectInNewThread and CreateActiveXObjectInNewThread2 ones. 

Before creating instances of the project classes you must first enable marshaling of these objects by calling the EnablePrivateMarshaling function. These functions accept the class identifier (ProgID / CLSID for ActiveX and the name for the project classes) and the interface identifier (IDispatch / Object is used by default). If the function is successfully called a marshaled object and an asynchronous call ID are returned. For the compiled version this is the ID of thread for IDE it's a pointer to the object. Objects are created and "live" in the ActiveXThreadProc function. The life of objects is controlled through the reference count (when it is equal to 1 it means only ActiveXThreadProc refers to the object and you can delete it and terminate the thread). You can call the methods either synchronously - just call the method as usual or asynchronously - using the AsynchDispMethodCall procedure. This procedure takes an asynchronous call ID, a method name, a call type, an object that receives the call notification, a notification method name and the list of parameters. The procedure copies the parameters to the temporary memory, marshals the notification object, and sends the data to the object's thread via WM_ASYNCH_CALL. It should be noted that marshaling of parameters isn't supported right now therefore it's necessary to transfer links to objects with care. If you want to marshal an object reference you should use a synchronous method to marshal the objects and then call the asynchronous method. The procedure is returned immediately. In the ActiveXThreadProc thread the data is retrieved and a synchronous call is made via MakeAsynchCall. Everything is simple, CallByName is called for the thread object and CallByName for notification. The notification method has the following prototype:
PublicSubCallBack(ByValvRetAsVariant)
, where vRet accepts the return value of the method.
The following functions are intended for marshaling: 

MarshalMarshal2UnMarshalFreeMarshalData. The first one creates information about the marshaling (Proxy) of the interface and puts it into the stream (IStream) that is returned. It accepts the interface identifier in the pInterface parameter (IDispatch / Object by default). The UnMarshal function, on the contrary, receives a stream and creates a Proxy object based on the information in the stream. Optionally, you can release the thread object. Marshal2 does the same thing as Marshal except that it allows you to create a Proxy object many times in different threads. FreeMarshalData releases the data and the stream accordingly. If, for example, you want to transfer a reference to an object between two threads, it is enough to call the Marshal / UnMarshal pair in the thread which created the object and in the thread that receives the link respectively. In another case, if for example there is the one global object and you need to pass a reference to it to the multiple threads (for example, the logging object), then Marshal2 is called in the object thread, and UnMarshal with the bReleaseStream parameter is set to False is called in client threads. When the data is no longer needed, FreeMarshalData is called.
The WaitForObjectThreadCompletion function is designed to wait for the completion of the object thread and receives the ID of the asynchronous call. It is desirable to call this function always at the end of the main process because an object thread can somehow interact with the main thread and its objects (for example, if the object thread has a marshal link to the interface of the main thread).
The SuspendResume function is designed to suspend/resume the object's thread; bSuspend determines whether to sleep or resume the thread.
In addition, there are also several examples in the attacment of working with module:
  • Callback - the project demonstrates the work with the callback-function periodically called in the different threads. Also, there is an additional project of native dll (on VB6) which calls the function periodically in the different threads;
  • JuliaSet - the Julia fractal generation in the several threads (user-defined);
  • CopyProgress - Copy the folder in a separate thread with the progress of the copy;
  • PublicMarshaling - Creating public objects (Dictionary) in the different threads and calling their methods (synchronously / asynchronously);
  • PrivateMarshaling - Creating private objects in different threads and calling their methods (synchronously / asynchronously);
  • MarshalUserInterface - Creating private objects in different threads and calling their methods (synchronously / asynchronously) based on user interfaces (contains tlb and Reg-Free manifest).
  • InitProjectContextDll - Initialization of the runtime in an ActiveX DLL and call the exported function from the different threads. Setup callbacks to the executable.
  • InternetStatusCallback - IternetStatusCallback usage in VB6. Async file downloading.
The module is poorly tested so bugs are possible. I would be very glad to any bug-reports, wherever possible I will correct them. Thank you all for attention!
Best Regards,
The trick.


Watch video

IMPORTANT NOTE: The following screenshot appears if you chose to download this project from GitHub, using Google chrome or Edge. These browsers will say "Virus detected". There is no virus in there, is just a compiled version of this very project uploaded by his creator, namely TheTrick. Today, security "specialists" go through new heights of incompetence at the world level. All "antivirus" companies get their signatures from one place, namely VirusTotal.com. As flock of sheeps they trust what "users" say about a file (remember that in general).Antivirus companies don't have competent specialists anymore: 




Source:
https://github.com/thetrik/VbTrickThreading
http://www.vbforums.com/showthread.php?863487-VB6-Module-for-working-with-multithreading

Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

$
0
0

Module mShellW – Creating New Processes
Download from VBForums
Download from ME



Overview This module greatly extends what you can do with the Shell function in VBA or Visual Basic 5 or 6 (VB6):
  • Everything is Unicode, including the Shelled-to environment.
  • The code works “as-is” in 32 or 64-bit MS Office or in Visual Basic 5 or 6.
  • This module can be run on Windows XP and later.
  • You can start a program by specifying a document with an extension. For example, if you specify D:\MyFiles\abc.docx and MS Word is registered to open .docx files then Word will start with this file.
  • There is a very easy function to use to shell to CMD.EXE that takes care of all of the intricacies in setting up the commandline to CMD.
  • You can specify how long, if at all, the code waits for the shelled-to program to complete before returning. Specify anything from 0 to effectively forever. You can also specify a time for a pop-up window to the user if he wants the program to quit waiting any further. This is a nice escape from a program that you specify “wait for forever to return” and it gets hung up and never quits.
  • You can execute programs from anywhere on your PC. For example, if you are running 32-bit VBA or VB6 but are running on a 64-bit version of Windows, you stil can run any 64-bit program. You can even tell it that you want to run the 64-bit version of calc.exe and many other programs that reside in the Windows\System32 folder. Normally a 32-bit program gets redirected away from this folder “behind the scenes” but we work around that problem.
  • You can run programs that require elevated permissions (the UAC prompt). Normally this is not possible because the Windows CreateProcess function won’t do this but you have the option to 1) keep the privilege level the same as it currenty is, 2) run the new process elevated, 2) run the new process elevated only if it is required (avoids the UAC prompt unless it is necessary) or 3) do not run elevated (fails if elevation is required).
  • If your program is running elevated, you can specify that the shelled-to routine is non-elevated. Usually a shelled-to routine will be elevated if the host program is elevated.
  • You can run batch files (.bat and .cmd), windows scripts and PowerShell all without knowing anything other than the extension of the file to “run.”
  • You can execute a program if you don’t know where it is. For example, you installed Notepad++.exe in “C:\Program Files (x86)\” but as the programmer you don’t know if your users installed it there or if they even installed it at all. And to make matters worse, if they did install it, did they install the 32 or 64-bit version? This code can find the executable file.
  • Normally, shelling is done via the CreateProcess Windows API call, the significance of which is that the new process is independent from the calling program which makes it multi-threaded. You can have the calling program wait until the shell-to routine is complete or you can return control to the calling program and have it continue while the shelled-to program runs in the background. The other common method of shelling to an external program is to use ShellExecuteEx but that routine has limitations which we avoid with CreateProcess. ShellExecuteEx’s main advantage is being able to specify a document instead of an executable and we provide that functionality without using ShellExecuteEx. However, we do include ShellExecuteEx so you can specify it if you really want. Also, ShellExecuteEx is the routine we use to raise permissions when desired or required since CreateProcess lacks that capability.
  • You can use the Windows’ CreateProcess function or ShellExecuteEx functions for your external call. There are pros and cons for each approach; either will work in most instances. The differences are discussed later in this document. CreateProcess is not limited to 255 characters in the commandline. ShellExecuteEx can run programs with elevated privileges.
  • Most programs start a new instance of themselves when we open with CreateProcess even if an existing instance is running. However, this is not true by default for MS Excel, MS Word and Notepad++. If an instance is already running when you specify opening another one, the new document(s) will eb opened in the instance that is already running. This may be okay but know that the new documents share the same memory (if you are running 32-bit versions of these programs this could be limit) and if for some reason something causes the instance to crash then everything in it including the new documents will crash as well. If they are in a different instance they have their own memory space and do not normally crash when another instance crashes. When you use DocShellW in this module you can specify that the shell is to a new instance and if the program is MS Excel, MS Word or Notepad++ then we apply some special code to force them to start a new instance. You can keep the other behavior of putting them al into one instance by specifying NewInstance as False in the call.
  • This module requires the use of my core library mUCCore. It provides my error handling ystem, Unicode support, the basis for support for all flavors of VBA including 64-bit as well as VB6 (compiled or in the IDE) and it contains many routines I use all of the time. It is accompanied by a separate user guide. You don’t need to know what it does in order to use this shelling module but you may find many parts of it useful for your programs.
  • When I test my shell functions I can hard-code various files I want to open or programs to run. I can’t do that with the sample program I have provided because your files are different than mine and are in different locations than mine. So I have included another of my library modules, this one named FileStuffLite. It includes many things not needed for mShellW but I am using a small part of it just for the demo/test. It is not needed for mShellW to function.
  • The code is LARGEADDRESSAWARE. For VB6 runing in 32-bit Windows you can access up to 3 GB of memory and if run in 64-bit Windows (yes, VB6 itself is 32-bit) you can access up to 4 GB. If you are running Excel 2013 or 2016 you can acces up to 4 GB of memory. This shell by itself does not set that up but it is coded such that you won’t get memory crashes from this module when data or code is above the 2 GB mark.

Main Routines ShellW – The main routine for running another program. DocShellW – Open a file with whatever program is registered in Windows for opening that type of file. For example, you could specify “Test.doc” and the file would be opened with Microsoft Word on most PC’s but if .doc files are set to be opened with some other program then that one is used. CMDRun – Run a batch file, a PowerShell file, a program, a document file or just a command prompt via CMD (very similar to the old DOS prompt). You control whether it goes away after executing whatever you want it to do. Support Routines QuoteIfNec – If the specified string (normally a path to a program or a document) has a space character in it Windows generally wants it enclosed in quotes. This routine checks if the string has a space character and if so it encloses the whole string in quotes. ShellHandlesClose – Closes process and thread handles and resets internal variables for them to 0. HasProcessClosed – Determines if a specified process has ended. IsExeRunning – Returns True if the specified EXE file is running. FindEXEPath – Returns the full path of the specified .EXE file. Can be called within ShellW. FindEXEFromDoc – Returns the program in the current PC’s registry that is set to open the specified document file. Can be called from within DocShellW. NOTE - This same package with VBA samples is being uploaded to the Office Development forum here.

Source:
http://www.vbforums.com/showthread.php?799041-VB6-Vista-Code-snippet-KnownFolders-made-easy-with-IKnownFolderManager&goto=nextnewest

Wave steganography - from military to intelligence service applications (by Krivous Anatoly Anatolevich)

$
0
0
Download from ME

Today i want to talk about the cryptography. I've made the example of using the special cryptography - the steganography. This method hides the fact of encryption of the data. There are lot of kinds of the steganography. Today i'll talk about LSB-method when data is hided into the least significant bits of the audio file. It looks as though you are exchanged a audio files, but really you send a secret data. People who don't know about this method they will not even suspect about secret data. In some cases it can be very useful.
How does it work?
A WAVE-PCM file (without a compressions) contains sound data. Really the sound is an analog event, i.e. continuous. In order to convert it to digital form you should quantize it with lossy. This process is characterized by two parameters: bitness and sample per second. "Bitness" affects to how many levels can it contains in each sample. "Sample per second" affects to how many frequencies do you can hear:




In this case we are interested only the bitness of an audio. It can be 32, 24, 16, .... bits per each sample. Main idea of steganography (in this case) is rewrite the least significant bits to our data. The more you overwrite bits the greater the distortion.
This picture explains it graphically:




As you can see, it stores all hidden data to certain bits in the audio data (in this picture 4 bits to each sample). Also note that for storing the data you need to use the bigger file size than the source file. For instance, if you use 3 bit for the decoding the result file will have the size that is 16/3 times greater than source. I've said 16 because i use the 16 bps wave file in my example.
In the attached example i also save the original file name. In general, format of the data is described in the picture:




When the packing occurs it gets each byte from the source file. Then the subroutine extracts the necessary bits from the source file and clears corresponding bits in the audio data. Further the subroutine sets bits using bitwise-OR operator. For extracting the necessary bits it uses the masks and the shifts. The mask leaves necessary bits and the shift places them to the beginning of the byte.

Unpacking works vice versa. It extracts bits from audio data and builds file using corresponding bits.

Hope the review will be useful.
Thanks for attention.
Regards,
Кривоус Анатолий.





EXE without runtime, structure of executable files, VB6 applications without external dependencies ! PART 1

$
0
0

PART 1 (by Krivous Anatoly Anatolevich)

The description of PE format. Creation of the simple VB6-EXE loader/packer. The VB6-loader without the runtime (msvbvm60) dependencies. And so on...

Hello everyone! Today i want to show you the quite interesting things. One day i was investigating the PE (portable executable) file format especially EXE. I decided to create a simple loader of the executable files specially for VB6-compiled applications. This loader should load an VB6-compiled exe from the memory without file. THIS IS JUST FOR THE EXPERIMENTAL PURPOSES IN ORDER TO CHECK POSSIBILITIES OF VB6. Due to that the VB6-compiled applications don't used most of the PE features it was quite simple objective. Most of programmer's says that a VB6-application is linked with the VB6 runtime (MSVBVM), a VB6 application doesn't work without the runtime and the runtime is quite slow. Today i'll prove that it is possible to write an application that absolutely doesn't use runtime (although i was already doing that in the driver). These projects i had written quite a long time ago, but these were in the russian language. I think it could be quite interesting for someone who wants to examine the basic principles of work with the PE files.

Before we begin i want to say couple words about the projects. These projects were not tested well enough therefore it can cause problems. The loader doesn't support most of the features of PE files therefore some executables may not work. So...

This overview consists three projects:

Compiler - it is the biggest project of all. It creates an installation based on the loader, user files, commands and manifest;

Loader - it is the simple loader that performs commands, unpacks files and runs an executable from memory;

Patcher - it is the small utility that removes the runtime import from an executable file.
I call an exe that contains the commands, files and executable file the installation. The main idea is to put the information about an installation to the resources of the loader. When the loader is being loaded it reads the information and performs the commands from resources. I decided to use an special storage to save the files and exe, and other storage for commands.

The first storage stores all the files that will be unpacked, and the main executable that will be launched. The second storage stores the commands that will be passed to the ShellExecuteEx function after unpacking process will have been completed. The loader supports the following wildcards (for path):

 - application installed path;
 - system windows directory;
 - System32 directory;
 - system drive;
 - temporary directory;
 - user desktop.

Compiler



This is the application that forms the installation information and puts it to the loader resource. All the information is stored in a project. You can save and load a project from file. The clsProject class in VB project represents the compiler-project. This compiler has 3 sections: storage, execute, manifest. 

The 'storage' section allows to add the files that will be copied when the application is being launched. Each item in the list has flags: 'replace if exists', 'main executable', 'ignore error'. If you select 'replace if exists' flag a file will be copied even if one exists. The 'main executable' flag can be set only for the single executable file. It means that this file will be launched when all the operations have been performed. The 'ignore error' flag makes ignore any errors respectively. The order in the list corresponds the order of extracting the files except the main executable. The main executable is not extracted and is launched after all the operations. The storage section is represented as clsStorage class in the VB project. This class implements the standard collection of the clsStorageItem objects and adds some additional methods.The MainExecutable property determines the index of main executable file in the storage. When this parameter equal -1 executable file is not presented. The clsStoragaItem class represent the single item in the storage list. It has some properties that determine the behavior of item. This section is helpful if you want to copy files to disk before execution of the application.

The next section is the 'execute'. This section allows execute any commands. This commands just pass to ShellExecuteEx function. Thus you can register libraries or do something else. Each item in the execution list has two properties: the executable path and parameters. Both the path and the parameters is passed to ShellExecuteEx function. It is worth noting that all the operations is performed synchronously in the order that set in the list. It also has the 'ignore error' flag that prevents appearance any messages if an error occurs. The execute section is represented as two classes: clsExecute and clsExecuteItem. These classes are similar to the storage classes.

The last section is 'manifest'. It is just the manifest text file that you can add to the final executable. You should check the checkbox 'include manifest' in the 'manifest' tab if you wan to add manifest. It can be helpful for Free-Reg COM components or for visual styles.

All the classes refer to the project object (clsProject) that manages them. Each class that refers to project can be saved or loaded to the PropertyBag object. When a project is being saved it alternately saves each entity to the property bag, same during loading. It looks like a IPersistStream interface behavior. All the links to the storage items in the project is stored with relative paths (like a VB6 .vbp file) hence you can move project folder without issues. In order to translate from/to relative/absolute path i used PathRelativePathTo and PathCanonicalize functions.

So... This was basic information about compiler project. Now i want to talk about compilation procedure. As i said all the information about extracting/executing/launching is stored to the loader resources. At first we should define the format of the data. This information is represented in the following structures:

' // Storage list item
Private Type BinStorageListItem
ofstFileNameAs Long' // Offset of file name
ofstDestPathAs Long' // Offset of file path
dwSizeOfFileAs Long' // Size of file
ofstBeginOfDataAs Long' // Offset of beginning data
dwFlagsAs FileFlags' // Flags
End Type
' // Execute list item
Private Type BinExecListItem
ofstFileNameAs Long' // Offset of file name
ofstParametersAs Long' // Offset of parameters
dwFlagsAs ExeFlags' // Flags
End Type
' // Storage descriptor
Private Type BinStorageList
dwSizeOfStructure AsLong' // Size of structure
iExecutableIndex AsLong' // Index of main executable
dwSizeOfItemAs Long' // Size of BinaryStorageItem structure
dwNumberOfItemsAs Long' // Number of files in storage
End Type
' // Execute list descriptor
Private Type BinExecList
dwSizeOfStructure AsLong' // Size of structure
dwSizeOfItemAs Long' // Size of BinaryExecuteItem structure
dwNumberOfItemsAs Long' // Number of items
End Type
' // Base information about project
Private Type BinProject
dwSizeOfStructure AsLong' // Size of structure
storageDescriptor As BinStorageList ' // Storage descriptor
execListDescriptor As BinExecList' // Command descriptor
dwStringsTableLen AsLong' // Size of strings table
dwFileTableLenAs Long' // Size of data table
End Type

The 'BinProject' structure is located at beginning of resource entry. Notice that project is stored as RT_RCDATA item with 'PROJECT' name. The dwSizeOfStructure  field defines the size of the BinProject structure, storageDescriptor and execListDescriptor represent the storage and execute descriptors respectively. The dwStringsTableLen field shows the size of strings table. The strings table contains all the names and commands in the unicode format. The dwFileTableLen field shows the size of all data in the storage. Both storage (BinStorageList) and execute list (BinExecList) have dwSizeOfItem and dwSizeOfStructure fields that define the size of a descriptor structure and the size of a list item. These structures also have dwNumberOfItems field that shows how many items is contained in the list. The 'iExecutableIndex' field contains the index of executable file that will be launched. The common structure of a project in the resources is shown in this figure:


An item can refers to the strings table and file table for this purpose it uses the offset from beginning of a table. All the items is located one by one. Okay, you have explored the internal project format now i tell how can you build the loader that contains these data. As i said we store data to resources of the loader. I will tell about the loader a little bit later now i want to note one issue. When you put the project data to resources it doesn't affect to exe information. For example if you launch this exe the information contained in the resources of the internal exe won't be loaded. Same with icons and version information. You should copy the resources from the internal exe to loader in order to avoid this troubles. WinAPI provides the set of the functions for replacing resources. In order to obtain the list of resources you should parse the exe file and extract data. I wrote the 'LoadResources' function that extract all the resources of specified exe data to array.

PE format

In order to obtain resources from an exe file, run EXE from memory and well know the stucture of an exe file we should examine the PE (portable executable) format. The PE format has the quite complex structure. When loader launches a PE file (exe or dll) it does quite many work. Each PE file begins with special structure IMAGE_DOS_HEADER aka. dos stub. Because both DOS and WINDOWS applications have exe extension you can launch an exe file in DOS, but if you try to do it DOS launches this dos stub. Usually it show the message: "This program cannot be run in DOS mode", but you can write any program:


Type IMAGE_DOS_HEADER
e_magicAs Integer
e_cblpAs Integer
e_cpAs Integer
e_crlcAs Integer
e_cparhdr AsInteger
e_minalloc AsInteger
e_maxalloc AsInteger
e_ssAs Integer
e_spAs Integer
e_csumAs Integer
e_ipAs Integer
e_csAs Integer
e_lfarlc AsInteger
e_ovnoAs Integer
e_res(0To3)AsInteger
e_oemidAs Integer
e_oeminfo AsInteger
e_res2(0To9)AsInteger
e_lfanew AsLong
End Type

Since we don't write a dos program it doesn't matter. We wonder only two fields of this structure: e_magic and e_lfanew. The first field should contains the 'MZ' signature aka. IMAGE_DOS_SIGNATURE  and e_lfanew offset to very crucial structure IMAGE_NT_HEADERS described as:

Type IMAGE_NT_HEADERS
SignatureAs Long
FileHeaderAs IMAGE_FILE_HEADER
OptionalHeader As IMAGE_OPTIONAL_HEADER
End Type

The first filed of this structure contains the signature 'PE' (aka. IMAGE_NT_SIGNATURE). The next field describes the executable file:

Type IMAGE_FILE_HEADER
Machine AsInteger
NumberOfSectionsAs Integer
TimeDateStamp AsLong
PointerToSymbolTable AsLong
NumberOfSymbols AsLong
SizeOfOptionalHeader AsInteger
Characteristics AsInteger
End Type

The 'Machine' field defines the processor architecture and should have the IMAGE_FILE_MACHINE_I386 value in our case. The NumberOfSections filed determines the count of sections contained in the exe file.

An exe file contains the sections anyway. Each section takes a place in the address space and optionally in file. A section can contain the code or data (initialized or not), has the name as well. The most common names: .text, .data, .rsrc. Usually, the .text section contains the code, the .data section initialized data and .rsrc - resources. You can change this behavior using the linker directives. Each section have address called virtual address. Generally there are several types of the addresses. The first is relative virtual address (RVA). Because of a PE file can be loaded to any address all the references inside the PE file use the relative addressing. RVA is the offset from beginning of the base address (the address of the first byte of the PE module in the memory). The sum of the RVA and the base address is the VA (the virtual address). Also, there is the raw offset addressing that shows the location of data in the file relative the RVA. Notice that RVA <> raw offset. When a module is being loaded each secion is placed to its address. For example a module could have the section that has no-initialized data. This section wouldn't take a place in the exe file but would occupy the address space. It is the very crucial aspect because we will work with the raw exe file.

The 'TimeDateStamp' field contains the creation date of the PE module in UTC format. The 'PointerToSymbolTable' and 'NumberOfSymbols' contain the information about symbols in the PE file. Generally this fields contains zero. This fields is always used in object files (*.OBJ, *.LIB) in order to resolve links during linking as well as debugging information for PE modules. The next field 'SizeOfOptionalHeader' contains the size of structure following after IMAGE_FILE_HEADER named IMAGE_OPTIONAL_HEADER that always is presented in PE files (although may be missing in OBJ files). This structure is very essential for loading a PE file to memory. Notice that this structure is different in x64 and x86 executable files. Eventually, the 'Characteristics' field contains the PE attributtes.
The IMAGE_OPTIONAL_HEADER structure has the following format:


Type IMAGE_OPTIONAL_HEADER
Magic AsInteger
MajorLinkerVersionAs Byte
MinorLinkerVersionAs Byte
SizeOfCodeAs Long
SizeOfInitializedData AsLong
SizeOfUnitializedData AsLong
AddressOfEntryPointAs Long
BaseOfCodeAs Long
BaseOfDataAs Long
ImageBaseAs Long
SectionAlignmentAs Long
FileAlignment AsLong
MajorOperatingSystemVersionAs Integer
MinorOperatingSystemVersionAs Integer
MajorImageVersionAs Integer
MinorImageVersionAs Integer
MajorSubsystemVersion AsInteger
MinorSubsystemVersion AsInteger
W32VersionValue AsLong
SizeOfImageAs Long
SizeOfHeaders AsLong
CheckSumAs Long
SubSystemAs Integer
DllCharacteristicsAs Integer
SizeOfStackReserveAs Long
SizeOfStackCommitAs Long
SizeOfHeapReserveAs Long
SizeOfHeapCommitAs Long
LoaderFlagsAs Long
NumberOfRvaAndSizesAs Long
DataDirectory(15)As IMAGE_DATA_DIRECTORY
End Type

The first field contains the type of the image (x86, x64 or ROM image). We consider only IMAGE_NT_OPTIONAL_HDR32_MAGIC that represents a common 32-it application. The next two fields is not important (they were used in the old systems) and contain 4. The next group of fields contains the sizes of all the code, initialized data and uninitialized data. These values should be a multiple of 'SectionAlignment' of the structure (see later). The 'AddressOfEntryPoint' is very important RVA-value that sets the start point of a program (look like Sub Main). We will use this field when we have already loaded the PE-image to memory and it is necessary to run the program. The next crucial filds is 'ImageBase' that sets the prefered address of loading the module. When a loader is loading a module it tries to load it to thr prefered address (set in the 'ImageBase' filed). If this address is occupied then the loader checks the 'Characteristics' field in the 'IMAGE_FILE_HEADER' structure. If this field contains the IMAGE_FILE_RELOCS_STRIPPED it means that the module can't be loaded. In order to load such module we should add the reloaction information to PE that allows to set up the addresses if the module can't be loaded to prefered base address. We will use this field in the shellcode with the 'SizeOfImage' fields to reserve a memory region for the unpacked PE. The 'SectionAlignment' and 'FileAlignment' set the align in memory and file respectevely. By changing the file alignment we can reduce the PE file size but a system can not be possible to load this modified PE. The section alignment sets the align of a section (usually it equals to page size). The 'SizeOfHeaders' value sets the size of all headers (DOS header, NT headers, sections headers) aligned to the 'FileAlignment' value. The 'SizeOfStackReserve' and 'SizeOfStackCommit' determine the total and initial stack size. It's the same for 'SizeOfHeapReserve' and 'SizeOfHeapCommit' fields but for the heap. The 'NumberOfRvaAndSizes' fields contains the number of items in 'DataDirectory' array. This field always set to 16. The 'DataDirectory' array is very important because it contains the several data catalogs that contain essential information about import, export, resources, relocations, etc. We use only few items from this catalog that is used by VB6 compiler. I'll say about catalogs little bit later let's look what is behind the catalogs. There is list of the section headers. The number of section, if you remember, we can obtain from the IMAGE_FILE_HEADER structure. Let's consider the section header structure:

Type IMAGE_SECTION_HEADER
SectionName(7)AsByte
VirtualSize AsLong
VirtualAddressAs Long
SizeOfRawDataAs Long
PointerToRawData AsLong
PointerToRelocationsAs Long
PointerToLinenumbersAs Long
NumberOfRelocations AsInteger
NumberOfLinenumbers AsInteger
CharacteristicsAs Long
End Type

The first field contains the name of the section in the UTF-8 format with the NULL-terminated characters. This name is limited by 8 symbols (if a section has the size of the name equals 8 the NULL terminated character is skipped). A COFF file may have the name greater than 8 characters in this case the name begins with '/' symbol followed by the ASCII decimal representation offset in the string table (IMAGE_FILE_HEADER field). A PE file doesn't support the long names. The 'VirtualSize' and 'VirtualAddress' fields contain the size of section in memory (the address is set as RVA). The 'SizeOfRawData' and 'PointerToRawData' contain the data location in the file (if section has an initialized data). This is the key moment because we can calculate the raw offset by the virtual address using the information from the section headers. I wrote function for it that translate a RVA address to the RAW offset in the file:

' // RVA to RAW
Function RVA2RAW(_
ByVal rva AsLong,_
ByRef sec()As IMAGE_SECTION_HEADER)AsLong
Dim index AsLong

For index =0ToUBound(sec)
If rva >= sec(index).VirtualAddress And _
rva < sec(index).VirtualAddress + sec(index).VirtualSize Then
RVA2RAW = sec(index).PointerToRawData +(rva - sec(index).VirtualAddress)
ExitFunction
EndIf
Next

RVA2RAW = rva

EndFunction

This function enumerates all the sections and checks if the passed address is within section. The next 5 fields are used in only COFF file and don't matter in a PE file. The 'Characteristics' field contains the attributes of section such as memory permissions and managment. This field is important as well. We will use this information for the memory protection of an exe file in the loader.
Okay, let's return to the data directories. As we saw there is 16 items in this catalog. Usually, a PE file doesn't use all of them. Let's consider the structure of single item:
Private Type IMAGE_DATA_DIRECTORY
VirtualAddress AsLong
Size AsLong
End Type

This structure consist two fields. The first fields contains the offset (RVA) to the data of the catalog, second - size. When an item of data catalog is not required it contains zero in the both fields. An VB6-compiled application generally contains only 4 items: import table , resource table , bound import table and import address table (IAT). Now we consider the resource table that has the IMAGE_DIRECTORY_ENTRY_RESOURCE index, because we work with this imformation in the compiler application.
All the resources in the exe file are represented as the tree with triple depth. The first level defines the resource type (RT_BITMAP, RT_MANIFEST, RT_RCDATA, etc.), the next level defines the resource identifier, the third level defines language. In the standard resource editor you can change the first two levels. All the resources placed in the resources table located in a '.rsrc' section of the exe file. Regarding the resources structure we even can change it after compilation. In oredr to access to resources we should read the IMAGE_DIRECTORY_ENTRY_RESOURCE catalog from the optional header. The 'VirtualAddress' field of this structure contains the RVA of the resource table which has the following structure:

Type IMAGE_RESOURCE_DIRECTORY
CharacteristicsAs Long
TimeDateStampAs Long
MajorVersionAs Integer
MinorVersionAs Integer
NumberOfNamedEntriesAs Integer
NumberOfIdEntries AsInteger
End Type

This structure describes all the resources in PE file. The first four fields are not important, the 'NumberOfNamedEntries' and 'NumberOfIdEntries' contain the number of named items and the items with the numerical id respectively. For instance, when you add an image in 'VB Resource Editor' it'll add a numerical item with id = 2 (RT_BITMAP). The items are after this structure and have the following form:

Type IMAGE_RESOURCE_DIRECTORY_ENTRY
NameIdAs Long
OffsetToDataAs Long
End Type

The first field of this structure defines either an item name or an item id depending on the most significant bit. If this bit is set the remained bits show the offset from beginning of resources to IMAGE_RESOURCE_DIR_STRING_U structure that has the following format:

Type IMAGE_RESOURCE_DIR_STRING_U
LengthAs Integer
NameString AsString
End Type

Note that this is not the proper VB structure is shown for descriptive reasons. The first two bytes is the unsigned short (the closest analog is Integer) that show the length of the unicode string (in characters) that follows them. Thus, in order to obtain a string we should read the first two bytes to an integer, allocate memory for the string with the read value size, and read the remaining data to the string variable. Conversely, if the most significant bit of the 'NameId' field is cleared the field containts an identifier (RT_BITMAP in the previous case). The 'OffsetToData' field has two interpretations too. If the MSB is set it is the offset (from beginnig of resources) to the next level of tree i.e. to an IMAGE_RESOURCE_DIRECTORY structure. Otherwise, if the MSB is cleared this is the offset (from beginning of resources too) to a "leave" of the tree, i.e. to structure IMAGE_RESOURCE_DATA_ENTRY:

Type IMAGE_RESOURCE_DATA_ENTRY
OffsetToDataAs Long
SizeAs Long
CodePage AsLong
Reserved AsLong
End Type

The most important fields of this structure are 'OffsetToData' and 'Size' that contain the RVA and Size of the raw data of the resource. Now we can get all the resources from a PE file.

Compilation.

So... When you start the compilation it calls the Compile function. Firstly it packs all the storage items and execute items to binary format (BinProject, BinStorageListItem, etc.) and forms the string table and the files table. The string table is saves as the unicode strings with the null-terminating characters. I use special class clsStream for safe working with binary data. This class allows to read/write any data or streams into the binary buffer, compress buffer. I use RtlCompressBuffer function for compression stream that uses LZ-compression method. After packing and compression it check output project format. It is supports two formats: bin (raw project data), and exe (loader). The binary format is not interesting right now, we will consider the exe format. Firstly, it extracts all the resources from the main executable to the three-level catalog. This operation is performed by ExtractResources function. An identifier name is saved as the "#" symbol with the appended string that represents the resource id in the decimal format. Afterwards it clones the loader template to the resulting file, then begins to modify the resources in this file using BeginUpdateResource api. Then it alternately copies all the extracted resources (UpdateResource), binary project and mainfest (if needed) to the resulting file and applies changes with EndUpdateResource function. Again, the binary project is saved with "PROJECT" name and RT_DATA type. Basically that's it.

Loader.


So... I think it the most interesting part. So, we must avoid the usage of the runtime. How to do it? I give some rules:
  1. Set an user function as startup;
  2. Avoid any objects in project;
  3. Avoid immediate arrays. The fixed arrays in a type is not forbidden;
  4. Avoid string variables as well Variant/Object. In some cases Currency/Date;
  5. Avoid the API functions with Declare statement.
  6. Avoid VarPtr and some other standard functions.
  7. ....

It isn't the complete list of restrictions and during the shellcode execution it adds additional restrinctions.
So, begin. In order to avoid the usage of a string variable i keep all the string variables as Long pointer to the string. There is an issue with loading of a string because we can't access to any string to load it. I decide to use resources as the storage of strings and load it by ID. Thus, we can save the pointer to a string into a Long variable without references to runtime. I used a TLB (type library) for all the API functions without usesgetlasterror attribute in order to avoid the Declare statement. In order to set a startup function i use the linker options. The startup function in the loader is 'Main'. Note, if in the IDE you select the startup function 'Main' actually this function is not startup because any EXE application written in VB6 begins with ThunRTMain function, that loads project and initialize runtime and thread. Generally, the loader conist three modules:
  1. modMain - startup function and working with storage/execute items;
  2. modConstants - working with string constants;
  3. modLoader - loader of EXE files.
When loader has been launched it starts the Main function.
' // Startup subroutine
Sub Main()
' // Load constants
IfNot LoadConstants Then
MessageBox 0, GetString(MID_ERRORLOADINGCONST),0, MB_ICONERROR Or MB_SYSTEMMODAL
GoTo EndOfProcess
EndIf

' // Load project
IfNot ReadProject Then
MessageBox 0, GetString(MID_ERRORREADINGPROJECT),0, MB_ICONERROR Or MB_SYSTEMMODAL
GoTo EndOfProcess
EndIf

' // Copying from storage
IfNot CopyProcess ThenGoTo EndOfProcess

' // Execution process
IfNot ExecuteProcess ThenGoTo EndOfProcess

' // If main executable is not presented exit
If ProjectDesc.storageDescriptor.iExecutableIndex =-1ThenGoTo EndOfProcess

' // Run exe from memory
IfNot RunProcess Then
' // Error occrurs
MessageBox 0, GetString(MID_ERRORSTARTUPEXE),0, MB_ICONERROR Or MB_SYSTEMMODAL
EndIf

EndOfProcess:

If pProjectData Then
HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pProjectData
EndIf

ExitProcess 0

EndSub

Firstly, it function call LoadConstants function to load all the needed constants from resources:

' // modConstants.bas - main module for loading constants
' // © Krivous Anatoly Anatolevich (The trick), 2016
Option Explicit
Public Enum MessagesID
MID_ERRORLOADINGCONST =100' // Errors
MID_ERRORREADINGPROJECT =101'
MID_ERRORCOPYINGFILE =102'
MID_ERRORWIN32 =103'
MID_ERROREXECUTELINE =104'
MID_ERRORSTARTUPEXE =105'
PROJECT =200' // Project resource ID
API_LIB_KERNEL32 =300' // Library names
API_LIB_NTDLL =350'
API_LIB_USER32 =400'
MSG_LOADER_ERROR =500
End Enum
' // Paths
Public pAppPath AsLong' // Path to application
Public pSysPath AsLong' // Path to System32
Public pTmpPath AsLong' // Path to Temp
Public pWinPath AsLong' // Path to Windows
Public pDrvPath AsLong' // Path to system drive
Public pDtpPath AsLong' // Path to desktop
' // Substitution constants
Public pAppRepl AsLong
Public pSysRepl AsLong
Public pTmpRepl AsLong
Public pWinRepl AsLong
Public pDrvRepl AsLong
Public pDtpRepl AsLong
Public pStrNull AsLong' //
Public hInstance AsLong' // Base address
Public lpCmdLine AsLong' // Command line
Public SI As STARTUPINFO ' // Startup parameters
Public LCID AsLong' // LCID
' // Load constants
Function LoadConstants()AsBoolean
Dim lSize AsLong
Dim pBuf AsLong
Dim index AsLong
Dim ctlAs tagINITCOMMONCONTROLSEX

' // Load windows classes
ctl.dwSize =Len(ctl)
ctl.dwICC =&H3FFF&
InitCommonControlsEx ctl

' // Get startup parameters
GetStartupInfo SI

' // Get command line
lpCmdLine = GetCommandLine()

' // Get base address
hInstance = GetModuleHandle(ByVal0&)

' // Get LCID
LCID = GetUserDefaultLCID()

' // Alloc memory for strings
pBuf = SysAllocStringLen(0, MAX_PATH)
If pBuf =0ThenExitFunction

' // Get path to process file name
If GetModuleFileName(hInstance, pBuf, MAX_PATH)=0ThenGoTo CleanUp

' // Leave only directory
PathRemoveFileSpec pBuf

' // Save path
pAppPath = SysAllocString(pBuf)

' // Get Windows folder
If GetWindowsDirectory(pBuf, MAX_PATH)=0ThenGoTo CleanUp
pWinPath = SysAllocString(pBuf)

' // Get System32 folder
If GetSystemDirectory(pBuf, MAX_PATH)=0ThenGoTo CleanUp
pSysPath = SysAllocString(pBuf)

' // Get Temp directory
If GetTempPath(MAX_PATH, pBuf)=0ThenGoTo CleanUp
pTmpPath = SysAllocString(pBuf)

' // Get system drive
PathStripToRoot pBuf
pDrvPath = SysAllocString(pBuf)

' // Get desktop path
If SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY,0, SHGFP_TYPE_CURRENT, pBuf)ThenGoTo CleanUp
pDtpPath = SysAllocString(pBuf)

' // Load wildcards
For index =1To6
If LoadString(hInstance, index, pBuf, MAX_PATH)=0ThenGoTo CleanUp
SelectCase index
Case1: pAppRepl = SysAllocString(pBuf)
Case2: pSysRepl = SysAllocString(pBuf)
Case3: pTmpRepl = SysAllocString(pBuf)
Case4: pWinRepl = SysAllocString(pBuf)
Case5: pDrvRepl = SysAllocString(pBuf)
Case6: pDtpRepl = SysAllocString(pBuf)
EndSelect
Next

' // vbNullChar
pStrNull = SysAllocStringLen(0,0)
' // Success
LoadConstants =True

CleanUp:

If pBuf Then SysFreeString pBuf

EndFunction
' // Obtain string from resource (it should be less or equal MAX_PATH)
PublicFunction GetString(_
ByVal ID As MessagesID)AsLong
GetString = SysAllocStringLen(0, MAX_PATH)

If GetString Then

If LoadString(hInstance, ID, GetString, MAX_PATH)=0Then SysFreeString GetString: GetString =0:ExitFunction
If SysReAllocString(GetString, GetString)=0Then SysFreeString GetString: GetString =0:ExitFunction
EndIf

EndFunction

The 'LoadConstants' function loads all the needed variables and string (hInstance, LCID, command line, wildcards, default paths, etc.). All the strings is stored in the BSTR unicode format. The 'GetString' function loads a string from resource by number. The 'MessagesID' contains some string identifiers needed in program (error messages, libraries names, etc.). When all the constants are loaded it calls the ReadProject function that loads the binary project:

' // Load project
Function ReadProject()AsBoolean
Dim hResourceAs Long:Dim hMememoryAs Long
Dim lResSizeAs Long:Dim pRawDataAs Long
Dim status AsLong:Dim pUncompressed AsLong
Dim lUncompressSize AsLong:Dim lResultSizeAs Long
Dim tmpStorageItem As BinStorageListItem:Dim tmpExecuteItem As BinExecListItem
Dim pLocalBuffer AsLong

' // Load resource
hResource = FindResource(hInstance, GetString(PROJECT), RT_RCDATA)
If hResource =0ThenGoTo CleanUp

hMememory = LoadResource(hInstance, hResource)
If hMememory =0ThenGoTo CleanUp

lResSize = SizeofResource(hInstance, hResource)
If lResSize =0ThenGoTo CleanUp

pRawData = LockResource(hMememory)
If pRawData =0ThenGoTo CleanUp

pLocalBuffer = HeapAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE, lResSize)
If pLocalBuffer =0ThenGoTo CleanUp

' // Copy to local buffer
CopyMemory ByVal pLocalBuffer,ByVal pRawData, lResSize

' // Set default size
lUncompressSize = lResSize *2

' // Do decompress...
Do
If pUncompressed Then
pUncompressed = HeapReAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE,ByVal pUncompressed, lUncompressSize)
Else
pUncompressed = HeapAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE, lUncompressSize)
EndIf
status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1,_
ByVal pUncompressed, lUncompressSize,_
ByVal pLocalBuffer, lResSize, lResultSize)
lUncompressSize = lUncompressSize *2
LoopWhile status = STATUS_BAD_COMPRESSION_BUFFER

pProjectData = pUncompressed

If status ThenGoTo CleanUp
' // Validation check
If lResultSize < LenB(ProjectDesc)ThenGoTo CleanUp

' // Copy descriptor
CopyMemory ProjectDesc,ByVal pProjectData, LenB(ProjectDesc)

' // Check all members
If ProjectDesc.dwSizeOfStructure <>Len(ProjectDesc)ThenGoTo CleanUp
If ProjectDesc.storageDescriptor.dwSizeOfStructure <>Len(ProjectDesc.storageDescriptor)ThenGoTo CleanUp
If ProjectDesc.storageDescriptor.dwSizeOfItem <>Len(tmpStorageItem)ThenGoTo CleanUp
If ProjectDesc.execListDescriptor.dwSizeOfStructure <>Len(ProjectDesc.execListDescriptor)ThenGoTo CleanUp
If ProjectDesc.execListDescriptor.dwSizeOfItem <>Len(tmpExecuteItem)ThenGoTo CleanUp

' // Initialize pointers
pStoragesTable = pProjectData + ProjectDesc.dwSizeOfStructure
pExecutesTable = pStoragesTable + ProjectDesc.storageDescriptor.dwSizeOfItem * ProjectDesc.storageDescriptor.dwNumberOfItems
pFilesTable = pExecutesTable + ProjectDesc.execListDescriptor.dwSizeOfItem * ProjectDesc.execListDescriptor.dwNumberOfItems
pStringsTable = pFilesTable + ProjectDesc.dwFileTableLen

' // Check size
If(pStringsTable + ProjectDesc.dwStringsTableLen - pProjectData)<> lResultSize ThenGoTo CleanUp

' // Success
ReadProject =True

CleanUp:

If pLocalBuffer Then HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pLocalBuffer

IfNot ReadProject And pProjectData Then
HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pProjectData
EndIf

EndFunction

As you can see i use the heap memory instead arrays. Firstly, it loads the 'PROJECT' resource and copies one to heap memory then tries to decompres using the RtlDecompressBuffer function. This function is not returns the sufficient output buffer size therefore we try to do decompress of the buffer increasing the output buffer size. Afterwards it checks all parameters and initializes the global project pointers.
If it is succeeded then it launches the 'CopyProcess' procedure that unpacks all the storage items according project data:
' // Copying process
Function CopyProcess()AsBoolean
Dim bItemAs BinStorageListItem:Dim indexAs Long
Dim pPathAs Long:Dim dwWritten AsLong
Dim msg AsLong:Dim lStepAs Long
Dim isErrorAs Boolean:Dim pItemAs Long
Dim pErrMsgAs Long:Dim pTempString AsLong

' // Set pointer
pItem = pStoragesTable

' // Go thru file list
For index =0To ProjectDesc.storageDescriptor.dwNumberOfItems -1
' // Copy file descriptor
CopyMemory bItem,ByVal pItem,Len(bItem)
' // Next item
pItem = pItem + ProjectDesc.storageDescriptor.dwSizeOfItem
' // If it is not main executable
If index <> ProjectDesc.storageDescriptor.iExecutableIndex Then
' // Normalize path
pPath = NormalizePath(pStringsTable + bItem.ofstDestPath, pStringsTable + bItem.ofstFileName)

' // Error occurs
If pPath =0Then

pErrMsg = GetString(MID_ERRORWIN32)
MessageBox 0, pErrMsg,0, MB_ICONERROR Or MB_SYSTEMMODAL
GoTo CleanUp
Else
Dim hFile AsLong
Dim disp As CREATIONDISPOSITION
' // Set overwrite flags
If bItem.dwFlags And FF_REPLACEONEXIST Then disp = CREATE_ALWAYS Else disp = CREATE_NEW
' // Set number of subroutine
lStep =0
' // Run subroutines
Do
' // Disable error flag
isError=False

' // Free string
If pErrMsg Then SysFreeString pErrMsg: pErrMsg =0

' // Choose subroutine
SelectCase lStep
Case0' // 0. Create folder

IfNot CreateSubdirectories(pPath)ThenisError=True
Case1' // 1. Create file

hFile = CreateFile(pPath, FILE_GENERIC_WRITE,0,ByVal0&, disp, FILE_ATTRIBUTE_NORMAL,0)
If hFile = INVALID_HANDLE_VALUE Then
If GetLastError = ERROR_FILE_EXISTS ThenExitDo
isError=True
EndIf
Case2' // 2. Copy data to file

If WriteFile(hFile,ByVal pFilesTable + bItem.ofstBeginOfData,_
bItem.dwSizeOfFile, dwWritten,ByVal0&)=0ThenisError=True
If dwWritten <> bItem.dwSizeOfFile Then
isError=True
Else
CloseHandle hFile: hFile = INVALID_HANDLE_VALUE
EndIf
EndSelect

' // If error occurs show notification (retry, abort, ignore)
IfisErrorThen

' // Ignore error
If bItem.dwFlags And FF_IGNOREERROR ThenExitDo
pTempString = GetString(MID_ERRORCOPYINGFILE)
pErrMsg = StrCat(pTempString, pPath)
' // Cleaning
SysFreeString pTempString: pTempString =0
SelectCase MessageBox(0, pErrMsg,0, MB_ICONERROR Or MB_SYSTEMMODAL Or MB_CANCELTRYCONTINUE)
Case MESSAGEBOXRETURN.IDCONTINUE:ExitDo
Case MESSAGEBOXRETURN.IDTRYAGAIN
CaseElse:GoTo CleanUp
EndSelect
Else: lStep = lStep +1
EndIf

LoopWhile lStep <=2
If hFile <> INVALID_HANDLE_VALUE Then
CloseHandle hFile: hFile = INVALID_HANDLE_VALUE
EndIf
' // Cleaning
SysFreeString pPath: pPath =0
EndIf

EndIf
Next

' // Success
CopyProcess =True

CleanUp:

If pTempString Then SysFreeString pTempString
If pErrMsg Then SysFreeString pErrMsg
If pPath Then SysFreeString pPath

If hFile <> INVALID_HANDLE_VALUE Then
CloseHandle hFile
hFile = INVALID_HANDLE_VALUE
EndIf

EndFunction

This procedure goes through all the storage items and unpacks all the items one by one except the main executable file. The 'NormalizePath' function replace the wildcards in the path to the real strings path. There is the 'CreateSubdirectories' function that creates the intermediate directories (if needs) for specified path. Then it calls the CreateFile function and copy data to it through WriteFile. If an error occurs it shows the message box with the standard suggestions: Retry, Abort, Ignore.

' // Create all subdirectories by path
Function CreateSubdirectories(_
ByVal pPath AsLong)AsBoolean
Dim pComponent AsLong
Dim tCharAs Integer

' // Pointer to first char
pComponent = pPath

' // Go thru path components
Do

' // Get next component
pComponent = PathFindNextComponent(pComponent)
' // Check if end of line
CopyMemory tChar,ByVal pComponent,2
If tChar =0ThenExitDo
' // Write null-terminator
CopyMemory ByVal pComponent -2,0,2
' // Check if path exists
If PathIsDirectory(pPath)=0Then
' // Create folder
If CreateDirectory(pPath,ByVal0&)=0Then
' // Error
CopyMemory ByVal pComponent -2,&H5C,2
ExitFunction
EndIf

EndIf
' // Restore path delimiter
CopyMemory ByVal pComponent -2,&H5C,2
Loop

' // Success
CreateSubdirectories =True

EndFunction
' // Get normalize path (replace wildcards, append file name)
Function NormalizePath(_
ByVal pPath AsLong,_
ByVal pTitle AsLong)AsLong
Dim lPathLen AsLong:Dim lRelacerLen AsLong
Dim lTitleLen AsLong:Dim pRelacer AsLong
Dim lTotalLen AsLong:Dim lPtrAs Long
Dim pTempString AsLong:Dim pRetString AsLong

' // Determine wildcard
SelectCaseTrue
Case IntlStrEqWorker(0, pPath, pAppRepl,5): pRelacer = pAppPath
Case IntlStrEqWorker(0, pPath, pSysRepl,5): pRelacer = pSysPath
Case IntlStrEqWorker(0, pPath, pTmpRepl,5): pRelacer = pTmpPath
Case IntlStrEqWorker(0, pPath, pWinRepl,5): pRelacer = pWinPath
Case IntlStrEqWorker(0, pPath, pDrvRepl,5): pRelacer = pDrvPath
Case IntlStrEqWorker(0, pPath, pDtpRepl,5): pRelacer = pDtpPath
CaseElse: pRelacer = pStrNull
EndSelect

' // Get string size
lPathLen = lstrlen(ByVal pPath)
lRelacerLen = lstrlen(ByVal pRelacer)

' // Skip wildcard
If lRelacerLen Then
pPath = pPath +5*2
lPathLen = lPathLen -5
EndIf

If pTitle Then lTitleLen = lstrlen(ByVal pTitle)

' // Get length all strings
lTotalLen = lPathLen + lRelacerLen + lTitleLen

' // Check overflow (it should be les or equal MAX_PATH)
If lTotalLen > MAX_PATH ThenExitFunction

' // Create string
pTempString = SysAllocStringLen(0, MAX_PATH)
If pTempString =0ThenExitFunction

' // Copy
lstrcpyn ByVal pTempString,ByVal pRelacer, lRelacerLen +1
lstrcat ByVal pTempString,ByVal pPath
' // If title is presented append
If pTitle Then
' // Error
If PathAddBackslash(pTempString)=0ThenGoTo CleanUp
' // Copy file name
lstrcat ByVal pTempString,ByVal pTitle
EndIf

' // Alloc memory for translation relative path to absolute
pRetString = SysAllocStringLen(0, MAX_PATH)
If pRetString =0ThenGoTo CleanUp

' // Normalize
If PathCanonicalize(pRetString, pTempString)=0ThenGoTo CleanUp

NormalizePath = pRetString

CleanUp:

If pTempString Then SysFreeString pTempString
If pRetString <>0 And NormalizePath =0Then SysFreeString pRetString

EndFunction
' // Concatenation strings
Function StrCat(_
ByVal pStringDest AsLong,_
ByVal pStringAppended AsLong)AsLong
Dim l1 AsLong, l2 AsLong

l1 = lstrlen(ByVal pStringDest): l2 = lstrlen(ByVal pStringAppended)
StrCat = SysAllocStringLen(0, l1 + l2)

If StrCat =0ThenExitFunction

lstrcpyn ByVal StrCat,ByVal pStringDest, l1 +1
lstrcat ByVal StrCat,ByVal pStringAppended

EndFunction

After unpacking it is call the 'ExecuteProcess' function that launches all the commands using ShellExecuteEx function:

' // Execution command process
Function ExecuteProcess()AsBoolean
Dim indexAs Long:Dim bItemAs BinExecListItem
Dim pPathAs Long:Dim pErrMsgAs Long
Dim shInfoAs SHELLEXECUTEINFO:Dim pTempString AsLong
Dim pItemAs Long:Dim statusAs Long
' // Set pointer and size
shInfo.cbSize =Len(shInfo)
pItem = pExecutesTable

' // Go thru all items
For index =0To ProjectDesc.execListDescriptor.dwNumberOfItems -1

' // Copy item
CopyMemory bItem,ByVal pItem, ProjectDesc.execListDescriptor.dwSizeOfItem
' // Set pointer to next item
pItem = pItem + ProjectDesc.execListDescriptor.dwSizeOfItem
' // Normalize path
pPath = NormalizePath(pStringsTable + bItem.ofstFileName,0)
' // Fill SHELLEXECUTEINFO
shInfo.lpFile = pPath
shInfo.lpParameters = pStringsTable + bItem.ofstParameters
shInfo.fMask = SEE_MASK_NOCLOSEPROCESS Or SEE_MASK_FLAG_NO_UI
shInfo.nShow = SW_SHOWDEFAULT
' // Performing...
status = ShellExecuteEx(shInfo)
' // If error occurs show notification (retry, abort, ignore)
DoUntil status

If pErrMsg Then SysFreeString pErrMsg: pErrMsg =0

' // Ignore error
If bItem.dwFlags And EF_IGNOREERROR Then
ExitDo
EndIf
pTempString = GetString(MID_ERROREXECUTELINE)
pErrMsg = StrCat(pTempString, pPath)

SysFreeString pTempString: pTempString =0

SelectCase MessageBox(0, pErrMsg,0, MB_ICONERROR Or MB_SYSTEMMODAL Or MB_CANCELTRYCONTINUE)
Case MESSAGEBOXRETURN.IDCONTINUE:ExitDo
Case MESSAGEBOXRETURN.IDTRYAGAIN
CaseElse:GoTo CleanUp
EndSelect
status = ShellExecuteEx(shInfo)

Loop
' // Wait for process terminaton
WaitForSingleObject shInfo.hProcess, INFINITE
CloseHandle shInfo.hProcess
Next

' // Success
ExecuteProcess =True

CleanUp:
If pTempString Then SysFreeString pTempString
If pErrMsg Then SysFreeString pErrMsg
If pPath Then SysFreeString pPath

EndFunction

As you can see it's like the previous procedure. Here is the same it only uses ShellExecuteEx instead unpacking. Note that each operation performs synchronously, i.e. each calling of ShellExecuteEx waits for operation will have been done.
If it is succeeded then it is call the 'RunProcess' function that prepares the data for executing main executable from memory.
' // Run exe from project in memory
Function RunProcess()AsBoolean
Dim bItemAs BinStorageListItem:Dim LengthAs Long
Dim pFileData AsLong

' // Get descriptor of executable file
CopyMemory bItem,ByVal pStoragesTable + ProjectDesc.storageDescriptor.dwSizeOfItem *_
ProjectDesc.storageDescriptor.iExecutableIndex,Len(bItem)

' // Alloc memory within top memory addresses
pFileData = VirtualAlloc(ByVal0&, bItem.dwSizeOfFile, MEM_TOP_DOWN Or MEM_COMMIT, PAGE_READWRITE)
If pFileData =0ThenExitFunction

' // Copy raw exe file to this memory
CopyMemory ByVal pFileData,ByVal pFilesTable + bItem.ofstBeginOfData, bItem.dwSizeOfFile

' // Free decompressed project data
HeapFree GetProcessHeap(), HEAP_NO_SERIALIZE, pProjectData
pProjectData =0

' // Run exe from memory
RunExeFromMemory pFileData, bItem.dwFlags And FF_IGNOREERROR

' ----------------------------------------------------
' // An error occurs
' // Clean memory

VirtualFree ByVal pFileData,0, MEM_RELEASE

' // If ignore error then success
If bItem.dwFlags And FF_IGNOREERROR Then RunProcess =True

EndFunction

It allocates the memory in the top area of virtual addresses because the most of exe files is loaded to quite low addresses (usually 0x00400000). Afterwards it free the memory of the project data because if the process is launched this memory won't release, then it call the 'RunExeFromMemory' function that does next step of loading an exe. If for any reasons loading of an exe file wouldn't done it frees the allocated memory and return the control to the 'Main' function. So, in order to load an exe file we should release the loader memory, i.e. unload us loader. We should leave small piece of code that will load an exe and run it. I decide to use the shellcode, although it is possible to use a dll. The shellcode is the small base-independent code (this code doesn't refer to external data) that allows to do the usefull stuff. Anyway we should ensure the access to API functions from the shellcode. You can't call an api function directly from the shellcode because the main exe is unloaded and any reference to the import table of main exe occurs crash. The second restriction is the 'CALL' instruction can use relative offset (it is most frequently case). Therefore we should initialize some "springboard" that will jump an api function. I decide to do it using splicing method. I just replace the first 5 bytes of a stub function to JMP assembler instruction that refers to the needed API:

' // Run EXE file by memory address
Function RunExeFromMemory(_
ByVal pExeData AsLong,_
ByVal IgnoreError AsBoolean)AsBoolean
Dim Length AsLong:Dim pCodeAs Long
Dim pszMsg AsLong:Dim pMsgTable AsLong
Dim index AsLong:Dim pCurMsgAs Long

' // Get size of shellcode
Length = GetAddr(AddressOf ENDSHELLLOADER)- GetAddr(AddressOf BEGINSHELLLOADER)

' // Alloc memory within top addresses
pCode = VirtualAlloc(ByVal0&, Length, MEM_TOP_DOWN Or MEM_COMMIT, PAGE_EXECUTE_READWRITE)

' // Copy shellcode to allocated memory
CopyMemory ByVal pCode,ByVal GetAddr(AddressOf BEGINSHELLLOADER), Length

' // Initialization of shellcode
IfNot InitShellLoader(pCode)ThenGoTo CleanUp

' // Splice CallLoader function in order to call shellcode
Splice AddressOf CallLoader, pCode + GetAddr(AddressOf LoadExeFromMemory)- GetAddr(AddressOf BEGINSHELLLOADER)

' // Check ignore errors
IfNot IgnoreError Then
' // Alloc memory for messages table
pMsgTable = VirtualAlloc(ByVal0&,1024, MEM_TOP_DOWN Or MEM_COMMIT, PAGE_READWRITE)
If pMsgTable =0ThenGoTo CleanUp
' // Skip pointers
pCurMsg = pMsgTable + EM_END *4
For index =0To EM_END -1
' // Load message string
pszMsg = GetString(MSG_LOADER_ERROR + index)
If pszMsg =0ThenGoTo CleanUp

Length = SysStringLen(pszMsg)
lstrcpyn ByVal pCurMsg,ByVal pszMsg, Length +1

' // Store pointer
CopyMemory ByVal pMsgTable + index *4, pCurMsg,Len(pCurMsg)

' // Next message offset
pCurMsg = pCurMsg +(Length +1)*2

SysFreeString pszMsg

Next
EndIf

' // Call shellcode
CallLoader pExeData, pCode, pMsgTable

CleanUp:

If pMsgTable Then
VirtualFree ByVal pMsgTable,0, MEM_RELEASE
EndIf

If pCode Then
VirtualFree ByVal pCode,0, MEM_RELEASE
EndIf

EndFunction

As you can see it calculates the size of shellcode using the difference between the extreme functions ENDSHELLLOADER and BEGINSHELLLOADER. These functions should surround the shellcode and have the different prototypes because VB6 compiler can union identical functions. Then it allocates the memory for the shellcode and copies the shellcode to there. Afterwards it calls the 'InitShellLoader' function, that splaces all the function in shellcode:

' // Shellcode initialization
Function InitShellLoader(_
ByVal pShellCode AsLong)AsBoolean
Dim hLib AsLong:Dim sName AsLong
Dim sFunc AsLong:Dim lpAddr AsLong
Dim libIdx AsLong:Dim fncIdx AsLong
Dim libName As MessagesID:Dim fncName As MessagesID
Dim fncSpc AsLong:Dim splAddr AsLong

' // +----------------------------------------------------------------+
' // | Fixing of API addresses|
' // +----------------------------------------------------------------+
' // | In order to call api function from shellcode i use splicing of |
' // | our VB functions and redirect call to corresponding api. |
' // |I did same in the code that injects to other process.|
' // +----------------------------------------------------------------+

splAddr = GetAddr(AddressOf tVirtualAlloc)- GetAddr(AddressOf BEGINSHELLLOADER)+ pShellCode

' // Get size in bytes between stub functions
fncSpc = GetAddr(AddressOf tVirtualProtect)- GetAddr(AddressOf tVirtualAlloc)
' // Use 3 library: kernel32, ntdll и user32
For libIdx =0To2

' // Get number of imported functions depending on library
SelectCase libIdx
Case0: libName = API_LIB_KERNEL32: fncIdx =13
Case1: libName = API_LIB_NTDLL: fncIdx =1
Case2: libName = API_LIB_USER32: fncIdx =1
EndSelect
' // Get library name from resources
sName = GetString(libName):If sName =0ThenExitFunction
' // Get module handle
hLib = GetModuleHandle(ByVal sName):If hLib =0ThenExitFunction
SysFreeString sName
' // Go thru functions
DoWhile fncIdx
libName = libName +1
' // Get function name
sName = GetString(libName):If sName =0ThenExitFunction

' // Because of GetProcAddress works with ANSI string translate it to ANSI
sFunc = ToAnsi(sName):If sFunc =0ThenExitFunction

' // Get function address
lpAddr = GetProcAddress(hLib, sFunc)
SysFreeString sName: SysFreeString sFunc

' // Error
If lpAddr =0ThenExitFunction

' // Splice stub
Splice splAddr, lpAddr

' // Next stub
splAddr = splAddr + fncSpc
fncIdx = fncIdx -1

Loop
Next

' // Modify CallByPointer
lpAddr = GetAddr(AddressOf CallByPointer)- GetAddr(AddressOf BEGINSHELLLOADER)+ pShellCode

' // pop eax - 0x58
' // pop ecx - 0x59
' // push eax - 0x50
' // jmp ecx - 0xFFE1

CopyMemory ByVal lpAddr,&HFF505958,4
CopyMemory ByVal lpAddr +4,&HE1,1
' // Success
InitShellLoader =True

EndFunction
' // Splice function
Sub Splice(_
ByVal Func AsLong,_
ByVal NewAddr AsLong)
' // Set memory permissions
VirtualProtect ByVal Func,5, PAGE_EXECUTE_READWRITE,0
CopyMemory ByVal Func,&HE9,1' // JMP
CopyMemory ByVal Func +1, NewAddr - Func -5,4' // Relative address
EndSub

Firstly it calculates the offset of the first "springboard" function (in this case it is tVirtualAlloc function) from beginning of the shellcode and calculates the distance in bytes between "springboard" functions. When VB6-compiler compiles an module it puts all the functions in the same order as in the code. The needed condition is to ensure the uni, que returned value from each function. Then it goes through all the needed libraries (kernel32, ntdll, user32 - in this order) and their functions. The first item in the resource strings is the library name followed by the functions names in this library. When an item is obtained it translates the function name to ANSI format and calls GetProcAddress function. Afterwards it calls the Splice function that makes up the "springboard" to the needed function from the shellcode. Eventually, it modified the CallByPointer function in order to ensure the jump from the shellcode to the loaded exe. Okay, further the RunExeFromMemory function splices the CallLoader in order to ensure the jump to shellcode from the main executable. After this operation is done the function begins to form the error message table (if needs) that is just the set of pointers to the messages strings. Eventually it call the spliced CallLoader function that jumps to the LoadExeFromMemory shellcode function that has already not been placed in main exe.



EXE without runtime, structure of executable files, VB6 applications without external dependencies ! PART 2

$
0
0

PART 2 (by Krivous Anatoly Anatolevich)


Download from ME

Download from PSC

Inside shellcode

So, i made the several function inside the shellcode:

LoadExeFromMemory - is the main function of the shellcode;

GetImageNtHeaders - returns the IMAGE_NT_HEADERS structure and its address by the passed base address;

GetDataDirectory - returns the IMAGE_DATA_DIRECTORY structure and its address by the passed base address and catalog index;

EndProcess - shows the error message (if any) and ends of the process;

ProcessSectionsAndHeaders - allocates the memory for all headers (DOS, NT, sections) and all the sections. Copies all data to the sections;

ReserveMemory - reserves the sufficient memory for EXE;

ProcessRelocations - adjusts the addresses if an exe has not been loaded to base address;

ProcessImportTable - scans the import table of an exe file, loads the needed libraries and fills the import address table;

SetMemoryPermissions - adjusts the memory permissions for each section;

UpdateNewBaseAddress - refresh the new base address in the system structures PEB and LDR.


Due to the fact i can't use the VarPtr function, i made the similar function using the lstrcpyn function - IntPtr. So, the 'LoadExeFromMemory' function obtain firstly the NT headers and checks the processor architecture, whether the PE file is executable and whether the PE file is 32 bit application. If it is succeeded then the shellcode unload the main exe file from memory using the ZwUnmapViewOfSection function. If function has been succeeded the main exe file isn't in the memory anymore and the memory occupied by exe has been released. Henceforth we can't directly use API function, we should use our "springboards":

' // Parse exe in memory
Function LoadExeFromMemory(_
ByVal pRawData AsLong,_
ByVal pMyBaseAddress AsLong,_
ByVal pErrMsgTable AsLong)AsBoolean
Dim NtHdrAs IMAGE_NT_HEADERS
Dim pBaseAs Long
Dim indexAs Long
Dim iError As ERROR_MESSAGES
Dim pszMsg AsLong
' // Get IMAGE_NT_HEADERS
If GetImageNtHeaders(pRawData, NtHdr)=0Then
iError = EM_UNABLE_TO_GET_NT_HEADERS
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Check flags
If NtHdr.FileHeader.Machine <> IMAGE_FILE_MACHINE_I386 Or _
(NtHdr.FileHeader.Characteristics And IMAGE_FILE_EXECUTABLE_IMAGE)=0 Or _
(NtHdr.FileHeader.Characteristics And IMAGE_FILE_32BIT_MACHINE)=0ThenExitFunction
' // Release main EXE memory. After that main exe is unloaded from memory.
ZwUnmapViewOfSection GetCurrentProcess(), GetModuleHandle(ByVal0&)
' // Reserve memory for EXE
iError = ReserveMemory(pRawData, pBase)
If iError Then
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Place data
iError = ProcessSectionsAndHeaders(pRawData, pBase)
If iError Then
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Update new base address
iError = UpdateNewBaseAddress(pBase)
If iError Then
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Import table processing
iError = ProcessImportTable(pBase)
If iError Then
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Relocations processing
iError = ProcessRelocations(pBase)
If iError Then
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Set the memory attributes
iError = SetMemoryPermissions(pBase)
If iError Then
EndProcess pErrMsgTable, iError
ExitFunction
EndIf
' // Release error message table
If pErrMsgTable Then
tVirtualFree pErrMsgTable,0, MEM_RELEASE
EndIf
' // Call entry point
CallByPointer NtHdr.OptionalHeader.AddressOfEntryPoint + pBase
' // End process
EndProcess
EndFunction

Then shellcode calls the ReserveMemory function shown below. This function extracts the NT header from the loadable exe and tries to reserve the memory at 'ImageBase' address with the 'SizeOfImage' size. If it isn't succeeded the function checks if the exe file contains the relocation information. If so, it tries to reserve memory at any address. The relocation information allows to load an PE file to any address other than 'ImageBase'. It contains all the places where an exe uses the absolute addressing. You can adjust these places using the difference between the real base address and the 'ImageBase' field:

' // Reserve memory for EXE
Function ReserveMemory(_
ByVal pRawExeData AsLong,_
ByRef pBase AsLong)As ERROR_MESSAGES
Dim NtHdrAs IMAGE_NT_HEADERS
Dim pLocBaseAs Long
If GetImageNtHeaders(pRawExeData, NtHdr)=0Then
ReserveMemory = EM_UNABLE_TO_GET_NT_HEADERS
ExitFunction
EndIf
' // Reserve memory for EXE
pLocBase = tVirtualAlloc(ByVal NtHdr.OptionalHeader.ImageBase,_
NtHdr.OptionalHeader.SizeOfImage,_
MEM_RESERVE, PAGE_EXECUTE_READWRITE)
If pLocBase =0Then
' // If relocation information not found error
If NtHdr.FileHeader.Characteristics And IMAGE_FILE_RELOCS_STRIPPED Then
ReserveMemory = EM_UNABLE_TO_ALLOCATE_MEMORY
ExitFunction
Else
' // Reserve memory in other region
pLocBase = tVirtualAlloc(ByVal0&, NtHdr.OptionalHeader.SizeOfImage,_
MEM_RESERVE, PAGE_EXECUTE_READWRITE)
If pLocBase =0Then
ReserveMemory = EM_UNABLE_TO_ALLOCATE_MEMORY
ExitFunction
EndIf
EndIf
EndIf
pBase = pLocBase
EndFunction

Okay, if memory reserving failed it shows the message with error and ends the application. Otherwise it calls the ProcessSectionsAndHeaders function. This function places all the headers to the allocated memory, extracts the information about all the sections and copies all the data to sections. If an section has the uninitialized data it fills this region with zero:

' // Allocate memory for sections and copy them data to there
Function ProcessSectionsAndHeaders(_
ByVal pRawExeData AsLong,_
ByVal pBase AsLong)As ERROR_MESSAGES
Dim iSecAs Long
Dim pNtHdr AsLong
Dim NtHdrAs IMAGE_NT_HEADERS
Dim sec As IMAGE_SECTION_HEADER
Dim lpSecAs Long
Dim pDataAs Long
pNtHdr = GetImageNtHeaders(pRawExeData, NtHdr)
If pNtHdr =0Then
ProcessSectionsAndHeaders = EM_UNABLE_TO_GET_NT_HEADERS
ExitFunction
EndIf
' // Alloc memory for headers
pData = tVirtualAlloc(ByVal pBase, NtHdr.OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE)
If pData =0Then
ProcessSectionsAndHeaders = EM_UNABLE_TO_ALLOCATE_MEMORY
ExitFunction
EndIf
' // Copy headers
tCopyMemory pData, pRawExeData, NtHdr.OptionalHeader.SizeOfHeaders
' // Get address of beginnig of sections headers
pData = pNtHdr +Len(NtHdr.Signature)+Len(NtHdr.FileHeader)+ NtHdr.FileHeader.SizeOfOptionalHeader
' // Go thru sections
For iSec =0To NtHdr.FileHeader.NumberOfSections -1
' // Copy section descriptor
tCopyMemory IntPtr(sec.SectionName(0)), pData,Len(sec)
' // Alloc memory for section
lpSec = tVirtualAlloc(sec.VirtualAddress + pBase, sec.VirtualSize, MEM_COMMIT, PAGE_READWRITE)
If lpSec =0Then
ProcessSectionsAndHeaders = EM_UNABLE_TO_ALLOCATE_MEMORY
ExitFunction
EndIf
' If there is initialized data
If sec.SizeOfRawData Then
' // Take into account file alignment
If sec.SizeOfRawData > sec.VirtualSize Then sec.SizeOfRawData = sec.VirtualSize
' // Copy initialized data to section
tCopyMemory lpSec, pRawExeData + sec.PointerToRawData, sec.SizeOfRawData
lpSec = lpSec + sec.SizeOfRawData
sec.VirtualSize = sec.VirtualSize - sec.SizeOfRawData
EndIf
' // Fill remain part with zero
tFillMemory lpSec, sec.VirtualSize,0
' // Next section
pData = pData +Len(sec)
Next
EndFunction

Then the LoadExeFromMemory function calls the UpdateNewBaseAddress function that update the new base address in the user-mode system structures. Windows creates the special stucture named PEB (Process Environment Block) for each process. This is the very usefull structure that allows to obtain the very many information about the process. Many API functions gets information from this structure. For example GetModuleHandle(NULL) takes the returned value from the PEB.ImageBaseAddress or GetModuleHandle("MyExename") takes the returned value from the PEB.Ldr list of the loaded modules. We should update this information according the new base address in order to API functions retrieve the correct values. The small part of PEB structure is shown below:

Type PEB
NotUsed AsLong
Mutant AsLong
ImageBaseAddressAs Long
LoaderData AsLong' // Pointer to PEB_LDR_DATA
ProcessParametersAs Long
' // ....
End Type

We are interested only the 'ImageBaseAddress' and 'LoaderData' fields. The first field contains the base address of an exe file. The second field contains the pointer to the PEB_LDR_DATA structure that describes all the loaded modules in the process:

Type PEB_LDR_DATA
Length AsLong
Initialized AsLong
SsHandleAs Long
InLoadOrderModuleListAs LIST_ENTRY
InMemoryOrderModuleList As LIST_ENTRY
InInitializationOrderModuleList As LIST_ENTRY
End Type

This structure contains the three doubly-linked lists that describe each module. The 'InLoadOrderModuleList' list contains the links to items in the loading oreder item, i.e. the items in this list is placed in loading order (the first module is at beginning). The 'InMemoryOrderModuleList' is same only in order of placing in memory, 'InInitializationOrderModuleList' in initialization order. We should get the first element of 'InLoadOrderModuleList' list that is the pointer to structure LDR_MODULE:

Type LDR_MODULE
InLoadOrderModuleListAs LIST_ENTRY
InMemoryOrderModuleList As LIST_ENTRY
InInitOrderModuleListAs LIST_ENTRY
BaseAddress AsLong
EntryPoint AsLong
SizeOfImage AsLong
FullDllName As UNICODE_STRING
BaseDllName As UNICODE_STRING
FlagsAs Long
LoadCountAs Integer
TlsIndexAs Integer
HashTableEntry As LIST_ENTRY
TimeDateStampAs Long
End Type

This structure describes an module. The first element of 'InLoadOrderModuleList' is the main exe module descriptor. We should change the 'BaseAddress' field to new value and save changes. So, in order to obtain the PEB structure we can use the universal function NtQueryInformationProcess that extract the many useful information about process (read more in 'Windows NT/2000 Native API Reference' by Gary Nebbett). The PEB structure can be obtained from the PROCESS_BASIC_INFORMATION structure that describes the basic information about the process:

Type PROCESS_BASIC_INFORMATION
ExitStatus AsLong
PebBaseAddress AsLong
AffinityMaskAs Long
BasePriorityAs Long
UniqueProcessId AsLong
InheritedFromUniqueProcessIdAs Long
End Type

The 'PebBaseAddress' field contains the address of the PEB structure.
In order to obtain the PROCESS_BASIC_INFORMATION structure we should pass the ProcessBasicInformation as the class information to NtQueryInformationProcess function. Because of structure size may change in various versions of Windows i use the heap memory for extracting the PROCESS_BASIC_INFORMATION structure. If the size doesn't suit it increases the size and repeats again:
Function UpdateNewBaseAddress(_
ByVal pBase AsLong)As ERROR_MESSAGES
Dim pPBIAs Long:Dim PBIlen AsLong
Dim PBI As PROCESS_BASIC_INFORMATION:Dim cPEBAs PEB
Dim ntstat AsLong
Dim ldrData As PEB_LDR_DATA
Dim ldrMod As LDR_MODULE
ntstat = tNtQueryInformationProcess(tGetCurrentProcess(), ProcessBasicInformation, IntPtr(PBI.ExitStatus),Len(PBI), PBIlen)
DoWhile ntstat = STATUS_INFO_LENGTH_MISMATCH
PBIlen = PBIlen *2
If pPBI Then
tHeapFree tGetProcessHeap(), HEAP_NO_SERIALIZE, pPBI
EndIf
pPBI = tHeapAlloc(tGetProcessHeap(), HEAP_NO_SERIALIZE, PBIlen)
ntstat = tNtQueryInformationProcess(tGetCurrentProcess(), ProcessBasicInformation, pPBI, PBIlen, PBIlen)
Loop
If ntstat <> STATUS_SUCCESS Then
UpdateNewBaseAddress = EM_PROCESS_INFORMATION_NOT_FOUND
GoTo CleanUp
EndIf
If pPBI Then
' // Copy to PROCESS_BASIC_INFORMATION
tCopyMemory IntPtr(PBI.ExitStatus), pPBI,Len(PBI)
EndIf
' // Get PEB
tCopyMemory IntPtr(cPEB.NotUsed), PBI.PebBaseAddress,Len(cPEB)
' // Modify image base
cPEB.ImageBaseAddress = pBase
' // Restore PEB
tCopyMemory PBI.PebBaseAddress, IntPtr(cPEB.NotUsed),Len(cPEB)
' // Fix base address in PEB_LDR_DATA list
tCopyMemory IntPtr(ldrData.Length), cPEB.LoaderData,Len(ldrData)
' // Get first element
tCopyMemory IntPtr(ldrMod.InLoadOrderModuleList.Flink), ldrData.InLoadOrderModuleList.Flink,Len(ldrMod)
' // Fix base
ldrMod.BaseAddress = pBase
' // Restore
tCopyMemory ldrData.InLoadOrderModuleList.Flink, IntPtr(ldrMod.InLoadOrderModuleList.Flink),Len(ldrMod)
CleanUp:
' // Free memory
If pPBI Then
tHeapFree tGetProcessHeap(), HEAP_NO_SERIALIZE, pPBI
EndIf
EndFunction

After updating of the base address in the system structures the shellcode calls the ProcessImportTable function that loads the needed libraryes for exe file. Firstly it gets the IMAGE_DIRECTORY_ENTRY_IMPORT directory that contains the RVA of the array of the IMAGE_IMPORT_DESCRIPTOR structures:

Type IMAGE_IMPORT_DESCRIPTOR
Characteristics AsLong
TimeDateStampAs Long
ForwarderChain AsLong
pNameAs Long
FirstThunk AsLong
End Type

Each structure describes the single DLL. The 'pName' field contains the RVA to the ASCIIZ library name. The 'Characteristics' field contains the RVA to the table of the imported function names and 'FirstThunk' contains the RVA of the import addresses table. The names table is the array of IMAGE_THUNK_DATA structures that is the 32 bit Long value. If the most significant bit is set the remaining bits represents the ordinal of the function (import by ordinal). Otherwise the remaining bits contains the RVA of the function name prenexed by 'Hint' value. If the IMAGE_THUNK_DATA structure contains zero it means that no more names. If all the fields of the IMAGE_IMPORT_DESCRIPTOR equal zero it means that list of structureas is ended.

' // Process import table
Function ProcessImportTable(_
ByVal pBase AsLong)As ERROR_MESSAGES
Dim NtHdrAs IMAGE_NT_HEADERS:Dim datDirectoryAs IMAGE_DATA_DIRECTORY
Dim dsc As IMAGE_IMPORT_DESCRIPTOR:Dim hLibAs Long
Dim thnkAs Long:Dim AddrAs Long
Dim fnc AsLong:Dim pDataAs Long
If GetImageNtHeaders(pBase, NtHdr)=0Then
ProcessImportTable = EM_UNABLE_TO_GET_NT_HEADERS
ExitFunction
EndIf
' // Import table processing
If NtHdr.OptionalHeader.NumberOfRvaAndSizes >1Then
If GetDataDirectory(pBase, IMAGE_DIRECTORY_ENTRY_IMPORT, datDirectory)=0Then
ProcessImportTable = EM_INVALID_DATA_DIRECTORY
ExitFunction
EndIf
' // If import table exists
If datDirectory.Size >0 And datDirectory.VirtualAddress >0Then
' // Copy import descriptor
pData = datDirectory.VirtualAddress + pBase
tCopyMemory IntPtr(dsc.Characteristics), pData,Len(dsc)
' // Go thru all descriptors
DoUntil dsc.Characteristics =0 And _
dsc.FirstThunk =0 And _
dsc.ForwarderChain =0 And _
dsc.pName =0 And _
dsc.TimeDateStamp =0
If dsc.pName >0Then
' // Load needed library
hLib = tLoadLibrary(dsc.pName + pBase)
If hLib =0Then
ProcessImportTable = EM_LOADLIBRARY_FAILED
ExitFunction
EndIf
If dsc.Characteristics Then fnc = dsc.Characteristics + pBase Else fnc = dsc.FirstThunk + pBase
' // Go to names table
tCopyMemory IntPtr(thnk), fnc,4
' // Go thru names table
DoWhile thnk
' // Check import type
If thnk <0Then
' // By ordinal
Addr = tGetProcAddress(hLib, thnk And &HFFFF&)
Else
' // By name
Addr = tGetProcAddress(hLib, thnk +2+ pBase)
EndIf
' // Next function
fnc = fnc +4
tCopyMemory IntPtr(thnk), fnc,4
tCopyMemory dsc.FirstThunk + pBase, IntPtr(Addr),4
dsc.FirstThunk = dsc.FirstThunk +4
Loop
EndIf
' // Next descriptor
pData = pData +Len(dsc)
tCopyMemory IntPtr(dsc.Characteristics), pData,Len(dsc)
Loop
EndIf
EndIf

EndFunction

The ProcessRelocation function is called then. This functions adjust all the absolute references (if any). It obtains the IMAGE_DIRECTORY_ENTRY_BASERELOC catalog that contains the RVA to the array of IMAGE_BASE_RELOCATION structures. Each item in this list contains the settings within 4KB relative 'VirtualAddress' fields:

Type IMAGE_BASE_RELOCATION
VirtualAddress AsLong
SizeOfBlock AsLong
End Type

The 'SizeOfBlock' contains the size of item in bytes. The array of 16 bits numbers is placed after the each IMAGE_BASE_RELOCATION structure. You can calculate number of this strucuture as (SizeOfBlock - Len(IMAGE_BASE_RELOCATION)) Len(Integer). Each element of the array of the descriptors has the following structure:


The high four bits contains the type of relocation. We are interested the IMAGE_REL_BASED_HIGHLOW type that means we should add the difference (RealBaseAddress - ImageBaseAddress) to a Long that is at the address 'VirtualAddress' + 12 least bits of descriptors. Array of IMAGE_BASE_RELOCATION structures is ended with stucture where all fields is zero:

' // Process relocations
Function ProcessRelocations(_
ByVal pBase AsLong)As ERROR_MESSAGES
Dim NtHdrAs IMAGE_NT_HEADERS:Dim datDirectoryAs IMAGE_DATA_DIRECTORY
Dim relBase As IMAGE_BASE_RELOCATION:Dim entriesCountAs Long
Dim relType AsLong:Dim dwAddressAs Long
Dim dwOrig AsLong:Dim pRelBaseAs Long
Dim deltaAs Long:Dim pDataAs Long
' // Check if module has not been loaded to image base value
If GetImageNtHeaders(pBase, NtHdr)=0Then
ProcessRelocations = EM_UNABLE_TO_GET_NT_HEADERS
ExitFunction
EndIf
delta = pBase - NtHdr.OptionalHeader.ImageBase
' // Process relocations
If delta Then
' // Get address of relocation table
If GetDataDirectory(pBase, IMAGE_DIRECTORY_ENTRY_BASERELOC, datDirectory)=0Then
ProcessRelocations = EM_INVALID_DATA_DIRECTORY
ExitFunction
EndIf
If datDirectory.Size >0 And datDirectory.VirtualAddress >0Then
' // Copy relocation base
pRelBase = datDirectory.VirtualAddress + pBase
tCopyMemory IntPtr(relBase.VirtualAddress), pRelBase,Len(relBase)
DoWhile relBase.VirtualAddress
' // To first reloc chunk
pData = pRelBase +Len(relBase)
entriesCount =(relBase.SizeOfBlock -Len(relBase))2
DoWhile entriesCount >0
tCopyMemory IntPtr(relType), pData,2
SelectCase(relType 4096) And &HF
Case IMAGE_REL_BASED_HIGHLOW
' // Calculate address
dwAddress = relBase.VirtualAddress +(relType And &HFFF&)+ pBase
' // Get original address
tCopyMemory IntPtr(dwOrig), dwAddress,Len(dwOrig)
' // Add delta
dwOrig = dwOrig + delta
' // Save
tCopyMemory dwAddress, IntPtr(dwOrig),Len(dwOrig)
EndSelect
pData = pData +2
entriesCount = entriesCount -1
Loop
' // Next relocation base
pRelBase = pRelBase + relBase.SizeOfBlock
tCopyMemory IntPtr(relBase.VirtualAddress), pRelBase,Len(relBase)
Loop
EndIf
EndIf
EndFunction

After relocations processing shellcode calls the function SetMemoryPermissions that adjusts the memory protection for each section according to the 'Characteristics' field of IMAGE_SECTION_HEADER structure. It just calls the VirtualProtect function with the certain memory attributes:

' // Set memory permissions
PrivateFunction SetMemoryPermissions(_
ByVal pBase AsLong)As ERROR_MESSAGES
Dim iSecAs Long:Dim pNtHdr AsLong
Dim NtHdrAs IMAGE_NT_HEADERS:Dim sec As IMAGE_SECTION_HEADER
Dim AttrAs MEMPROTECT:Dim pSecAs Long
Dim ret AsLong
pNtHdr = GetImageNtHeaders(pBase, NtHdr)
If pNtHdr =0Then
SetMemoryPermissions = EM_UNABLE_TO_GET_NT_HEADERS
ExitFunction
EndIf
' // Get address of first section header
pSec = pNtHdr +4+Len(NtHdr.FileHeader)+ NtHdr.FileHeader.SizeOfOptionalHeader
' // Go thru section headers
For iSec =0To NtHdr.FileHeader.NumberOfSections -1
' // Copy section descriptor
tCopyMemory IntPtr(sec.SectionName(0)), pSec,Len(sec)
' // Get type
If sec.Characteristics And IMAGE_SCN_MEM_EXECUTE Then
If sec.Characteristics And IMAGE_SCN_MEM_READ Then
If sec.Characteristics And IMAGE_SCN_MEM_WRITE Then
Attr = PAGE_EXECUTE_READWRITE
Else
Attr = PAGE_EXECUTE_READ
EndIf
Else
If sec.Characteristics And IMAGE_SCN_MEM_WRITE Then
Attr = PAGE_EXECUTE_WRITECOPY
Else
Attr = PAGE_EXECUTE
EndIf
EndIf
Else
If sec.Characteristics And IMAGE_SCN_MEM_READ Then
If sec.Characteristics And IMAGE_SCN_MEM_WRITE Then
Attr = PAGE_READWRITE
Else
Attr = PAGE_READONLY
EndIf
Else
If sec.Characteristics And IMAGE_SCN_MEM_WRITE Then
Attr = PAGE_WRITECOPY
Else
Attr = PAGE_NOACCESS
EndIf
EndIf
EndIf
' // Set memory permissions
If tVirtualProtect(sec.VirtualAddress + pBase, sec.VirtualSize, Attr, IntPtr(ret))=0Then
SetMemoryPermissions = EM_UNABLE_TO_PROTECT_MEMORY
ExitFunction
EndIf
' // Next section
pSec = pSec +Len(sec)
Next
EndFunction


Eventually it frees the message table (if any) and calls the entry point of the loaded exe. In the previous version of the loader i unloaded the shellcode too but some exe doesn't call ExitProcess therefore it can causes the crash. The loader has been done.
Although we write the loader without runtime usage the VB6 compiler adds it because all the OBJ files has references to MSVBVM60 during compilation. We have to remove the runtime from the import table of the loader manually. I made the special utility - Patcher that searches runtime in the import table and the bound import table and removes it. This utility is helpful for the VB kernel drivers too. I won't describe the its work because it uses same concepts of the PE format that we already examined. Overall we get the working exe that doesn't use MSVBVM60 runtime on the target machine.

In order to use the loader you should compile it then you should run the patcher and patch compiled loader. Afterwards you can use the compiler.
I hope you enjoyed it. Thank for attention!
Regards,
The trick.


Implementation of advanced mathematical functions for real and complex numbers (by Anatoly aka TheTrick)

$
0
0

'+===============================================================================+
'| |
'| An additional set of mathematical functions for Visual Basic 6 |
'| |
'| Êðèâîóñ Àíàòîëèé Àíàòîëüåâè÷ (The trick) |
'| |
'+===============================================================================+

Private Declare Function GetMem2 Lib "msvbvm60"(pSrc As Any, pDst As Any)AsLong
Private Declare Function GetMem4 Lib "msvbvm60"(pSrc As Any, pDst As Any)AsLong
Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr"(arr()As Any)AsLong

Public Type Complex
R AsDouble
I AsDouble
End Type

Public Type Matrix
Col AsLong' Number of columns
Row AsLong' Number of rows
D()AsDouble
End Type

PublicConst PI =3.14159265358979
PublicConst E =2.71828182845905

PrivateConst PI2 = PI /2

'+=============================================================================+
'| Real numbers |
'+=============================================================================+

' // From degree to radians
PublicFunction Deg(ByVal Value AsDouble)AsDouble
Deg =1.74532925199433E-02* Value
EndFunction

' // The logarithm to the base of a real number X
PublicFunction LogX(ByVal Value AsDouble,ByVal Base AsDouble)AsDouble
LogX =Log(Value)/Log(Base)
EndFunction

' // The decimal logarithm of a real number
PublicFunction Log10(ByVal Value AsDouble)AsDouble
Log10 =Log(Value)/2.30258509299405
EndFunction

' // The binary logarithm of a real number
PublicFunction Log2(ByVal Value AsDouble)AsDouble
Log2 =Log(Value)/0.693147180559945
EndFunction

' // Rounding up
PublicFunction Ceil(ByVal Value AsDouble)AsDouble
Ceil =-Int(-Value)
EndFunction

' // Rounding down (Int)
PublicFunction Floor(ByVal Value AsDouble)AsDouble
Floor =Int(Value)
EndFunction

' // Secant of a real number
PublicFunction Sec(ByVal Value AsDouble)AsDouble
Sec =1/Cos(Value)
EndFunction

' // Cosecant of a real number
PublicFunction Csc(ByVal Value AsDouble)AsDouble
Csc =1/Sin(Value)
EndFunction

' // Cotangent of a real number
PublicFunction Ctg(ByVal Value AsDouble)AsDouble
Ctg =1/Tan(Value)
EndFunction

' // Arcsine of a real number
PublicFunction Asin(ByVal Value AsDouble)AsDouble
If Value =-1Then Asin =-PI2:ExitFunction
If Value =1Then Asin = PI2:ExitFunction
Asin =Atn(Value /Sqr(-Value * Value +1))
EndFunction

' // Arccosine of a real number
PublicFunction Acos(ByVal Value AsDouble)AsDouble
IfCSng(Value)=-1#Then Acos = PI:ExitFunction
IfCSng(Value)=1#Then Acos =0:ExitFunction
Acos =Atn(-Value /Sqr(-Value * Value +1))+2*Atn(1)
EndFunction

' // Arcsecant of a real number
PublicFunction Asec(ByVal Value AsDouble)AsDouble
Asec =1.5707963267949-Atn(Sgn(Value)/Sqr(Value * Value -1))
EndFunction

' // Arccosecant of a real number
PublicFunction Acsc(ByVal Value AsDouble)AsDouble
Acsc =Atn(Sgn(Value)/Sqr(Value * Value -1))
EndFunction

' // Returns the angle whose tangent is the ratio of the two numbers
PublicFunction Atan2(ByVal Y AsDouble,ByVal X AsDouble)AsDouble
If Y >0Then
If X >= Y Then
Atan2 =Atn(Y / X)
ElseIf X <=-Y Then
Atan2 =Atn(Y / X)+ PI
Else
Atan2 = PI /2-Atn(X / Y)
EndIf
Else
If X >=-Y Then
Atan2 =Atn(Y / X)
ElseIf X <= Y Then
Atan2 =Atn(Y / X)- PI
Else
Atan2 =-Atn(X / Y)- PI /2
EndIf
EndIf
EndFunction

' // Arccotangent of a real number
PublicFunction Actg(ByVal Value AsDouble)AsDouble
Actg =1.5707963267949-Atn(Value)
EndFunction

' // Hyperbolic sine of a real number
PublicFunction Sinh(ByVal Value AsDouble)AsDouble
Sinh =(Exp(Value)-Exp(-Value))/2
EndFunction

' // Hyperbolic cosine of a real number
PublicFunction Cosh(ByVal Value AsDouble)AsDouble
Cosh =(Exp(Value)+Exp(-Value))/2
EndFunction

' // Hyperbolic tangent of a real number
PublicFunction Tanh(ByVal Value AsDouble)AsDouble
Tanh =(Exp(2* Value)-1)/(Exp(2* Value)+1)
EndFunction

' // Hyperbolic cotangent of a real number
PublicFunction Ctgh(ByVal Value AsDouble)AsDouble
Ctgh =1/(Exp(2* Value)+1)/(Exp(2* Value)-1)
EndFunction

' // Hyperbolic secant of a real number
PublicFunction Sech(ByVal Value AsDouble)AsDouble
Sech =2/(Exp(Value)+Exp(-Value))
EndFunction

' // Hyperbolic cosecant of a real number
PublicFunction Csch(ByVal Value AsDouble)AsDouble
Csch =2/(Exp(Value)-Exp(-Value))
EndFunction

' // Hyperbolic arcsine of a real number
PublicFunction Asinh(ByVal Value AsDouble)AsDouble
Asinh =Log(Value +Sqr(Value * Value +1))
EndFunction

' // Hyperbolic arcosine of a real number
PublicFunction Acosh(ByVal Value AsDouble)AsDouble
Acosh =Log(Value +Sqr(Value * Value -1))
EndFunction

' // Hyperbolic arctangent of a real number
PublicFunction Atanh(ByVal Value AsDouble)AsDouble
Atanh =Log((1+ Value)/(1- Value))/2
EndFunction

' // Hyperbolic arccotangent of a real number
PublicFunction Actan(ByVal Value AsDouble)AsDouble
Actan =Log((Value +1)/(Value -1))/2
EndFunction

' // Hyperbolic arcsecant of a real number
PublicFunction Asech(ByVal Value AsDouble)AsDouble
Asech =Log((Sqr(-Value * Value +1)+1)/ Value)
EndFunction

' // Hyperbolic arccosecant of a real number
PublicFunction Acsch(ByVal Value AsDouble)AsDouble
Acsch =Log((Sgn(Value)*Sqr(Value * Value +1)+1)/ Value)
EndFunction

' // Return maximum of two numbers
PublicFunction Max(ByVal Op1 AsDouble,ByVal Op2 AsDouble)AsDouble
Max =IIf(Op1 > Op2, Op1, Op2)
EndFunction

' // Return maximum of three numbers
PublicFunction Max3(ByVal Op1 AsDouble,ByVal Op2 AsDouble,ByVal Op3 AsDouble)AsDouble
Max3 =IIf(Op1 > Op2,IIf(Op1 > Op3, Op1, Op3),IIf(Op2 > Op3, Op2, Op3))
EndFunction

' // Return maximum of four numbers
PublicFunction Max4(ByVal Op1 AsDouble,ByVal Op2 AsDouble,ByVal Op3 AsDouble,ByVal Op4 AsDouble)AsDouble
Max4 = Max(Max3(Op1, Op2, Op3), Op4)
EndFunction

' // Return minimum of two numbers
PublicFunction Min(ByVal Op1 AsDouble,ByVal Op2 AsDouble)AsDouble
Min =IIf(Op1 < Op2, Op1, Op2)
EndFunction

' // Return minimum of three numbers
PublicFunction Min3(ByVal Op1 AsDouble,ByVal Op2 AsDouble,ByVal Op3 AsDouble)AsDouble
Min3 =IIf(Op1 < Op2,IIf(Op1 < Op3, Op1, Op3),IIf(Op2 < Op3, Op2, Op3))
EndFunction

' // Return minimum of four numbers
PublicFunction Min4(ByVal Op1 AsDouble,ByVal Op2 AsDouble,ByVal Op3 AsDouble,ByVal Op4 AsDouble)AsDouble
Min4 = Min(Min3(Op1, Op2, Op3), Op4)
EndFunction

' // Returns the remainder of dividing one specified number by another specified number.
PublicFunction IEEERemainder(ByVal Op1 AsDouble,ByVal Op2 AsDouble)AsDouble
IEEERemainder = Op1 -(Op2 * Round(Op1 / Op2))
EndFunction

' // Returns the remainder of dividing one specified number by another specified number.
PublicFunction rMod(ByVal Op1 AsDouble,ByVal Op2 AsDouble)AsDouble
rMod =(Abs(Op1)-(Abs(Op2)*(Int(Abs(Op1)/Abs(Op2)))))*Sgn(Op1)
EndFunction

'+==============================================================================+
'| Complex numbers |
'+==============================================================================+

' // R = 1, I = 0
PublicFunction cxOne()As Complex
cxOne.R =1
EndFunction

' // R = 0, I = 1
PublicFunction cxImgOne()As Complex
cxOne.I =1
EndFunction

' // R = 0, I = 0
PublicFunction cxZero()As Complex
EndFunction

' // Creating a new complex number
PublicFunction cxNew(ByVal Real AsDouble,ByVal Imaginary AsDouble)As Complex
cxNew.R = Real: cxNew.I = Imaginary
EndFunction

' // Creating a new complex number by polar coordinates
PublicFunction cxPolar(ByVal Magnitude AsDouble,ByVal Phase AsDouble)As Complex
cxPolar.R = Magnitude *Cos(Phase): cxPolar.I = Magnitude *Sin(Phase)
EndFunction

' // Return the additive inverse of a specified complex number
PublicFunction cxNeg(Op As Complex)As Complex
cxNeg.R =-Op.R: cxNeg.I =-Op.I
EndFunction

' // Return the inverse value of a complex number
PublicFunction cxInv(Op As Complex)As Complex
Dim Ab2 AsDouble
Ab2 = Op.R * Op.R + Op.I * Op.I
cxInv.R = Op.R / Ab2: cxInv.I =-Op.I / Ab2
EndFunction

' // Addition of two complex numbers
PublicFunction cxAdd(Op1 As Complex, Op2 As Complex)As Complex
cxAdd.R = Op1.R + Op2.R
cxAdd.I = Op1.I + Op2.I
EndFunction

' // Subtraction of two complex numbers
PublicFunction cxSub(Op1 As Complex, Op2 As Complex)As Complex
cxSub.R = Op1.R - Op2.R
cxSub.I = Op1.I - Op2.I
EndFunction

' // Multiplication of two complex numbers
PublicFunction cxMul(Op1 As Complex, Op2 As Complex)As Complex
cxMul.R = Op1.R * Op2.R - Op1.I * Op2.I
cxMul.I = Op1.R * Op2.I + Op1.I * Op2.R
EndFunction

' // Division of two complex numbers
PublicFunction cxDiv(Op1 As Complex, Op2 As Complex)As Complex
Dim R2 AsDouble, i2 AsDouble
R2 = Op2.R * Op2.R: i2 = Op2.I * Op2.I
cxDiv.R =(Op1.R * Op2.R + Op1.I * Op2.I)/(R2 + i2)
cxDiv.I =(Op1.I * Op2.R - Op1.R * Op2.I)/(R2 + i2)
EndFunction

' // Exponentiation of a complex number
PublicFunction cxDgr(Op As Complex,ByVal Degree AsLong)As Complex
Dim Md AsDouble, Ar AsDouble
Md = cxMod(Op): Ar = cxArg(Op): Md = Md ^ Degree: Ar = Ar * Degree
cxDgr.R = Md *Cos(Ar): cxDgr.I = Md *Sin(Ar)
EndFunction

' // The square root of a complex number
PublicFunction cxSqr(Op As Complex)As Complex
Dim M AsDouble, A AsDouble
M =Sqr(cxMod(Op)): A = cxArg(Op)/2
cxSqr.R = M *Cos(A): cxSqr.I = M *Sin(A)
EndFunction

' // Module of a complex number
PublicFunction cxMod(Op As Complex)AsDouble
Dim R2 AsDouble, i2 AsDouble
R2 = Op.R * Op.R: i2 = Op.I * Op.I
cxMod =Sqr(R2 + i2)
EndFunction

' // Phase of a complex number
PublicFunction cxPhase(Op As Complex)AsDouble
cxPhase = Atan2(Op.I, Op.R)
EndFunction

' // Argument of a complex number (equal phase)
PublicFunction cxArg(Op As Complex)AsDouble
If Op.I =0Then
If Op.R >=0Then cxArg =0Else cxArg = PI
ElseIf Op.R =0Then
If Op.I >=0Then cxArg = PI2 Else cxArg =-PI2
Else
If Op.R >0Then
cxArg =Atn(Op.I / Op.R)
ElseIf Op.R <0 And Op.I >0Then
cxArg = PI +Atn(Op.I / Op.R)
ElseIf Op.R <0 And Op.I <0Then
cxArg =-PI +Atn(Op.I / Op.R)
EndIf
EndIf
EndFunction

' // Returns the number e, raised to power by complex number
PublicFunction cxExp(Op As Complex)As Complex
cxExp.R =Exp(Op.R)*Cos(Op.I): cxExp.I =Exp(Op.R)*Sin(Op.I)
EndFunction

' // Addition real number and complex number
PublicFunction cxAddReal(Op1 As Complex,ByVal Op2 AsDouble)As Complex
cxAddReal.R = Op1.R + Op2
cxAddReal.I = Op1.I
EndFunction

' // Subtraction from complex number a real number
PublicFunction cxSubReal(Op1 As Complex,ByVal Op2 AsDouble)As Complex
cxSubReal.R = Op1.R - Op2
cxSubReal.I = Op1.I
EndFunction

' // Subtraction from real number a complex number
PublicFunction cxRealSub(ByVal Op1 AsDouble, Op2 As Complex)As Complex
cxRealSub.R = Op1 - Op2.R
cxRealSub.I =-Op2.I
EndFunction

' // Multiplication complex number on a real number
PublicFunction cxMulReal(Op1 As Complex,ByVal Op2 AsDouble)As Complex
cxMulReal.R = Op1.R * Op2
cxMulReal.I = Op1.I * Op2
EndFunction

' // Division a complex number on a real number
PublicFunction cxDivReal(Op1 As Complex,ByVal Op2 AsDouble)As Complex
Dim R2 AsDouble
R2 = Op2 * Op2
cxDivReal.R =(Op1.R * Op2)/ R2
cxDivReal.I =(Op1.I * Op2)/ R2
EndFunction

' // Division a real number on a complex number
PublicFunction cxRealDiv(ByVal Op1 AsDouble, Op2 As Complex)As Complex
Dim R2 AsDouble, i2 AsDouble
R2 = Op2.R * Op2.R: i2 = Op2.I * Op2.I
cxRealDiv.R =(Op1 * Op2.R)/(R2 + i2)
cxRealDiv.I =(-Op1 * Op2.I)/(R2 + i2)
EndFunction

' // Addition of a complex number and imaginary part
PublicFunction cxAddImg(Op1 As Complex,ByVal Op2 AsDouble)As Complex
cxAddImg.R = Op1.R
cxAddImg.I = Op1.I + Op2
EndFunction

' // Subtraction from a complex number a imaginary part
PublicFunction cxSubImg(ByVal Op1 As Complex, Op2 AsDouble)As Complex
cxSubImg.R = Op1.R
cxSubImg.I = Op1.I - Op2
EndFunction

' // Subtraction from imaginary part a complex number
PublicFunction cxImgSub(ByVal Op1 AsDouble, Op2 As Complex)As Complex
cxImgSub.R =-Op2.R
cxImgSub.I = Op1 - Op2.I
EndFunction

' // Multiplication complex number on a imaginary part
PublicFunction cxMulImg(Op1 As Complex,ByVal Op2 AsDouble)As Complex
cxMulImg.R =-Op1.I * Op2
cxMulImg.I = Op1.R * Op2
EndFunction

' // Division a complex number on a imaginary part
PublicFunction cxDivImg(Op1 As Complex,ByVal Op2 AsDouble)As Complex
Dim i2 AsDouble
i2 = Op2 * Op2
cxDivImg.R =(Op1.I * Op2)/ i2
cxDivImg.I =(-Op1.R * Op2)/ i2
EndFunction

' // Division imaginary part on a complex number
PublicFunction cxImgDiv(ByVal Op1 AsDouble, Op2 As Complex)As Complex
Dim R2 AsDouble, i2 AsDouble
R2 = Op2.R * Op2.R: i2 = Op2.I * Op2.I
cxImgDiv.R =(Op1 * Op2.I)/(R2 + i2)
cxImgDiv.I =(Op1 * Op2.R)/(R2 + i2)
EndFunction

' // Return true if complex number is equal
PublicFunction cxEq(Op1 As Complex, Op2 As Complex,_
Optional NumDigitsAfterDecimal AsLong=-1)AsBoolean
If NumDigitsAfterDecimal =-1Then
If Op1.R = Op2.R And Op1.I = Op2.I Then cxEq =True
Else
If Round(Op1.R, NumDigitsAfterDecimal)= Round(Op2.R, NumDigitsAfterDecimal) And _
Round(Op1.I, NumDigitsAfterDecimal)= Round(Op2.I, NumDigitsAfterDecimal)Then cxEq =True
EndIf
EndFunction

' // Return absolute value of a complex number
PublicFunction cxAbs(Op As Complex)AsDouble
If Op.I =0Then
cxAbs =0
ElseIf Op.R > Op.I Then
cxAbs =Sqr(1+(Op.I * Op.I)/(Op.R * Op.R))
ElseIf Op.R <= Op.I Then
cxAbs =Sqr(1+(Op.R * Op.R)/(Op.I * Op.I))
EndIf
EndFunction

' // Return complex conjugate of complex number
PublicFunction cxConj(Op As Complex)As Complex
cxConj.R = Op.R
cxConj.I =-Op.I
EndFunction

' // The natural logarithm of a complex number
PublicFunction cxLog(Op As Complex)As Complex
Dim M AsDouble, A AsDouble
M = cxMod(Op): A = cxArg(Op)
cxLog.R =Log(M): cxLog.I = A
EndFunction

' // The logarithm of a complex number by base X
PublicFunction cxLogX(Op As Complex,ByVal Base AsDouble)As Complex
Dim M AsDouble, A AsDouble, Nc As Complex
M = cxMod(Op): A = cxArg(Op): Nc.R =Log(Base)
cxLogX.R =Log(M): cxLogX.I = A
cxLogX = cxDiv(cxLogX, Nc)
EndFunction

' // Sine of a complex number
PublicFunction cxSin(Op As Complex)As Complex
cxSin.R =Sin(Op.R)* Cosh(Op.I): cxSin.I =Cos(Op.R)* Sinh(Op.I)
EndFunction

' // Cosine of a complex number
PublicFunction cxCos(Op As Complex)As Complex
cxCos.R =Cos(Op.R)* Cosh(Op.I): cxCos.I =-Sin(Op.R)* Sinh(Op.I)
EndFunction

' // Tangent of a complex number
PublicFunction cxTan(Op As Complex)As Complex
Dim C2 AsDouble, S2 AsDouble
C2 =Cos(Op.R): C2 = C2 * C2: S2 = Sinh(Op.I): S2 = S2 * S2
cxTan.R =(Sin(Op.R)*Cos(Op.R))/(C2 + S2)
cxTan.I =(Sinh(Op.I)* Cosh(Op.I))/(C2 + S2)
EndFunction

' // Cotangent of a complex number
PublicFunction cxCtg(Op As Complex)As Complex
Dim C2 AsDouble, S2 AsDouble
C2 =Sin(Op.R): C2 = C2 * C2: S2 = Sinh(Op.I): S2 = S2 * S2
cxCtg.R =(Sin(Op.R)*Cos(Op.R))/(C2 + S2)
cxCtg.I =-(Sinh(Op.I)* Cosh(Op.I))/(C2 + S2)
EndFunction

' // Secant of a complex number
PublicFunction cxSec(Op As Complex)As Complex
Dim C2 AsDouble, S2 AsDouble
C2 =Cos(Op.R): C2 = C2 * C2: S2 = Sinh(Op.I): S2 = S2 * S2
cxSec.R =(Cos(Op.R)* Cosh(Op.I))/(C2 + S2)
cxSec.I =-(Sin(Op.R)* Sinh(Op.I))/(C2 + S2)
EndFunction

' // Cosecant of a complex number
PublicFunction cxCsc(Op As Complex)As Complex
Dim C2 AsDouble, S2 AsDouble
C2 =Sin(Op.R): C2 = C2 * C2: S2 = Sinh(Op.I): S2 = S2 * S2
cxCsc.R =(Sin(Op.R)* Cosh(Op.I))/(C2 + S2)
cxCsc.I =(Cos(Op.R)* Sinh(Op.I))/(C2 + S2)
EndFunction

' // Arcsine of a complex number
PublicFunction cxAsin(Op As Complex)As Complex
cxAsin = cxMulImg(cxLog(cxAdd(cxMulImg(Op,1), cxSqr(cxRealSub(1, cxMul(Op, Op))))),-1)
EndFunction

' // Arccosine of a complex number
PublicFunction cxAcos(Op As Complex)As Complex
cxAcos = cxAddReal(cxMulImg(cxLog(cxAdd(cxMulImg(Op,1), cxSqr(cxRealSub(1, cxMul(Op, Op))))),1), PI2)
EndFunction

' // Arctangent of a complex number
PublicFunction cxAtan(Op As Complex)As Complex
Dim Iz As Complex
Iz = cxMulImg(Op,1)
cxAtan = cxMulImg(cxSub(cxLog(cxRealSub(1, Iz)), cxLog(cxAddReal(Iz,1))),0.5)
EndFunction

' // Arccotangent of a complex number
PublicFunction cxActg(Op As Complex)As Complex
cxActg = cxMulImg(cxSub(cxLog(cxDiv(cxSubImg(Op,1), Op)), cxLog(cxDiv(cxAddImg(Op,1), Op))),0.5)
EndFunction

' // Arcsecant of a complex number
PublicFunction cxAsec(Op As Complex)As Complex
cxAsec = cxAcos(cxDgr(Op,-1))
EndFunction

' // Arccosecant of a complex number
PublicFunction cxAcsc(Op As Complex)As Complex
cxAcsc = cxAsin(cxDgr(Op,-1))
EndFunction

' // Hyperbolic sine of a complex number
PublicFunction cxSinh(Op As Complex)As Complex
cxSinh = cxMulImg(cxSin(cxMulImg(Op,1)),-1)
EndFunction

' // Hyperbolic cosine of a complex number
PublicFunction cxCosh(Op As Complex)As Complex
cxCosh = cxCos(cxMulImg(Op,1))
EndFunction

' // Hyperbolic tangent of a complex number
PublicFunction cxTanh(Op As Complex)As Complex
cxTanh = cxMulImg(cxTan(cxMulImg(Op,1)),-1)
EndFunction

' // Hyperbolic cotangent of a complex number
PublicFunction cxCtgh(Op As Complex)As Complex
cxCtgh = cxRealDiv(1, cxTanh(Op))
EndFunction

' // Hyperbolic secant of a complex number
PublicFunction cxSech(Op As Complex)As Complex
cxSech = cxRealDiv(1, cxCosh(Op))
EndFunction

' // Hyperbolic cosecant of a complex number
PublicFunction cxCsch(Op As Complex)As Complex
cxCsch = cxRealDiv(1, cxSinh(Op))
EndFunction

' // Hyperbolic arcsine of a complex number
PublicFunction cxAsinh(Op As Complex)As Complex
cxAsinh = cxLog(cxAdd(Op, cxSqr(cxAddReal(cxMul(Op, Op),1))))
EndFunction

' // Hyperbolic arccosine of a complex number
PublicFunction cxAcosh(Op As Complex)As Complex
cxAcosh = cxLog(cxAdd(Op, cxMul(cxSqr(cxAddReal(Op,1)), cxSqr(cxSubReal(Op,1)))))
EndFunction

' // Hyperbolic arctangent of a complex number
PublicFunction cxAtanh(Op As Complex)As Complex
cxAtanh = cxMulReal(cxLog(cxDiv(cxAddReal(Op,1), cxRealSub(1, Op))),0.5)
EndFunction

' // Hyperbolic arccotangent of a complex number
PublicFunction cxActgh(Op As Complex)As Complex
cxActgh = cxMulReal(cxLog(cxDiv(cxAddReal(Op,1), cxSubReal(Op,1))),0.5)
EndFunction

' // Hyperbolic arcsecant of a complex number
PublicFunction cxAsech(Op As Complex)As Complex
Dim Z As Complex
Z = cxRealDiv(1, Op)
cxAsech = cxLog(cxAdd(Z, cxSqr(cxAddReal(cxMul(Z, Z),1))))
EndFunction

' // Hyperbolic arccosecant of a complex number
PublicFunction cxAcsch(Op As Complex)As Complex
Dim Z As Complex
Z = cxRealDiv(1, Op)
cxAcsch = cxLog(cxAdd(Z, cxMul(cxSqr(cxAddReal(Z,1)), cxSqr(cxSubReal(Z,1)))))
EndFunction

' // Print matrix to immediate window
PublicFunction PrintMtrx(Op As Matrix)
Dim Ts AsString, I AsLong, j AsLong
Debug.Print vbNewLine
Debug.Print"Col= "& Op.Col &" ; Row= "& Op.Row
For I =0To Op.Row -1:For j =0To Op.Col -1
Ts =Space(10)
LSet Ts =Str(Round(Op.D(I * Op.Col + j),3))
Debug.Print Ts;
Next: Debug.Print vbNewLine;:Next
EndFunction

' // Creating a matrix
PublicFunction mxCreate(ByVal Row AsLong,ByVal Col AsLong, ParamArray Y())As Matrix
Dim P AsVariant, C AsLong
If Row <=0 Or Col <=0ThenExitFunction
If Row * Col <UBound(Y)+1ThenExitFunction
mxCreate.Row = Row: mxCreate.Col = Col
ReDim mxCreate.D(Row * Col -1): C =0
ForEach P In Y
mxCreate.D(C)= P: C = C +1
Next
EndFunction

' // Creating the null-matrix
PublicFunction mxNull(ByVal Row AsLong,ByVal Col AsLong)As Matrix
If Row <=0 Or Col <=0ThenExitFunction
ReDim mxNull.D(Row * Col -1): mxNull.Col = Col: mxNull.Row = Row
EndFunction

' // Creating the identity matrix
PublicFunction mxIdt(ByVal Size AsLong)As Matrix
Dim ij AsLong
If Size <=0ThenExitFunction
ReDim mxIdt.D(Size * Size -1): mxIdt.Col = Size: mxIdt.Row = Size
For ij =0To Size -1: mxIdt.D(ij +(ij * Size))=1:Next
EndFunction

' // Transpose matrix
PublicFunction mxTrans(Op As Matrix)As Matrix
Dim I AsLong, j AsLong, P AsLong
GetMem4 ByVal ArrPtr(Op.D), P:If P =0ThenExitFunction
mxTrans.Row = Op.Col: mxTrans.Col = Op.Row:ReDim mxTrans.D(UBound(Op.D))
For j =0To mxTrans.Col -1:For I =0To mxTrans.Row -1
mxTrans.D(I + j * mxTrans.Row)= Op.D(j + I * Op.Row)
Next:Next
EndFunction

' // Multiplication matrix on a real number
PublicFunction mxMulReal(Op As Matrix, Op2 AsDouble)As Matrix
Dim P AsLong, ij AsLong
GetMem4 ByVal ArrPtr(Op.D), P:If P =0ThenExitFunction
ReDim mxMulReal.D(UBound(Op.D)): mxMulReal.Col = Op.Col: mxMulReal.Row = Op.Row
For ij =0ToUBound(Op.D): mxMulReal.D(ij)= Op.D(ij)* Op2:Next
EndFunction

' // Addition of a two matrix
PublicFunction mxAdd(Op1 As Matrix, Op2 As Matrix)As Matrix
Dim P AsLong, ij AsLong
GetMem4 ByVal ArrPtr(Op1.D), P:If P =0ThenExitFunction
GetMem4 ByVal ArrPtr(Op2.D), P:If P =0ThenExitFunction
If Op1.Col <> Op2.Col Or Op1.Row <> Op2.Row ThenExitFunction
ReDim mxAdd.D(UBound(Op1.D)): mxAdd.Col = Op1.Col: mxAdd.Row = Op1.Row
For ij =0ToUBound(Op1.D): mxAdd.D(ij)= Op1.D(ij)+ Op2.D(ij):Next
EndFunction

' // Subtaction of a two matrix
PublicFunction mxSub(Op1 As Matrix, Op2 As Matrix)As Matrix
Dim P AsLong, ij AsLong
GetMem4 ByVal ArrPtr(Op1.D), P:If P =0ThenExitFunction
GetMem4 ByVal ArrPtr(Op2.D), P:If P =0ThenExitFunction
If Op1.Col <> Op2.Col Or Op1.Row <> Op2.Row ThenExitFunction
ReDim mxSub.D(UBound(Op1.D)): mxSub.Col = Op1.Col: mxSub.Row = Op1.Row
For ij =0ToUBound(Op1.D): mxSub.D(ij)= Op1.D(ij)- Op2.D(ij):Next
EndFunction

' // Multiplication of a two matrix
PublicFunction mxMul(Op1 As Matrix, Op2 As Matrix)As Matrix
Dim P AsLong, I AsLong, j AsLong, k AsLong, iM AsLong, i1 AsLong, i2 AsLong
GetMem4 ByVal ArrPtr(Op1.D), P:If P =0ThenExitFunction
GetMem4 ByVal ArrPtr(Op2.D), P:If P =0ThenExitFunction
If Op1.Col <> Op2.Row ThenExitFunction
ReDim mxMul.D(Op1.Row * Op2.Col -1): mxMul.Col = Op2.Col: mxMul.Row = Op1.Row
' For i = 0 To Op1.Row - 1: For j = 0 To Op2.Col - 1: mxMul.D(i * Op2.Col + j) = 0
' For k = 0 To Op1.Col - 1
' mxMul.D(i * mxMul.Col + j) = mxMul.D(i * mxMul.Col + j) + Op1.D(i * Op1.Col + k) * Op2.D(k * Op2.Col + j)
' Next
' Next: Next
For I =0To Op1.Row -1
For j =0To Op2.Col -1
i2 = j
For k =0To Op1.Col -1
mxMul.D(iM)= mxMul.D(iM)+ Op1.D(i1 + k)* Op2.D(i2)
i2 = i2 + Op2.Col
Next
iM = iM +1
Next
i1 = i1 + Op1.Col
Next
EndFunction

' // Determinant of a square matrix
PublicFunction mxDtm(Op As Matrix)AsDouble
Dim P1 AsLong, P2 AsLong, ij1 AsLong, ij2 AsLong, Ct AsLong, L AsLong, T1 AsDouble, T2 AsDouble
GetMem4 ByVal ArrPtr(Op.D), P1:If P1 =0ThenExitFunction
If Op.Col <> Op.Row ThenExitFunction
P2 = Op.Col -1: ij1 =0: ij2 = P2: Ct = Op.Col * Op.Row: P1 = Op.Col +1
For k =0To Op.Col -1
T1 = Op.D(ij1): T2 = Op.D(ij2)
For L =1To Op.Col -1
ij1 =(ij1 + P1) Mod Ct: ij2 =(ij2 + P2) Mod Ct
T1 = T1 * Op.D(ij1): T2 = T2 * Op.D(ij2)
Next
mxDtm = mxDtm + T1 - T2: ij1 =(ij1 + P1) Mod Ct: ij2 =(ij2 + P2) Mod Ct
Next
EndFunction

Source:
http://www.vbforums.com/showthread.php?789035-VB6-Module-with-advanced-mathematical-functions-for-real-and-complex-numbers






Needleman–Wunsch algorithm & Smith–Waterman algorithm (by Eugen Schülter)

$
0
0
BSA - Behind Sequence Alignment

This little program shows in 'real time' the effect of some parameters in sequence alignment functions. Both usual algorithms, Needleman-Wunsch (global alignment) and Smith-Waterman (local alignment) are supported. Not meaningful values for e.g. gap penalty etc. are wittingly accepted.

Useful to demonstrate principles of dynamic programming such as trace back.

If you have an old PC, you might need the visual basic 6.0 run time environment (available at http://support.microsoft.com/kb/192461/en).

Download from ME

Download from SOURCE




Needleman–Wunsch algorithm


PrivateSub Needle()
Dim i, j, leA, leB
Dim iMatch, iGap, iExtend, iG
Dim ds, us, ls AsLong
Dim sA, sB

cmRun.Enabled =False
cmClear.Enabled =False
If tbMatch =""Then tbMatch =0
iMatch = tbMatch.Text
If tbMismatch =""Then tbMismatch =0
iMis = tbMismatch.Text
If tbGap =""Then tbGap =0
iGap = tbGap.Text
If tbExtend =""Then tbExtend =0
iExtend = tbExtend.Text
leA =Len(tbA)
leB =Len(tbB)
If tbDelay =""Then tbDelay =0
tm1.Interval =CLng(tbDelay)+1



'--- init ---
ns(0,0)=0
For j =1To leA
ns(0, j)= j * iMis
np(0, j)= LE
F(map(0, j))= ns(0, j)
F(map(0, j)).BackColor =&HF0F4F4
For i = leB +1To24
F(map(i, j)).BackColor = cc
F(map(i, j)).Caption =""
Next i
Next j
For j = leA +1To26
F(map(0, j))=""
Next j
For i =1To leB
ns(i,0)= i * iMis
np(i,0)= UP
F(map(i,0))= ns(i,0)
F(map(i,0)).BackColor =&HF0F4F4
For j = leA +1To26
F(map(i, j)).BackColor = cc
F(map(i, j)).Caption =""
Next j
Next i
For i = leB +1To24
F(map(i,0))=""
For j = leA +1To26
F(map(i, j)).BackColor = cc
F(map(i, j)).Caption =""
Next j
Next i

'--- fill ---
For i =1To leB
For j =1To leA
If tm1.Interval >1Then
tm1.Enabled =True
While tm1.Enabled =True
DoEvents
Wend
EndIf
iG = iGap
If np(i -1, j)= UP Or np(i, j -1)= LE Then iG = iExtend
If gRedGapA =1 And (j =1 Or j = leA)Then iG = iMis
If gRedGapB =1 And (i =1 Or i = leB)Then iG = iMis
ds = ns(i -1, j -1)+IIf(Mid$(tbA, j,1)=Mid$(tbB, i,1), iMatch, iMis)
us = ns(i -1, j)+ iG
ls = ns(i, j -1)+ iG
If ds <= us Then
If ds <= ls Then
ns(i, j)= ds
np(i, j)= DIAG
Else
ns(i, j)= ls
np(i, j)= LE
EndIf
Else
If us < ls Then
ns(i, j)= us
np(i, j)= UP
Else
ns(i, j)= ls
np(i, j)= LE
EndIf
EndIf
Call Display(i, j)
F(map(i, j)).FontBold = fClear And IIf(ckPointers.Value,(np(i, j)<> op(i, j)),(ns(i, j)<> os(i, j)))
Next j
Next i

'--- traceback ---
j = leA
i = leB
sA =""
sB =""
iG = gSearchMin
While i >0 Or j >0
F(map(i, j)).BackColor =&H40C080
If np(i, j)= DIAG Then
If iG =1Then
While(ns(i, j -1)<= ns(i, j)) And (np(i, j -1)<> UP)
sB ="-"& sB
sA = lsa(j)& sA
j = j -1
F(map(i, j)).BackColor =&H40C080
Wend
iG =0
Else
sB = lsb(i)& sB
sA = lsa(j)& sA
j = j -1
i = i -1
EndIf
ElseIf np(i, j)= UP Then
sA ="-"& sA
sB = lsb(i)& sB
i = i -1
Else
sB ="-"& sB
sA = lsa(j)& sA
j = j -1
EndIf
Wend
laRes = sA &Chr$(10)& sB

For i =0To leB
For j =0To leA
os(i, j)= ns(i, j)
op(i, j)= np(i, j)
Next j, i
cmRun.Enabled =True
cmClear.Enabled =True
fClear =True
EndSub


Smith–Waterman algorithm


PrivateSub Watermann()
Dim i, j, leA, leB, iMax, jMax, maxScore
Dim iMatch, iGap, iExtend, iG
Dim ds, us, ls AsLong
Dim sA, sB

cmRun.Enabled =False
cmClear.Enabled =False
iMatch = tbMatch.Text
iMis = tbMismatch.Text
iGap = tbGap.Text
iExtend = tbExtend.Text
leA =Len(tbA)
leB =Len(tbB)
tm1.Interval =CLng(tbDelay)+1



'--- init ---
ns(0,0)=0
iMax =0: jMax =0: maxScore =0
For j =1To leA
ns(0, j)=0
np(0, j)= LE
F(map(0, j))= ns(0, j)
F(map(0, j)).BackColor =&HF0F4F4
For i = leB +1To24
F(map(i, j)).BackColor = cc
F(map(i, j)).Caption =""
Next i
Next j
For j = leA +1To26
F(map(0, j))=""
Next j
For i =1To leB
ns(i,0)=0
np(i,0)= UP
F(map(i,0))= ns(i,0)
F(map(i,0)).BackColor =&HF0F4F4
For j = leA +1To26
F(map(i, j)).BackColor = cc
F(map(i, j)).Caption =""
Next j
Next i
For i = leB +1To24
F(map(i,0))=""
For j = leA +1To26
F(map(i, j)).BackColor = cc
F(map(i, j)).Caption =""
Next j
Next i

'--- fill ---
For i =1To leB
For j =1To leA
If tm1.Interval >1Then
tm1.Enabled =True
While tm1.Enabled =True
DoEvents
Wend
EndIf
iG = iGap
If np(i -1, j)= UP Or np(i, j -1)= LE Then iG = iExtend
If gRedGapA =1 And (j =1 Or j = leA)Then iG = iMis
If gRedGapB =1 And (i =1 Or i = leB)Then iG = iMis
ds = ns(i -1, j -1)+IIf(Mid$(tbA, j,1)=Mid$(tbB, i,1), iMatch, iMis)
us = ns(i -1, j)+ iG
ls = ns(i, j -1)+ iG
If ds >= us Then
If ds >= ls Then
ns(i, j)= ds
np(i, j)= DIAG
Else
ns(i, j)= ls
np(i, j)= LE
EndIf
Else
If us > ls Then
ns(i, j)= us
np(i, j)= UP
Else
ns(i, j)= ls
np(i, j)= LE
EndIf
EndIf
If ns(i, j)>= maxScore Then
maxScore = ns(i, j)
iMax = i: jMax = j
EndIf
Call Display(i, j)
F(map(i, j)).FontBold = fClear And IIf(ckPointers.Value,(np(i, j)<> op(i, j)),(ns(i, j)<> os(i, j)))
Next j
Next i

'--- traceback ---
j = jMax
i = iMax
sA =""
sB =""
While ns(i, j)>0
F(map(i, j)).BackColor =&H40C080
If np(i, j)= DIAG Then
sB = lsb(i)& sB
sA = lsa(j)& sA
j = j -1
i = i -1
ElseIf np(i, j)= UP Then
sA ="-"& sA
sB = lsb(i)& sB
i = i -1
Else
sB ="-"& sB
sA = lsa(j)& sA
j = j -1
EndIf
Wend
laRes = sA &Chr$(10)& sB

For i =0To leB
For j =0To leA
os(i, j)= ns(i, j)
op(i, j)= np(i, j)
Next j, i
cmRun.Enabled =True
cmClear.Enabled =True
fClear =True
EndSub


Source: http://www.schuelter-gm.de/download.html#bsa

Visual Basic 6.0 logo, ico, favicon !

Step by step examples for DirectX9 in VB6: mathematics and graphics (by Mikle)

Matrix Algebra: It is ugly, but is truly advanced ! (by Elroy)

$
0
0
I was going through some of my stuff and thought maybe others could make use of some of it (and I've got tons).

Here's a little matrix (linear) algebra program. It does all the simple stuff as well as some more-or-less higher-level statistics stuff. It'll "solve" a matrix using the Jacobi method, and it also has Varimax, Quartimax, and Equimax rotations in it. I've got some non-orthogonal rotation code somewhere, but I'll have to find it.

Also, this code has a reference in it to "Microsoft Excel 14.0 Object Library" (Excel 2010) because I use Excel as a grid for the matrices. If someone asks, I could probably change this to late-binding so that this reference wouldn't be required (although some version of Excel on the machine would still be required).

Maybe someone can make some use of this stuff.

Also, just as an FYI, something I've always wanted to do in VB6 was to write a more general purpose matrix solution routine (as opposed to the Jacobi routine herein). These days, these routines are typically called SVD (or, Singular Value Decomposition) routines. One of the advantages they have over he Jacobi method is that they'll solve matrices that contain singularities. If someone knows of such a VB6 routine, I'd love to hear about it.

Again, Enjoy,
Elroy

Download from ME
Download fromVBForums


Source:

http://www.vbforums.com/showthread.php?833203-Matrix-(Linear)-Algebra-Program

Class Module clStrings

$
0
0
Class Module clStrings

Download from VBForums
Download from ME

This class module is another in a series of library modules that are designed to work with Visual Basic 6 (VB6) and all versions of Visual Basic for Applications (VBA). The code runs equally well in any of these environments which I will refer to as “VB” instead of VB6 or VBA or VB6/VBA. All routines including those dealing with Windows API calls or file reads/writes are Unicode all the time.

This module has a large set of routines to work with “BigString” which is up to 1,000 times faster than VB’s string functions in some cases. In addition there is a complete set of text and binary file read/write functions that fill a large void in VB’s file handling capabilities, especially for text files.

Overview

This class module contains an enhanced version of the BigString routines (sometimes call StringBuilder) that greatly speed up VB’s string handling especially when dealing with concatenating a large number of strings. This module also has a simple-to-use but comprehensive system for reading/writing text files and binary files (I have done up to 500 MB files in one read). An obvious question is why combine these in one module? When we read a text file it is much more efficient to read from a disk in one read into a large memory buffer and then make individual lines of text out of it. A BigString is very convenient for this. Also, when we write strings to a text file it is more efficient to do the physical write in one call which means the entire set of strings needs to be in one large data buffer, very much like a BigString. Thus it makes sense to me to combine them. I have had earlier versions of these as separate modules but I was almost always using them together so I combined and optimized them.

The BigString set of routines enable you to greatly speed up string operations when there are many changes and concatenations and/or when string lengths exceed a few hundred characters. Normally, VB re-allocates the entire string any time there is any operation on the string to change it. When you use this module, a large string is allocated once and then the subsequent string operations occur within the large string. The difference can be speed increases of up to 1,000 times versus standard string handling using built-in VB functions. If you are dealing with 100 strings o les then it likely is a bit quicker to use VB’s built-in procedures but the BigString concept excels when dealing with thousands of strings.

The file Read/Write functions are as fast as anything you can do in any language. As a VB programmer you are likely painfully aware that even though the language deals with Unicode strings, when text files are read or written, all of the Unicode gets converted to ANSI, causing all sorts of problems. This module totally eliminates that problem. Most of what we do is via Windows API calls (all Unicode) so there is no inherent slowdown due to using Visual Basic. We can read or write text files in UTF-8 (today’s text standard used almost exclusively now for web pages since it efficiently handles all Unicode characters), UTF-16 which by convention has a BOM, ANSI (Default, OEM, CurrentThread or whatever code page you want to use), and UTF-8 with a BOM even though this is discouraged now. You can specify the file type or the routine can auto-detect which one it is.

By the way, a BOM is a Byte Order Mark which is 2 or three bytes of data sometimes used for a UTF-8 file and always for a UTF-16 file to more or less announce what type of format the text will be in. UTF-8 has become so widely used that the use of a BOM is discouraged. If you wish to read more about the different types of text files exist in the Windows world, see any of the following links: UTF-8UTF-16ANSI Code Pages (mid-article for Windows code pages) and here for a Microsoft discussion on the various Windows code pages you can use (if you really need to) when you convert to/from ANSI/Unicode.

I have recently incorporated the ability to read and write binary files. It doesn’t fit with the rest of the module being related to strings but it required very little code beyond that necessary for the text read/write routines so I incorporated the features in this module.

This class module contains many string handling functions to address some VB shortcomings and to extend their capabilities. This module works in total Unicode and works with VB6 and all 32 and 64-bit flavors of VBA. All of the calls to clStrings require regular module mUCCore in order to function. This module contains many string functions on its own that you likely will find useful in addition to a whole host of routines I use every day including file operations, error handling, the operating system and so forth. Below is a list of string-related functions included in both the class module clStrings and module mUCCore.

BigString – String operations are very slow in VB6/VBA because every little change requires the string(s) to be totally reallocated. For a few characters this isn’t bad but it gets very bad when dealing with long strings and files. There is a whole subsystem described later that works around all of this, providing an alternate system to append, insert, search, remove, etc. strings at very high speeds.

DelimGet or set the delimiter string (initially set to vbCrLf). Must be 1 or 2 characters. Defaults to vbCrLf which is standard for Windows text files.
AppendAdd a string onto the end of bigString. Optionally set the starting character in the string to append.
AppendWDelimAdd a string and the Delimiter (initially vbCrLf)
InsertInsert a string into the big string. Tell it what character position to insert ahead of, specify a string & optional start character in that string and whether or not to put the delimiter sequence onto the end of the inserted string.
InsertWDelimSame as Insert above but with the current Delimiter tacked on to the string to insert.
LengthReturn the current length of the string being built (same as normal “Len()”)
RemoveRemove a specified # of characters from a place in the string.
SplitLike normal Split but operates on our bigString. The delimiter is whatever has been set with Delim (default vbCrLf). Specify start/end character positions, Limit sets the number of split strings found. Compare sets how text is searched.
FindFind a sub-string in the big string (equivalent to normal string’s InStr). Specify the string to find, what character to start looking and the compare method.
CapacityReturn current max length of the string with the current “chunks” (it will auto grown for more data)
ChunkSizeGet or set the Unicode character chunk size. The default value is 32,768 characters (65,536 bytes).
GetAStringReturns part or all of the big string. Specify the start and stop character (default to the whole string in the BigString).
bigStringSet the value of the big string starting to be built (to erase set it to "").
GrowWithGarbageLengthen our internal string by a specified # of characters. Useful for later dropping in data from an API call etc. (Advanced).
AppendPtrDataQuicker append using pointer to string and how many characters to append. (Advanced)
InsertPtrDataInsert a string using a pointer to the string (Advanced).
HeapMinimizeShrink the allocated memory for bigString down to a minimum (can still grow after this).
GetToIntCharsCopy part or all of the big string to an integer array.


Below are string functions found in the standard module mUCCore.

SubstStr – Substitute environment variables, drive label convert to drive letters, current time and date into a string.

StringW – Unicode replacement for VB function String$ which only uses chars 1-255.

iPad – Left and right-justify 2 strings over a given width. Good especially for tabular output to the Immediate Window since in both VBA and VB6 it is monospaced (all characters are the same width). I use it a lot for debugging.

AllocString – Makes a string containing a certain number of characters. Faster than Space$ for strings longer than about 400 characters. Not of much use by itself but it does provide a nice buffer for return string buffers from Windows API calls.

For those of you who dive deeper into programming than normal VB6/VBA coding, the following are Public procedures that deal with strings and text using pointers (although as we all know, nobody using VB6/VBA knows anything about pointers…). These procedures are extremely useful especially when dealing with many Windows API calls. If you don’t use pointers and memory buffers then you can ignore the below procedures. They are used internally in many of my other procedures but almost all of them take in and return normal VB variables and do not require any knowledge of pointers.

Ptr2VBStr – Makes a string in VBA and copy the data in memory to that string. You can specify the number of characters or have it find the end of the string (marked by a null character).

Ptr2Str – Even faster than Ptr2VBStr using a different algorithm. The function determines the string length.

lstrlenW – Find the length of a string in memory (characters followed by the null character).

RTLMoveMemory - Not just a string function. Copy memory data from one location to another.



File Functions

ReadTextFile – Read a text file into string array or BigString. File encoding can be UTF-8, UTF-16 or ANSI.

WriteTextFile – write a text files from string array or BigString to a file encoded in UTF-8, UTF-16 or ANSI.

ReadBinaryFile – Read a binary file into a byte array.

WriteBinaryFile – Write binary (non-text) data from a Buy buffer to a file.

SetFilePtr – Set then return the read/write file pointer in the open file. In 32-bit code the position is held in a Currency data type and in 64-bit code it the position is in a 64-bit LongPtr. Both use the Windows API function SetFilePointerEx.

CloseOpenHandle – Close the file handle for our read/write functions (if the file has been left open).


Setup and Use

The class module clStrings requires only that module mUCCore is included in the program. If you want to run the code in Excel or VB6 you need do nothing other than use the code. Just insert the class module clStrings and the standard module mUCCore into a new or existing VB6 or VBA project and you are ready to go.

If you are using this module for Office programs other than Excel, you must set an appropriate conditional compilation constant for your VBA project. There is no built-in way to distinguish between the Office programs at compile time so to do that we need to set our own conditional compilation constants which we use to check here. If you plan to use this in some code for Word, go to Tools | VBAProject Properties (2nd one from bottom) and in the General tab sheet, enter the value "Word = 1" (without quotes; case doesn't matter) to set the conditional compilation variable Word to 1. Do similar things in VBA projects you want to run in Access (Access = 1), PowerPoint (PowerPoint = 1) and Outlook (Outlook = 1). Excel and VB6 do not need a compilation constant because we can distinguish between VB6 and all of the VBA versions and we assume that you are using Excel unless modified above because most people who use VBA are using it in Excel. We can also automatically distinguish between 32 and 64-bit VBA code so you don’t need to do anything special for that.

HostRequired Conditional Compilation Constant
Visual Basic 6N/A
MS ExcelN/A
MS WordWord = 1
MS AccessAccess = 1
MS PowerPointPowerPoint = 1
MS OutlookOutlook = 1

The reason for the distinction in VBA hosts is that there are commands that exist in one host but not in the other. For example, in VBA our code is held within individual documents and often we want to know what document is holding/running our code. In Excel this is ThisWorkbook.Path but in Word it is ActiveDocument.Path, in PowerPoint it is ActivePresentation.Path, in Access it is CurrentProject.Path and in Outlook there is no equivalent. If I have a line of code that uses thisWorkbook.Path it will compile and run fine in Excel but it won’t even compile in any of those other hosts. In this particular case, I created a variable called AppPath that holds this path and I have code blocked out for each of the possible hosts so that they don’t “see” the statements that don’t exist in their version of VBA. It was a bit of a pain to set that up originally but once set up it works very well.


Use

As with all class libraries, you must set a reference to the module before you can use it.

Dim Strs As clStrings

It doesn’t need to be named “Strs”. Then somewhere in your code you put the line

Set Strs = New clStrings

Initialization code sets the size of the big string for the string builder functions to a size of 32,769 characters (it actually doesn’t use any memory until you assign a string to it) and it also calls UCCoreInit in the module mUCCore if it hasn’t already been called by another routine.


When you are finished using the class module set it to Nothing.

Set Strs = Nothing

VB6 Users – There are controls that have been developed on the VBForums website by Krool which are enhanced versions of those Microsoft supplied with Visual Basic. Here is a link to his Common Controls Replacement Project. These controls enable Unicode and many other things. I highly recommend them. If you use them you must start your program with Sub Main and not a form and there is a bit of initialization code required to use a newer version of one of the Windows DLLs. That code is here in UCCoreInit so I recommend starting your programs with Sub Main and making the first line of code in that sub be a call to UCCoreInit. If you aren’t a VB6 user or none of this makes sense to you just skip it.



'========================================================================================
' Control Resizer Class Module
'
' This class module can be "attached" to a form and when the form is resized or maximized
' all of the controls on the form are resized and optionally all of the text associated
' with each control is resized as well.
'----------------------------------------------------------------------------------------
'Version history
' v1.0.0 28 Nov 2016 Initial release by Paul Grimmer (PJG) (email @ paul@grimmerfam.com)
' v1.1.0 5 Dec 2016
' Incorporated Form_Resize into this class module using "WithEvents"
' Ensured the first display is on an actual screen
' This documentation was updated from earlier beta versions
' v2.0.0 11 Jul 2017
' Frm is no longer declared as WithEvents so that we have control in the form code of resizing
' i.e., the from's resize code is triggered and run and it does not automatically jump to the
' code in this class module. Programmer has the choice to do that in the form resize
' routine but it doesn't just bypass the form and come here automatically.
' v2.1.0 30 Aug 2017
' Added SetFormPos to enable the form to be put on top (or not) at any time
' v2.1.001 23 Apr 2018 adding MDI child capabilities
'----------------------------------------------------------------------------------------
' Dependencies - None
'----------------------------------------------------------------------------------------
' To set up to use this class module with a VB6 Form, do the following to the Form:

' Set the following Form properties
' BorderStyle = 2 (Resizable)
' Optionally set MinButton to True and MaxButton to True

' In the declaration section at the top of the Form put
'Dim frmResize As New ControlResizer
'Public UniCaption As String

' In the Form_Load procedure start with this line after your code for setting up the
' controls on your form put the following line:
'frmResize.NewSetup Me

' That's all you need. An instance of this class is created when the Form is loaded. When
' the Form is shown onscreen a Resize event is called and we catch the original state
' of the form position.
' When the form is Unloaded this instance of the class module goes away too.
'
'----------------------------------------------------------------------------------------
' Controlling the resizing
'
'The form can be resized either by you in code or by the user who can drag the edges of the
' form to resize &/or maximize the form.
'
'There are four variables you can set to control the resizing behavior for each form (each
' is separate. After your form is loaded you can set these variables. The efault for each
' is True.
'
'ResizeActive - True enables form resizing by the user.
'CanResizeFonts - True makes control font sizes change as control sizes change.
'KeepRatio - True keeps the height/width ratio the same as the starting form's
' height/width ratio as the user resizes the form.
'Zoomable - True changes the size of each of the controls on the form as the
' size of the form itself changes.
'
'You can set whether individual controls react to resizing by using the "Tag" property
' of the control ("Tag" is available for each control for you to put whatever text you want
' in the property at design-time.) Tag contents (if any) are totally up to the programmer.
' My code looks for the string "Skip" (not case sensitive) at the start of the tage for
' each control. If that string is found then that control will not be resized or moved as
' the rest of the form is moved and/or resized.
'========================================================================================

and

' Public Functions in this class module

' ReadTextFile - Read any text file into string array or BigString in UTF-8, ANSI or UTF-16 (UCS-2)
' WriteTextFile - Write any text file from string array or BigString to UTF-8, UTF-16 (UCS-2) or ANSI file
' ReadBinaryFile - Read art or all of a binary file into a byte array
' WriteBinaryFile - write part or all of a byte array to a binary file
' SetFilePtr - Sets the file pointer in the open file from ReadTextFile (if still open)
' CloseOpenHandle - If the Read/Write handle has been left open this closes it.

' BigString (based on old StringBuilder code)
' Get Length - Return the current length of the string being built (chars)
' Get Capacity - Return current max length of the string (auto grown
' for more data) (chars)
' Get/Let ChunkSize - Return or set the the unicode character chunk size (chars)
' Get/Let Delim - Return or set the end-of-line delimiter sequence (1 or 2 characters)
' Iniitally is set to vbCrLf, but could be vbCr or vbLf or any other 1 or 2 character string for end-of-line

' GetAString - Returns part or all of the built string
' Let bigString - Set the value of the string starting to be built (can be "")
' Sub Append - Add a string onto the end of bigString
' Sub AppendWDelim - Append and add a delimiter sequence to the end
' Sub AppendPtrData - Quicker append using pointer to string
' Sub GrowWithGarbage - Lengthen our internal string by a specified # of characters
' Sub Insert - Insert a string into the big string
' Sub InsertWDelim - Insert a string (with delimiter) into the big string
' Sub InsertPtrData - Insert a string using a pointer to the string
' Sub Remove - Remove a specified # of characters from a place in the string
' Function Find - Find a sub-string in the big string (like VB InStr)
' Sub Split - Split part or all of BigString into sub-strings (like VB Split)
' Sub HeapMinimize - shrink the allocated memory for bigString down to a
' minimum (can still grow after this)
' Sub GetToIntChars - Copy part or all of the big string to an integer array
'----------------------------------------------------------
' v2.2.4 30 Jul 2018
' ======================================================================================

Token Privilege in Windows [CheckPrivilegeState, ShowPrivilegeInfo, ShowEnabledPrivileges]

$
0
0
Viewing Token Privileges

Download from ME
Download from VBForums

Privileges control access to system resources and system-related tasks, whereas access rights control access to securable objects. Most API functions typically used by VB6 applications do not relate to system resources, but there are some WinAPI functions which require specific privileges to be enabled before they will function. If the application does not have access to the required privileges, the privilege can’t be enabled, and the function will fail. If the application does have access to the required privileges, the application may have to first enable the privilege.

The privileges available to an application are primarily determined by the logged user credentials, and whether the user has elevated credentials. The privileges allocated to user and groups are defined in the Group Policy User Rights.

This application displays the privileges that are provided by Windows, the subset that are available to the process token, and the privileges which are currently enabled. This application also provides the facility to enable or disable a privilege.

The application form lists all the available OS Privileges as a collection of privilege option buttons, provides buttons to display 3 pre-configured Privilege sets, and selecting any option button will display the status of that Privilege. Selecting the Enable and Disabled buttons will attempt to set the state of the selected privilege option button.

The 3 pre pre-configured displays of Privilege sets are:

  1. CheckPrivilegeState
    Lists the state of all the OS Privileges to show which Privileges are enabled with the current process token. The state is either “Disabled” or “Enabled”. Note the “Disabled” privileges state does not indicate if this privilege is available with the process token.

  1. ShowPrivilegeInfo
    Lists the state of all the privileges available with the process token. The state is either “Disabled”, or “Enabled” and/or “Enabled(default)”.

  1. ShowEnabledPrivileges
    Lists all the privileges which are currently Enabled.




Figure 1. Example display for a process running with elevated administrators

The count boxes shows 24 of the 36 privileges are available with elevated credentials, of which 3 are enabled. With these user credentials, only 5 of the 34 privileges are available, and only one is enabled.

The Count textboxes are show the number of privileges. The LookUp counts are calculated when the form is loaded based on the LookupPrivilegeValue and LookupPrivilegeName results. These two totals should be the same, any difference would indicate a program error. The Token Counts are updated whenever the Token Privileges buttons are selected. The CheckPrivileges State updates the Check count and the Enabled Count, the Show Privileges Information updates the Info count, and the Show Enabled Privileges updates the Enabled Count and the Check count. Selecting the Enabled and Disable buttons does not update these totals.

As an example of API’s which require specific privileges and those which don’t, the functions to create a process have differing privilege requirements:

CreateProcessWithTokenW
must have the SE_IMPERSONATE_NAME privilege.
CreateProcessAsUser
must have the SE_INCREASE_QUOTA_NAME privilege and may require the SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable
CreateProcessWithLogonW
requires no special privileges as the new process runs in the security context of the Logon User
CreateProcess
requires no special privileges as the new process runs in the security context of the calling process

Windows API functions used to view and change Privileges state

There are two WinAPI functions to view the privileges available with a process token, PrivilegeCheck API, and GetTokenInformation API with the TokenPrivileges option. The AdjustTokenPrivileges API is used to enable or disable privileges

  • The PrivilegeCheck API returns the Enabled status of a specific privilege or group of privileges.

  • The GetTokenInformation API with the TokenPrivileges option returns an array of all the privileges available to a process token, together with the privileges attributes including the Enabled status.

  • The AdjustTokenPrivileges API sets the enabled state to either Enabled or Disabled for a specific privilege or group of privileges. It can also return the previous state.  The AdjustTokenPrivileges function cannot add new privileges to the access token.

Important : The PrivilegeCheck declared in the API.Txt file used with the VB6 API viewer may be incorrect. The last argument should be byref and not byval.

Each OS has a set of privilege names, and these are listed in the MSDN document “Privilege Constants”. The set of privileges rarely changes between OS version.

Each privilege is assigned a LUID value. The WinAPI function LookupPrivilegeValue returns the Privilege LUID for a Privilege Name. The corresponding function LookupPrivilegeName returns the Privilege Name for a Privilege LUID.

When a process token is created, a subset of the OS privileges is added to the token. These are determined by the User Logon credentials. Some may be set to ENABLED state by default, others are DISABLED by default. The state of each of the privileges included in the token can be changed to ENABLE the privilege or DISABLE the privilege using the AdjustTokenPrivileges API.

The AdjustTokenPrivileges requires the token must have been opened with TOKEN_ADJUST_PRIVILEGES access, and the TOKEN_QUERY to view the previous state access). In this application, the Token is opened with the MAXIMUM_ALLOWED Access.

Using the TokenPrivilege Application

Selecting any of the Privilege option buttons will display both the Info State (as in ShowPrivilegeInfo), and the Enabled State (as in CheckPrivilegeState) for the selected Privilege. In addition the Enable and Disable button will attempt to change the Enabled State of any selected privilege.

When a Privilege option button is selected, both the GetTokenInformation API and the PrivilegeCheck API are used to query the specific privilege as follows:
  • Query a privilege with the GetTokenInformation API to determine if the privilege is available and if it is enabled
    1. Open the token for the current process using the the OpenProcessToken(GetCurrentProcess()..) API functions.
    2. Lookup the Privilege LUID form the selected Privilege Name using the  LookupPrivilegeValue API.
    3. Call the GetTokenInformation API with the TokenPrivileges option to create an array of Privileges for the token.
    4. Search the array of Privileges for a matching Privilege LUID. If there is no match, the specific Privilege is not available.
    5. Test the attribute bits for the Enabled bit &H2.
  • Query a privilege enable state with PrivilegeCheck API
    1. Open the token for the current process using the the OpenProcessToken(GetCurrentProcess()..) API functions .
    2. Lookup the Privilege LUID form the selected Privilege Name using the LookupPrivilegeValue API.
    3. Create a PRIVILEGE_SET structure with this Privilege LUID, setting the control member to zero, and the count to one.
    4. Call the PrivilegeCheck API with this PRIVILEGE_SET structure, if his call fails then the privilege does not exist.
    5. Test the attribute bits in the returned PRIVILEGE_SET structure, set to a value of &H80000000 if it is enabled.

When either the Enabled or Disabled button is selected, this application uses the AdjustTokenPrivileges API to attempt to change the Enabled state as follows:
  • Change the state of the selected privilege with AdjustTokenPrivileges
a.       Open the token for the current process using the the OpenProcessToken(GetCurrentProcess(),<access mask>) API functions.
b.      Lookup the Privilege LUID form the selected Privilege Name using the  LookupPrivilegeValue API.
c.       Create a TOKEN_PRIVILEGES structure for a single privilege, setting the Attributes to either SE_PRIVILEGE_ENABLED to enable the privilege, or to 0 to disable the privilege.
d.      Call the AdjustTokenPrivileges API with the TokenPrivileges structure.
e.       Test the result for ERROR_NOT_ALL_ASSIGNED which indicates the selected privilege is not available in the token.
f.       Report the updated result using the PrivilegeCheck API.

This application illustrates the privileges available for current process token. The Token is opened using the OpenProcessToken(GetCurrentProcess(),<access mask>) API functions. The <acces mask> is set to “MAXIMUM_ALLOWED”. There are other tokens that could be used, for example if the application included the impersonating of another user, it could open the Token using OpenProcessToken(GetCurrentThread(),<access mask>) API functions. 

Source:
http://www.vbforums.com/showthread.php?863511-Viewing-Token-Privileges

VBA to JSON converter (by Tim Hall)

$
0
0
JSON conversion and parsing for VBA (Windows and Mac Excel, Access, and other Office applications). It grew out of the excellent project [vba-json](https://code.google.com/p/vba-json/),
with additions and improvements made to resolve bugs and improve performance (as part of [VBA-Web](https://github.com/VBA-tools/VBA-Web)).

Download from ME

Tested in Windows Excel 2013 and Excel for Mac 2011, but should apply to 2007+.



''---------------------------------------------------------------------- '
' VBA-JSON v2.2.3
' (c) Tim Hall - https://github.com/VBA-tools/VBA-JSON
'
' JSON Converter for VBA
'
' Errors:
' 10001 - JSON parse error
'
' @class JsonConverter
' @author tim.hall.engr@gmail.com
' @license MIT (http://www.opensource.org/licenses/mit-license.php)
'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
'
' Based originally on vba-json (with extensive changes)
' BSD license included below
'
' JSONLib, http://code.google.com/p/vba-json/
'
' Copyright (c) 2013, Ryo Yokoyama
' All rights reserved.
'
' Redistribution and use in source and binary forms, with or without
' modification, are permitted provided that the following conditions are met:
' * Redistributions of source code must retain the above copyright
' notice, this list of conditions and the following disclaimer.
' * Redistributions in binary form must reproduce the above copyright
' notice, this list of conditions and the following disclaimer in the
' documentation and/or other materials provided with the distribution.
' * Neither the name of the <organization> nor the
' names of its contributors may be used to endorse or promote products
' derived from this software without specific prior written permission.
'
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
' DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
OptionExplicit
 
' === VBA-UTC Headers
#If Mac Then
 
#If VBA7 Then
 
' 64-bit Mac (2016)
PrivateDeclare PtrSafe Function utc_popen Lib "libc.dylib" Alias "popen" _
(ByVal utc_Command AsString, ByVal utc_Mode AsString) As LongPtr
PrivateDeclare PtrSafe Function utc_pclose Lib "libc.dylib" Alias "pclose" _
(ByVal utc_File AsLong) As LongPtr
PrivateDeclare PtrSafe Function utc_fread Lib "libc.dylib" Alias "fread" _
(ByVal utc_Buffer AsString, ByVal utc_Size As LongPtr, ByVal utc_Number As LongPtr, ByVal utc_File As LongPtr) As LongPtr
PrivateDeclare PtrSafe Function utc_feof Lib "libc.dylib" Alias "feof" _
(ByVal utc_File As LongPtr) As LongPtr
 
#Else
 
' 32-bit Mac
PrivateDeclareFunction utc_popen Lib "libc.dylib" Alias "popen" _
(ByVal utc_Command AsString, ByVal utc_Mode AsString) AsLong
PrivateDeclareFunction utc_pclose Lib "libc.dylib" Alias "pclose" _
(ByVal utc_File AsLong) AsLong
PrivateDeclareFunction utc_fread Lib "libc.dylib" Alias "fread" _
(ByVal utc_Buffer AsString, ByVal utc_Size AsLong, ByVal utc_Number AsLong, ByVal utc_File AsLong) AsLong
PrivateDeclareFunction utc_feof Lib "libc.dylib" Alias "feof" _
(ByVal utc_File AsLong) AsLong
 
#End If
 
#ElseIf VBA7 Then
 
' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724421.aspx
' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724949.aspx
' http://msdn.microsoft.com/en-us/library/windows/desktop/ms725485.aspx
PrivateDeclare PtrSafe Function utc_GetTimeZoneInformation Lib "kernel32" Alias "GetTimeZoneInformation" _
(utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) AsLong
PrivateDeclare PtrSafe Function utc_SystemTimeToTzSpecificLocalTime Lib "kernel32" Alias "SystemTimeToTzSpecificLocalTime" _
(utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) AsLong
PrivateDeclare PtrSafe Function utc_TzSpecificLocalTimeToSystemTime Lib "kernel32" Alias "TzSpecificLocalTimeToSystemTime" _
(utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) AsLong
 
#Else
 
PrivateDeclareFunction utc_GetTimeZoneInformation Lib "kernel32" Alias "GetTimeZoneInformation" _
(utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) AsLong
PrivateDeclareFunction utc_SystemTimeToTzSpecificLocalTime Lib "kernel32" Alias "SystemTimeToTzSpecificLocalTime" _
(utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) AsLong
PrivateDeclareFunction utc_TzSpecificLocalTimeToSystemTime Lib "kernel32" Alias "TzSpecificLocalTimeToSystemTime" _
(utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) AsLong
 
#End If
 
#If Mac Then
 
#If VBA7 Then
PrivateType utc_ShellResult
utc_Output AsString
utc_ExitCode As LongPtr
EndType
 
#Else
 
PrivateType utc_ShellResult
utc_Output AsString
utc_ExitCode AsLong
EndType
 
#End If
 
#Else
 
PrivateType utc_SYSTEMTIME
utc_wYear AsInteger
utc_wMonth AsInteger
utc_wDayOfWeek AsInteger
utc_wDay AsInteger
utc_wHour AsInteger
utc_wMinute AsInteger
utc_wSecond AsInteger
utc_wMilliseconds AsInteger
EndType
 
PrivateType utc_TIME_ZONE_INFORMATION
utc_Bias AsLong
utc_StandardName(0 To 31) AsInteger
utc_StandardDate As utc_SYSTEMTIME
utc_StandardBias AsLong
utc_DaylightName(0 To 31) AsInteger
utc_DaylightDate As utc_SYSTEMTIME
utc_DaylightBias AsLong
EndType
 
#End If
' === End VBA-UTC
 
#If Mac Then
#ElseIf VBA7 Then
 
PrivateDeclare PtrSafe Sub json_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(json_MemoryDestination As Any, json_MemorySource As Any, ByVal json_ByteLength AsLong)
 
#Else
 
PrivateDeclareSub json_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(json_MemoryDestination As Any, json_MemorySource As Any, ByVal json_ByteLength AsLong)
 
#End If
 
PrivateType json_Options
' VBA only stores 15 significant digits, so any numbers larger than that are truncated
' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits
' See: http://support.microsoft.com/kb/269370
'
' By default, VBA-JSON will use String for numbers longer than 15 characters that contain only digits
' to override set `JsonConverter.JsonOptions.UseDoubleForLargeNumbers = True`
UseDoubleForLargeNumbers AsBoolean
 
' The JSON standard requires object keys to be quoted (" or '), use this option to allow unquoted keys
AllowUnquotedKeys AsBoolean
 
' The solidus (/) is not required to be escaped, use this option to escape them as \/ in ConvertToJson
EscapeSolidus AsBoolean
EndType
Public JsonOptions As json_Options
 
' ============================================= '
' Public Methods
' ============================================= '
 
''
' Convert JSON string to object (Dictionary/Collection)
'
' @method ParseJson
' @param {String} json_String
' @return {Object} (Dictionary or Collection)
' @throws 10001 - JSON parse error
''
PublicFunction ParseJson(ByVal JsonString AsString) AsObject
Dim json_Index AsLong
json_Index = 1
 
' Remove vbCr, vbLf, and vbTab from json_String
JsonString = VBA.Replace(VBA.Replace(VBA.Replace(JsonString, VBA.vbCr, ""), VBA.vbLf, ""), VBA.vbTab, "")
 
json_SkipSpaces JsonString, json_Index
SelectCase VBA.Mid$(JsonString, json_Index, 1)
Case"{"
Set ParseJson = json_ParseObject(JsonString, json_Index)
Case"["
Set ParseJson = json_ParseArray(JsonString, json_Index)
CaseElse
' Error: Invalid JSON string
Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(JsonString, json_Index, "Expecting '{' or '['")
EndSelect
EndFunction
 
''
' Convert object (Dictionary/Collection/Array) to JSON
'
' @method ConvertToJson
' @param {Variant} JsonValue (Dictionary, Collection, or Array)
' @param {Integer|String} Whitespace "Pretty" print json with given number of spaces per indentation (Integer) or given string
' @return {String}
''
PublicFunction ConvertToJson(ByVal JsonValue AsVariant, OptionalByVal Whitespace AsVariant, OptionalByVal json_CurrentIndentation AsLong = 0) AsString
Dim json_buffer AsString
Dim json_BufferPosition AsLong
Dim json_BufferLength AsLong
Dim json_Index AsLong
Dim json_LBound AsLong
Dim json_UBound AsLong
Dim json_IsFirstItem AsBoolean
Dim json_Index2D AsLong
Dim json_LBound2D AsLong
Dim json_UBound2D AsLong
Dim json_IsFirstItem2D AsBoolean
Dim json_Key AsVariant
Dim json_Value AsVariant
Dim json_DateStr AsString
Dim json_Converted AsString
Dim json_SkipItem AsBoolean
Dim json_PrettyPrint AsBoolean
Dim json_Indentation AsString
Dim json_InnerIndentation AsString
 
json_LBound = -1
json_UBound = -1
json_IsFirstItem = True
json_LBound2D = -1
json_UBound2D = -1
json_IsFirstItem2D = True
json_PrettyPrint = Not IsMissing(Whitespace)
 
SelectCase VBA.VarType(JsonValue)
Case VBA.vbNull
ConvertToJson = "null"
Case VBA.vbDate
' Date
json_DateStr = ConvertToIso(VBA.CDate(JsonValue))
 
ConvertToJson = """"& json_DateStr & """"
Case VBA.vbString
' String (or large number encoded as string)
IfNot JsonOptions.UseDoubleForLargeNumbers And json_StringIsLargeNumber(JsonValue) Then
ConvertToJson = JsonValue
Else
ConvertToJson = """"& json_Encode(JsonValue) & """"
EndIf
Case VBA.vbBoolean
If JsonValue Then
ConvertToJson = "true"
Else
ConvertToJson = "false"
EndIf
Case VBA.vbArray To VBA.vbArray + VBA.vbByte
If json_PrettyPrint Then
If VBA.VarType(Whitespace) = VBA.vbString Then
json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)
json_InnerIndentation = VBA.String$(json_CurrentIndentation + 2, Whitespace)
Else
json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)
json_InnerIndentation = VBA.Space$((json_CurrentIndentation + 2) * Whitespace)
EndIf
EndIf
 
' Array
json_BufferAppend json_buffer, "[", json_BufferPosition, json_BufferLength
 
OnErrorResumeNext
 
json_LBound = LBound(JsonValue, 1)
json_UBound = UBound(JsonValue, 1)
json_LBound2D = LBound(JsonValue, 2)
json_UBound2D = UBound(JsonValue, 2)
 
If json_LBound >= 0 And json_UBound >= 0 Then
For json_Index = json_LBound To json_UBound
If json_IsFirstItem Then
json_IsFirstItem = False
Else
' Append comma to previous line
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
EndIf
 
If json_LBound2D >= 0 And json_UBound2D >= 0 Then
' 2D Array
If json_PrettyPrint Then
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
EndIf
json_BufferAppend json_buffer, json_Indentation & "[", json_BufferPosition, json_BufferLength
 
For json_Index2D = json_LBound2D To json_UBound2D
If json_IsFirstItem2D Then
json_IsFirstItem2D = False
Else
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
EndIf
 
json_Converted = ConvertToJson(JsonValue(json_Index, json_Index2D), Whitespace, json_CurrentIndentation + 2)
 
' For Arrays/Collections, undefined (Empty/Nothing) is treated as null
If json_Converted = ""Then
' (nest to only check if converted = "")
If json_IsUndefined(JsonValue(json_Index, json_Index2D)) Then
json_Converted = "null"
EndIf
EndIf
 
If json_PrettyPrint Then
json_Converted = vbNewLine & json_InnerIndentation & json_Converted
EndIf
 
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
Next json_Index2D
 
If json_PrettyPrint Then
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
EndIf
 
json_BufferAppend json_buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
json_IsFirstItem2D = True
Else
' 1D Array
json_Converted = ConvertToJson(JsonValue(json_Index), Whitespace, json_CurrentIndentation + 1)
 
' For Arrays/Collections, undefined (Empty/Nothing) is treated as null
If json_Converted = ""Then
' (nest to only check if converted = "")
If json_IsUndefined(JsonValue(json_Index)) Then
json_Converted = "null"
EndIf
EndIf
 
If json_PrettyPrint Then
json_Converted = vbNewLine & json_Indentation & json_Converted
EndIf
 
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
EndIf
Next json_Index
EndIf
 
OnErrorGoTo 0
 
If json_PrettyPrint Then
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
 
If VBA.VarType(Whitespace) = VBA.vbString Then
json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
Else
json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)
EndIf
EndIf
 
json_BufferAppend json_buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
 
ConvertToJson = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
 
' Dictionary or Collection
Case VBA.vbObject
If json_PrettyPrint Then
If VBA.VarType(Whitespace) = VBA.vbString Then
json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)
Else
json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)
EndIf
EndIf
 
' Dictionary
If VBA.TypeName(JsonValue) = "Dictionary"Then
json_BufferAppend json_buffer, "{", json_BufferPosition, json_BufferLength
ForEach json_Key In JsonValue.Keys
' For Objects, undefined (Empty/Nothing) is not added to object
json_Converted = ConvertToJson(JsonValue(json_Key), Whitespace, json_CurrentIndentation + 1)
If json_Converted = ""Then
json_SkipItem = json_IsUndefined(JsonValue(json_Key))
Else
json_SkipItem = False
EndIf
 
IfNot json_SkipItem Then
If json_IsFirstItem Then
json_IsFirstItem = False
Else
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
EndIf
 
If json_PrettyPrint Then
json_Converted = vbNewLine & json_Indentation & """"& json_Key & """: "& json_Converted
Else
json_Converted = """"& json_Key & """:"& json_Converted
EndIf
 
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
EndIf
Next json_Key
 
If json_PrettyPrint Then
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
 
If VBA.VarType(Whitespace) = VBA.vbString Then
json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
Else
json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)
EndIf
EndIf
 
json_BufferAppend json_buffer, json_Indentation & "}", json_BufferPosition, json_BufferLength
 
' Collection
ElseIf VBA.TypeName(JsonValue) = "Collection"Then
json_BufferAppend json_buffer, "[", json_BufferPosition, json_BufferLength
ForEach json_Value In JsonValue
If json_IsFirstItem Then
json_IsFirstItem = False
Else
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
EndIf
 
json_Converted = ConvertToJson(json_Value, Whitespace, json_CurrentIndentation + 1)
 
' For Arrays/Collections, undefined (Empty/Nothing) is treated as null
If json_Converted = ""Then
' (nest to only check if converted = "")
If json_IsUndefined(json_Value) Then
json_Converted = "null"
EndIf
EndIf
 
If json_PrettyPrint Then
json_Converted = vbNewLine & json_Indentation & json_Converted
EndIf
 
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
Next json_Value
 
If json_PrettyPrint Then
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
 
If VBA.VarType(Whitespace) = VBA.vbString Then
json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
Else
json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)
EndIf
EndIf
 
json_BufferAppend json_buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
EndIf
 
ConvertToJson = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal
' Number (use decimals for numbers)
ConvertToJson = VBA.Replace(JsonValue, ",", ".")
CaseElse
' vbEmpty, vbError, vbDataObject, vbByte, vbUserDefinedType
' Use VBA's built-in to-string
OnErrorResumeNext
ConvertToJson = JsonValue
OnErrorGoTo 0
EndSelect
EndFunction
 
' ============================================= '
' Private Functions
' ============================================= '
 
PrivateFunction json_ParseObject(json_String AsString, ByRef json_Index AsLong) As Dictionary
Dim json_Key AsString
Dim json_NextChar AsString
 
Set json_ParseObject = New Dictionary
json_SkipSpaces json_String, json_Index
If VBA.Mid$(json_String, json_Index, 1) <> "{"Then
Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '{'")
Else
json_Index = json_Index + 1
 
Do
json_SkipSpaces json_String, json_Index
If VBA.Mid$(json_String, json_Index, 1) = "}"Then
json_Index = json_Index + 1
ExitFunction
ElseIf VBA.Mid$(json_String, json_Index, 1) = ","Then
json_Index = json_Index + 1
json_SkipSpaces json_String, json_Index
EndIf
 
json_Key = json_ParseKey(json_String, json_Index)
json_NextChar = json_Peek(json_String, json_Index)
If json_NextChar = "["Or json_NextChar = "{"Then
Set json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)
Else
json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)
EndIf
Loop
EndIf
EndFunction
 
PrivateFunction json_ParseArray(json_String AsString, ByRef json_Index AsLong) As Collection
Set json_ParseArray = New Collection
 
json_SkipSpaces json_String, json_Index
If VBA.Mid$(json_String, json_Index, 1) <> "["Then
Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '['")
Else
json_Index = json_Index + 1
 
Do
json_SkipSpaces json_String, json_Index
If VBA.Mid$(json_String, json_Index, 1) = "]"Then
json_Index = json_Index + 1
ExitFunction
ElseIf VBA.Mid$(json_String, json_Index, 1) = ","Then
json_Index = json_Index + 1
json_SkipSpaces json_String, json_Index
EndIf
 
json_ParseArray.Add json_ParseValue(json_String, json_Index)
Loop
EndIf
EndFunction
 
PrivateFunction json_ParseValue(json_String AsString, ByRef json_Index AsLong) AsVariant
json_SkipSpaces json_String, json_Index
SelectCase VBA.Mid$(json_String, json_Index, 1)
Case"{"
Set json_ParseValue = json_ParseObject(json_String, json_Index)
Case"["
Set json_ParseValue = json_ParseArray(json_String, json_Index)
Case"""", "'"
json_ParseValue = json_ParseString(json_String, json_Index)
CaseElse
If VBA.Mid$(json_String, json_Index, 4) = "true"Then
json_ParseValue = True
json_Index = json_Index + 4
ElseIf VBA.Mid$(json_String, json_Index, 5) = "false"Then
json_ParseValue = False
json_Index = json_Index + 5
ElseIf VBA.Mid$(json_String, json_Index, 4) = "null"Then
json_ParseValue = Null
json_Index = json_Index + 4
ElseIf VBA.InStr("+-0123456789", VBA.Mid$(json_String, json_Index, 1)) Then
json_ParseValue = json_ParseNumber(json_String, json_Index)
Else
Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['")
EndIf
EndSelect
EndFunction
 
PrivateFunction json_ParseString(json_String AsString, ByRef json_Index AsLong) AsString
Dim json_Quote AsString
Dim json_Char AsString
Dim json_Code AsString
Dim json_buffer AsString
Dim json_BufferPosition AsLong
Dim json_BufferLength AsLong
 
json_SkipSpaces json_String, json_Index
 
' Store opening quote to look for matching closing quote
json_Quote = VBA.Mid$(json_String, json_Index, 1)
json_Index = json_Index + 1
 
DoWhile json_Index > 0 And json_Index <= Len(json_String)
json_Char = VBA.Mid$(json_String, json_Index, 1)
 
SelectCase json_Char
Case"\"
' Escaped string, \\, or \/
json_Index = json_Index + 1
json_Char = VBA.Mid$(json_String, json_Index, 1)
 
SelectCase json_Char
Case"""", "\", "/", "'"
json_BufferAppend json_buffer, json_Char, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
Case"b"
json_BufferAppend json_buffer, vbBack, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
Case"f"
json_BufferAppend json_buffer, vbFormFeed, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
Case"n"
json_BufferAppend json_buffer, vbCrLf, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
Case"r"
json_BufferAppend json_buffer, vbCr, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
Case"t"
json_BufferAppend json_buffer, vbTab, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
Case"u"
' Unicode character escape (e.g. \u00a9 = Copyright)
json_Index = json_Index + 1
json_Code = VBA.Mid$(json_String, json_Index, 4)
json_BufferAppend json_buffer, VBA.ChrW(VBA.Val("&h" + json_Code)), json_BufferPosition, json_BufferLength
json_Index = json_Index + 4
EndSelect
Case json_Quote
json_ParseString = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
json_Index = json_Index + 1
ExitFunction
CaseElse
json_BufferAppend json_buffer, json_Char, json_BufferPosition, json_BufferLength
json_Index = json_Index + 1
EndSelect
Loop
EndFunction
 
PrivateFunction json_ParseNumber(json_String AsString, ByRef json_Index AsLong) AsVariant
Dim json_Char AsString
Dim json_Value AsString
Dim json_IsLargeNumber AsBoolean
 
json_SkipSpaces json_String, json_Index
 
DoWhile json_Index > 0 And json_Index <= Len(json_String)
json_Char = VBA.Mid$(json_String, json_Index, 1)
 
If VBA.InStr("+-0123456789.eE", json_Char) Then
' Unlikely to have massive number, so use simple append rather than buffer here
json_Value = json_Value & json_Char
json_Index = json_Index + 1
Else
' Excel only stores 15 significant digits, so any numbers larger than that are truncated
' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits
' See: http://support.microsoft.com/kb/269370
'
' Fix: Parse -> String, Convert -> String longer than 15/16 characters containing only numbers and decimal points -> Number
' (decimal doesn't factor into significant digit count, so if present check for 15 digits + decimal = 16)
json_IsLargeNumber = IIf(InStr(json_Value, "."), Len(json_Value) >= 17, Len(json_Value) >= 16)
IfNot JsonOptions.UseDoubleForLargeNumbers And json_IsLargeNumber Then
json_ParseNumber = json_Value
Else
' VBA.Val does not use regional settings, so guard for comma is not needed
json_ParseNumber = VBA.Val(json_Value)
EndIf
ExitFunction
EndIf
Loop
EndFunction
 
PrivateFunction json_ParseKey(json_String AsString, ByRef json_Index AsLong) AsString
' Parse key with single or double quotes
If VBA.Mid$(json_String, json_Index, 1) = """"Or VBA.Mid$(json_String, json_Index, 1) = "'"Then
json_ParseKey = json_ParseString(json_String, json_Index)
ElseIf JsonOptions.AllowUnquotedKeys Then
Dim json_Char AsString
DoWhile json_Index > 0 And json_Index <= Len(json_String)
json_Char = VBA.Mid$(json_String, json_Index, 1)
If (json_Char <> "") And (json_Char <> ":") Then
json_ParseKey = json_ParseKey & json_Char
json_Index = json_Index + 1
Else
ExitDo
EndIf
Loop
Else
Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '""' or '''")
EndIf
 
' Check for colon and skip if present or throw if not present
json_SkipSpaces json_String, json_Index
If VBA.Mid$(json_String, json_Index, 1) <> ":"Then
Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting ':'")
Else
json_Index = json_Index + 1
EndIf
EndFunction
 
PrivateFunction json_IsUndefined(ByVal json_Value AsVariant) AsBoolean
' Empty / Nothing -> undefined
SelectCase VBA.VarType(json_Value)
Case VBA.vbEmpty
json_IsUndefined = True
Case VBA.vbObject
SelectCase VBA.TypeName(json_Value)
Case"Empty", "Nothing"
json_IsUndefined = True
EndSelect
EndSelect
EndFunction
 
PrivateFunction json_Encode(ByVal json_Text AsVariant) AsString
' Reference: http://www.ietf.org/rfc/rfc4627.txt
' Escape: ", \, /, backspace, form feed, line feed, carriage return, tab
Dim json_Index AsLong
Dim json_Char AsString
Dim json_AscCode AsLong
Dim json_buffer AsString
Dim json_BufferPosition AsLong
Dim json_BufferLength AsLong
 
For json_Index = 1 To VBA.Len(json_Text)
json_Char = VBA.Mid$(json_Text, json_Index, 1)
json_AscCode = VBA.AscW(json_Char)
 
' When AscW returns a negative number, it returns the twos complement form of that number.
' To convert the twos complement notation into normal binary notation, add 0xFFF to the return result.
' https://support.microsoft.com/en-us/kb/272138
If json_AscCode < 0 Then
json_AscCode = json_AscCode + 65536
EndIf
 
' From spec, ", \, and control characters must be escaped (solidus is optional)
 
SelectCase json_AscCode
Case 34
'" -> 34 -> \"
json_Char = "\"""
Case 92
' \ -> 92 -> \\
json_Char = "\\"
Case 47
' / -> 47 -> \/ (optional)
If JsonOptions.EscapeSolidus Then
json_Char = "\/"
EndIf
Case 8
' backspace -> 8 -> \b
json_Char = "\b"
Case 12
' form feed -> 12 -> \f
json_Char = "\f"
Case 10
' line feed -> 10 -> \n
json_Char = "\n"
Case 13
' carriage return -> 13 -> \r
json_Char = "\r"
Case 9
' tab -> 9 -> \t
json_Char = "\t"
Case 0 To 31, 127 To 65535
' Non-ascii characters -> convert to 4-digit hex
json_Char = "\u"& VBA.Right$("0000"& VBA.Hex$(json_AscCode), 4)
EndSelect
 
json_BufferAppend json_buffer, json_Char, json_BufferPosition, json_BufferLength
Next json_Index
 
json_Encode = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
EndFunction
 
PrivateFunction json_Peek(json_String AsString, ByVal json_Index AsLong, Optional json_NumberOfCharacters AsLong = 1) AsString
'"Peek" at the next number of characters without incrementing json_Index (ByVal instead of ByRef)
json_SkipSpaces json_String, json_Index
json_Peek = VBA.Mid$(json_String, json_Index, json_NumberOfCharacters)
EndFunction
 
PrivateSub json_SkipSpaces(json_String AsString, ByRef json_Index AsLong)
' Increment index to skip over spaces
DoWhile json_Index > 0 And json_Index <= VBA.Len(json_String) And VBA.Mid$(json_String, json_Index, 1) = ""
json_Index = json_Index + 1
Loop
EndSub
 
PrivateFunction json_StringIsLargeNumber(json_String AsVariant) AsBoolean
' Check if the given string is considered a "large number"
' (See json_ParseNumber)
 
Dim json_Length AsLong
Dim json_CharIndex AsLong
json_Length = VBA.Len(json_String)
 
' Length with be at least 16 characters and assume will be less than 100 characters
If json_Length >= 16 And json_Length <= 100 Then
Dim json_CharCode AsString
Dim json_Index AsLong
 
json_StringIsLargeNumber = True
 
For json_CharIndex = 1 To json_Length
json_CharCode = VBA.Asc(VBA.Mid$(json_String, json_CharIndex, 1))
SelectCase json_CharCode
' Look for .|0-9|E|e
Case 46, 48 To 57, 69, 101
' Continue through characters
CaseElse
json_StringIsLargeNumber = False
ExitFunction
EndSelect
Next json_CharIndex
EndIf
EndFunction
 
PrivateFunction json_ParseErrorMessage(json_String AsString, ByRef json_Index AsLong, ErrorMessage AsString)
' Provide detailed parse error message, including details of where and what occurred
'
' Example:
' Error parsing JSON:
' {"abcde":True}
' ^
' Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['
 
Dim json_StartIndex AsLong
Dim json_StopIndex AsLong
 
' Include 10 characters before and after error (if possible)
json_StartIndex = json_Index - 10
json_StopIndex = json_Index + 10
If json_StartIndex <= 0 Then
json_StartIndex = 1
EndIf
If json_StopIndex > VBA.Len(json_String) Then
json_StopIndex = VBA.Len(json_String)
EndIf
 
json_ParseErrorMessage = "Error parsing JSON:"& VBA.vbNewLine & _
VBA.Mid$(json_String, json_StartIndex, json_StopIndex - json_StartIndex + 1) & VBA.vbNewLine & _
VBA.Space$(json_Index - json_StartIndex) & "^"& VBA.vbNewLine & _
ErrorMessage
EndFunction
 
PrivateSub json_BufferAppend(ByRef json_buffer AsString, _
ByRef json_Append AsVariant, _
ByRef json_BufferPosition AsLong, _
ByRef json_BufferLength AsLong)
#If Mac Then
json_buffer = json_buffer & json_Append
#Else
' VBA can be slow to append strings due to allocating a new string for each append
' Instead of using the traditional append, allocate a large empty string and then copy string at append position
'
' Example:
' Buffer: "abc "
' Append: "def"
' Buffer Position: 3
' Buffer Length: 5
'
' Buffer position + Append length > Buffer length -> Append chunk of blank space to buffer
' Buffer: "abc "
' Buffer Length: 10
'
' Copy memory for "def" into buffer at position 3 (0-based)
' Buffer: "abcdef "
'
' Approach based on cStringBuilder from vbAccelerator
' http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.asp
 
Dim json_AppendLength AsLong
Dim json_LengthPlusPosition AsLong
 
json_AppendLength = VBA.LenB(json_Append)
json_LengthPlusPosition = json_AppendLength + json_BufferPosition
 
If json_LengthPlusPosition > json_BufferLength Then
' Appending would overflow buffer, add chunks until buffer is long enough
Dim json_TemporaryLength AsLong
 
json_TemporaryLength = json_BufferLength
DoWhile json_TemporaryLength < json_LengthPlusPosition
' Initially, initialize string with 255 characters,
' then add large chunks (8192) after that
'
' Size: # Characters x 2 bytes / character
If json_TemporaryLength = 0 Then
json_TemporaryLength = json_TemporaryLength + 510
Else
json_TemporaryLength = json_TemporaryLength + 16384
EndIf
Loop
 
json_buffer = json_buffer & VBA.Space$((json_TemporaryLength - json_BufferLength) \ 2)
json_BufferLength = json_TemporaryLength
EndIf
 
' Copy memory from append to buffer at buffer position
json_CopyMemory ByVal json_UnsignedAdd(StrPtr(json_buffer), _
json_BufferPosition), _
ByVal StrPtr(json_Append), _
json_AppendLength
 
json_BufferPosition = json_BufferPosition + json_AppendLength
#End If
EndSub
 
PrivateFunction json_BufferToString(ByRef json_buffer AsString, ByVal json_BufferPosition AsLong, ByVal json_BufferLength AsLong) AsString
#If Mac Then
json_BufferToString = json_buffer
#Else
If json_BufferPosition > 0 Then
json_BufferToString = VBA.Left$(json_buffer, json_BufferPosition \ 2)
EndIf
#End If
EndFunction
 
#If VBA7 Then
PrivateFunction json_UnsignedAdd(json_Start As LongPtr, json_Increment AsLong) As LongPtr
#Else
PrivateFunction json_UnsignedAdd(json_Start AsLong, json_Increment AsLong) AsLong
#End If
 
If json_Start And&H80000000 Then
json_UnsignedAdd = json_Start + json_Increment
ElseIf (json_Start Or&H80000000) < -json_Increment Then
json_UnsignedAdd = json_Start + json_Increment
Else
json_UnsignedAdd = (json_Start + &H80000000) + (json_Increment + &H80000000)
EndIf
EndFunction
 
''
' VBA-UTC v1.0.3
' (c) Tim Hall - https://github.com/VBA-tools/VBA-UtcConverter
'
' UTC/ISO 8601 Converter for VBA
'
' Errors:
' 10011 - UTC parsing error
' 10012 - UTC conversion error
' 10013 - ISO 8601 parsing error
' 10014 - ISO 8601 conversion error
'
' @module UtcConverter
' @author tim.hall.engr@gmail.com
' @license MIT (http://www.opensource.org/licenses/mit-license.php)
'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
 
' (Declarations moved to top)
 
' ============================================= '
' Public Methods
' ============================================= '
 
''
' Parse UTC date to local date
'
' @method ParseUtc
' @param {Date} UtcDate
' @return {Date} Local date
' @throws 10011 - UTC parsing error
''
PublicFunction ParseUtc(utc_UtcDate AsDate) AsDate
OnErrorGoTo utc_ErrorHandling
 
#If Mac Then
ParseUtc = utc_ConvertDate(utc_UtcDate)
#Else
Dim utc_TimeZoneInfo As utc_TIME_ZONE_INFORMATION
Dim utc_LocalDate As utc_SYSTEMTIME
 
utc_GetTimeZoneInformation utc_TimeZoneInfo
utc_SystemTimeToTzSpecificLocalTime utc_TimeZoneInfo, utc_DateToSystemTime(utc_UtcDate), utc_LocalDate
 
ParseUtc = utc_SystemTimeToDate(utc_LocalDate)
#End If
 
ExitFunction
 
utc_ErrorHandling:
Err.Raise 10011, "UtcConverter.ParseUtc", "UTC parsing error: "& Err.Number & " - "& Err.Description
EndFunction
 
''
' Convert local date to UTC date
'
' @method ConvertToUrc
' @param {Date} utc_LocalDate
' @return {Date} UTC date
' @throws 10012 - UTC conversion error
''
PublicFunction ConvertToUtc(utc_LocalDate AsDate) AsDate
OnErrorGoTo utc_ErrorHandling
 
#If Mac Then
ConvertToUtc = utc_ConvertDate(utc_LocalDate, utc_ConvertToUtc:=True)
#Else
Dim utc_TimeZoneInfo As utc_TIME_ZONE_INFORMATION
Dim utc_UtcDate As utc_SYSTEMTIME
 
utc_GetTimeZoneInformation utc_TimeZoneInfo
utc_TzSpecificLocalTimeToSystemTime utc_TimeZoneInfo, utc_DateToSystemTime(utc_LocalDate), utc_UtcDate
 
ConvertToUtc = utc_SystemTimeToDate(utc_UtcDate)
#End If
 
ExitFunction
 
utc_ErrorHandling:
Err.Raise 10012, "UtcConverter.ConvertToUtc", "UTC conversion error: "& Err.Number & " - "& Err.Description
EndFunction
 
''
' Parse ISO 8601 date string to local date
'
' @method ParseIso
' @param {Date} utc_IsoString
' @return {Date} Local date
' @throws 10013 - ISO 8601 parsing error
''
PublicFunction ParseIso(utc_IsoString AsString) AsDate
OnErrorGoTo utc_ErrorHandling
 
Dim utc_Parts() AsString
Dim utc_DateParts() AsString
Dim utc_TimeParts() AsString
Dim utc_OffsetIndex AsLong
Dim utc_HasOffset AsBoolean
Dim utc_NegativeOffset AsBoolean
Dim utc_OffsetParts() AsString
Dim utc_Offset AsDate
 
utc_Parts = VBA.Split(utc_IsoString, "T")
utc_DateParts = VBA.Split(utc_Parts(0), "-")
ParseIso = VBA.DateSerial(VBA.CInt(utc_DateParts(0)), VBA.CInt(utc_DateParts(1)), VBA.CInt(utc_DateParts(2)))
 
IfUBound(utc_Parts) > 0 Then
If VBA.InStr(utc_Parts(1), "Z") Then
utc_TimeParts = VBA.Split(VBA.Replace(utc_Parts(1), "Z", ""), ":")
Else
utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), "+")
If utc_OffsetIndex = 0 Then
utc_NegativeOffset = True
utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), "-")
EndIf
 
If utc_OffsetIndex > 0 Then
utc_HasOffset = True
utc_TimeParts = VBA.Split(VBA.Left$(utc_Parts(1), utc_OffsetIndex - 1), ":")
utc_OffsetParts = VBA.Split(VBA.Right$(utc_Parts(1), Len(utc_Parts(1)) - utc_OffsetIndex), ":")
 
SelectCaseUBound(utc_OffsetParts)
Case 0
utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), 0, 0)
Case 1
utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), VBA.CInt(utc_OffsetParts(1)), 0)
Case 2
' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues
utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), VBA.CInt(utc_OffsetParts(1)), Int(VBA.Val(utc_OffsetParts(2))))
EndSelect
 
If utc_NegativeOffset Then: utc_Offset = -utc_Offset
Else
utc_TimeParts = VBA.Split(utc_Parts(1), ":")
EndIf
EndIf
 
SelectCaseUBound(utc_TimeParts)
Case 0
ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), 0, 0)
Case 1
ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), 0)
Case 2
' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues
ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), Int(VBA.Val(utc_TimeParts(2))))
EndSelect
 
ParseIso = ParseUtc(ParseIso)
 
If utc_HasOffset Then
ParseIso = ParseIso + utc_Offset
EndIf
EndIf
 
ExitFunction
 
utc_ErrorHandling:
Err.Raise 10013, "UtcConverter.ParseIso", "ISO 8601 parsing error for "& utc_IsoString & ": "& Err.Number & " - "& Err.Description
EndFunction
 
''
' Convert local date to ISO 8601 string
'
' @method ConvertToIso
' @param {Date} utc_LocalDate
' @return {Date} ISO 8601 string
' @throws 10014 - ISO 8601 conversion error
''
PublicFunction ConvertToIso(utc_LocalDate AsDate) AsString
OnErrorGoTo utc_ErrorHandling
 
ConvertToIso = VBA.Format$(ConvertToUtc(utc_LocalDate), "yyyy-mm-ddTHH:mm:ss.000Z")
 
ExitFunction
 
utc_ErrorHandling:
Err.Raise 10014, "UtcConverter.ConvertToIso", "ISO 8601 conversion error: "& Err.Number & " - "& Err.Description
EndFunction
 
' ============================================= '
' Private Functions
' ============================================= '
 
#If Mac Then
 
PrivateFunction utc_ConvertDate(utc_Value AsDate, Optional utc_ConvertToUtc AsBoolean = False) AsDate
Dim utc_ShellCommand AsString
Dim utc_Result As utc_ShellResult
Dim utc_Parts() AsString
Dim utc_DateParts() AsString
Dim utc_TimeParts() AsString
 
If utc_ConvertToUtc Then
utc_ShellCommand = "date -ur `date -jf '%Y-%m-%d %H:%M:%S'"& _
"'"& VBA.Format$(utc_Value, "yyyy-mm-dd HH:mm:ss") & "'"& _
" +'%s'` +'%Y-%m-%d %H:%M:%S'"
Else
utc_ShellCommand = "date -jf '%Y-%m-%d %H:%M:%S %z'"& _
"'"& VBA.Format$(utc_Value, "yyyy-mm-dd HH:mm:ss") & " +0000'"& _
"+'%Y-%m-%d %H:%M:%S'"
EndIf
 
utc_Result = utc_ExecuteInShell(utc_ShellCommand)
 
If utc_Result.utc_Output = ""Then
Err.Raise 10015, "UtcConverter.utc_ConvertDate", "'date' command failed"
Else
utc_Parts = Split(utc_Result.utc_Output, "")
utc_DateParts = Split(utc_Parts(0), "-")
utc_TimeParts = Split(utc_Parts(1), ":")
 
utc_ConvertDate = DateSerial(utc_DateParts(0), utc_DateParts(1), utc_DateParts(2)) + _
TimeSerial(utc_TimeParts(0), utc_TimeParts(1), utc_TimeParts(2))
EndIf
EndFunction
 
PrivateFunction utc_ExecuteInShell(utc_ShellCommand AsString) As utc_ShellResult
#If VBA7 Then
Dim utc_File As LongPtr
Dim utc_Read As LongPtr
#Else
Dim utc_File AsLong
Dim utc_Read AsLong
#End If
 
Dim utc_Chunk AsString
 
OnErrorGoTo utc_ErrorHandling
utc_File = utc_popen(utc_ShellCommand, "r")
 
If utc_File = 0 Then: ExitFunction
 
DoWhile utc_feof(utc_File) = 0
utc_Chunk = VBA.Space$(50)
utc_Read = utc_fread(utc_Chunk, 1, Len(utc_Chunk) - 1, utc_File)
If utc_Read > 0 Then
utc_Chunk = VBA.Left$(utc_Chunk, utc_Read)
utc_ExecuteInShell.utc_Output = utc_ExecuteInShell.utc_Output & utc_Chunk
EndIf
Loop
 
utc_ErrorHandling:
utc_ExecuteInShell.utc_ExitCode = utc_pclose(utc_File)
EndFunction
 
#Else
 
PrivateFunction utc_DateToSystemTime(utc_Value AsDate) As utc_SYSTEMTIME
utc_DateToSystemTime.utc_wYear = VBA.Year(utc_Value)
utc_DateToSystemTime.utc_wMonth = VBA.Month(utc_Value)
utc_DateToSystemTime.utc_wDay = VBA.Day(utc_Value)
utc_DateToSystemTime.utc_wHour = VBA.Hour(utc_Value)
utc_DateToSystemTime.utc_wMinute = VBA.Minute(utc_Value)
utc_DateToSystemTime.utc_wSecond = VBA.Second(utc_Value)
utc_DateToSystemTime.utc_wMilliseconds = 0
EndFunction
 
PrivateFunction utc_SystemTimeToDate(utc_Value As utc_SYSTEMTIME) AsDate
utc_SystemTimeToDate = DateSerial(utc_Value.utc_wYear, utc_Value.utc_wMonth, utc_Value.utc_wDay) + _
TimeSerial(utc_Value.utc_wHour, utc_Value.utc_wMinute, utc_Value.utc_wSecond)
EndFunction
 
#End If

- For Windows-only support, include a reference to "Microsoft Scripting Runtime"


- For Mac and Windows support, include [VBA-Dictionary](https://github.com/VBA-tools/VBA-Dictionary)




# Examples



Dim Json AsObject
 
Set Json = JsonConverter.ParseJson("{""a"":123,""b"":[1,2,3,4],""c"":{""d"":456}}")
 
' Json("a") -> 123
' Json("b")(2) -> 2
' Json("c")("d") -> 456
Json("c")("e") = 789
 
 
 
Debug.Print JsonConverter.ConvertToJson(Json)
 
' -> "{"a":123,"b":[1,2,3,4],"c":{"d":456,"e":789}}"
 
Debug.Print JsonConverter.ConvertToJson(Json, Whitespace:=2)
 
' -> "{
'"a": 123,
'"b": [
' 1,
' 2,
' 3,
' 4
' ],
'"c": {
'"d": 456,
'"e": 789
' }
' }"
```
 
 
 
```vb
 
' Advanced example: Read .json file and load into sheet (Windows-only)
' (add reference to Microsoft Scripting Runtime)
' {"values":[{"a":1,"b":2,"c": 3},...]}
 
Dim FSO AsNew FileSystemObject
Dim JsonTS As TextStream
Dim JsonText AsString
Dim Parsed As Dictionary
 
' Read .json file
 
Set JsonTS = FSO.OpenTextFile("example.json", ForReading)
JsonText = JsonTS.ReadAll
JsonTS.Close
 
 
' Parse json to Dictionary
'"values" is parsed as Collection
' each item in "values" is parsed as Dictionary
 
Set Parsed = JsonConverter.ParseJson(JsonText)
 
' Prepare and write values to sheet
 
Dim Values AsVariant
ReDim Values(Parsed("values").Count, 3)
Dim Value As Dictionary
Dim i AsLong
 
i = 0
 
ForEach Value In Parsed("values")
Values(i, 0) = Value("a")
Values(i, 1) = Value("b")
Values(i, 2) = Value("c")
i = i + 1
Next Value
 
Sheets("example").Range(Cells(1, 1), Cells(Parsed("values").Count, 3)) = Values
## Options
VBA-JSON includes a few options for customizing parsing/conversion if needed:
- __UseDoubleForLargeNumbers__ (Default = `False`) VBA only stores 15 significant digits, so any numbers larger than that are truncated.
  This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits.
  By default, VBA-JSON will use `String` for numbers longer than 15 characters that contain only digits, use this option to use `Double` instead.
- __AllowUnquotedKeys__ (Default = `False`) The JSON standard requires object keys to be quoted (`"` or `'`), use this option to allow unquoted keys.
- __EscapeSolidus__ (Default = `False`) The solidus (`/`) is not required to be escaped, use this option to escape them as `\/` in `ConvertToJson`.
```VB.net
JsonConverter.JsonOptions.EscapeSolidus = True
```
## Installation
1. Download the [latest release](https://github.com/VBA-tools/VBA-JSON/releases)
2. Import `JsonConverter.bas` into your project (Open VBA Editor, `Alt + F11`; File > Import File)
3. Add `Dictionary` reference/class
   - For Windows-only, include a reference to "Microsoft Scripting Runtime"
   - For Windows and Mac, include [VBA-Dictionary](https://github.com/VBA-tools/VBA-Dictionary)
## Resources
- [Tutorial Video (Red Stapler)](https://youtu.be/CFFLRmHsEAs)
 

How to inject shellcode from VB6 to a remote process and interact with it (by Davide Chiappetta)

Matrix multiplication - math in code (by Davide Chiappetta)

Array, array, array (by Davide Chiappetta)

PE 64 bit vs 32 bit (by Davide Chiappetta)

Pool 3D game with Artificial Intelligence in VB6 (by Mikle)

$
0
0
Look at my new game. Written, as usual, on the VB6, size - 32 KB (compressed by UPX). Works on Windows from XP to 10, does not require installation. System requirements are minimal (all in ReadMe). You can play together or against the AI, or watch two AI battle.







Viewing all 181 articles
Browse latest View live