Recent

Author Topic: DLL hook/performance questions  (Read 1640 times)

Mike_HDF

  • New member
  • *
  • Posts: 9
DLL hook/performance questions
« on: April 09, 2019, 01:29:34 pm »
Hi, everyone.

I have an old game and I'm trying to tweak it a little with the help of free pascal. The game reads some files in it's EXE folder using the FindFirstFileA() and FindNextFileA() functions of the kernel32.dll. The problem is that I need to sort in alphabetical order that list of files so I can make a useful mod for it and the functions don't do it (depends on Windows version or partition system, not sure).

I want to make my own dll to rewrite only those functions but I'm not sure about the best approach. Some people seems to override some old dll and put theirs own version (letting, for example, to play the soundtrack in MP3 format instead of the Audio CD), but then I would have to re-link a lot of functions to the original dll, right? It could affect to game performance?

Should I try some dll hooking method? Its relatively important that the EXE keeps it's original size and don't want antivirus alerts if is needed some external application.

Thanks in advance.
« Last Edit: April 09, 2019, 01:41:21 pm by Mike_HDF »

howardpc

  • Hero Member
  • *****
  • Posts: 3153
Re: DLL hook/performance questions
« Reply #1 on: April 09, 2019, 04:05:37 pm »
An obvious approach would be to read the filenames into a TStringList, and set Sorted to True. You might need to write a simple TStringList.CustomSort method, depending on the encoding of the filenames.

Mike_HDF

  • New member
  • *
  • Posts: 9
Re: DLL hook/performance questions
« Reply #2 on: April 09, 2019, 05:51:16 pm »
Yes, that's the way I made it. The dll is working well, my question is more related to the hook of the kernel32.dll call. I've seen people with questions about dll injection here, but maybe it's not the appropriate forum.

Thanks for your response anyway.

I'll test the dll by replacing all the functions the exe uses.

440bx

  • Hero Member
  • *****
  • Posts: 1129
Re: DLL hook/performance questions
« Reply #3 on: April 09, 2019, 06:13:47 pm »
The dll is working well, my question is more related to the hook of the kernel32.dll call. I've seen people with questions about dll injection here, but maybe it's not the appropriate forum.
I'll test the dll by replacing all the functions the exe uses.

First, I want to make sure I understand the problem correctly.  You want to trap API calls made by another process and, for this you want to cause the other process to load your dll (or alternatively inject it into the other process), is this correct ?


using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Mike_HDF

  • New member
  • *
  • Posts: 9
Re: DLL hook/performance questions
« Reply #4 on: April 09, 2019, 09:13:00 pm »
The dll is working well, my question is more related to the hook of the kernel32.dll call. I've seen people with questions about dll injection here, but maybe it's not the appropriate forum.
I'll test the dll by replacing all the functions the exe uses.

First, I want to make sure I understand the problem correctly.  You want to trap API calls made by another process and, for this you want to cause the other process to load your dll (or alternatively inject it into the other process), is this correct ?

Yes, I would prefer not to need a third program but yes: I want to override some of the functions (FindFirstFileA, FindNextFileA, FindClose) with the cleanest solution. I'm testing right now and trying to figure out why it crashes. What I have done is rewrite the game EXE to search for my dll instead of kernel32.dll.

My english is limited, sorry if it's hard to understand me and thanks again.

440bx

  • Hero Member
  • *****
  • Posts: 1129
Re: DLL hook/performance questions
« Reply #5 on: April 09, 2019, 10:31:22 pm »
Yes, I would prefer not to need a third program but yes: I want to override some of the functions (FindFirstFileA, FindNextFileA, FindClose) with the cleanest solution. I'm testing right now and trying to figure out why it crashes. What I have done is rewrite the game EXE to search for my dll instead of kernel32.dll.
I'm going to suggest something that you won't find anywhere mentioned but, it is a variation on techniques used to analyze malware.

As you know, the typical/"normal" way of solving the problem you want to solve is to inject a dll into the other process (that what MS wants you to do.)  The other way of solving that problem - which has its pros and cons - is to load the exe as if it were a DLL into your process and executing it there.

This is roughly how it's done:

1. Use LoadLibraryEx to load the exe into your process specifying LOAD_LIBRARY_AS_IMAGE_RESOURCE.  This will cause LoadLibraryEx to map the exe with the same layout as when it is loaded to be executed.  However, when mapped this way, the loader does not do any fixups, including relocations and updating the Import Address Table (IAT) with the actual address of the functions the exe imports.

