Jump to content

Kas Ob.

Members
  • Content Count

    613
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by Kas Ob.

  1. Kas Ob.

    LoadLibrary and FreeLibrary notification

    It just hit me, Don't cache them all, as some DLL will never show in the stack, like never, even very common libraries like "AdvApi32.dll" or "Ws2_32.dll" will not be on stack call unless they used callback (used once), so if cached only the found ones you will end up with short list and sorted by importance and appearance.
  2. Kas Ob.

    LoadLibrary and FreeLibrary notification

    Yes, it might be be slower, but the idea is even with your cache you will have to walk it, and if you re-refactored my code above it is really just loop over linked-list, In case you want to test it, then notice that PEB is immovable like its LdrData, so that part can be in initialization clause, this will leave the code close to function FindModuleByAddress(Address: Pointer): TModuleInfo; var ModuleEntry: PLDR_DATA_TABLE_ENTRY; CurrentEntry, FirstEntry: PLIST_ENTRY; ModuleBaseAddr, ModuleEndAddr: NativeUInt; FlinkPtr: PPointer; ProtCounter: Integer; // protection counter against looping over ring doubly linked list begin Result.IsValid := False; FlinkPtr := PPointer(NativeUInt(LdrData) + Flink_OFFSET); // Offset is 12 for 32bit and 24 for 64bit CurrentEntry := FlinkPtr^; if AddressViolateMemoryRange(CurrentEntry) then Exit; FirstEntry := CurrentEntry; // protection against endless looping over ring list ProtCounter := 0; // protection against endless looping over ring list in case FirstEntry is unloaded after the fact while Assigned(CurrentEntry) and (not AddressViolateMemoryRange(CurrentEntry)) and (CurrentEntry <> PList_Entry(NativeUInt(LdrData) + Flink_OFFSET)) do begin ModuleEntry := PLDR_DATA_TABLE_ENTRY(CurrentEntry); try if Assigned(ModuleEntry^.DllBase) then begin ModuleBaseAddr := NativeUInt(ModuleEntry^.DllBase); ModuleEndAddr := ModuleBaseAddr + ModuleEntry^.SizeOfImage; if (NativeUInt(Address) >= ModuleBaseAddr) and (NativeUInt(Address) < ModuleEndAddr) then begin // found module that have the address .. Result.IsValid := True; Exit; end; end; except // Ignore access violations, do not handle anything here end; if ProtCounter >= LOOPING_PROTECTION_LIMIT then Exit; Inc(ProtCounter); CurrentEntry := CurrentEntry^.Flink; if CurrentEntry = FirstEntry then Break; end; end; As for caching, i think you are the best one to decide if you want to cache few (may be 3 and that it) modules addresses that will be %90 of the times in any stack call, the EXE module itself and kernel and User32, saving these 3 will make the protection counter way better than random 1000, by comparing against loaded EXE address and trigger exit after second occurrence, this will prevent loop up to 1000. In all cases, you are welcome.
  3. Kas Ob.

    LoadLibrary and FreeLibrary notification

    Here a more precise and protected version will less redundant code unit lcAddressToModule; interface uses Windows; type TModuleInfo = record ModuleBase: Pointer; ModuleSize: Cardinal; ModuleName: string; FullPath: string; EntryPoint: Pointer; IsValid: Boolean; end; function FindModuleByAddress(Address: Pointer): TModuleInfo; implementation const Flink_OFFSET = {$IFDEF CPUX64} 24 {$ELSE} 12 {$ENDIF}; // Offset is 12 for 32bit and 24 for 64bit LOOPING_PROTECTION_LIMIT = 1000; type PLIST_ENTRY = ^LIST_ENTRY; LIST_ENTRY = packed record Flink: PLIST_ENTRY; Blink: PLIST_ENTRY; end; PUNICODE_STRING = ^UNICODE_STRING; UNICODE_STRING = packed record Length: Word; MaximumLength: Word; {$IFDEF CPUX64} Padding: array[0..3] of Byte; // Align Buffer to 8 bytes {$ENDIF} Buffer: PWideChar; end; PLDR_DATA_TABLE_ENTRY = ^LDR_DATA_TABLE_ENTRY; LDR_DATA_TABLE_ENTRY = packed record InLoadOrderLinks: LIST_ENTRY; InMemoryOrderLinks: LIST_ENTRY; InInitializationOrderLinks: LIST_ENTRY; DllBase: Pointer; EntryPoint: Pointer; SizeOfImage: ULONG; {$IFDEF CPUX64} Padding: array[0..3] of Byte; // Align FullDllName to 60 {$ENDIF} FullDllName: UNICODE_STRING; BaseDllName: UNICODE_STRING; end; PPEB_LDR_DATA = ^PEB_LDR_DATA; PEB_LDR_DATA = packed record Length: ULONG; Initialized: Boolean; {$IFDEF CPUX64} Padding: array[0..2] of Byte; // Align SsHandle to 8 bytes {$ENDIF} SsHandle: Pointer; InLoadOrderModuleList: LIST_ENTRY; end; PPEB = ^PEB; PEB = packed record Reserved: array[0..2] of Byte; BeingDebugged: Byte; {$IFDEF CPUX64} Reserved2: array[0..11] of Byte; // Align Ldr to 24 {$ELSE} Reserved2: array[0..3] of Byte; // Align Ldr to 12 {$ENDIF} ImageBaseAddress: Pointer; Ldr: PPEB_LDR_DATA; end; function RtlGetCurrentPeb: Pointer; stdcall; external 'ntdll.dll'; function GetCurrentPEB: PPEB; begin Result := RtlGetCurrentPeb; end; function AddressViolateMemoryRange(Address: Pointer): Boolean; begin Result := (NativeUInt(Address) < $10000) or // always in user mode then it shold be above 64kb // $BFFFFFFF when (/3G) and/or IMAGE_FILE_LARGE_ADDRESS_AWARE is enabled, when disabled it should be $7FFFFFFF (NativeUInt(Address) > {$IFDEF CPUX64} $7FFFFFFFFFFFFFFF {$ELSE} $BFFFFFFF {$ENDIF}); end; function FindModuleByAddress(Address: Pointer): TModuleInfo; var PEB: PPEB; LdrData: PPEB_LDR_DATA; ModuleEntry: PLDR_DATA_TABLE_ENTRY; CurrentEntry, FirstEntry: PLIST_ENTRY; ModuleBaseAddr, ModuleEndAddr: NativeUInt; FlinkPtr: PPointer; ProtCounter: Integer; // protection counter against looping over ring doubly linked list begin FillChar(Result, SizeOf(Result), 0); Result.IsValid := False; if not Assigned(Address) or AddressViolateMemoryRange(Address) then Exit; PEB := GetCurrentPEB; if not Assigned(PEB) or not Assigned(PEB^.Ldr) then Exit; LdrData := PEB^.Ldr; if not LdrData^.Initialized then Exit; FlinkPtr := PPointer(NativeUInt(LdrData) + Flink_OFFSET); // Offset is 12 for 32bit and 24 for 64bit CurrentEntry := FlinkPtr^; if AddressViolateMemoryRange(CurrentEntry) then Exit; FirstEntry := CurrentEntry; // protection against endless looping over ring list ProtCounter := 0; // protection against endless looping over ring list in case FirstEntry is unloaded after the fact while Assigned(CurrentEntry) and (not AddressViolateMemoryRange(CurrentEntry)) and (CurrentEntry <> PList_Entry(NativeUInt(LdrData) + Flink_OFFSET)) do begin ModuleEntry := PLDR_DATA_TABLE_ENTRY(CurrentEntry); try if Assigned(ModuleEntry^.DllBase) then begin ModuleBaseAddr := NativeUInt(ModuleEntry^.DllBase); ModuleEndAddr := ModuleBaseAddr + ModuleEntry^.SizeOfImage; if (NativeUInt(Address) >= ModuleBaseAddr) and (NativeUInt(Address) < ModuleEndAddr) then begin // found module that have the address Result.ModuleBase := ModuleEntry^.DllBase; Result.ModuleSize := ModuleEntry^.SizeOfImage; Result.EntryPoint := ModuleEntry^.EntryPoint; if Assigned(ModuleEntry^.BaseDllName.Buffer) and (ModuleEntry^.BaseDllName.Length > 0) then Result.ModuleName := ModuleEntry^.BaseDllName.Buffer; if Assigned(ModuleEntry^.FullDllName.Buffer) and (ModuleEntry^.FullDllName.Length > 0) then Result.FullPath := ModuleEntry^.FullDllName.Buffer; Result.IsValid := True; Exit; end; end; except // Ignore access violations, do not handle anything here end; if ProtCounter >= LOOPING_PROTECTION_LIMIT then Exit; Inc(ProtCounter); CurrentEntry := CurrentEntry^.Flink; if CurrentEntry = FirstEntry then Break; end; end; end.
  4. Kas Ob.

    LoadLibrary and FreeLibrary notification

    This may be better than using Assembly function RtlGetCurrentPeb: Pointer; stdcall; external 'ntdll.dll'; function GetCurrentPEB: PPEB; begin Result := RtlGetCurrentPeb; end;
  5. Kas Ob.

    LoadLibrary and FreeLibrary notification

    @Pierre le Riche I searched for an article i read sometimes ago, scrapped few lines based on its idea, which is using PEB to walk the loaded modules, so no callback notification and no caching is needed, you can resolve on spot and when needed, also you can extract the table in-place to use per one stack walk operation. the article https://exploit-notes.hdks.org/exploit/reverse-engineering/debugger/windows-process-internals-with-windbg/ PEB structure https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm PEB_LDR_DATA https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntpsapi_x/peb_ldr_data.htm?tx=186 LDR_DATA_TABLE_ENTRY https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry/index.htm?ta=10&amp;tx=96,97,105,106,109,114,173,177,179,186,192,195 the unit unit lcAddressToModule; interface uses Windows; type PLIST_ENTRY = ^LIST_ENTRY; LIST_ENTRY = packed record Flink: PLIST_ENTRY; Blink: PLIST_ENTRY; end; PUNICODE_STRING = ^UNICODE_STRING; UNICODE_STRING = packed record Length: Word; MaximumLength: Word; {$IFDEF CPUX64} Padding: array[0..3] of Byte; // Align Buffer to 8 bytes {$ENDIF} Buffer: PWideChar; end; PLDR_DATA_TABLE_ENTRY = ^LDR_DATA_TABLE_ENTRY; LDR_DATA_TABLE_ENTRY = packed record InLoadOrderLinks: LIST_ENTRY; InMemoryOrderLinks: LIST_ENTRY; InInitializationOrderLinks: LIST_ENTRY; DllBase: Pointer; EntryPoint: Pointer; SizeOfImage: ULONG; {$IFDEF CPUX64} Padding: array[0..3] of Byte; // Align FullDllName to 60 {$ENDIF} FullDllName: UNICODE_STRING; BaseDllName: UNICODE_STRING; end; PPEB_LDR_DATA = ^PEB_LDR_DATA; PEB_LDR_DATA = packed record Length: ULONG; Initialized: Boolean; {$IFDEF CPUX64} Padding: array[0..2] of Byte; // Align SsHandle to 8 bytes {$ENDIF} SsHandle: Pointer; InLoadOrderModuleList: LIST_ENTRY; end; PPEB = ^PEB; PEB = packed record Reserved: array[0..2] of Byte; BeingDebugged: Byte; {$IFDEF CPUX64} Reserved2: array[0..11] of Byte; // Align Ldr to 24 {$ELSE} Reserved2: array[0..3] of Byte; // Align Ldr to 12 {$ENDIF} ImageBaseAddress: Pointer; Ldr: PPEB_LDR_DATA; end; TModuleInfo = record ModuleBase: Pointer; ModuleSize: ULONG; ModuleName: string; FullPath: string; EntryPoint: Pointer; IsValid: Boolean; end; function FindModuleByAddress(Address: Pointer): TModuleInfo; implementation function GetCurrentPEB: PPEB; asm {$IFDEF CPUX64} mov rax, gs:[$60] // PEB at offset 0x60 in TEB on x64 {$ELSE} mov eax, fs:[$30] // PEB at offset 0x30 in TEB on x86 {$ENDIF} end; function AddressViolateUpperRange(Address: Pointer): Boolean; begin Result := NativeUInt(Address) > {$IFDEF CPUX64} $7FFFFFFFFFFF {$ELSE} $7FFFFFFF {$ENDIF}; end; function AddressViolateLowerRange(Address: Pointer): Boolean; begin Result := NativeUInt(Address) < $10000; // always in user mode then it shold be above 64kb end; function FindModuleByAddress(Address: Pointer): TModuleInfo; var PEB: PPEB; LdrData: PPEB_LDR_DATA; ModuleEntry: PLDR_DATA_TABLE_ENTRY; CurrentEntry, FirstEntry: PLIST_ENTRY; ModuleBaseAddr, ModuleEndAddr: NativeUInt; FlinkPtr: PPointer; begin FillChar(Result, SizeOf(Result), 0); Result.IsValid := False; if not Assigned(Address) or AddressViolateLowerRange(Address) or AddressViolateUpperRange(Address) then Exit; PEB := GetCurrentPEB; if not Assigned(PEB) or not Assigned(PEB^.Ldr) then Exit; LdrData := PEB^.Ldr; if not LdrData^.Initialized then Exit; {$IFDEF CPUX64} FlinkPtr := PPointer(NativeUInt(LdrData) + 24); // InLoadOrderModuleList.Flink at offset 24 on x64 {$ELSE} FlinkPtr := PPointer(NativeUInt(LdrData) + 12); // InLoadOrderModuleList.Flink at offset 12 on x86 {$ENDIF} CurrentEntry := FlinkPtr^; if AddressViolateLowerRange(CurrentEntry) or AddressViolateUpperRange(CurrentEntry) then Exit; FirstEntry := CurrentEntry; while Assigned(CurrentEntry) and (CurrentEntry <> PList_Entry(NativeUInt(LdrData) + {$IFDEF CPUX64} 24 {$ELSE} 12 {$ENDIF})) do begin if AddressViolateLowerRange(CurrentEntry) or AddressViolateUpperRange(CurrentEntry) then Break; ModuleEntry := PLDR_DATA_TABLE_ENTRY(CurrentEntry); try if Assigned(ModuleEntry^.DllBase) then begin ModuleBaseAddr := NativeUInt(ModuleEntry^.DllBase); ModuleEndAddr := ModuleBaseAddr + ModuleEntry^.SizeOfImage; if (NativeUInt(Address) >= ModuleBaseAddr) and (NativeUInt(Address) < ModuleEndAddr) then begin Result.ModuleBase := ModuleEntry^.DllBase; Result.ModuleSize := ModuleEntry^.SizeOfImage; Result.EntryPoint := ModuleEntry^.EntryPoint; if Assigned(ModuleEntry^.BaseDllName.Buffer) then Result.ModuleName := ModuleEntry^.BaseDllName.Buffer else Result.ModuleName := ''; if Assigned(ModuleEntry^.FullDllName.Buffer) then Result.FullPath := ModuleEntry^.FullDllName.Buffer else Result.FullPath := ''; Result.IsValid := True; Exit; end; end; except // Ignore access violations end; CurrentEntry := CurrentEntry^.Flink; if (not Assigned(CurrentEntry)) or (CurrentEntry = FirstEntry) then Break; end; end; end. Test project for it program AddressToModuleTest; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Windows, lcAddressToModule in 'lcAddressToModule.pas'; function PointerToHex(P: Pointer): string; begin Result := IntToHex(NativeUInt(P), 8); end; procedure FindModuleFromAddress(Address: Pointer); var ModuleInfo: TModuleInfo; begin Writeln('Resolving address : ' + PointerToHex(Address)); ModuleInfo := FindModuleByAddress(Address); if ModuleInfo.IsValid then begin Writeln(#9'Found module : ' + ModuleInfo.ModuleName); Writeln(#9'Full Path : ', ModuleInfo.FullPath); Writeln(#9'Base Address : ', PointerToHex(ModuleInfo.ModuleBase)); Writeln(#9'Module Size : ', ModuleInfo.ModuleSize); Writeln(#9'Entry Point : ', PointerToHex(ModuleInfo.EntryPoint)); end else begin Writeln('Failed to get module....'); end; Writeln; end; procedure ResolveAddressesIntoModules; var DLLHandle: HMODULE; Address: Pointer; begin Address := nil; FindModuleFromAddress(nil); FindModuleFromAddress(Addr(PointerToHex)); // address to local function FindModuleFromAddress(Addr(ResolveAddressesIntoModules)); // address to local function // test PsApi.dll DLLHandle := LoadLibrary('PsApi.dll'); if DLLHandle <> INVALID_HANDLE_VALUE then begin Address := GetProcAddress(DLLHandle, 'EnumProcessModules'); if Assigned(Address) then FindModuleFromAddress(Address); FreeLibrary(DLLHandle); end; // test Ws2_32.dll DLLHandle := LoadLibrary('Ws2_32.dll'); if DLLHandle <> INVALID_HANDLE_VALUE then begin Address := GetProcAddress(DLLHandle, 'WSAStartup'); if Assigned(Address) then FindModuleFromAddress(Address); FreeLibrary(DLLHandle); end; if Assigned(Address) then FindModuleFromAddress(Address); // should be "not module found" // invalid address FindModuleFromAddress(Pointer($DEADBEEF)); end; begin try ResolveAddressesIntoModules; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Writeln('Done.'); Readln; end. the output for 64bit (also works for 32bit) Resolving address : 00000000 Failed to get module.... Resolving address : 00426640 Found module : AddressToModuleTest.exe Full Path : D:\Projects Delphi\PEBDemo\Win64\Debug\AddressToModuleTest.exe Base Address : 00400000 Module Size : 1531904 Entry Point : 00429FB0 Resolving address : 00426A30 Found module : AddressToModuleTest.exe Full Path : D:\Projects Delphi\PEBDemo\Win64\Debug\AddressToModuleTest.exe Base Address : 00400000 Module Size : 1531904 Entry Point : 00429FB0 Resolving address : 7FFFB7411010 Found module : PsApi.dll Full Path : C:\WINDOWS\System32\PsApi.dll Base Address : 7FFFB7410000 Module Size : 32768 Entry Point : 7FFFB7411110 Resolving address : 7FFFB762EB10 Found module : Ws2_32.dll Full Path : C:\WINDOWS\System32\Ws2_32.dll Base Address : 7FFFB7620000 Module Size : 438272 Entry Point : 7FFFB7634300 Resolving address : 7FFFB762EB10 Failed to get module.... Resolving address : DEADBEEF Failed to get module.... Done. Here i want to point that the above is tested on few OSs, i used and manipulated PEB so many times and i know for fact that Geoff Chappell documentation is very accurate have no errors and no mistake, and based on his documentation the above code should work from Windows 2000 till Windows 11. Hope you find that helpful or useful and thank you for your great contribution over so many years ! ps: the structures was big and it hit me for this specific use we don't the complexity of that structures so i cut them short, and i think they can be trimmed even more or removed in whole, leaving few offsets in their places.
  6. It was a joke of cutting text out of context, and how this anything other than joke, (may be not funny though).
  7. I don't know, but this sound like human trafficking.
  8. Kas Ob.

    TSslCertTools for generate CSR

    I highly recommend using OID instead of NID, they are documented, there is so many internet resources and DB populate them, and most important you can find the needed entry by its OID from any certificate or CSR. In this page there is few lines on how to convert OID in its text formatted syntax into OBJ https://docs.openssl.org/1.0.2/man3/OBJ_nid2obj/#examples then use OBJ instead of NID, as there is the same equivalent APIs for each of them Also important note here, "Set" might not be acting as "Add" and i can't find details about this, but if there is Add then it should be used, and not depending on Set From translated code from C++, this code might work, i say might as i didn't test it, just translated it // Create ASN1_OBJECT for givenName OID 2.5.4.42 objGivenName := OBJ_txt2obj(PAnsiChar(AnsiString('2.5.4.42')), 1); if objGivenName = nil then raise Exception.Create('Failed to create ASN1_OBJECT for givenName(2.5.4.42)'); X509_NAME_add_entry_by_OBJ(name, objGivenName, MBSTRING_ASC, PBYTE(AnsiString('John')), -1, -1, 0); ASN1_OBJECT_free(objGivenName); Now as suggestions for Angus, it might be useful to add generic functions to this, this will be future proof, allowing any non essential entries or exotic objects to be added or enumerated, in other words allow customization in its purest way. It could use NID, Text and OID, implement once and can be used whenever request like this pop then the solution is easy, find the OID (or text) for the entry then add it or read it using the custom entry access givenName = 2.5.4.42 surname = 2.5.4.4 https://oid-base.com/cgi-bin/display?oid=2.5.4.4&amp;submit=Display&amp;action=display https://oid-base.com/cgi-bin/display?oid=2.5.4.42&amp;submit=Display&amp;action=display Using NID is also nice but only if it is already Known and declared in OpenSSL Pascal headers, so it will be limited.
  9. Kas Ob.

    OTA vs NTA

    I remember i already shared here in the forum a way to hook compiler messages, not OTA nor NTA, but good old fashion hooking. interacting might be hard to very hard to pin, but getting compiler message(s) is doable, well, tested on few old IDEs, up to XE8, if that will help then i can search for that fun project and try to adjust it for you.
  10. Here another and simpler example, try this interface with the above function TInterfacedObjectFooBar = class(TInterfacedObject, IBar, IFoo) procedure DoSomething; end; TInterfacedObjectFooBarEx = class(TInterfacedObjectFooBar, IBar, IFoo) procedure DoSomething; end; And its result Implemented interfaces in TInterfacedObjectFooBar 0. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D3160 offest: 12 1. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D3170 offest: 16 Implemented interfaces in TInterfacedObject 0. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 00402358 offest: 8 Implemented interfaces in TInterfacedObjectFooBarEx 0. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D32F8 offest: 20 1. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D3308 offest: 24 Implemented interfaces in TInterfacedObjectFooBar 0. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D3160 offest: 12 1. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D3170 offest: 16 Implemented interfaces in TInterfacedObject 0. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 00402358 offest: 8 So make sure you are looking at the right VMT, remember that TObject VMT order is irrelevant with its interfaces VMTs structure and order.
  11. It is right. OK, hold your horses here for second, and i want you to check if you are checking the VMT for the interfaces or something else like TObject, also are they named (with TGUID attached) ? I recommend that you go to this blog post and and read this very article and study the output carefully, try to get how interface inheritance and their VMT works. Here a modified example from the blog program Project6; {$APPTYPE CONSOLE} uses Classes, SysUtils, TypInfo, ComObj; type IFoo = interface ['{11111111-0000-0000-0000-000000000001}'] procedure DoSomething; end; IBar = interface ['{22222222-0000-0000-0000-000000000002}'] procedure DoSomething; end; TFooBar = class(TObject, IUnknown, IFoo, IBar) function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; procedure DoSomething; end; TBarFoo = class(TObject, IBar, IFoo, IUnknown) function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; procedure DoSomething; end; TFooBarOnly = class(TObject, IFoo, IBar) function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; procedure DoSomething; end; TBarFooOnly = class(TObject, IBar, IFoo) function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; procedure DoSomething; end; procedure DumpInterfaces(AClass: TClass); var i: integer; InterfaceTable: PInterfaceTable; InterfaceEntry: PInterfaceEntry; begin while Assigned(AClass) do begin InterfaceTable := AClass.GetInterfaceTable; if Assigned(InterfaceTable) then begin writeln('Implemented interfaces in ', AClass.ClassName); for i := 0 to InterfaceTable.EntryCount - 1 do begin InterfaceEntry := @InterfaceTable.Entries[i]; Write(Format('%d. GUID = %s', [i, GUIDToString(InterfaceEntry.IID)])); Writeln(' VMT addr: ', IntToHex(Cardinal(InterfaceEntry.VTable), SizeOf(Pointer) * 2), ' offest: ', InterfaceEntry.IOffset); end; end; AClass := AClass.ClassParent; end; writeln; end; { TFooBar } procedure TFooBar.DoSomething; begin end; function TFooBar.QueryInterface(const IID: TGUID; out Obj): HResult; begin end; function TFooBar._AddRef: Integer; begin end; function TFooBar._Release: Integer; begin end; { TBarFoo } procedure TBarFoo.DoSomething; begin end; function TBarFoo.QueryInterface(const IID: TGUID; out Obj): HResult; begin end; function TBarFoo._AddRef: Integer; begin end; function TBarFoo._Release: Integer; begin end; { TBarFooOnly } procedure TBarFooOnly.DoSomething; begin end; function TBarFooOnly.QueryInterface(const IID: TGUID; out Obj): HResult; begin end; function TBarFooOnly._AddRef: Integer; begin end; function TBarFooOnly._Release: Integer; begin end; { TFooBarOnly } procedure TFooBarOnly.DoSomething; begin end; function TFooBarOnly.QueryInterface(const IID: TGUID; out Obj): HResult; begin end; function TFooBarOnly._AddRef: Integer; begin end; function TFooBarOnly._Release: Integer; begin end; begin DumpInterfaces(TComponent); DumpInterfaces(TComObject); DumpInterfaces(TComObjectFactory); DumpInterfaces(TFooBar); DumpInterfaces(TBarFoo); DumpInterfaces(TFooBarOnly); DumpInterfaces(TBarFooOnly); readln; end. The output from my XE8 Implemented interfaces in TComponent 0. GUID = {E28B1858-EC86-4559-8FCD-6B4F824151ED} VMT addr: 0048C71C offest: 56 1. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 0048C72C offest: 60 Implemented interfaces in TComObject 0. GUID = {DF0B3D60-548F-101B-8E65-08002B2BD119} VMT addr: 004CF25C offest: 24 1. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 004CF26C offest: 28 Implemented interfaces in TComObjectFactory 0. GUID = {B196B28F-BAB4-101A-B69C-00AA00341D07} VMT addr: 004CF93C offest: 72 1. GUID = {00000001-0000-0000-C000-000000000046} VMT addr: 004CF93C offest: 72 2. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 004CF93C offest: 72 Implemented interfaces in TFooBar 0. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D2880 offest: 4 1. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D2890 offest: 8 2. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 004D2880 offest: 4 Implemented interfaces in TBarFoo 0. GUID = {00000000-0000-0000-C000-000000000046} VMT addr: 004D2AC4 offest: 4 1. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D2AC4 offest: 4 2. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D2AD4 offest: 8 Implemented interfaces in TFooBarOnly 0. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D2D08 offest: 4 1. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D2D18 offest: 8 Implemented interfaces in TBarFooOnly 0. GUID = {11111111-0000-0000-0000-000000000001} VMT addr: 004D2F34 offest: 4 1. GUID = {22222222-0000-0000-0000-000000000002} VMT addr: 004D2F44 offest: 8 Now to simplify your miss understanding, if what you said about off by 3 then the position of the interface (order) in the declaration will matter, and that can't be, right ?
  12. Hi, VMT is not a problem here, VMT for such interfaces are different beast from VMT for objects/classes, VMT tables with COM interfaces (interfaces with GUID which i like to call "named interfaces" or "IDed interfaces") are separated into their own tables identified by their GUID, even for one object/interface, so VMT will be alright no matter what inheritance is there, also VMT for each interface are agnostic for other GUID declaration. As for the interfaces you listed IInterface and IUnknown, this might be a problem as they declared with the same GUID (TGUID) hence they will compete to replace one another, they are identical in structure but different in parameters (declaration), so they will work unless the compiler will complain about stuff like Integer vs Cardinal or TGUID vs PGUID..., the problem here is how compiler see them and when. I might be wrong here, but the fix should be removing Windows.Foundation.IUnknown , in other words, the already known interfaces should not be redeclared/generated.
  13. Also i can't help here, but have question; Are these repositories referring to the same QuickJS ? https://github.com/mengmo/QuickJS-Windows-Build https://github.com/Coldzer0/QuickJS-Pascal
  14. Kas Ob.

    Interesting read about Sleep(0/1) and SwitshToThread

    No sure i do understand that, but lets say on single core the test will yield similar result to the article, meaning Sleep(0) is magnitude slower than Sleep(1) and SwitchToThread, then that OS should not be working at all ! and if worked then it will be slower than computer in 70s and 80s, i mean less than 10Mhz CPU.
  15. Kas Ob.

    Interesting read about Sleep(0/1) and SwitshToThread

    Spent two hours trying to reproduce anything close to that mentioned article https://joeduffyblog.com/2006/08/22/priorityinduced-starvation-why-sleep1-is-better-than-sleep0-and-the-windows-balance-set-manager/ I read that article years ago, many years, yet i couldn't reproduce anything even small hint if that is the case with Sleep(0) vs Sleep(1) vs SwitchToThread, So i just wrote two tests, one is mimicking the starvation pointed and presented in the article producer/consumer, and the other just how efficient these three method. Also to be clear, i am not saying the article is misleading or wrong, but it is most likely testing different thing completely (namely the efficiency of ThreadPool in C# in 2006), or the test is happening is single core CPU belongs to past and gone era, know this Sleep(1) by definition can't perform less than OS timer granularity which is by default 1000/64 seconds almost 15-16 ms, and in best case scenario it will be 1 ms, this is guaranteed by the OS, so the article result is irrelevant today. First test program ThreadIterationTest; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes, Winapi.Windows; {$WARN SYMBOL_PLATFORM OFF} type TSwitchMethod = (smSleep0, smSleep1, smSwitchToThread); TCounterThread = class(TThread) private FSwitchMethod: TSwitchMethod; FIterationCount: Int64; FDuration: Integer; procedure Execute; override; public constructor Create(SwitchMethod: TSwitchMethod; Duration: Integer; Priority: TThreadPriority); property IterationCount: Int64 read FIterationCount; property SwitchMethod: TSwitchMethod read FSwitchMethod; end; constructor TCounterThread.Create(SwitchMethod: TSwitchMethod; Duration: Integer; Priority: TThreadPriority); begin inherited Create(True); // Create suspended FSwitchMethod := SwitchMethod; FDuration := Duration; FIterationCount := 0; Self.Priority := Priority; FreeOnTerminate := True; end; procedure TCounterThread.Execute; var StartTick, Duration: Cardinal; begin Duration := FDuration * 1000; StartTick := GetTickCount; while (GetTickCount - StartTick) < Duration do begin Inc(FIterationCount); case FSwitchMethod of {(*} smSleep0: Sleep(0); smSleep1: Sleep(1); smSwitchToThread: SwitchToThread; {*)} end; end; end; function ThPriorityToString(ThProirity: TThreadPriority): string; var P: Integer; begin case ThProirity of {(*} tpIdle: Result:= 'Idle'; tpLowest: Result:= 'Lowest'; tpLower: Result:= 'Lower'; tpNormal: Result:= 'Normal'; tpHigher: Result:= 'Higher'; tpHighest: Result:= 'Highest'; tpTimeCritical: Result:= 'TimeCritical'; {*)} else Result := 'Unknown'; end; Result := Result + '('; case ThProirity of {(*} tpIdle: P:= THREAD_PRIORITY_IDLE; tpLowest: P:= THREAD_PRIORITY_LOWEST; tpLower: P:= THREAD_PRIORITY_BELOW_NORMAL; tpNormal: P:= THREAD_PRIORITY_NORMAL; tpHigher: P:= THREAD_PRIORITY_ABOVE_NORMAL; tpHighest: P:= THREAD_PRIORITY_HIGHEST; tpTimeCritical: P:= THREAD_PRIORITY_TIME_CRITICAL; {*)} else P := 999; end; Result := Result + IntToStr(P) + ')'; end; procedure RunTest(Duration: Integer; Priority1, Priority2, Priority3: TThreadPriority); var Thread1, Thread2, Thread3: TCounterThread; begin Writeln('Starting test with duration: ', Duration, ' seconds'); Writeln('Thread priorities: Sleep(0)=', ThPriorityToString(Priority1), ', Sleep(1)=', ThPriorityToString(Priority2), ', SwitchToThread=', ThPriorityToString(Priority3)); Thread1 := TCounterThread.Create(smSleep0, Duration, Priority1); Thread2 := TCounterThread.Create(smSleep1, Duration, Priority2); Thread3 := TCounterThread.Create(smSwitchToThread, Duration, Priority3); Thread1.Start; Thread2.Start; Thread3.Start; WaitForSingleObject(Thread1.Handle, INFINITE); WaitForSingleObject(Thread2.Handle, INFINITE); WaitForSingleObject(Thread3.Handle, INFINITE); Writeln('Results:'); Writeln('Sleep(0) iterations: ', Thread1.IterationCount); Writeln('Sleep(1) iterations: ', Thread2.IterationCount); Writeln('SwitchToThread iterations: ', Thread3.IterationCount); Writeln; end; begin try Writeln('Test 1: All threads with normal priority'); RunTest(3, tpNormal, tpNormal, tpNormal); Writeln('Test 1.1: All threads with normal priority'); RunTest(1, tpNormal, tpNormal, tpNormal); Writeln('Test 1.5: All threads with normal priority'); RunTest(5, tpNormal, tpNormal, tpNormal); Writeln('Test 2: Different priorities'); RunTest(5, tpHigher, tpNormal, tpLower); Writeln('Test 3: Different priorities'); RunTest(5, tpLowest, tpHighest, tpNormal); Writeln('Test 4: Different priorities'); RunTest(5, tpLowest, tpLowest, tpLowest); Writeln('Done.'); Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. Its result on my device Test 1: All threads with normal priority Starting test with duration: 3 seconds Thread priorities: Sleep(0)=Normal(0), Sleep(1)=Normal(0), SwitchToThread=Normal(0) Results: Sleep(0) iterations: 15700875 Sleep(1) iterations: 175 SwitchToThread iterations: 19869985 Test 1.1: All threads with normal priority Starting test with duration: 1 seconds Thread priorities: Sleep(0)=Normal(0), Sleep(1)=Normal(0), SwitchToThread=Normal(0) Results: Sleep(0) iterations: 5266693 Sleep(1) iterations: 60 SwitchToThread iterations: 6658333 Test 1.5: All threads with normal priority Starting test with duration: 5 seconds Thread priorities: Sleep(0)=Normal(0), Sleep(1)=Normal(0), SwitchToThread=Normal(0) Results: Sleep(0) iterations: 26351894 Sleep(1) iterations: 269 SwitchToThread iterations: 33344803 Test 2: Different priorities Starting test with duration: 5 seconds Thread priorities: Sleep(0)=Higher(1), Sleep(1)=Normal(0), SwitchToThread=Lower(-1) Results: Sleep(0) iterations: 26332342 Sleep(1) iterations: 299 SwitchToThread iterations: 33324362 Test 3: Different priorities Starting test with duration: 5 seconds Thread priorities: Sleep(0)=Lowest(-2), Sleep(1)=Highest(2), SwitchToThread=Normal(0) Results: Sleep(0) iterations: 26220753 Sleep(1) iterations: 299 SwitchToThread iterations: 33216074 Test 4: Different priorities Starting test with duration: 5 seconds Thread priorities: Sleep(0)=Lowest(-2), Sleep(1)=Lowest(-2), SwitchToThread=Lowest(-2) Results: Sleep(0) iterations: 26350390 Sleep(1) iterations: 291 SwitchToThread iterations: 33374685 Done. Sleep(1) is in the expected range of 60-64 per second Now different test to emulate the article example, not using Delphi RTL thread pool and anonymous thread, because i never trust them on my XE8. program ThreadStarvationTest; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes, Winapi.Windows; {$WARN SYMBOL_PLATFORM OFF} type TYieldMethod = (ymSleep0, ymSleep1, ymSwitchToThread); TStarvationThread = class(TThread) private FYieldMethod: TYieldMethod; FIsProducer: Boolean; FDuration: Cardinal; procedure Execute; override; public constructor Create(YieldMethod: TYieldMethod; IsProducer: Boolean; Priority: TThreadPriority); property Duration: Cardinal read FDuration; end; var x: Integer = 0; constructor TStarvationThread.Create(YieldMethod: TYieldMethod; IsProducer: Boolean; Priority: TThreadPriority); begin inherited Create(True); FYieldMethod := YieldMethod; FIsProducer := IsProducer; Self.Priority := Priority; //FreeOnTerminate := True; // don't care, irrelevant FDuration := 0; end; procedure TStarvationThread.Execute; var StartTick: Cardinal; begin // run threads on one core, core 0 SetThreadAffinityMask(GetCurrentThread, 1); if FIsProducer then begin //Sleep(50); // Sleep(500); // Sleep(1500); x := 1; // Producer sets x end else begin StartTick := GetTickCount; while x = 0 do begin case FYieldMethod of ymSleep0: Sleep(0); ymSleep1: Sleep(1); ymSwitchToThread: SwitchToThread; end; end; FDuration := GetTickCount - StartTick; end; end; function ThPriorityToString(ThPriority: TThreadPriority): string; var P: Integer; begin case ThPriority of {(*} tpIdle: Result := 'Idle'; tpLowest: Result := 'Lowest'; tpLower: Result := 'Lower'; tpNormal: Result := 'Normal'; tpHigher: Result := 'Higher'; tpHighest: Result := 'Highest'; tpTimeCritical: Result := 'TimeCritical'; {*)} else Result := 'Unknown'; end; Result := Result + '('; case ThPriority of {(*} tpIdle: P := THREAD_PRIORITY_IDLE; tpLowest: P := THREAD_PRIORITY_LOWEST; tpLower: P := THREAD_PRIORITY_BELOW_NORMAL; tpNormal: P := THREAD_PRIORITY_NORMAL; tpHigher: P := THREAD_PRIORITY_ABOVE_NORMAL; tpHighest: P := THREAD_PRIORITY_HIGHEST; tpTimeCritical: P := THREAD_PRIORITY_TIME_CRITICAL; {*)} else P := 999; end; Result := Result + IntToStr(P) + ')'; end; function YieldMethodToStr(YieldMethod:TYieldMethod):string; begin case YieldMethod of {(*} ymSleep0: Result := 'Sleep(0)'; ymSleep1: Result := 'Sleep(1)'; ymSwitchToThread: Result := 'SwitchToThread'; {*)} end; end; procedure RunStarvationTest(YieldMethod: TYieldMethod; ConsumerPriority, ProducerPriority: TThreadPriority); var Consumer, Producer: TStarvationThread; begin Writeln('Starting starvation test with ', YieldMethodToStr(YieldMethod), ', Consumer=', ThPriorityToString(ConsumerPriority), ', Producer=', ThPriorityToString(ProducerPriority)); x := 0; Consumer := TStarvationThread.Create(YieldMethod, False, ConsumerPriority); Producer := TStarvationThread.Create(YieldMethod, True, ProducerPriority); Consumer.Start; Producer.Start; Consumer.WaitFor; Producer.WaitFor; Writeln('Result: ', YieldMethodToStr(YieldMethod), ' time: ', Consumer.Duration, ' ms'); Writeln; end; begin try // Test Sleep(0) with equal priorities RunStarvationTest(ymSleep0, tpNormal, tpNormal); // Test Sleep(0) with different priorities RunStarvationTest(ymSleep0, tpNormal, tpLower); // Test Sleep(0) with different priorities RunStarvationTest(ymSleep0, tpLower, tpNormal); // Test Sleep(1) with equal priorities RunStarvationTest(ymSleep1, tpNormal, tpNormal); // Test Sleep(1) with different priorities RunStarvationTest(ymSleep1, tpNormal, tpLower); // Test Sleep(1) with different priorities RunStarvationTest(ymSleep1, tpLower, tpNormal); // Test SwitchToThread with equal priorities RunStarvationTest(ymSwitchToThread, tpNormal, tpNormal); // Test SwitchToThread with different priorities RunStarvationTest(ymSwitchToThread, tpNormal, tpLower); // Test SwitchToThread with different priorities RunStarvationTest(ymSwitchToThread, tpLower, tpNormal); Writeln('Done.'); Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. Its result with and without delaying the producer by uncommenting the Sleep(50).. no starvation observed at all and the result is consistent with the delay introduced being Sleep(50), Sleep(500) or Sleep(1500) Starting starvation test with Sleep(0), Consumer=Normal(0), Producer=Normal(0) Result: Sleep(0) time: 0 ms Starting starvation test with Sleep(0), Consumer=Normal(0), Producer=Lower(-1) Result: Sleep(0) time: 0 ms Starting starvation test with Sleep(0), Consumer=Lower(-1), Producer=Normal(0) Result: Sleep(0) time: 0 ms Starting starvation test with Sleep(1), Consumer=Normal(0), Producer=Normal(0) Result: Sleep(1) time: 0 ms Starting starvation test with Sleep(1), Consumer=Normal(0), Producer=Lower(-1) Result: Sleep(1) time: 15 ms Starting starvation test with Sleep(1), Consumer=Lower(-1), Producer=Normal(0) Result: Sleep(1) time: 0 ms Starting starvation test with SwitchToThread, Consumer=Normal(0), Producer=Normal(0) Result: SwitchToThread time: 0 ms Starting starvation test with SwitchToThread, Consumer=Normal(0), Producer=Lower(-1) Result: SwitchToThread time: 0 ms Starting starvation test with SwitchToThread, Consumer=Lower(-1), Producer=Normal(0) Result: SwitchToThread time: 0 ms Done. True, there is no silver bullet, but Sleep(1) is better for completely different reason and it is impossible to be less than 1ms delay, unless with my vivid imagination your PC has no other threads running, like the OS has nothing else to do, so it will be faced with either 1) put the CPU/core to sleep, i mean really sleep signal and reduce the power for the core. 2) ignore the delay continue, even in this case, the article result can't be reproduce because we have two threads not one, hence the producer will execute releasing the consumer. ps: these tests are for specific measurement, and i know they are not very helpful in real life, but they are accurate in the delays and shows how SwitchToThread is faster then Sleep(0), due the reduced context switch with threads from different processes, as for mixing the priorities, well this is way different subject and longer, but even so as tests shows it is irrelevant with Windows 10 and modern CPU, my CPU is Sandy Bridge so around 15 years old. I would love to see result form modern and different CPUs, just for general information, also from different Windows versions, that would be nice, the discussion of how much relevant the test in real life doesn't concern me, as i am trying to replicate that article strange result.
  16. Kas Ob.

    Blocking hackers

    Dear Angus try the RAW approach for fun, and see how those can be f*** brutal.
  17. Kas Ob.

    Blocking hackers

    Right, but doesn't really cost your server that much resource ? To put it in different way, without too much explanation of the last one i point to in (D) , see, raw packets are always useless with TCP as any switch/router will drop them as they don't belong to any steam, namely established TCP stream, but and here big bug the connection is there and established, the stream is legit, and your packet will reach its destination, can cause resource depletion on their part as you closed ( terminate abruptly) the connection after sending that packet or similar ones, even you keep yours open and repeat this behavior it becomes more like contest of whos bone is tougher, your server with usual resources, their part with havoc and unpredictable behavior to manage.
  18. Kas Ob.

    Bitmaps to Video for Mediafoundation

    You lost me here. What 10m, and what's gps? Honestly i lost my self reading that, fps not gps, (stupid auto correct and clumsy fingers), and 10m=10000000 vs 1m =1000000, as dominator for the rate at setup.
  19. Kas Ob.

    Blocking hackers

    @Angus Robertson I know a few tricks, funny ones and one might say genius while laughing at them, used some and still using in production, but they are not for public or posting in a forum, so i am writing in private to you, you can do them as they are very simple, they are just like step back a little and look at the big picture.
  20. Kas Ob.

    Bitmaps to Video for Mediafoundation

    In 10 tests i did, it is synced and difference is at the beginning is 4 ms and in the middle 4ms and at the end still 4ms, that is very accurate considering the acceptable desyncing between audio and video is constrained and small; https://video.stackexchange.com/questions/25064/by-how-much-can-video-and-audio-be-out-of-sync What is still perplexing me is; 1) why the frames are grouped, so i added something like this "OutputDebugString(PChar('Audio: '+IntToStr(AudioSampleDuration)));" before SafeRelease, same for video, the debug output is clearly showing an interleaved frames one by one ! beautiful interleaving, yet the result video frames are grouped, so it might be something has to do with WMF and its codec or missing some some settings somewhere, in other words you code is doing it right. 2) the duration at 1000 and i am not talking about the timestamp but the relevancy of nominator and video frames is 1000, i tried to tweak things and it didn't change, even used the recommended 10m instead of 1m you are using, still didn't change, so this also might be like above a setting or a constrained bit/frame/packet limitation specific to this very codec, one test video is 60gps with 200 duration, the output is 1000 at 30fps, while it should be 400. Yes in some way, see if there is gap then the audio is distorted and the final video is bad or low quality, so yes decoding the audio into PCM from some exotic audio format, then use more standard audio codec from WMF will be the best thing to keep the quality. Anyway, here a nice answer on SO leading to very beautiful SDK, you might find it very useful https://stackoverflow.com/questions/41326231/network-media-sink-in-microsoft-media-foundation https://www.codeproject.com/Articles/1017223/CaptureManager-SDK-Capturing-Recording-and-Streami#twentythirddemoprogram Now, why i keep looking at this drifting in audio and video you might ask, the answer is long time ago i wanted to know how those media players could read from slow HDD huge chunks of data and decode them then render them, everything is irrelevant here except one behavior you can watch, they like WMP and VLC do strange thing, they read the header of the video, then load huge buffers form the beginning then seek to the end of that file then again load huge chunk, from the end they try to see how much the streams drifted, only after that they play, those players saw it all, so they do tricks of resyncing at there own, when the video/audio stream are desynced and it is possible then adjust and cover it (fix it) Why is this is relevant here if all modern and used players doing this and fix things, because this will fail when you stream that video there is no way to seek to the end, so the player will play what he get, being WebRTC, RTMP, RTSP... Think video conference or WebCam or even security cams being received by server that will encoded and save the videos while allowing the user to monitor one or more cam online, audio and video syncing is important here, and players tricks will not help. Anyway, nice and thank you, you did really nice job.
  21. This version doesn't, i have others, this one i use to capture debug from different tools, parse the output then run command command, it is bidirectional, i use it for building applications instead of batch files, so it should be able to handle InnoSetup and WinLicense ..etc
  22. Mine showed difference in timing with when used 16kb.
  23. @pyscripter i don't have your hardware or anything even close to it, so when you show a command like "dir C:\Windows /s" takes less than 20 seconds, well this amazing, Can you confirm if both libraries on the same reading buffer length, your i think yours at 16kb by default, while mine is left fixed by a constant at 4kb, could that have such huge difference ? in theory it might as "dir C:\Windows /s" do takes minutes on my machine, meaning the output is huge and again the flush operations could causing this difference.
  24. Here a suggestion and highly important; Make sure all call backs from the system 100% safe against exception, you can't raise an exception or allow the RTL to raise one, make sure to encapsulate the code with try..except and no re-raise.
×