Sure! Here’s the updated translation with your additional information included:
Hello everyone,
I’m a senior JS+PHP developer, but I have no experience with Delphi or Data Abstract. I have an old application in my company, around 16 years old, that has been abandoned (the original developer is no longer available, and we transitioned to new technologies some time ago, which I now manage). I’m trying to retrieve data from this program, but it’s quite challenging. The application was a legacy platform used to make reservations for certain services for our clients. It didn’t have an installer; instead, it was a single client.exe file that, once launched, generated .dat files in the same folder as the executable. These .dat files aren’t plain text and seem to represent database tables (like cities.exe, customers.exe, and so on), which sync with a database located on the same network. I assume that back then, there wasn’t a request/response protocol but rather a data synchronization process between the client and server.
My goal is to be able to read these .dat files somehow. I used a tool called PE Explorer on the client.exe file and gathered several pieces of information, including that the software was developed in Delphi and accesses the data through the Data Abstract for Delphi method Bin2DataStreamer. I assume that these .dat files are serialized using this method. Through PE Explorer, I can also see the structure of the data, for example, the Cities table has fields like ID, Description, and Language ID.
Now, I’d like to attempt data retrieval, and I noticed that there are Data Abstract libraries for JS, which also include the Bin2DataStreamer method. However, I can’t find a starting point to write the code because it seems that Bin2DataStreamer requires a stream/endpoint to function, and I can’t directly pass it a file. Of course, I could access the client since I have the passwords, but extracting this data manually is impossible, and the software doesn’t have an export system. I’m looking for any advice on how to start this reverse engineering process. Unfortunately, the local database isn’t accessible because it’s running on a VM I don’t have physical access to, so I’m trying to recover the data directly from the client.
As you pointed I cannot see any readable header or meta data, could it be there is some kind of compression around this? I’m pretty sure that the .dat are stored with Bin2DataStreamer beacuse of this part of Delphi code I found where the structure of cities is declared, that seems to relies to DataStreamer = ClientDataModule.DataStreamer for serialization/streaming procedures:
// <DFM> TDTMCACHECLIENT = class(TForm);
object dtmCacheClient: TdtmCacheClient
OldCreateOrder = False
OnCreate = DataModuleCreate
OnDestroy = DataModuleDestroy
Height = 746
Width = 1015
object cdsBookingType: TDAMemDataTable
Tag = 2
Fields = <
item
Name = 'ID'
DataType = datAutoInc
GeneratorName = 'dbo.BookingType'
Required = True
InPrimaryKey = True
end
item
Name = 'Code'
DataType = datString
Size = 5
Required = True
end
item
Name = 'DefaultDescr'
DataType = datString
Size = 50
Required = True
end
item
Name = 'SwActive'
DataType = datInteger
end
item
Name = 'SortID'
DataType = datInteger
end
item
Name = 'BookingTypeUID'
DataType = datInteger
end
item
Name = 'BookingColor'
DataType = datInteger
end>
LogicalName = 'BookingType'
Params = <>
RemoteDataAdapter = CacheRemoteDataAdapter
RemoteUpdatesOptions = []
StreamingOptions = [soDisableEventsWhileStreaming, soDisableFiltering]
IndexDefs = <>
Left = 40
Top = 24
end
object CacheRemoteDataAdapter: TDARemoteDataAdapter
GetSchemaCall.RemoteService = CaCheRemoteService
GetDataCall.RemoteService = CaCheRemoteService
UpdateDataCall.RemoteService = CaCheRemoteService
GetScriptsCall.RemoteService = CaCheRemoteService
RemoteService = CaCheRemoteService
DataStreamer = ClientDataModule.DataStreamer
Left = 760
Top = 24
end
object CaCheRemoteService: TRORemoteService
ServiceName = 'CachService'
Channel = ClientDataModule.ClientChannel
Message = ClientDataModule.Message
Left = 760
Top = 80
end
object cdsCities: TDAMemDataTable
Tag = 2
Fields = <
item
Name = 'id'
DataType = datAutoInc
end
item
Name = 'descr'
DataType = datString
Size = 50
end
item
Name = 'langid'
DataType = datInteger
end>
LogicalName = 'Cities'
Params = <>
RemoteDataAdapter = CacheRemoteDataAdapter
RemoteUpdatesOptions = []
StreamingOptions = [soDisableEventsWhileStreaming, soDisableFiltering]
IndexDefs = <>
Left = 624
Top = 80
end
// <DFM> TCLIENTDATAMODULE = class(TForm);
object ClientDataModule: TClientDataModule
OldCreateOrder = True
OnCreate = DataModuleCreate
OnDestroy = DataModuleDestroy
Height = 659
Width = 1170
object ClientChannel: TROSuperTCPChannel
RequestTimeout = 600000
StoreActive = False
SynchronizeEvents = True
DispatchOptions = []
OnLoginNeeded = ClientChannel_OnLoginNeeded
ServerLocators = <>
TargetUrl = 'supertcp://localhost:8095'
Host = 'localhost'
Left = 24
Top = 8
end
object Message: TROBinMessage
Envelopes = <>
Left = 88
Top = 8
end
object RemoteService: TRORemoteService
ServiceName = 'DataService'
Channel = ClientChannel
Message = Message
Left = 256
Top = 72
end
object DataStreamer: TDABin2DataStreamer
Left = 160
Top = 8
end
object RemoteDataAdapter: TDARemoteDataAdapter
GetSchemaCall.RemoteService = RemoteService
GetDataCall.RemoteService = RemoteService
GetDataCall.MethodName = 'GetData'
GetDataCall.Params = <
item
Name = 'Result'
DataType = rtBinary
Flag = fResult
Value = Null
end
item
Name = 'aTableNameArray'
DataType = rtUserDefined
Flag = fIn
TypeName = 'StringArray'
Value = Null
end
item
Name = 'aTableRequestInfoArray'
DataType = rtUserDefined
Flag = fIn
TypeName = 'TableRequestInfoArray'
Value = Null
end>
GetDataCall.Default = False
GetDataCall.OutgoingTableNamesParameter = 'aTableNameArray'
GetDataCall.OutgoingTableRequestInfosParameter = 'aTableRequestInfoArray'
GetDataCall.IncomingDataParameter = 'Result'
UpdateDataCall.RemoteService = RemoteService
GetScriptsCall.RemoteService = RemoteService
RemoteService = RemoteService
CacheSchema = True
DataStreamer = DataStreamer
Left = 256
Top = 8
end
object GeneralDataServiceRemote: TRORemoteService
ServiceName = 'GeneralDataService'
Channel = ClientChannel
Message = Message
Left = 376
Top = 72
end
object GeneralRemoteAdaptor: TDARemoteDataAdapter
DynamicSelect = True
GetSchemaCall.RemoteService = GeneralDataServiceRemote
GetDataCall.RemoteService = GeneralDataServiceRemote
GetDataCall.MethodName = 'GetData'
GetDataCall.Params = <
item
Name = 'aTableNameArray'
DataType = rtUserDefined
Flag = fIn
TypeName = 'StringArray'
end
item
Name = 'aTableRequestInfoArray'
DataType = rtUserDefined
Flag = fIn
TypeName = 'TableRequestInfoArray'
end
item
Name = 'Result'
DataType = rtBinary
Flag = fResult
end>
GetDataCall.Default = False
GetDataCall.OutgoingTableNamesParameter = 'aTableNameArray'
GetDataCall.OutgoingTableRequestInfosParameter = 'aTableRequestInfoArray'
GetDataCall.IncomingDataParameter = 'Result'
UpdateDataCall.RemoteService = GeneralDataServiceRemote
GetScriptsCall.RemoteService = GeneralDataServiceRemote
RemoteService = GeneralDataServiceRemote
DataStreamer = DataStreamer
Left = 376
Top = 8
end
Dear thank you, since I can clearly see in the code I posted the DataStreamer: TDABin2DataStreamer, could it be that there is a further compression or encryption method that I cannot see after using DA Streamer?
Dear sorry again,
I understand that must be some kind of encryption on the local tables, but following tcpdump flow I can view R0107 and DABIN200 data, like the below request for availability, pheraps I could try to make a huge query in the application and decode the tcpdump using the js framework. If you could just give to me a starting point to accomplish this, I know is not the best way but at least I could fetch some data.