2. Apply the relocations and update the IAT.  That's fairly easy (same process as when rebasing an executable followed by a loop over the IAT calling GetProcAddress and updating all the IAT slots.) Be careful in this step, some exe's put the IAT in a read only section of the PE file, in those cases, it only becomes read only after the loader has patched the IAT but, when you're doing the loader's job, you either change the protection (VirtualProtect) or use WriteProcessMemory which will work around that "inconvenience".

Steps 3 and 4 that follow should not be necessary but they are a good precaution to have in place.

3. Somewhere in your program have a function that is the same as the typical DLLMAIN.  You'll tell Windows that's the "guest" exe's entry point which it may call (it usually won't) when attach/detach events occur.  This step may not be necessary since Windows didn't load the exe for execution but, better to play it safe, just in case Windows has a change of heart.

4. change the exe's entry point to point to your "DLLMAIN".  (of course save the exe's original entry point since you'll need it to start the exe with a call to CreateThread.  NOTE: DisableThreadLibraryCalls is not a way to get around having to do this because even when disabled, the DLLs still get a process attach and detach, if there is no handler for that and, Windows has decided to call the "dll" entry point, it can cause your process to go south when ending.

Once those are done, you have a lot of advantages over injecting a dll into another process.  Among them:

1. you have complete control over what the exe can and cannot do as well as when it gets to do it.
2. you can track and modify its TLS callbacks
3. you can debug that process easily since it is in your process space instead of another.
4. you can implement a "poor man's" debugger.  Slap an int 3 anywhere in the exe if you need it to break and the exception will be routed to your exception handler(s).
5. You can attach a console to your process and use writeln in the traps you've installed to see what's going on in real time.

One thing you need to watch out for is thread local storage.  If the exe uses static thread variables and your program does too then you need to ensure that your program doesn't overwrite any of the "guest" exe's thread variables.  Easy way to solve that problem is not to use static thread variables instead use TlsAlloc. You may also need to execute the exe's thread callbacks since those are usually meant to initialize the thread variables as the program needs them.

Another thing to watch out for when doing that is, Windows will see your program as the process owner (instead of being owned by the "guest" exe) and there are some assumptions it will make as a result of that.  Some API calls made by the "guest" will return addresses that won't be "ideal".  The usual culprits are APIs that default to a process' resource (such as GetProcessHeap).  Those can be a source of undesirable side effects.  Use CreateHeap, trap GetProcessHeap and hand it the heap handle of the heap (edited) you created.  That way, it stays out of your program's way and your program doesn't get in its way either.   Go through the APIs the "guest" exe imports, any API that returns a default is a potential source of problems.

It takes some work to get it setup but, once you've gotten steps 1 and 2 done right, any additional steps can be determined with the use of a debugger.  Once it's set up, everything is so much easier when the executable is in your own process.  If you decide to give it a shot that way, I recommend you download ProcessHacker (free open source utility), it will allow you to inspect your process memory layout with ease (you'll need that!.)  Essentially, it's just a "poor man's" sandbox.







« Last Edit: April 09, 2019, 11:31:10 pm by 440bx »
using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Mike_HDF

  • New member
  • *
  • Posts: 9
Re: DLL hook/performance questions
« Reply #6 on: April 10, 2019, 09:39:19 am »
Wow, 440bx, thanks a lot for your detailed response. Since I want to investigate the game's exe for fixing and tweaking, I'll take a deep look into it.

I have a working DLL by now.

This is an excerpt of the working code of the DLL (the game runs perfectly):

Code: Pascal  [Select]
  1. library sortfile;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$R sortfile.res}
  5.  
  6. uses
  7.   Classes,windows
  8.   { you can add units after this };
  9.  
  10. function FindFirstFileA(lpFileName: LPCSTR; var lpFindFileData: TWIN32FindDataA): THandle; external 'kernel32' name 'FindFirstFileA';
  11. function FindNextFileA(hFindFile: THandle; var lpFindFileData: TWIN32FindDataA): BOOL; external 'kernel32' name 'FindNextFileA';
  12. function FindClose(hFindFile : THandle): BOOL; external 'kernel32' name 'FindClose';
  13.  
  14. exports
  15.   FindFirstFileA,
  16.   FindNextFileA,
  17.   FindClose;
  18. begin
  19. end.
  20.  

