I got the task to encrypt a firmware file that is decrypted on a small device using AES.
The firmware library comes with an AES framework, so I need to get that one in sync with my Delphi code....
To get the task done I used the DEC cipher suite which we also use for different purposes in the software... now the thing is that I cannot sync the output of the firmware tool set with DEC...
Here is the code I use:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
DECBaseClass,
DECCipherBase,
DECHash,
DECCiphers,
DECTypes,
DECFormatBase,
DECFormat;
type
Binary = RawByteString;
var
ACipherClass: TDECCipherClass = TCipher_AES;
ACipherMode: TCipherMode = cmCBCx;
ATextFormat: TDECFormatClass = TFormat_Mime64;
InvertUINT32 : boolean = False;
InvertText : boolean = False;
type
//TMK6AESTextSize = Array[0..15] of UInt32;
TMK6AESTextSize = Array[0..3] of UInt32;
TMK6Key = Array[0..3] of UINT32;
const mk6Key : TMK6Key = ($1, $0, $0, $0);
//mk6Key : TMK6Key = ($09CF4F3C, $ABF71588, $28AED2A6, $2B7E1516 ); // rotated
//mk6IV : TMK6Key = ($00010203 , $04050607 , $08090A0B , $0C0D0E0F);
mk6IV : TMK6Key = ($0 , $0 , $0 , $0);
//mk6IV : TMK6Key = ($0C0D0E0F, $08090A0B, $04050607, $00010203);
// mk6Plaintext : TMK6AESTextSize =
// ( $E2BEC16B ,$969F402E ,$117E3DE9 ,$2A179373 ,
// $578A2DAE ,$9CAC031E ,$AC6FB79E ,$518EAF45 ,
// $461CC830 ,$11E45CA3 ,$19C1FBE5 ,$EF520A1A ,
// $45249FF6 ,$179B4FDF ,$7B412BAD ,$10376CE6 );
// mk6Plaintext : TMK6AESTextSize =
// ( $E2BEC16B ,$969F402E ,$117E3DE9 ,$2A179373 );
//mk6Plaintext : TMK6AESTextSize =
// ( $2A179373, $117E3DE9, $969F402E, $E2BEC16B );
mk6Plaintext : TMK6AESTextSize = ( $0, $0, $0, $0 );
type
TUINT32Byte = Array[0..3] of Byte;
PUINT32Byte = ^TUINT32Byte;
function InvUINT32( value : UINT32 ) : UINT32;
var v1, v2 : PUINT32Byte;
begin
v1 := #value;
v2 := #Result;
v2^[3] := v1^[0];
v2^[2] := v1^[1];
v2^[1] := v1^[2];
v2^[0] := v1^[3];
end;
function GenMK6Text( const txt : TMK6AESTextSize ) : TMK6AESTextSize;
var i : integer;
begin
if InvertText then
begin
for i := 0 to High(txt) do
Result[i] := InvUINT32(txt[i]);
end
else
Result := txt;
end;
function GenMK6Key( const aKey : TMK6Key ) : TMK6Key;
var i : integer;
begin
Result := aKey;
if InvertUINT32 then
begin
for i := 0 to High(aKey) do
Result[i] := InvUINT32(aKey[i]);
end;
end;
function EncryptBin(const buf : TMK6AESTextSize): Binary;
var AData: Binary;
cipher : TCipher_AES128; //AES
aKey : TMK6Key;
aIV : TMK6Key;
aBuf : TMK6AESTextSize;
begin
cipher := TCipher_AES128.Create;
try
cipher.Mode := ACipherMode;
aKey := GenMK6Key(mk6Key);
aIV := GenMK6Key(mk6IV);
aBuf := GenMK6Text(buf);
cipher.Init( aKey, sizeof(mk6key), aIV, sizeof(mk6IV) );
SetLength(AData, sizeof(buf));
cipher.Encode(aBuf, AData[1], Length(AData));
Result := AData;
finally
cipher.Free;
end;
end;
function DecryptBin(buf : Binary): TMK6AESTextSize;
var AData: Binary;
//ACheck: Binary;
//APass: Binary;
ALen: Integer;
ARes : Binary;
cipher : TCipher_AES128;
aKey : TMK6Key;
aIV : TMK6Key;
begin
cipher := TCipher_AES128.Create;
try
//AData := ValidFormat(ATextFormat).Decode(AText);
AData := buf;
ALen := Length(AData); // - cipher.Context.BufferSize;
//ALen := Length(AData); // - cipher.Context.BufferSize;
//ACheck := System.Copy(Adata, ALen + 1, cipher.Context.BufferSize);
cipher.Mode := ACipherMode;
aKey := GenMK6Key(mk6Key);
aIV := GenMK6Key(mk6IV);
cipher.Init(aKey, sizeof(mk6key), aIV, sizeof(mk6IV));
SetLength(ARes, ALen);
cipher.Decode(AData[1], ARes[1], ALen);
//if ACheck <> cipher.CalcMAC then
// raise Exception.Create('Invalid data');
Assert(Length(ARes) >= sizeof(Result), 'WTF');
Move(ARes[1], Result, sizeof(Result));
Result := GenMK6Text(Result);
finally
cipher.Free;
end;
end;
var binBuf : Binary;
decBuf : TMK6AESTextSize;
i : integer;
begin
try
binBuf := EncryptBin(mk6Plaintext);
for i := 1 to Length(binBuf) do
begin
Write( IntToHex( Ord(binBuf[i]), 2 ) + ' ' );
end;
Writeln;
decBuf := DecryptBin(binBuf);
for i := 0 to Length(decBuf) - 1 do
Writeln( IntToHex( decBuf[i], 4 ) );
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
The output of the firmware tool is D5 AA 45 05 7C A9 A2 6D 43 D1 63 36 84 1C 3D 2A
(which actually also is the output if I use this input on http://aes.online-domain-tools.com/ ) so I guess I miss something here....
Please note that if all input data (Key, IV data) is set to zero, I get the output
66 E9 4B D4 EF 8A 2C 3B 88 4C FA 59 CA 34 2B 2E which is close to the one I get from the other tool:
D4 4B E9 66 3B 2C 8A EF 59 FA 4C 88 2E 2B 34 CA ... so basically only the UINT32 byte order is different.
The thing is that I cannot get any close if just one bit is different from 0 in the key vector (hence the one value of 1.) ... I tried various things... change the position of the "1", changed the lowest to the highest bit - nothing worked...
What am I missing here?
You are using older DEC library that was written to be compatible with Delphi 2009.
Instead of this version you should be using newer DEC library that is fully compatible with with newer Delphi versions including Delphi 11 that you are using.
Switching to newer version of DEC library should solve all of your problems.
Related
I'm trying to call NtOpenFile, and it's failing with error:
STATUS_OBJECT_PATH_SYNTAX_BAD = NTSTATUS($C000003B);
Object Path Component was not a directory object.
The basic gist is:
//The file we'll test with
filename: UnicodeString := 'C:\Windows\Explorer.exe'; //23 characters
//Convert the filename to counted UNICODE_STRING
cs: UNICODE_STRING;
cs.Length := Length(filename) * sizeof(WideChar); //46 bytes
cs.MaximumLength := cs.Length + 2; //48 bytes
cs.Buffer := PWideChar(Filename); //"C:\Windows\Explorer.exe"
//Define the OBJECT_ATTRIBUTES
oa: OBJECT_ATTRIBUTES := Default(OBJECT_ATTRIBUTES);
oa.Length := sizeof(OBJECT_ATTRIBUTES); //24 bytes
oa.Attributes := OBJ_CASE_INSENSITIVE;
oa.ObjectName := #cs; //UNICODE_STRING
//Open the file (by Object Attributes) and get a file handle
hFile: THandle;
iosb: IO_STATUS_BLOCK;
status: NTSTATUS := NtOpenFile(#hFile, FILE_READ_ATTRIBUTES, #oa, #iosb, FILE_SHARE_READ, 0);
What am I doing wrong?
Basic gist (C#-style psuedocode)
//The file we'll test with
UnicodeString filename = "C:\Windows\Explorer.exe"; //23 characters
//Convert the filename to counted UNICODE_STRING
UNICODE_STRING cs;
cs.Length = Length(filename) * sizeof(WideChar); //46 bytes
cs.MaximumLength = cs.Length + 2; //48 bytes
cs.Buffer = Filename; //"C:\Windows\Explorer.exe"
//Define the OBJECT_ATTRIBUTES
OBJECT_ATTRIBUTES oa = Default(OBJECT_ATTRIBUTES);
oa.Length = sizeof(OBJECT_ATTRIBUTES); //24 bytes
oa.Attributes = OBJ_CASE_INSENSITIVE;
oa.ObjectName = cs; //UNICODE_STRING
//Open the file (by Object Attributes) and get a file handle
THandle hFile;
IO_STATUS_BLOCK iosb;
NTSTATUS status = NtOpenFile(out hFile, FILE_READ_ATTRIBUTES, ref oa, out iosb, FILE_SHARE_READ, 0);
Other styles of filenames
Filename
Result
Description
"C:\Windows\Explorer.exe"
STATUS_OBJECT_PATH_SYNTAX_BAD
Object Path Component was not a directory object.
"\global??\C:\Windows\Explorer.exe"
0xC0000033
Object Name invalid
"\??\C:\Windows\Explorer.exe"
0xC0000033
Object Name invalid
OBJECT_ATTRIBUTES raw memory dump
Because there can be issues of memory layout, alignment, padding, and missing members, lets dump the raw oa memory:
18 00 00 00 ;Length. 0x00000018 = 24 bytes (sizeof)
00 00 00 00 ;RootDirectory. 0x00000000 = NULL
28 FF 19 00 ;ObjectName. PUNICODE_STRING 0x0019FF28
40 00 00 00 ;Attributes. (0x00000040 = OBJ_CASE_INSENSITIVE)
00 00 00 00 ;SecurityDescriptor 0x0000000 = NULL
00 00 00 00 ;SecurityQualityOfService 0x00000000 = NULL
**0x0019FF28:**
3C FF 19 00 ;PUNICODE_STRING 0x0019FF3C
**0x0019FF3C**
36 00 ; String length in bytes 0x0036
38 00 ; Buffer size in bytes 0x0038
E8 B6 4E 00 ; PWideChar 0x004EB6E8 ==> "C:\Windows\Explorer.exe"
Which, now I see my problem: PUNICODE_STRING -> PUNICODE_STRING.
CMRE
program NtOpenFileDemo;
{$APPTYPE CONSOLE}
{$R *.res}
{$ALIGN 8}
{$MINENUMSIZE 4}
uses
SysUtils, Windows, ComObj;
type
NTSTATUS = Cardinal;
const
STATUS_SUCCESS = 0;
type
UNICODE_STRING = packed record
Length: Word; // Length of the string, in bytes (not including the null terminator)
MaximumLength: Word; // Size of the buffer, in bytes
Buffer: PWideChar; //really a PWideChar
end;
PUNICODE_STRING = ^UNICODE_STRING;
type
IO_STATUS_BLOCK = packed record
Status: NTSTATUS;
Information: Pointer;
end;
PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
OBJECT_ATTRIBUTES = packed record
Length: Cardinal;
RootDirectory: THandle;
ObjectName: PUNICODE_STRING;
Attributes: Cardinal;
SecurityDescriptor: Pointer;
SecurityQualityOfService: Pointer;
end;
POBJECT_ATTRIBUTES = ^OBJECT_ATTRIBUTES;
const
// Define share access rights to files and directories
FILE_SHARE_READ = $00000001;
FILE_SHARE_WRITE = $00000002;
FILE_SHARE_DELETE = $00000004;
FILE_SHARE_VALID_FLAGS = $00000007;
// Valid values for the Attributes field
const
OBJ_INHERIT = $00000002;
OBJ_PERMANENT = $00000010;
OBJ_EXCLUSIVE = $00000020;
OBJ_CASE_INSENSITIVE = $00000040;
OBJ_OPENIF = $00000080;
OBJ_OPENLINK = $00000100;
OBJ_KERNEL_HANDLE = $00000200;
OBJ_VALID_ATTRIBUTES = $000003F2;
function NtOpenFile(FileHandle: PHandle; DesiredAccess: ACCESS_MASK; ObjectAttributes: POBJECT_ATTRIBUTES;
IoStatusBlock: PIO_STATUS_BLOCK; ShareAccess: DWORD; OpenOptions: DWORD): NTSTATUS; stdcall; external 'ntdll.dll';
function NtClose(Handle: THandle): NTSTATUS; stdcall; external 'ntdll.dll';
function FormatNTStatusMessage(const NTStatusMessage: NTSTATUS): string;
var
Buffer: PChar;
Len: Integer;
hMod: HMODULE;
function MAKELANGID(p, s: WORD): WORD;
begin
Result := WORD(s shl 10) or p;
end;
begin
{
KB259693: How to translate NTSTATUS error codes to message strings
Let the OS initialize the Buffer variable. Need to LocalFree it afterward.
}
hMod := SafeLoadLibrary('ntdll.dll');
Len := FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER or
FORMAT_MESSAGE_FROM_SYSTEM or
// FORMAT_MESSAGE_IGNORE_INSERTS or
// FORMAT_MESSAGE_ARGUMENT_ARRAY or
FORMAT_MESSAGE_FROM_HMODULE,
Pointer(hMod),
NTStatusMessage,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
#Buffer, 0, nil);
try
//Remove the undesired line breaks and '.' char
while (Len > 0) and (CharInSet(Buffer[Len - 1], [#0..#32, '.'])) do Dec(Len);
//Convert to Delphi string
SetString(Result, Buffer, Len);
finally
//Free the OS allocated memory block
LocalFree(HLOCAL(Buffer));
end;
FreeLibrary(hMod);
end;
procedure TestCase;
var
filename: UnicodeString;
cs: PUNICODE_STRING;
hFile: THandle;
oa: OBJECT_ATTRIBUTES;
iosb: IO_STATUS_BLOCK;
status: NTSTATUS;
begin
filename := 'C:\Windows\Explorer.exe'; //23 characters
{
Convert the filename to an "Object Attributes" structure
OBJECT_ATTRIBUTES.Length <-- 24
OBJECT_ATTRIBUTES.Attributes <-- OBJ_CASE_INSENSITIVE
OBJECT_ATTRIBUTES.ObjectName.Length <-- 46
OBJECT_ATTRIBUTES.ObjectName.MaximumLength <-- 48
OBJECT_ATTRIBUTES.ObjectName.Buffer <-- "C:\Windows\Explorer.exe"
}
cs.Length := Length(Filename) * sizeof(WideChar);
cs.MaximumLength := cs.Length + 2; //the null terminator
cs.Buffer := PWideChar(Filename);
oa := Default(OBJECT_ATTRIBUTES);
oa.Length := sizeof(OBJECT_ATTRIBUTES);
oa.Attributes := OBJ_CASE_INSENSITIVE;
oa.ObjectName := #cs;
//Open the file (by Object Attributes) and get a file handle
status := NtOpenFile(#hFile, FILE_READ_ATTRIBUTES, #oa, #iosb, FILE_SHARE_READ, 0);
if status <> STATUS_SUCCESS then
begin
WriteLn('Error opening file "'+Filename+'": '+FormatNTStatusMessage(status)+' (0x'+IntToHex(status, 8)+')');
Exit;
end;
try
WriteLn('Successfully opened file');
finally
NtClose(hFile);
end;
end;
begin
try
TestCase;
WriteLn('Press enter to close...');
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Bonus Reading
MSDN: RtlInitUnicodeString
MSDN: NtOpenFile function
Technet: How to open a file from a kernel mode device driver and how to read from or write to the file archive
MSDN: InitializeObjectAttributes macro
MSDN: OBJECT_ATTRIBUTES structure
Google Project Zero: The Definitive Guide on Win32 to NT Path Conversionarchive
Found it.
Two things:
I didn't know "NT Paths" are different from "DOS Paths"
"C:\Windows\Notepad.exe" → "\??\C:\Windows\Notepad.exe"
To OBJECT_ATTRIBUTES.ObjectName I was assigning #PUNICODE_STRING, rather than #UNICODE_STRING
Use Case - Processes using file
I was using NtOpenFile in order to get the Process IDs that are using a file.
The short version is:
call NtOpenFile to open the file you're interested in
call NtQueryInformationFile with the FileProcessIdsUsingFileInformation constant
iterate each returned Process ID (pid)
call OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid) to get the process name
function GetProcessIDsUsingFile(Filename: UnicodeString; out ProcessIDs: array of DWORD): string;
var
hFile: THandle;
oa: OBJECT_ATTRIBUTES;
iosb: TIOStatusBlock;
status: NTSTATUS;
fi: FILE_PROCESS_IDS_USING_FILE_INFORMATION;
i: Integer;
pid: DWORD;
hProcess: THandle;
s: string;
dw: DWORD;
cs: UNICODE_STRING;
ntFilename: UnicodeString;
const
PROCESS_QUERY_LIMITED_INFORMATION = $1000;
begin
Result := '';
(*
Decription
Get list of processes that are using a file.
Sample usage
GetProcessIDsUsingFile('C:\Windows\Notepad.exe', {out}pids);
Returns
Result: a text description of the processes using the file
"PID:12345 (Explorer.exe)"
ProcessIDs: an array of process IDs (PIDs)
[12345]
*)
ntFilename := Filename;
{
Convert the "DOS path" into an "NT Path". NT Paths are not the same as DOS paths.
DOS Path: C:\Windows\Explorer.exe
NT Path: \??\C:\Windows\Explorer.exe
The short version is:
\DosDevices is a symbolic link for \??
\?? is a special value understood by the object manager's parser to mean the global \DosDevices directory
\GLOBAL?? is a folder that is home to global device.
And in the end all 3 (eventually) end up in the same place.
But you want to use "??" rather than "\GLOBAL??" because the former understands per-user (i.e. local) drive mappings.
Bonus Reading:
- Google Project Zero: The Definitive Guide on Win32 to NT Path Conversion (Google Project Zero)
https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
- Google Project Zero: Windows Drivers are True’ly Tricky
https://googleprojectzero.blogspot.com/2015/10/windows-drivers-are-truely-tricky.html
- Nynaeve: The kernel object namespace and Win32, part 3
http://www.nynaeve.net/?p=92
Apparently you can also use some undocumented functions (exported by name) to convert a DOS path to an NT path; there are like 7 variations
- RtlGetFullPathName_U
- RtlDosPathNameToRelativeNtPathName_U
- RtlDosPathNameToNtPathName_U_WithStatus
But i'll just prefix it with "\??\"
}
if Copy(ntFilename, 1, 4) <> '\??\' then
ntFilename := '\??\'+Filename;
{
Convert the filename to an "Object Attributes" structure
OBJECT_ATTRIBUTES.Length <-- 24
OBJECT_ATTRIBUTES.Attributes <-- OBJ_CASE_INSENSITIVE
OBJECT_ATTRIBUTES.ObjectName.Length <-- 46
OBJECT_ATTRIBUTES.ObjectName.MaximumLength <-- 48
OBJECT_ATTRIBUTES.ObjectName.Buffer <-- "C:\Windows\Explorer.exe"
}
cs.Length := Length(ntFilename) * sizeof(WideChar);
cs.MaximumLength := cs.Length + 2; //the null terminator
cs.Buffer := PWideChar(ntFilename);
oa := Default(OBJECT_ATTRIBUTES);
oa.Length := sizeof(OBJECT_ATTRIBUTES);
oa.Attributes := OBJ_CASE_INSENSITIVE;
oa.ObjectName := #cs;
//Open the file (by Object Attributes) and get a file handle
status := NtOpenFile(#hFile, FILE_READ_ATTRIBUTES, #oa, #iosb, FILE_SHARE_READ, 0);
if status <> STATUS_SUCCESS then
raise EOleSysError.Create('Error opening file "'+Filename+'": '+FormatNTStatusMessage(status)+' (0x'+IntToHex(status, 8)+')', status, 0);
try
//Query for information about the file (by handle)
status := NtQueryInformationFile(hFile, #iosb, #fi, sizeof(fi), FileProcessIdsUsingFileInformation);
if status <> STATUS_SUCCESS then
raise EOleSysError.Create('Error querying file "'+Filename+'" information: '+FormatNTStatusMessage(status), status, 0);
for i := 0 to fi.NumberOfProcessIdsInList-1 do
begin
pid := fi.ProcessIdList[i];
if Result <> '' then
Result := Result+#13#10;
Result := Result+'PID: '+IntToStr(pid);
hProcess := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid);
if hProcess = 0 then
RaiseLastOSError;
try
SetLength(s, 32767);
dw := GetModuleFileNameEx(hProcess, 0, PChar(s), Length(s));
if dw <= 0 then
RaiseLastOSError;
SetLength(s, dw); //returns characters (not including null terminator)
Result := Result+' ('+s+')';
finally
CloseHandle(hProcess);
end;
end;
finally
NtClose(hFile);
end;
end;
In my project I receive data from a tcp connection with a custom protocol in packets of 1095 bytes, then I must look for a sync word and try to show gray scale image.
At first step I read data and save them in a TStringList fifo
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
rowFrame : string;
data: TIdBytes;
begin
offReCStatus := false;
repeat
AContext.Connection.IOHandler.ReadBytes(data, 1099, False);
rowFrame :='';
for I := 0 to length(data)-1 do
begin
rowFrame := rowFrame + (data[i].ToHexString);
end;
tcpFrameList.Append( rowFrame );
until offReCStatus = true;
end;
Then in a separated thread, I try the data from the list.
{I added some comments in code}
Get first string from string list
Convert it to binary and append to previous data
Find sync word and copy data after sync word
Split image data to 1024 * 10 bits to load image
Draw image from data
Find new sync word(number 3)
Note: one very important thing is the sync-word is not byte,its bits and can start from middle of a byte for example 10 101011-00010101-00001100-10011001-01111111-00 111111 in this case 10 at first and 111111 at the end merged to sync word and its not AC543265FC any more.in the past in fpga I wrote code that shift the bits until find the 40 bits sync word but i don't know how this can be done in Delphi!
procedure TMyThread.Execute;
var
str3,str4,frameStr,frameId,strData, str6 : string;
iPos,y ,imageBit , frameIdNum :integer;
imageRol : TStringList;
begin
while not Terminated do
begin
FTermEvent.WaitFor( 500 );
if not Terminated then
begin
while tcpFrameList.Count >0 do //process que
begin
try
dta := dta + HexStrToBinStr(tcpFrameList[0]);//convert hex data to binary string and append to olddata
tcpFrameList.Delete(0);//delete converted thread
str3 := '1010110001010100001100100110010111111100';//sync word "AC543265FC"
iPos := pos( str3 , dta );//find 1st sync word in binary data
while dta.Length>20000 do //process data to find sync words
begin
Delete(dta,1, iPos-1 );//delete data until first sync word
str4 := copy( dta , 1, 12240);//copy image frame data after sync word
Delete(dta,1, 12240 );//delete image frame data that copied
strData := copy(BinToHex(str4),11); //hex image data
frameId := copy( strData , 1, 6 ); //get image column id from data
frameStr := copy( strData , 107, 330 );//get image color data as protocol
frameStr := frameStr + copy( strData , 501, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 1011, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 1521, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 2031, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 2541, 446 );//get image data as in protocol
imageBin := HexStrToBinStr( frameStr );
//now we have 10240 bit that for one frame column .10240 is 1024 of 10 bits for each pixel
imageRol := TstringList.Create;
imageRol := spliToLength( imageBin,10);//split 10240 to 1024 *10
frameIdNum := HexToDec(frameId);//frame id to show image
//application.ProcessMessages;
TThread.Synchronize (TThread.CurrentThread,
procedure () var y,n:integer;
begin
form1.Image1.Width := frameIdNum+1;//set TImage width
for y := 0 to imageRol.Count-1 do //process imageRol to grab 1024 pixel color of new column
begin
str6 := imageRol[y];
imageBit := trunc( BinToDec( str6 ) /4 );//div 10bit(1024) to 4 to get a number 0-255 for color
form1.Image1.Canvas.Pixels[frameIdNum ,y)] := RGB( imageBit , imageBit , imageBit );//gray scale image
end;
end);
iPos := pos( str3 , dta );
end;
except
on E : Exception do
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
form1.Memo1.Lines.Add(E.ClassName+' , message: '+E.Message);
end);
end;
end;
end;
end;
end;
The code above is working good but its slow..
I don't know how can process data as bits so try to convert data between hex and string to complete the process. Is there a way to do this job without any hex converting from tcp layer!?
I commented the code to explain what happening.but tell me to add some more data where necessary.
Here is an example how you could process the Binary data.
DISCLAMER
This code sample is far from optimized as I tried to keep it simple so one can grasp the concept how to process binary data.
The main concept here is that we have a 40 bit sync word (marker) but since we are dealing with individual bits, it can be on a non byte boundary. So all we need to do is read at least 48 bits (6 bytes) into a 64 bit integer and shift the bits to the right until we find our marker. I did not include the RGB pixel extraction logic, I leave that as an exercise for you :), I think you can decode it with WIC as GUID_WICPixelFormat32bppBGR101010
program SO59584303;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Classes,
System.SysUtils;
type ImageArray = TArray<Byte>;
const FrameSync : UInt64 = $AC543265FC; // we need Int64 as our marker is > 32 bits
function GetByte(const Value : UInt64; const ByteNum : Byte) : Byte; inline;
begin
Result := (Value shr ((ByteNum-1)*8)) and $FF ;
end;
procedure WriteInt64BigEndian(const Value: UInt64; NumberOfBytes : Integer; var Stream : TBytes; var Ps : Integer);
var
I : Integer;
begin
for I := NumberOfBytes downto 1 do
begin
Stream[Ps] := GetByte(Value, I);
Inc(Ps);
end;
end;
function ReadInt64BigEndian(const NumberOfBytes : Integer; const Stream : TBytes; var Ps : Integer) : UInt64;
var
I : Integer;
B : Byte;
begin
Result := 0;
for I := NumberOfBytes downto 1 do
begin
B := Stream[Ps];
Result := Result or (UInt64(B) shl ((I-1)* 8));
Inc(Ps);
// sanity check
if Ps >= Length(Stream) then
Exit;
end;
end;
procedure ReadPixelData(const Stream : TBytes; Var Ps : Integer; const Shift : Byte; var Buffer : ImageArray);
// our buffer
var
I : UInt64;
BPos : Integer;
begin
BPos := 0;
// 1024 * 10 bit pixel = 10240 bits = 1280 bytes // initialize buffer
SetLength(Buffer, 1280);
// fill with 0's
FillChar(Buffer[0], Length(Buffer), 0);
if Shift = 0 then
begin
// if we are byte boundary, we can just copy our data
Move(Stream[Ps], Buffer[0], Length(Buffer));
Inc(Ps, Length(Buffer));
end
else
while Bpos < Length(Buffer) do
begin
// Read 8 bytes at a time and shift x bits to the right, mask off highest byte
// this means we can get max 7 bytes at a time
I := (ReadInt64BigEndian(8, Stream, Ps) shr Shift) and $00FFFFFFFFFFFFFF;
// Write 7 bytes to our image data buffer
WriteInt64BigEndian(I, 7, Buffer, BPos);
// go one position back for the next msb bits
Dec(Ps);
end;
end;
procedure WritePixelData(var Stream : TBytes; Var Ps : Integer; var Shift : Byte);
var
Count : Integer;
ByteNum : Byte;
Data : UInt64;
begin
for Count := 1 to 160 do
begin
// write four bytes at a time, due to the shifting we get 5 bytes in total
Data := $F1F2F3F4;
if (Shift > 0) then
begin
// special case, we need to fillup shift bits on last written byte in the buffer with highest byte from our UInt64
Data := Data shl Shift;
Stream[Ps-1] := Stream[Ps-1] or GetByte(Data, 5);
end;
WriteInt64BigEndian(Data, 4, Stream, Ps);
Data := $F5F6F7F8;
if (Shift > 0) then
begin
// special case, we need to fillup shift bits on last written byte in the buffer with highest byte from our UInt64
Data := Data shl Shift;
Stream[Ps-1] := Stream[Ps-1] or GetByte(Data, 5);
end;
WriteInt64BigEndian(Data, 4, Stream, Ps);
end;
end;
procedure GenerateData(var Stream : TBytes);
var
Count : Integer;
I : UInt64;
Ps : Integer;
Shift : Byte;
begin
Count := 1285*4+10;
SetLength(Stream, Count); // make room for 4 Imageframes (1280 bytes or 10240 bits) and 5 byte marker (40 bits) + 10 bytes extra room
FillChar(Stream[0], Count, 0);
Ps := 1;
// first write some garbage
Stream[0] := $AF;
// our first marker will be shifted 3 bits to the left
Shift := 3;
I := FrameSync shl Shift;
// write our Framesync (40+ bits = 6 bytes)
WriteInt64BigEndian(I, 6, Stream, Ps);
// add our data, 1280 bytes or 160 times 8 bytes, we use $F1 F2 F3 F4 F5 F6 F7 F8 as sequence
// (fits in Int 64) so that we can verify our decoding stage later on
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AE;
Inc(Ps);
// our second marker will be shifted 2 bits to the left
Shift := 2;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AD;
Inc(Ps);
// our third marker will be shifted 1 bit to the left
Shift := 1;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AC;
Inc(Ps);
// our third marker will be shifted 5 bits to the left
Shift := 5;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
SetLength(Stream, Ps-1)
end;
procedure DecodeData(const Stream : TBytes);
var
Ps : Integer;
OrgPs : Integer;
BPos : Integer;
I : UInt64;
Check : UInt64;
Shift : Byte;
ByteNum : Byte;
ImageData : ImageArray;
begin
Ps := 0;
Shift := 0;
while Ps < Length(Stream) do
begin
// try to find a marker
// determine the number of bytes we need to read, 40bits = 5 bytes,
// when we have shifted bits this will require 6 bytes
if Shift = 0 then
ByteNum := 5
else
ByteNum := 6;
// save initial position in the stream
OrgPs := Ps;
// read our marker
I := ReadInt64BigEndian(ByteNum, Stream, Ps);
// if we have shifted bits, shift them on byte boundary and make sure we only have the 40 lower bits
if Shift > 0 then
I := (I shr Shift) and $FFFFFFFFFF;
if I = FrameSync then
begin
// we found our marker, process pixel data (ie read next 10240 bits, taking shift into account)
// If we have shift, our first bits will be found in the last marker byte, so go back one position in the stream
if Shift > 0 then
Dec(Ps);
ReadPixelData(Stream, Ps, Shift, ImageData);
// process Image array accordingly, here we will just check that we have our written data back
BPos := 0;
Check := $F1F2F3F4F5F6F7F8;
for ByteNum := 1 to 160 do
begin
I := ReadInt64BigEndian(8, ImageData, BPos);
// if our data is not correct, raise error
Assert(I = Check, 'error decoding image data');
end;
end
else
begin
Ps := OrgPs;
// we did not find our marker, advance 1 bit
Inc(Shift);
if Shift > 7 then
begin
// reset shift value
Shift := 0;
// advance to next byte boundary
Inc(Ps);
end;
end;
end;
end;
Var
AStream : TBytes;
begin
try
GenerateData(AStream);
DecodeData(AStream);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
When I run this code in XE4, the application ends up using ~800 MB.
Why not closer to 100 MB ?
Using Ansistring instead of string makes no difference.
const
N = 10000000; // 10 million
M = 10;
var
i,j: integer;
s: string;
X: array of string;
begin
setlength(X,N);
for i:= 1 to N do
begin
s:= '';
for j:= 1 to M do s:= s+chr(65+random(25));
X[i-1]:= s;
end;
showmessage('pause');
end;
A string of length 10 in XE4 uses 34 Bytes of memory (see DocWiki). 20 Bytes for the content, 2 Bytes for the #0 terminator and 12 bytes management data.
Each array entry is a pointer to that kind of memory. Thus those 10 million strings in the array end up using 380 MB (340 for the strings and 40 for the array items) of memory at minimum.
Try this
const MaxString = 15; // you said so
type stringholder = record
strict private
var Cell: string[ MaxString * SizeOf(Char) div SizeOf(AnsiChar) ];
function GetUS: String; // in xe 4 that is a shortcut to UnicodeString actual type
procedure SetUS(const US: string);
public
property Value: string read GetUS write SetUS;
class operator Implicit(const from: string): stringholder; inline;
class operator Implicit(const from: stringholder): string; inline;
end;
function stringholder.GetUS: String;
var i: integer;
begin
i := Ord( Cell[0] );
SetLength( Result, i div (SizeOf(Char) div SizeOf(AnsiChar)) );
if i > 0 then
Move( Cell[1], Result[1], i);
end;
procedure SetUS(const US: string);
var i: integer;
begin
If US = '' then begin
Cell := ''; // constant here, not US itself
Exit;
End;
i := Length(US);
If i > MaxString then raise EInvalidCast.Create('.....'+US);
i := i * SizeOf(Char) div SizeOf(AnsiChar)
Move( US[1], Cell[1], i );
Cell(. 0 .) := AnsiChar(i);
end;
class operator stringholder.Implicit(const from: string): stringholder;
begin
Result.Value := from;
end;
class operator stringholder.Implicit(const from: stringholder): string;
begin
Result := from.Value;
end;
const
N = 10000000; // 10 million
M = 10;
var
i,j: integer;
s: string;
X: array of stringholder;
begin
setlength(X,N);
for i:= 1 to N do
begin
s:= '';
for j:= 1 to M do s:= s+chr(65+random(25));
X(. i-1 .) := s;
end;
showmessage('pause');
end;
In Win7 RegEdit edit or view a binary just like 1A 2B 3C 4D
now I get a string
str := '1A,2B,3C,4D';
how to write str into Registry , and in Win7 RegEdit it display 1A 2B 3C 4D
var
Data: array of Byte; // or whatever binary container you want to use
Reg: TRegistry;
begin
...
SetLength(Data, 4);
Data[0] := $1A;
Data[1] := $2B;
Data[2] := $3C;
Data[3] := $4D;
Reg := TRegistry.Create(KEY_SET_VALUE);
try
Reg.RootKey := ...;
if Reg.OpenKey('...', True) then
begin
Reg.WriteBinaryData('Value', Data[0], 4);
Reg.CloseKey;
end;
finally
Reg.Free;
end;
...
end;
You can use TRegistry.WriteBinaryData. If you want to write binary $1A,$2B,$3C,$4D instead of string data '1A,2B,3C,4D' try to change it into #$1A#$2B#$3C#$4D.
str := #$1A#$2B#$3C#$4D;
and use WriteBinaryData to write the registry:
Reg.WriteBinaryData('KeyName', str, Length(str) * SizeOf(Byte));
this is my simple code to generate
all possible combinations of a set for
example
1,2,3:
Display:
123
132
213
231
312
321
i want to create variable number of for loops to let the user determine the length of given string...
does anyone have an idea...
thank's in advance.
type
TNumber = '0'..'9';
procedure TForm1.Button1Click(Sender: TObject);
var
Numbers: array[0..3] of TNumber;
a, b, c, d: Integer;
s: string;
begin
Numbers[0] := '1';
Numbers[1] := '8';
Numbers[2] := '7';
Numbers[3] := '2';
for a := low(Numbers) to High(Numbers) do
for b := low(Numbers) to High(Numbers) do
for c := low(Numbers) to High(Numbers) do
for d := low(Numbers) to High(Numbers) do
begin
s := Numbers[a] + Numbers[b] + Numbers[c] + Numbers[d];
if
(Occurrences('1', s) > 1 ) or
(Occurrences('8', s) > 1 ) or
(Occurrences('7', s) > 1 ) or
(Occurrences('2', s) > 1 )
then
Continue
else
Memo1.Lines.Add(s);
end;
end;
function TForm1.Occurrences(const Substring, Text: string): Integer;
var
Offset: Integer;
begin
Result := 0;
Offset := PosEx(Substring, Text, 1);
while Offset <> 0 do
begin
Inc(Result);
Offset := PosEx(Substring, Text, offset + length(Substring));
end;
end;
end.
Here is some code that produces the output you desire. You'd need to work it around a bit for your needs, but the concept expressed in this recursive solution is the important thing:
program Permuatations;
{$APPTYPE CONSOLE}
type
TElements = '1'..'3';
procedure EnumerateCombinations(const Stem: string; Len: Integer);
var
i: Integer;
el: TElements;
Used: set of TElements;
begin
if Len=0 then
exit;
Used := [];
for i := 1 to Length(Stem) do
Include(Used, Stem[i]);
for el := low(el) to high(el) do
begin
if el in Used then
continue;
if Len=1 then
Writeln(Stem+el)
else
EnumerateCombinations(Stem+el, Len-1)
end;
end;
procedure Main;
begin
EnumerateCombinations('', 1+ord(high(TElements))-ord(low(TElements)));
end;
begin
Main;
Readln;
end.
Output:
123
132
213
231
312
321
If you change the definition of TElements, for example to '1'..'4' then you will see the 24 possible permutations.