Any thoughts on security

General Help regarding HMG, Compilation, Linking, Samples

Moderator: Rathinagiri

franco
Posts: 877
Joined: Sat Nov 02, 2013 5:42 am
DBs Used: DBF
Location: Canada

Any thoughts on security

Post by franco »

Is there any posts or does anybody have thoughts on security of your written programs.
We spend a lot of time designing and if we sell a program to a client. How do we stop them from giving it to another location or even another person.
My bigger programs create the tables when not there then run. I have a few questions at the start and create asc codes from the answers.
Any thoughts. Thanks in advance.
Franco
All The Best,
Franco
Canada
User avatar
AUGE_OHR
Posts: 2093
Joined: Sun Aug 25, 2019 3:12 pm
DBs Used: DBF, PostgreSQL, MySQL, SQLite
Location: Hamburg, Germany

Re: Any thoughts on security

Post by AUGE_OHR »

hi Franco,

as i can say there is only Way are using Hardware-Dongle to protect Software.
all other "Tricks" can be hacked if know how
---

when change Hardware you might activate Windows again.
same can happens to your App when use Hardware based Protection.

so when Customer get new PC you want to make him pay for new License :?:
when use a Hardware-Dongle you can put it to new PC and is fine
have fun
Jimmy
User avatar
dragancesu
Posts: 930
Joined: Mon Jun 24, 2013 11:53 am
DBs Used: DBF, MySQL, Oracle
Location: Subotica, Serbia

Re: Any thoughts on security

Post by dragancesu »