But when I try to personalize the functions, got different SIGSEGV errors. I'll have different versions of Lazarus, both with a cross platform compiler:
1.6.2 in a laptop (the one I'm using right now) and it does compile and work with a test Exe I made, but can't produce an i386-win32 DLL, the compiler says it's not supported and the game is a 32 bits app. On home I have 1.9.x and can create the i386-win32 DLL but crashes on a lot of lines. I have Win7 x64 on both.

This simple comparison crash:

Code: Pascal  [Select]
  1. uses
  2.   Classes,windows
  3. ...
  4. function FindFirstFileA(lpFileName: LPCSTR; var lpFindFileData: TWIN32FindDataA): THandle; stdcall;
  5. var
  6.   myhandle : THandle;
  7. ...
  8.   myhandle := FindFirstFile(lpFileName,lpFindFileData);
  9.   if myhandle <> INVALID_HANDLE_VALUE then begin
  10. ...
  11.  

There must be a problem with using an x64 build of Lazarus when creating a x32 DLL or EXE that uses DLLs, Am I right?
« Last Edit: April 10, 2019, 09:52:52 am by Mike_HDF »

Mike_HDF

  • New member
  • *
  • Posts: 9
Re: DLL hook/performance questions
« Reply #7 on: April 10, 2019, 01:34:02 pm »
This is insane! Even installing Lazarus 2.0.0 (32 bits) and recompiling, it keeps crashing  :o.

440bx

  • Hero Member
  • *****
  • Posts: 1129
Re: DLL hook/performance questions
« Reply #8 on: April 10, 2019, 02:31:40 pm »
Wow, 440bx, thanks a lot for your detailed response. Since I want to investigate the game's exe for fixing and tweaking, I'll take a deep look into it.
You'll find that learning that technique is definitely worth it.

There must be a problem with using an x64 build of Lazarus when creating a x32 DLL or EXE that uses DLLs, Am I right?
As far as the exception you're getting, it's really difficult to debug things that are not in the same process.   If I were doing it the way you're doing it, I'd put an INT 3 (a call to DebugBreak()) at the entrance of every trap I've coded (and hope there is a way for the debugger you're using to activate itself on those.) I'd pay close attention to the stack upon entry and exit.  Very often the problems occur because the stack frame is not properly restored.    That's always the first thing to check, is the stack handled correctly and, in the specific case of FindFirstFile, FindNextFile, it's possible, even likely, that the exe is using recursion.  If you have any static variables being set in your traps, recursion will usually mess up the values.

One thing you can do, if you place calls to DebugBreak() at the entrance of your traps and the debugger you're using won't automatically activate and trap them, you can use the Visual Studio debugger.  When the exception occurs, Windows will present a box giving you the option to debug the program, tell it you want to debug it and it will activate a new instance of the VS debugger.  You'll get that too if you have calls to DebugBreak() in your traps.  Of course, you'll be debugging in raw (no symbols) assembler.

My first impression is that the 64bit Lazarus, while it may be causing the problem, it's more likely that the problem is caused by something in your code.

Without having the entire ball of wax to look at, I can only give you general ideas.

HTH.

using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: DLL hook/performance questions
« Reply #9 on: April 10, 2019, 02:43:13 pm »
I have a working DLL by now.

[snip]

There must be a problem with using an x64 build of Lazarus when creating a x32 DLL or EXE that uses DLLs, Am I right?
You are not using stdcall. Try at the top of your functions to add:
Code: Pascal  [Select]
  1. {$calling stdcall}

440bx

  • Hero Member
  • *****
  • Posts: 1129
Re: DLL hook/performance questions
« Reply #10 on: April 10, 2019, 03:04:35 pm »
You are not using stdcall. Try at the top of your functions to add:
Code: Pascal  [Select]
  1. {$calling stdcall}
Good observation.  Without it the stack got screwed up.
using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: DLL hook/performance questions
« Reply #11 on: April 10, 2019, 03:09:33 pm »
You are not using stdcall. Try at the top of your functions to add:
Code: Pascal  [Select]
  1. {$calling stdcall}
Good observation.  Without it the stack got screwed up.
You pointed at it. I checked his code after reading your previous post. We still don't see the method he used to replace these functions.

