How to get a mac address from scanning a local network when Firewall is blocking ping - ping

For a Security product I am currently writing in Delphi. I am using the below solution to use the ARP table to get MAC-Addresses from devices to detected what is on the network.
How to find MAC addresses from arp -a scan
I just do a range of Ping commands to fill the ARP table and read results from the ARP-table.
However when a Firewall on a machine is blocking Ping. Sometimes the MAC is still exposed in ARP, but not always. What is a better solution to detect all devices on a network and get MAC-Addresses from them?
//-----------------------------------------------------------------------------
{ ARP-table lists relations between remote IP and remote MAC-address.
NOTE: these are cached entries;when there is no more network traffic to a
node, entry is deleted after a few minutes.
}
//-----------------------------------------------------------------------------
procedure Get_ARPTable( aList: TStrings; aDeviceList : TObjectList<TNetworkDevice>);
var
lIPNetRow : TMibIPNetRow;
lTableSize : DWORD;
lNumEntries : DWORD;
lErrorCode : DWORD;
lIdx : Integer;
lPBuf : PAnsiChar;
lPhysAddr : TMACAddress;
lMacAddr2Str : string;
ldwAddr : string;
ldwType : string;
lNewDevice : TNetworkDevice;
begin
if not LoadIpHlp then Exit;
if not Assigned( aList ) then Exit;
if not Assigned( aDeviceList) then Exit;
Get_LocalNetworkDevices(aDeviceList);
aList.Clear;
lTableSize := 0;
lErrorCode := GetIPNetTable( Nil, #lTableSize, False );
//
if lErrorCode = ERROR_NO_DATA then
begin
aList.Add( ' ARP-cache empty.' );
EXIT;
end;
// get table
GetMem( lPBuf, lTableSize );
lNumEntries := 0;
try
lErrorCode := GetIpNetTable( PTMIBIPNetTable( lPBuf ), #lTableSize, False );
if lErrorCode = NO_ERROR then
begin
lNumEntries := PTMIBIPNetTable( lPBuf )^.dwNumEntries;
if lNumEntries > 0 then
begin
Inc( lPBuf, SizeOf( DWORD ) );
for lIdx := 1 to lNumEntries do
begin
lIPNetRow := PTMIBIPNetRow( lPBuf )^;
lMacAddr2Str := MacAddr2Str( lIPNetRow.bPhysAddr, lIPNetRow.dwPhysAddrLen );
lPhysAddr := lIPNetRow.bPhysAddr;
ldwAddr := IPAddr2StrTrunc(lIPNetRow.dwAddr);
ldwType := ARPEntryType[lIPNetRow.dwType];
lNewDevice := SeekDevice(aDeviceList, lMacAddr2Str);
if Assigned(lNewDevice) then
begin
lNewDevice.IP := ldwAddr;
lNewDevice.IsNew := False;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
if (lNewDevice.EntryType = 'Dynamic') or
(lNewDevice.EntryType = 'Static') then
lNewDevice.SetStamp;
end
else
begin
lNewDevice := TNetworkDevice.Create;
lNewDevice.IP := ldwAddr;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
lNewDevice.AddOrUpdate(lMacAddr2Str);
lNewDevice.SetFirstSeen;
lNewDevice.SetStamp;
lNewDevice.State := dtRogue;
lNewDevice.IsNew := True;
aDeviceList.Add(lNewDevice);
end;
with lIPNetRow do
begin
aList.Add( Format( '%8x | %12s | %16s| %10s',
[dwIndex, MacAddr2Str( bPhysAddr, dwPhysAddrLen ),
IPAddr2Str( dwAddr ), ARPEntryType[dwType]
]));
end;
Inc( lPBuf, SizeOf( lIPNetRow ) );
end;
end
else
aList.Add( ' ARP-cache empty.' );
end
else
aList.Add( SysErrorMessage( lErrorCode ) );
// we _must_ restore Pointer!
finally
Dec( lPBuf, SizeOf( DWORD ) + lNumEntries * SizeOf( lIPNetRow ) );
FreeMem( lPBuf );
end;
end;

To complement ping, you can try to establish a TCP connection on any port, for example port 80 (HTTP). Of course you'll get mostly connection denied but you'll still be able to find an entry in the ARP table.
Depending on the firewall, you may also simply get to response at all. Then you have to try another port. I would suggest 135 (RPC) or 445 or 139 (Both SMB) which should be open is network shares are enabled. Another one in 3389 used by RDP if enabled.
This is practical only if the subnet is small (Class-C, 256 addresses) or medium (Class-B, 16K addresses). Anyway, it would take a lot of time.

Related

How to create installer with inno setup with Port Checking [duplicate]

I need to check some port is usable or not? How can do that in Inno Setup?
Is there any way to use socket in to Inno Setup? Is there any library for this? If there how can import it and use it?
Thank you for your answers.
You can use my function to check, if a port is available:
function CheckPortOccupied(Port:String):Boolean;
var
ResultCode: Integer;
begin
Exec(ExpandConstant('{cmd}'), '/C netstat -na | findstr'+' /C:":'+Port+' "', '', 0,
ewWaitUntilTerminated, ResultCode);
if ResultCode <> 1 then
begin
Log('this port('+Port+') is occupied');
Result := True;
end
else
begin
Result := False;
end;
end;
Function to return (in MsgBox) service or program that use port 80.
MsgBox will not shown if output is empty.
function NextButtonClick(CurPage: Integer): Boolean;
var
TmpFileName, ExecStdout: string;
ResultCode: integer;
begin
if CurPage = wpWelcome then
begin
TmpFileName := ExpandConstant('{tmp}') + '\~pid.txt';
Exec('cmd.exe',
'/C FOR /F "usebackq tokens=5 delims= " %i IN (`netstat -ano ^|find "0.0:80"`) DO '
+ '#tasklist /fi "pid eq %i" | find "%i" > "' + TmpFileName + '"', '', SW_HIDE,
ewWaitUntilTerminated, ResultCode);
if LoadStringFromFile(TmpFileName, ExecStdout) then
begin
MsgBox('Port 80 is used by '#13 + ExecStdout, mbInformation, MB_OK);
end;
DeleteFile(TmpFileName);
end;
Result := True;
end;

Delphi 2010 : UniDAC vs Indy-MultiThread safety handleing method

I am doing develop Indy based application.
Server has several Indy TCP Server components.
So It works under multi-threads and handles mysql db.
I have faced one problem.
That is about the exceptions of MySQL DB in threads.
When serveral threads attack to same db table, then It says me like follows
UniQuery_Mgr: Duplicate field name 'id'
UniQuery_Mgr: Field 'grp_id' not found //of course grp_id field is really existed.
Assertion failure (C:\Program Files (x86)\unidac539src\Source\CRVio.pas, line 255)
Commands out of sync; You can't run this command now
ReceiveHeader: Net packets out of order: received[0], expected[1]
UniQuery_Mgr: Cannot perform this operation on a closed dataset
How to do I ? UniQuery_Mgr is TUniQuery component.
and my query handling code is normally like this
Code 1
sql := 'SELECT * FROM data_writed;';//for example
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
Code 2
try
sql := 'SELECT * FROM gamegrp_mgr;';
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
if UniQuery_Mgr.RecordCount > 0 then
begin
MAX_GAME_GROUP_COUNT := UniQuery_Mgr.RecordCount + 1;
UniQuery_Mgr.First;
i := 1;
while not UniQuery_Mgr.Eof do
begin
Game_Group_ID[i] := UniQuery_Mgr.FieldByName('grp_id').AsInteger;
Game_Game_ID[i] := UniQuery_Mgr.FieldByName('game_id').AsInteger;
UniQuery_Mgr.Next;
Inc(i);
end;
end;
except
on E : Exception do
begin
EGAMEMSG := Format('GAME group read error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]);
Exit;
end;
end;
Code 3
try
sql := 'UPDATE data_writed SET write_gamegrp = ' + QuotedStr('0') + ';';
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
except
on E : Exception do
begin
EGAMEMSG := Format('data updating error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]);
Exit;
end;
end;
My handling DB components is bad ? Other thread-safe method is existed???

Getting MySQL process output

I want to play with MySQL process and get what ever it write to console so I write this code in FreePascal:
I want to control MySQl and read & write whatever.
Process := TProcess.Create(nil);
with Process do
begin
Executable := 'C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysql.exe';
with Parameters do
begin
Options := [poUsePipes];
Add('-u');
Add('root');
Add('-p');
end;
Execute;
sleep(1000);
WriteLn(Process.Output.NumBytesAvailable); // will be 0 but it write "Enter password"
WriteLn(Process.Stderr.NumBytesAvailable); // will be 0 but it write "Enter password"
end;
TProcess is a component that control executing other programs,I even test Delphi codes but all result are the same.
But the problem is that this will freeze because there is no output but console window write :
Enter password:
How can I get this in my application and all others?
As I said I want to work with MySQL executable and read from and write in it, So I dont want to use its library or any other DB component.
EDIT:
Here is a Delphi version of my test from here with the same result :
function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string;
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of AnsiChar;
BytesRead: Cardinal;
WorkDir: string;
Handle: Boolean;
begin
Result := '';
with SA do begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
CreatePipe(StdOutPipeRead, StdOutPipeWrite, #SA, 0);
try
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
WorkDir := Work;
Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine),
nil, nil, True, 0, nil,
PChar(WorkDir), SI, PI);
CloseHandle(StdOutPipeWrite);
if Handle then
try
repeat
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
if BytesRead > 0 then
begin
Buffer[BytesRead] := #0;
Result := Result + Buffer;
end;
until not WasOK or (BytesRead = 0);
WaitForSingleObject(PI.hProcess, INFINITE);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
finally
CloseHandle(StdOutPipeRead);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
memo1.text:=GetDosOutput('"C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysql.exe" -u root -p');
end;
It will freeze in WasOK line .
Also I tested many other codes for this subject and neither worked.

Inno Setup GetExceptionMessage returns empty message

in Inno Setup script GetExceptionMessage returns empty message (it contains only colon ":" sign). The last version of Inno Setup (5.4.2) is used.
try
Log('Create IISNamespace');
// Create IIS namespace object
if Length(virtualDirectoryName) > 0 then
begin
IIS := CreateOleObject('IISNamespace');
Log('Get IIsWebService');
WebSite := IIS.GetObject('IIsWebService', IISServerName + '/w3svc');
Log('Get IIsWebServer');
WebServer := WebSite.GetObject('IIsWebServer', IISServerNumber);
Log('Get IIsWebVirtualDir');
WebRoot := WebServer.GetObject('IIsWebVirtualDir', 'Root');
Log('Delete IIsWebVirtualDir');
WebRoot.Delete('IIsWebVirtualDir', virtualDirectoryName);
WebRoot.SetInfo();
end;
except
MsgBox(ExpandConstant('{cm:IISException,'+ GetExceptionMessage +'}'),
mbInformation, mb_Ok);
Log('Uninstall IIS 6 exception: ' + GetExceptionMessage);
end;
The exception occurs during deleting IIsWebVirtualDir.
Is there any way to get exception type or real exception message?
Thanks, Denis.
I just wrote the following example to see if either GetExceptionMessage or ShowExceptionMessage are broken. I used both Inno setup 5.4.2 Unicode and Ansi versions.
[Setup]
AppName=Test
AppVersion=1.5
DefaultDirName={pf}\test
[Code]
function InitializeSetup(): Boolean;
var
I: Integer;
begin
try
I := I div 0; // Raise an exception
except
MsgBox(GetExceptionMessage,
mbError, MB_OK);
ShowExceptionMessage;
end;
result := false;
end;
I also ran CodeAutomation.iss that ships and it worked as expected. Which is contrary to the comment made by Alex K. that it may be broken.
Now that I know that the routines should work, I took your code and made the following setup test Script and ran it and it raised an exception on not finding the ISSNamespace as I don't have it installed.
[Setup]
AppName=Test
AppVersion=1.5
DefaultDirName={pf}\test
[CustomMessages]
IISException =ISS Exception " %1 " occured.
[Code]
const
IISServerName = 'localhost';
IISServerNumber = '1';
IISURL = 'http://127.0.0.1';
function InitializeSetup(): Boolean;
var
IIS, WebSite, WebServer, WebRoot, VDir: Variant;
virtualDirectoryName : String;
begin
virtualDirectoryName := 'test';
try
Log('Create IISNamespace');
// Create IIS namespace object
if Length(virtualDirectoryName) > 0 then
begin
IIS := CreateOleObject('IISNamespace');
Log('Get IIsWebService');
WebSite := IIS.GetObject('IIsWebService', IISServerName + '/w3svc');
Log('Get IIsWebServer');
WebServer := WebSite.GetObject('IIsWebServer', IISServerNumber);
Log('Get IIsWebVirtualDir');
WebRoot := WebServer.GetObject('IIsWebVirtualDir', 'Root');
Log('Delete IIsWebVirtualDir');
WebRoot.Delete('IIsWebVirtualDir', virtualDirectoryName);
WebRoot.SetInfo();
end;
except
MsgBox(ExpandConstant('{cm:IISException,'+ GetExceptionMessage +'}'),
mbInformation, mb_Ok);
Log('Uninstall IIS 6 exception: ' + GetExceptionMessage);
end;
end;
But I made a fatal flaw during the construction of this script that could be your problem.
Check your [CustomMesssages] section make sure you have %1 in the message. Otherwise nothing is returned.

mysql call to libmysql.dll to get my app to automatically reconnect after mysql timeout

I am using autohotkey to make mysql calls. The mysql interface was deciphered by referencing a visual basic api to mysql.
I am using the mysql connect calls referenced in this post: http://www.autohotkey.com/forum/viewtopic.php?t=12482
I would like to add a dllcall to replicate this perl call to mysql_options...
mysql_options(mysql, MYSQL_OPT_RECONNECT, &true);
It is my understanding that this call would enable my program to gracefully reconnect to mysql after the standard 8 hour mysql timeout. I want my application to remain up indefinitely.
Here is my code. A reference on googles source code libary suggests that the reconnect constant is 20. Everything works except the mysql_opt_reconnect call.
Can anyone help me determine the proper call to libmysql.dll to get my app to automatically reconnect after mysql timeout has occurred?
;============================================================
; mysql.ahk
;
; Provides a set of functions to connect and query a mysql database
;============================================================
FileInstall, libmysql.dll, %A_AppData%\libmysql.dll, 1
;============================================================
; Connect to mysql database and return db handle
;
; host = DTWRO-WS0061
; user = alan
; password = *******
; database = rush
;============================================================
dbConnect(host,user,password,database){
if (A_IsCompiled) {
ExternDir := A_AppData
} else {
ExternDir := A_WorkingDir
}
hModule := DllCall("LoadLibrary", "Str", ExternDir "\libmySQL.dll")
If (hModule = 0)
{
MsgBox 16, MySQL Error 233, Can't load libmySQL.dll from directory %ExternDir%
ExitApp
}
db := DllCall("libmySQL.dll\mysql_init", "UInt", 0)
If (db = 0)
{
MsgBox 16, MySQL Error 445, Not enough memory to connect to MySQL
ExitApp
}
; figure out how to turn on reconnect call!
; mysql_options(mysql, MYSQL_OPT_RECONNECT, &true);
value := DllCall("libmySQL.dll\mysql_options"
, "UInt", db
, "UInt", 20 ; is this the correct constant which represents MYSQL_OPT_RECONNECT?... see below
, "UInt", 1) ; true
connection := DllCall("libmySQL.dll\mysql_real_connect"
, "UInt", db
, "Str", host ; host name
, "Str", user ; user name
, "Str", password ; password
, "Str", database ; database name
, "UInt", 3306 ; port
, "UInt", 0 ; unix_socket
, "UInt", 0) ; client_flag
If (connection = 0)
{
HandleMySQLError(db, "Cannot connect to database")
Return
}
serverVersion := DllCall("libmySQL.dll\mysql_get_server_info", "UInt", db, "Str")
;MsgBox % "Ping database: " . DllCall("libmySQL.dll\mysql_ping", "UInt", db) . "`nServer version: " . serverVersion
return db
}
;============================================================
; mysql error handling
;============================================================
HandleMySQLError(db, message, query="") { ; the equal sign means optional
errorCode := DllCall("libmySQL.dll\mysql_errno", "UInt", db)
errorStr := DllCall("libmySQL.dll\mysql_error", "UInt", db, "Str")
MsgBox 16, MySQL Error: %message%, Error %errorCode%: %errorStr%`n`n%query%
Return
}
;============================================================
; mysql get address
;============================================================
GetUIntAtAddress(_addr, _offset)
{
local addr
addr := _addr + _offset * 4
Return *addr + (*(addr + 1) << 8) + (*(addr + 2) << 16) + (*(addr + 3) << 24)
}
;============================================================
; process query
;============================================================
dbQuery(_db, _query)
{
local resultString, result, requestResult, fieldCount
local row, lengths, length, fieldPointer, field
query4error := RegExReplace(_query , "\t", " ") ; convert tabs to spaces so error message formatting is legible
result := DllCall("libmySQL.dll\mysql_query", "UInt", _db , "Str", _query)
If (result != 0) {
errorMsg = %_query%
HandleMySQLError(_db, "dbQuery Fail", query4error)
Return
}
requestResult := DllCall("libmySQL.dll\mysql_store_result", "UInt", _db)
if (requestResult = 0) { ; call must have been an insert or delete ... a select would return results to pass back
return
}
fieldCount := DllCall("libmySQL.dll\mysql_num_fields", "UInt", requestResult)
Loop
{
row := DllCall("libmySQL.dll\mysql_fetch_row", "UInt", requestResult)
If (row = 0 || row == "")
Break
; Get a pointer on a table of lengths (unsigned long)
lengths := DllCall("libmySQL.dll\mysql_fetch_lengths" , "UInt", requestResult)
Loop %fieldCount%
{
length := GetUIntAtAddress(lengths, A_Index - 1)
fieldPointer := GetUIntAtAddress(row, A_Index - 1)
VarSetCapacity(field, length)
DllCall("lstrcpy", "Str", field, "UInt", fieldPointer)
resultString := resultString . field
If (A_Index < fieldCount)
resultString := resultString . "|" ; seperator for fields
}
resultString := resultString . "`n" ; seperator for records
}
; remove last newline from resultString
resultString := RegExReplace(resultString , "`n$", "")
Return resultString
}
It took me while to think outside the box, but I finally found a solution that works very well.
I simply added a settimer command to re-connect to the mysql database after 8 hours. 8 hours is the default database connection timeout.
Now the AHK app can remain running indefinitely and is always connected to the database!
Even better... I used an oop class to retain the mysql connection parameters, so that when the mysql connection timed out and a new mysql call is made, it can automatically reconnect.