I guess all programmers have the same problems, sometimes less, sometimes more.
As a colleague said, you have two ways, hardware and software.
Hardware's is as it is and you choose it as you like
But programmers prefer software, which implies that the software is tied to the computer: it can be tied to the bios (date), to the disk (serial number), or to some hidden file on the computer (in the Windows folder, so let's find which one it is 1000+ files)
it's simpler when the application is a client-server, so the program is in one place, and the data is in another place, the triggers in the database know everything...
edk
Posts: 999
Joined: Thu Oct 16, 2014 11:35 am
Location: Poland

Re: Any thoughts on security

Post by edk »

You can check the properties of the computer that runs the application and compare them with some algorithm that calculates the checksum of these properties.

In the example below, processors, disks, network cards and UUID are checked. If the calculated checksum of these properties does not match the sum saved in the example.ini file (it can of course be any database, etc.), the program will not run.

Activation methods can be implemented through various mechanisms, here I used a simple mechanism of calling the application with the "/ActivationByLicensor" parameter.

Code: Select all

#include <hmg.ch>

#pragma TEXTHIDDEN(1)       /* obfuscating strings in an executable file */

Function Main ( xMode )

Local cPC_Properties := GetPCProperties ( .F. )
Local cPC_ID         := hb_SHA384 ( cPC_Properties )
Local cINIFile       := hb_CWD() + "example.ini"
Local hINI

IF !File( cINIFile )
    hINI := hb_iniNew( .F. )
    hb_HSet ( hINI, "ID", "" )
    hb_iniWrite( cINIFile, hINI )
ENDIF

hb_iniSetComment( Chr ( 30 ), Chr ( 31 ) ) 

hINI := hb_iniRead( cINIFile, , , .F. )

IF .Not. Empty ( xMode ) .And. xMode == "/ActivationByLicensor"
    hINI [ "ID" ] := cPC_ID 
    hb_iniWrite( cINIFile, hINI )
ENDIF

IF .Not. ( cPC_ID == hINI [ "ID" ] )  
    MsgStop ( "This computer does not have the correct activation code for the application to work.", "STOP!" )
    CLOSE DATA
    QUIT
ENDIF

MsgInfo ( "Your PC Properties :" + CRLF + ;
          cPC_Properties + CRLF + CRLF +;
          "Your PC ID encrypted hash code :" + CRLF + ;
          cPC_ID )
                
Return

************************************
FUNCTION WMIService()
Local oWMI, oLocator
oLocator := win_oleCreateObject( "wbemScripting.SwbemLocator" )
oWMI     := oLocator:ConnectServer( , "root\cimv2" )
Return oWMI
************************************
FUNCTION GetPCProperties ( lAll )
Local oWMI, objItem
Local aProperties
Local cData := "", cProperty

Default lAll := .F.

oWMI := WMIService()

//CPUs
IF lAll
    aProperties := { "AddressWidth", "Architecture", "AssetTag", "Availability", "Caption", "Characteristics", ;
                "ConfigManagerErrorCode", "ConfigManagerUserConfig", "CpuStatus", "CreationClassName", ;
                "CurrentClockSpeed", "CurrentVoltage", "DataWidth", "Description", "DeviceID", "ErrorCleared", ;
                "ErrorDescription", "ExtClock", "Family", "InstallDate", "L2CacheSize", "L2CacheSpeed", ;
                "L3CacheSize", "L3CacheSpeed", "LastErrorCode", "Level", "LoadPercentage", "Manufacturer", ;
                "MaxClockSpeed", "Name", "NumberOfCores", "NumberOfEnabledCore", "NumberOfLogicalProcessors", ;
                "OtherFamilyDescription", "PartNumber", "PNPDeviceID", "PowerManagementCapabilities", ;
                "PowerManagementSupported", "ProcessorId", "ProcessorType", "Revision", "Role", ;
                "SecondLevelAddressTranslationExtensions", "SerialNumber", "SocketDesignation", "Status", ;
                "StatusInfo", "Stepping", "SystemCreationClassName", "SystemName", "ThreadCount", "UniqueId", ;
                "UpgradeMethod", "Version", "VirtualizationFirmwareEnabled", "VMMonitorModeExtensions", ;
                "VoltageCaps" }
ELSE
    aProperties := { "Name", "ProcessorId" }
ENDIF

For Each objItem In oWMI:ExecQuery("Select * from Win32_Processor")
    cData += "CPU: " + CRLF
    For Each cProperty In aProperties
        cData += cProperty + ": " + hb_ValToExp ( objItem:&(cProperty) ) + CRLF
    Next 
Next

//Disk Drivers
IF lAll
    aProperties := { "Availability", "BytesPerSector", "Capabilities", "CapabilityDescriptions", ;
                "Caption", "CompressionMethod", "ConfigManagerErrorCode", "ConfigManagerUserConfig", ;
                "CreationClassName", "DefaultBlockSize", "Description", "DeviceID", "ErrorCleared", ;
                "ErrorDescription", "ErrorMethodology", "FirmwareRevision", "Index", "InstallDate", ;
                "InterfaceType", "LastErrorCode", "Manufacturer", "MaxBlockSize", "MaxMediaSize", ;
                "MediaLoaded", "MediaType", "MinBlockSize", "Model", "Name", "NeedsCleaning", ;
                "NumberOfMediaSupported", "Partitions", "PNPDeviceID", "PowerManagementCapabilities", ;
                "PowerManagementSupported", "SCSIBus", "SCSILogicalUnit", "SCSIPort", "SCSITargetId", ;
                "SectorsPerTrack", "SerialNumber", "Signature", "Size", "Status", "StatusInfo", ;
                "SystemCreationClassName", "SystemName", "TotalCylinders", "TotalHeads", "TotalSectors", ;
                "TotalTracks", "TracksPerCylinder" }

ELSE

    aProperties := { "FirmwareRevision", "SerialNumber", "Signature" }

ENDIF

For Each objItem In oWMI:ExecQuery("Select * from Win32_DiskDrive")
    cData += "DISK: " + CRLF
    For Each cProperty In aProperties
        cData += cProperty + ": " + hb_ValToExp ( objItem:&(cProperty) ) + CRLF
    Next 
Next

//NICs
IF lAll
    aProperties := { "AdapterType",  "AdapterTypeId", "AutoSense", "Availability", "Caption", ;
                "ConfigManagerErrorCode", "ConfigManagerUserConfig", "CreationClassName", ;
                "Description", "DeviceID", "ErrorCleared", "ErrorDescription", "GUID", ;
                "Index", "InstallDate", "Installed", "InterfaceIndex", "LastErrorCode", ;
                "MACAddress", "Manufacturer", "MaxNumberControlled", "MaxSpeed", "Name", ;
                "NetConnectionID", "NetConnectionStatus", "NetEnabled", "NetworkAddresses", ;
                "PermanentAddress", "PhysicalAdapter", "PNPDeviceID", "PowerManagementCapabilities", ;
                "PowerManagementSupported", "ProductName", "ServiceName", "Speed", "Status", ;
                "StatusInfo", "SystemCreationClassName", "SystemName", "TimeOfLastReset" }
ELSE

    aProperties := { "GUID", "MACAddress" }

ENDIF

For Each objItem In oWMI:ExecQuery("Select * from Win32_NetworkAdapter Where PhysicalAdapter = TRUE")
    cData += "NIC: " + CRLF
    For Each cProperty In aProperties
        cData += cProperty + ": " + hb_ValToExp ( objItem:&(cProperty) ) + CRLF
    Next 
Next

//NIC Configs
IF lAll
    aProperties := { "ArpAlwaysSourceRoute", "ArpUseEtherSNAP", "Caption", "DatabasePath", ;
                "DeadGWDetectEnabled", "DefaultIPGateway", "DefaultTOS", "DefaultTTL", ;
                "Description", "DHCPEnabled", "DHCPLeaseExpires", "DHCPLeaseObtained", ;
                "DHCPServer", "DNSDomain", "DNSDomainSuffixSearchOrder","DNSEnabledForWINSResolution", ;
                "DNSHostName", "DNSServerSearchOrder", "DomainDNSRegistrationEnabled", ;
                "ForwardBufferMemory", "FullDNSRegistrationEnabled", "GatewayCostMetric", ;
                "IGMPLevel", "Index", "InterfaceIndex", "IPAddress", "IPConnectionMetric", ;
                "IPEnabled", "IPFilterSecurityEnabled", "IPPortSecurityEnabled", "IPSecPermitIPProtocols", ;
                "IPSecPermitTCPPorts", "IPSecPermitUDPPorts", "IPSubnet", "IPUseZeroBroadcast", ;
                "IPXAddress", "IPXEnabled", "IPXFrameType", "IPXMediaType", "IPXNetworkNumber", ;
                "IPXVirtualNetNumber", "KeepAliveInterval", "KeepAliveTime", "MACAddress", ;
                "MTU", "NumForwardPackets", "PMTUBHDetectEnabled", "PMTUDiscoveryEnabled", ;
                "ServiceName", "SettingID", "TcpipNetbiosOptions", "TcpMaxConnectRetransmissions", ;
                "TcpMaxDataRetransmissions", "TcpNumConnections", "TcpUseRFC1122UrgentPointer", ;
                "TcpWindowSize", "WINSEnableLMHostsLookup", "WINSHostLookupFile", "WINSPrimaryServer", ;
                "WINSScopeID", "WINSSecondaryServer" }

ELSE

    aProperties := { }
                
ENDIF
                
For Each objItem In oWMI:ExecQuery("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = TRUE")
    For Each cProperty In aProperties
        cData += cProperty + ": " + hb_ValToExp ( objItem:&(cProperty) ) + CRLF
    Next 
Next

//Computer System Product
IF lAll
    aProperties := { "Caption", "Description", "IdentifyingNumber", "Name", "SKUNumber", ;
                "Vendor", "Version", "UUID" }
ELSE
    aProperties := { "IdentifyingNumber", "Name", "SKUNumber", "Vendor", "UUID" }
ENDIF

For Each objItem In oWMI:ExecQuery("Select * From Win32_ComputerSystemProduct")
    cData += "CSP: " + CRLF
    For Each cProperty In aProperties
        cData += cProperty + ": " + hb_ValToExp ( objItem:&(cProperty) ) + CRLF
    Next 
Next

Return cData

#pragma TEXTHIDDEN(0)
User avatar
serge_girard
Posts: 3309
Joined: Sun Nov 25, 2012 2:44 pm
DBs Used: 1 MySQL - MariaDB
2 DBF
Location: Belgium
Contact:

Re: Any thoughts on security

Post by serge_girard »

Thanks for sharing Edward!
There's nothing you can do that can't be done...
franco
Posts: 877
Joined: Sat Nov 02, 2013 5:42 am
DBs Used: DBF
Location: Canada

Re: Any thoughts on security

Post by franco »

I just had a new thought. Email address. Everyone has a different email address. So scramble the email address that they put in at start of program.
Then if there is a change I could send an email to that address with a new code. The code would be a part of their original email.
If the email does not match the email in my sales file to them nothing will work. Microsoft and Google do this now.
All The Best,
Franco
Canada
franco
Posts: 877
Joined: Sat Nov 02, 2013 5:42 am
DBs Used: DBF
Location: Canada

Re: Any thoughts on security

Post by franco »

Sorry guys, I wrote a large post before my last post but it did not record.
Thanks for ideas.
Jimmy good idea but hardware can break and in a business environment there would be no time for repair.
Dragon, that file could be created from email address.
Edward, thanks very interesting, I need to study more.
I think I have asked this question before. Is there a way to open exe file and change or add code.
Also is there a way to get a gps location from inside hmg.
Thanks Franco
All The Best,
Franco
Canada
edk
Posts: 999
Joined: Thu Oct 16, 2014 11:35 am
Location: Poland

Re: Any thoughts on security

Post by edk »

It depends on how you license your software. If per company then Ok. If per PC then a rogue customer can run the application on an unlimited number of PCs registered to a common email address.

I have on large applications to check how many workstations are logged into a common enterprise database. This is done online, where each station at startup sends an enterprise tax ID to my server over the internet. My server then checks how many active initiations there are, if it doesn't exceed the license count then it allows the startup, if it exceeds then there is a message to log off from another workstation to work on the new one. The feedback from my server to the workstation each time is dynamically encoded, so that even if someone intercepts the feedback packet, when you try to log in again it is different from the previous one. If there is a problem with Internet communication then the application allows you to run without checking the license only for the next 36 hours of real application operation. After that time there must be a license check via the Internet. In exceptional situations I can send a one-time token to the customer to extend the offline operation for the next 24 hours. To avoid the problem of the client changing the date and clock, the application counts the working time and does not take data from the RTC.

Please forgive me but I will not share the code of my licensing for known reasons 😉
franco
Posts: 877
Joined: Sat Nov 02, 2013 5:42 am
DBs Used: DBF
Location: Canada

Re: Any thoughts on security

Post by franco »

Thanks for thought Edward. There seem so many ways that a person can set this up. My latest thought is to make sure the main program or server
in my case is in the C: drive where windows is. Easy to set up so terminals can only do part of what the main can do. THEY ARE WORK STATIONS.
This way we can bury information in the c drive to start with. What this information is we have to decide. I do not use on line servers.
All The Best,
Franco
Canada
franco
Posts: 877
Joined: Sat Nov 02, 2013 5:42 am
DBs Used: DBF
Location: Canada

Re: Any thoughts on security

Post by franco »

This is to do with my security idea.
I am trying to execute an exe file from inside another exe file.
The second program creates the tables for the first program. I only want them to be created once then erase the program creating them.
When I execute file() then I want to delete the file but the main program continues the file will not delete.
like below winlog1.ese is a renamed exe file

Code: Select all

if file('winlog1.ese')
	copy file winlog1.ese to winlog1.exe                          //This works
	msgbox('You Must Restart Program After Entering the Following')               //This works
	delete file('winlog1.ese')                                            //This works
	execute file('winlog1.exe')                                         //This works
	delete file('winlog1.exe')                  // this does not work I think because file is still open. Is there a way to make sure program has 
	cancel                                             //     finished so it can be deleted before continuing the first program.
	return
endif	                                             
I am also playing with after creating an exe file to edit it and take the first 2 characters of the file then later in a different program add them back on. This is the same as what I am trying to do above. obviously the program will not run with characters removed.
I have made this work but having a bit of trouble I know I made it work but forgot how. I need to check.
memvar := memoread('winlog1.exe')
memowrit('winlog1.exe','MZ'+substr(memvar,3,len(memvar)))
With this a program will not work but you change it from inside another program , then it will work. Then change it back with
memvar := memoread('winlog1.exe')
memowrit('winlog1.exe',substr(memvar,3,len(memvar)))

Thank You in Advance.
All The Best,
Franco
Canada
Post Reply