Mike_HDF

  • New member
  • *
  • Posts: 9
Re: DLL hook/performance questions
« Reply #12 on: April 10, 2019, 03:36:22 pm »
As far as the exception you're getting, it's really difficult to debug things that are not in the same process.   If I were doing it the way you're doing it, I'd put an INT 3 (a call to DebugBreak()) at the entrance of every trap I've coded (and hope there is a way for the debugger you're using to activate itself on those.) I'd pay close attention to the stack upon entry and exit.  Very often the problems occur because the stack frame is not properly restored.    That's always the first thing to check, is the stack handled correctly and, in the specific case of FindFirstFile, FindNextFile, it's possible, even likely, that the exe is using recursion.  If you have any static variables being set in your traps, recursion will usually mess up the values.

One thing you can do, if you place calls to DebugBreak() at the entrance of your traps and the debugger you're using won't automatically activate and trap them, you can use the Visual Studio debugger.  When the exception occurs, Windows will present a box giving you the option to debug the program, tell it you want to debug it and it will activate a new instance of the VS debugger.  You'll get that too if you have calls to DebugBreak() in your traps.  Of course, you'll be debugging in raw (no symbols) assembler.

Without having the entire ball of wax to look at, I can only give you general ideas.

HTH.

I'm not that good, I'm still learning about assembly, sorry  :-\

My first impression is that the 64bit Lazarus, while it may be causing the problem, it's more likely that the problem is caused by something in your code.

I found something weird: When I leave the default option for OS and CPU (then it compiles a 64 bits executable), the variable myhandle of THandle type says it's a QWord. When I choose Win32 and i386 it says it's a LongWord, I'm doing something wrong with the project configuration?

You are not using stdcall. Try at the top of your functions to add:
Code: Pascal  [Select]
  1. {$calling stdcall}

Thanks for your response.

I defined the functions with stdcall inside DLL. Tried what you suggested too but it fails.

Quote
Without having the entire ball of wax to look at, I can only give you general ideas.

This is a bigger excerpt of the DLL:
Code: Pascal  [Select]
  1. library sortfile;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$R sortfile.res}
  5.  
  6. uses
  7.   Classes,Windows,sysutils;
  8.  
  9. var
  10.   fileLst : TStringList;
  11.   idxLst : Integer;
  12.  
  13. type
  14.   PTOP_LEVEL_EXCEPTION_FILTER = function(ExceptionInfo: PEXCEPTION_POINTERS): LONG; stdcall;
  15.   {$EXTERNALSYM PTOP_LEVEL_EXCEPTION_FILTER}
  16.   LPTOP_LEVEL_EXCEPTION_FILTER = PTOP_LEVEL_EXCEPTION_FILTER;
  17.  
  18. //Funciones propias de kernel32.dll que son usadas por Total Annihilation
  19. function GetVolumeInformationA(lpRootPathName: LPCSTR; lpVolumeNameBuffer: LPSTR;
  20.          nVolumeNameSize: DWORD; lpVolumeSerialNumber: LPDWORD;
  21.          var lpMaximumComponentLength, lpFileSystemFlags: DWORD;
  22.          lpFileSystemNameBuffer: LPSTR; nFileSystemNameSize: DWORD): BOOL; external 'kernel32' name 'GetVolumeInformationA';
  23. function GetDriveTypeA(lpRootPathName: LPCSTR): UINT; external 'kernel32' name 'GetDriveTypeA';
  24. function InterlockedExchange(var Target: LONG; Value: LONG): LONG; external 'kernel32' name 'InterlockedExchange';
  25. function SetEvent(hEvent: HANDLE): BOOL; external 'kernel32' name 'SetEvent';
  26. function GetVersionExA(lpVersionInformation: LPOSVERSIONINFOA): BOOL; external 'kernel32' name 'GetVersionExA';
  27. function GlobalSize(hMem: HGLOBAL): SIZE_T; external 'kernel32' name 'GlobalSize';
  28. function GlobalLock(hMem: HGLOBAL): LPVOID; external 'kernel32' name 'GlobalLock';
  29. function GetEnvironmentStrings: LPWSTR; external 'kernel32' name 'GetEnvironmentStrings';
  30. function GetEnvironmentStringsW: LPWSTR; external 'kernel32' name 'GetEnvironmentStringsW';
  31. function SetHandleCount(uNumber: UINT): UINT; external 'kernel32' name 'SetHandleCount';
  32. function GetStdHandle(nStdHandle: DWORD): HANDLE; external 'kernel32' name 'GetStdHandle';
  33. function GetFileType(hFile: HANDLE): DWORD; external 'kernel32' name 'GetFileType';
  34. function HeapDestroy(hHeap: HANDLE): BOOL; external 'kernel32' name 'HeapDestroy';
  35. function HeapCreate(flOptions: DWORD; dwInitialSize: SIZE_T;
  36.          dwMaximumSize: SIZE_T): HANDLE; external 'kernel32' name 'HeapCreate';
  37. function SetStdHandle(nStdHandle: DWORD; hHandle: HANDLE): BOOL; external 'kernel32' name 'SetStdHandle';
  38. function GetStringTypeA(Locale:LCID; dwInfoType:DWORD; lpSrcStr:LPCSTR;
  39.          cchSrc:longint; lpCharType:LPWORD):WINBOOL; external 'kernel32' name 'GetStringTypeA';
  40. function GetStringTypeW(dwInfoType: DWORD; const lpSrcStr: PWideChar;
  41.          cchSrc: BOOL; var lpCharType: Word): BOOL; external 'kernel32' name 'GetStringTypeW';
  42. function FlushFileBuffers(hFile: HANDLE): BOOL; external 'kernel32' name 'FlushFileBuffers';
  43. function IsBadReadPtr(lp: LPVOID; ucb: UINT_PTR): BOOL; external 'kernel32' name 'IsBadReadPtr';
  44. function IsBadWritePtr(lp: LPVOID; ucb: UINT_PTR): BOOL; external 'kernel32' name 'IsBadWritePtr';
  45. function IsBadCodePtr(lpfn: FARPROC): BOOL; external 'kernel32' name 'IsBadCodePtr';
  46. function IsValidLocale(Locale:LCID; dwFlags:DWORD):WINBOOL; external 'kernel32' name 'IsValidLocale';
  47. function IsValidCodePage(CodePage:UINT):WINBOOL; external 'kernel32' name 'IsValidCodePage';
  48. function GetLocaleInfoA(Locale:LCID; LCType:LCTYPE; lpLCData:LPSTR; cchData:longint):longint; external 'kernel32' name 'GetLocaleInfoA';
  49. function EnumSystemLocalesA(lpLocaleEnumProc:LOCALE_ENUMPROCA; dwFlags:DWORD):WINBOOL; external 'kernel32' name 'EnumSystemLocalesA';
  50. function GetUserDefaultLCID:LCID; external 'kernel32' name 'GetUserDefaultLCID';
  51. function CompareStringA(Locale:LCID; dwCmpFlags:DWORD; lpString1:LPCSTR;
  52.          cchCount1:longint; lpString2:LPCSTR;cchCount2:longint):longint; external 'kernel32' name 'CompareStringA';
  53. function CompareStringW(Locale:LCID; dwCmpFlags:DWORD; lpString1:LPCWSTR;
  54.          cchCount1:longint; lpString2:LPCWSTR;cchCount2:longint):longint; external 'kernel32' name 'CompareStringW';
  55. function GetLocaleInfoW(Locale:LCID; LCType:LCTYPE; lpLCData:LPWSTR; cchData:longint):longint; external 'kernel32' name 'GetLocaleInfoW';
  56. function TlsGetValue(dwTlsIndex: DWORD): LPVOID; external 'kernel32' name 'TlsGetValue';
  57. function MultiByteToWideChar(CodePage:UINT; dwFlags:DWORD; lpMultiByteStr:LPCSTR;
  58.          cchMultiByte:longint; lpWideCharStr:LPWSTR;cchWideChar:longint):longint; external 'kernel32' name 'MultiByteToWideChar';
  59. procedure ExitProcess(uExitCode:UINT); external 'kernel32' name 'ExitProcess';
  60. function GlobalHandle(pMem: LPCVOID): HGLOBAL; external 'kernel32' name 'GlobalHandle';
  61. function lstrlenA(lpString: LPCSTR): Integer; external 'kernel32' name 'lstrlenA';
  62. function lstrcpyA(lpString1: LPSTR; lpString2: LPCSTR): LPSTR; external 'kernel32' name 'lstrcpyA';
  63. function lstrcpynA(lpString1: LPSTR; lpString2: LPCSTR; iMaxLength: Integer): LPSTR; external 'kernel32' name 'lstrcpynA';
  64. function CloseHandle(hObject: HANDLE): BOOL; external 'kernel32' name 'CloseHandle';
  65. [...]
  66. function GetThreadPriority(hThread: HANDLE): Integer; external 'kernel32' name 'GetThreadPriority';
  67. function SetUnhandledExceptionFilter(lpTopLevelExceptionFilter : LPTOP_LEVEL_EXCEPTION_FILTER) : LPTOP_LEVEL_EXCEPTION_FILTER; external 'kernel32' name 'SetUnhandledExceptionFilter';
  68.  
  69. //Funciones que vamos a personalizar, aunque también las usaremos internamente
  70. function FindFirstFile(lpFileName: LPCSTR; var lpFindFileData: TWIN32FindDataA): THandle; external 'kernel32' name 'FindFirstFileA';
  71. function FindNextFile(hFindFile: THandle; var lpFindFileData: TWIN32FindDataA): BOOL; external 'kernel32' name 'FindNextFileA';
  72. function FindCloseMe(hFindFile : THandle): BOOL; external 'kernel32' name 'FindClose';
  73.  
  74. { *** Aquí empieza la fiesta *** }
  75. {
  76. procedure asigna_estructura_de_1_a_2(p1 : TWIN32FindDataA; var p2 : TWIN32FindDataA);
  77. begin
  78.   p2.cAlternateFileName := p1.cAlternateFileName;
  79.   p2.cFileName := p1.cFileName;
  80.   p2.dwFileAttributes:= p1.dwFileAttributes;
  81.   p2.dwReserved0:= p1.dwReserved0;
  82.   p2.dwReserved1:= p1.dwReserved1;
  83.   p2.ftCreationTime := p1.ftCreationTime;
  84.   p2.ftLastAccessTime := p1.ftLastAccessTime;
  85.   p2.ftLastWriteTime := p1.ftLastWriteTime;
  86.   p2.nFileSizeHigh := p1.nFileSizeHigh;
  87.   p2.nFileSizeLow := p1.nFileSizeLow;
  88. end; //asigna_estructura_de_1_a_2
  89. }
  90. function FindFirstFileA(lpFileName: LPCSTR; var lpFindFileData: TWIN32FindDataA): THandle; stdcall;
  91. var
  92.   myhandle : THandle;
  93.   firstdata : TWIN32FindDataA;
  94. begin
  95.   fileLst := TStringList.Create();
  96.   idxLst := 0;
  97.  
  98.   myhandle := FindFirstFile(lpFileName,lpFindFileData);
  99.   //asigna_estructura_de_1_a_2(lpFindFileData,firstdata);
  100.  
  101.   if myhandle <> INVALID_HANDLE_VALUE then begin
  102.      repeat
  103.          fileLst.Add(lpFindFileData.cFileName);
  104.      until FindNextFile(myhandle,lpFindFileData) = FALSE;
  105.      FindCloseMe(myhandle);
  106.      fileLst.SaveToFile('ztmp.txt');
  107.  
  108.      fileLst.Sort();
  109.      //asigna_estructura_de_1_a_2(firstdata,lpFindFileData);
  110.      lpFindFileData.cFileName := fileLst.Strings[0];
  111.      idxLst := 1;
  112.   end;
  113.  
  114.   result := myhandle;
  115. end; //FindFirstFileA
  116.  
  117. function FindNextFileA(hFindFile: THandle; var lpFindFileData: TWIN32FindDataA): BOOL; stdcall;
  118. begin
  119.   if idxLst < fileLst.Count then begin
  120.      lpFindFileData.cFileName := fileLst.Strings[idxLst];
  121.      idxLst := idxLst + 1;
  122.      result := TRUE;
  123.   end else begin
  124.      result := FALSE;
  125.   end;
  126. end; //FindNextFileA
  127.  
  128. function FindClose(hFindFile : THandle): BOOL; stdcall;
  129. begin
  130.   fileLst.Clear();
  131.   fileLst := nil;
  132.   result := TRUE;
  133. end; //FindClose
  134.  
  135. exports
  136.   GetVolumeInformationA,
  137.   GetDriveTypeA,
  138.   InterlockedExchange,
  139.   SetEvent,
  140.   GetVersionExA,
  141.   GlobalSize,
  142.   GlobalLock,
  143.   GetEnvironmentStrings,
  144.   GetEnvironmentStringsW,
  145.   SetHandleCount,
  146.   GetStdHandle,
  147.   GetFileType,
  148.   HeapDestroy,
  149.   HeapCreate,
  150.   SetStdHandle,
  151.   GetStringTypeA,
  152.   GetStringTypeW,
  153.   FlushFileBuffers,
  154.   IsBadReadPtr,
  155.   IsBadWritePtr,
  156.   IsBadCodePtr,
  157.   IsValidLocale,
  158.   IsValidCodePage,
  159.   GetLocaleInfoA,
  160.   EnumSystemLocalesA,
  161.   GetUserDefaultLCID,
  162.   CompareStringA,
  163.   CompareStringW,
  164.   GetLocaleInfoW,
  165.   TlsGetValue,
  166.   MultiByteToWideChar,
  167.   ExitProcess,
  168.   GlobalHandle,
  169.   lstrlenA,
  170.   lstrcpyA,
  171.   lstrcpynA,
  172.   CloseHandle,
  173.   CreateFileA,
  174.   DebugBreak,
  175.   Sleep,
  176.   GetTickCount,
  177.   GetPrivateProfileIntA,
  178.   LoadLibraryA,
  179.   SetThreadPriority,
  180.   GetCurrentThread,
  181.   WaitForSingleObject,
  182.   GetPrivateProfileStringA,
  183.   FreeLibrary,
  184.   QueryPerformanceCounter,
  185.   WriteFile,
  186.   GlobalMemoryStatus,
  187.   CreateSemaphoreA,
  188.   OpenSemaphoreA,
  189.   GetModuleFileNameA,
  190.   SetFilePointer,
  191.   SetCurrentDirectoryA,
  192.   GlobalFree,
  193.   GlobalUnlock,
  194.   GetProcAddress,
  195.   GetFileSize,
  196.   GetOEMCP,
  197.   GetACP,
  198.   GetCPInfo,
  199.   UnhandledExceptionFilter,
  200.   LCMapStringW,
  201.   LCMapStringA,
  202.   WideCharToMultiByte,
  203.   FreeEnvironmentStringsW,
  204.   ReadFile,
  205.   SetLastError,
  206.   TlsFree,
  207.   TlsAlloc,
  208.   FatalAppExitA,
  209.   HeapReAlloc,
  210.   HeapSize,
  211.   HeapAlloc,
  212.   HeapFree,
  213.   RaiseException,
  214.   CreateDirectoryA,
  215.   FileTimeToSystemTime,
  216.   SetEnvironmentVariableA,
  217.   GetCurrentDirectoryA,
  218.   DeleteFileA,
  219.   MoveFileA,
  220.   ResumeThread,
  221.   SetEndOfFile,
  222.   GetVersion,
  223.   GetStartupInfoA,
  224.   SetConsoleCtrlHandler,
  225.   GetLocalTime,
  226.   GetTimeZoneInformation,
  227.   ExitThread,
  228.   TlsSetValue,
  229.   InterlockedIncrement,
  230.   InterlockedDecrement,
  231.   RtlUnwind,
  232.   TerminateProcess,
  233.   DeviceIoControl,
  234.   GetPriorityClass,
  235.   SetPriorityClass,
  236.   QueryPerformanceFrequency,
  237.   GetSystemTime,
  238.   SystemTimeToFileTime,
  239.   UnmapViewOfFile,
  240.   CreateFileMappingA,
  241.   MapViewOfFile,
  242.   DeleteCriticalSection,
  243.   FreeEnvironmentStringsA,
  244.   GetFileTime,
  245.   FileTimeToLocalFileTime,
  246.   FileTimeToDosDateTime,
  247.   GetSystemInfo,
  248.   GetLastError,
  249.   GetCurrentProcess,
  250.   GetModuleHandleA,
  251.   VirtualFree,
  252.   VirtualAlloc,
  253.   InitializeCriticalSection,
  254.   GlobalAlloc,
  255.   GetThreadPriority,
  256.   SetUnhandledExceptionFilter,
  257.   CreateThread,
  258.   GetCommandLineA,
  259.   VirtualQuery,
  260.   VirtualProtect,
  261.   EnterCriticalSection,
  262.   LeaveCriticalSection,
  263.   GetCurrentThreadId,
  264.   OutputDebugStringA,
  265.   ResetEvent,
  266.   CreateEventA,
  267.   GetFullPathNameA,
  268.   FindFirstFileA,
  269.   FindNextFileA,
  270.   FindClose;
  271. begin
  272. end.
  273.  

This is the test program code:
Code: Pascal  [Select]
  1. program TesteoDll;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$calling stdcall}
  5.  
  6. uses
  7.   Windows;
  8.  
  9. var
  10.   myhandle : THandle;
  11.   mydata : TWIN32FindDataA;
  12.  
  13. function FindFirstFile(lpFileName: LPCSTR; var lpFindFileData: TWIN32FindDataA): THandle; external 'sortfile' name 'FindFirstFileA';
  14. function FindNextFile(hFindFile: THandle; var lpFindFileData: TWIN32FindDataA): BOOL; external 'sortfile' name 'FindNextFileA';
  15. function FindClose(hFindFile : THandle): BOOL; external 'sortfile' name 'FindClose';
  16.  
  17. begin
  18.   myhandle := FindFirstFile('*.ccx',mydata);
  19.  
  20.   while (myhandle <> INVALID_HANDLE_VALUE) do begin
  21.     writeln(mydata.cFileName);
  22.     if (FindNextFile(myhandle,mydata) = FALSE) then begin
  23.       break;
  24.     end;
  25.   end;
  26.   FindClose(myhandle);
  27. end.
  28.  

Sure I made some mistake  :-[

440bx

  • Hero Member
  • *****
  • Posts: 1129
Re: DLL hook/performance questions
« Reply #13 on: April 10, 2019, 04:14:50 pm »
I'm not that good, I'm still learning about assembly, sorry  :-\
For what you're trying to do that will be a significant handicap.  Google "iczelion tutorials" that will be a good place to start.  For a programmer Assembler is like salt to a chef. 

I found something weird: When I leave the default option for OS and CPU (then it compiles a 64 bits executable), the variable myhandle of THandle type says it's a QWord. When I choose Win32 and i386 it says it's a LongWord, I'm doing something wrong with the project configuration?
That's completely normal.  In 32bit, handles are 32bit wide, in 64bit they are 64bit wide.  The Lazarus/FPC are using the unit versions that have the correct size for the compile target.  A nice feature.

I defined the functions with stdcall inside DLL. Tried what you suggested too but it fails.
From what I see, I get the impression you copied some headers into your Sortfile library.  That one also needs {$calling stdcall} otherwise the API calls it makes will leave the stack unbalanced.  As a rule of thumb, use your editor to change all occurrences of "external" to "stdcall; external" that's the safe way of declaring an API header (unless, of course, it is a cdecl function then you don't want "stdcall" in there.)

This is a bigger excerpt of the DLL:
That helped.  Sortfile, as mentioned above, seems to be missing the "stdcall" calling convention.

Sure I made some mistake  :-[
That is a reasonable (and likely correct) conclusion :)
« Last Edit: April 10, 2019, 04:21:45 pm by 440bx »
using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Mike_HDF

  • New member
  • *
  • Posts: 9
Re: DLL hook/performance questions
« Reply #14 on: April 10, 2019, 04:44:53 pm »
For what you're trying to do that will be a significant handicap.  Google "iczelion tutorials" that will be a good place to start.  For a programmer Assembler is like salt to a chef. 

I tried something easy to start and see what I got  :D, not very experienced with free pascal neither, is obvious. Thanks for the info, I'm searching those tutorials.

That's completely normal.  In 32bit, handles are 32bit wide, in 64bit they are 64bit wide.  The Lazarus/FPC are using the unit versions that have the correct size for the compile target.  A nice feature.

You're right. Don't know what I've been thinking about.

From what I see, I get the impression you copied some headers into your Sortfile library.  That one also needs {$calling stdcall} otherwise the API calls it makes will leave the stack unbalanced.  As a rule of thumb, use your editor to change all occurrences of "external" to "stdcall; external" that's the safe way of declaring an API header (unless, of course, it is a cdecl function then you don't want "stdcall" in there.)

That worked!

That is a reasonable (and likely correct) conclusion :)

You're right  :D.

Thanks a lot to everyone!