I know the topic "Fast GetProcAddress" by Petroizki:
http://www.masm32.com/board/index.php?topic=1144.0
But my GetProcAddress function should be faster..
Why?
- He uses as a 1st parameter hModule and
that is standard
From:MSDN
[in] Handle to the DLL module that contains the function or variable.
The LoadLibrary or GetModuleHandle function returns this handle."
I use as a 1st parameter pointer to zero ended string with
the name of the module
Example:
db "c:\Windows\system32\Kernel32.dll",0
or
db "Kernel32.dll",0
Hence, one Windows API less..
and our Windows program may be just
with one imported API -> LoadLibrary...
(which is mandatory to run properly)
.686
.mmx
;k3d
.xmm
.model flat, stdcall
option casemap:none, language:stdcall, dotname
assume fs:nothing
include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\macros\macros.asm
include c:\masm32\macros\ucmacros.asm
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
GetProcAddr proto :DWORD, :DWORD
dwtoa proto :DWORD, :DWORD
.data
;szDllName db "c:\windows\system32\Kernel32.dll",0
szDllName db "Kernel32.dll",0
;szDllName db "Ws2_32.dll",0
;szProcName db "WriteFile",0,0,0,0
;szProcName db "WSAStartup",0,0,0,0
szProcName db "HeapAlloc",0,0,0,0
.code ;
Start: ;
push offset szDllName ; lp Dir name
push offset szProcName ; lp Proc Name
;push 908 ; ordinal of WriteFile API
call GetProcAddr ;
jmp ExitProcess ;
;
;........................................;
;.......................................;
; Local Variables:
; [esp+0*4] -> ExportDirectory address + ExportDirectorySize
; [esp+1*4] -> flag for forwarded proc
;
; Proc Parameters:
; [esp+1*4+(4*4+2*4)] -> lpProc ; lp of proc name or ordinal value
; [esp+2*4+(4*4+2*4)] -> lpDll ; lp Dll name
;.......................................;
OPTION PROLOGUE:NONE ;
OPTION EPILOGUE:NONE ;
GetProcAddr proc lpProc:DWORD,lpDll:DWORD
;
;
add esp,-2*4-4*4 ; room for 4 registers and 2 local variables
mov [esp+2*4+0*4], edi ; saving registers
mov [esp+2*4+1*4], ebp ;
mov [esp+2*4+2*4], esi ;
mov [esp+2*4+3*4], ebx ;
mov dword ptr [esp+1*4], 0 ; [esp+1*4]-> clear flag for forwarded proc
GetStart: ;
mov edx, [esp+2*4+4*4+2*4] ; edx->lp Dll name
mov ebp, 20h ; ebp-> BaseDllName address (Unicode)
cmp byte ptr [edx+1], ":" ; "c:\...." Is it full path or just dll name?
jne @f ;
mov ebp, 18h ; ebp-> FullDllName (Unicode)
@@: ;
; Get module base address...............;
mov eax, fs:[30h] ; PEB base in eax
cmp dword ptr [esp+1*4], -1 ; If it is forwarded esi->ntdll.dll
mov eax, [eax+0Ch] ; eax-> PEB_LDR_DATA
mov edi, edx ; edi->lp Dll name
mov esi, [eax+1Ch] ; esi-> 1st entry in InitOrderModuleList
je @f ; else
mov esi, [esi] ; esi->Kernel32.dll
@@: ;
mov eax, [esi+ebp] ; eax-> BaseDllName or FullDllName (Unicode)
mov ebx, esi ; ebx-> the 1st LDR_MODULE in the chain
; Comparing strings ....................;
;
FindNextCharw: ;
mov ch, [eax] ; eax-> BaseDllName or FullDllName (Unicode)
add eax, 2 ;
cmp ch, 5Ah ;
ja @f ;
cmp ch, 41h ;
jl @f ;
or ch, 20h ;
@@: ;
mov cl, [edx] ; edx->lp dll name string "." or zero ended
add edx, 1 ;
cmp cl, 5Ah ;
ja @f ;
cmp cl, 41h ;
jl @f ;
or cl, 20h ;
@@: ;
cmp cl, ch ;
jne Next_LDRw ;
test ch, ch ;
je @f ;
cmp ch, "." ;
jne FindNextCharw ;
cmp dword ptr [esp+1*4], -1 ; flag for forwarded proc -> If it is forwarded
jne FindNextCharw ; copy until "." , else until zero
@@: ;
mov ebx, [esi+8] ; ebx-> Base Dll Name address
je GetNextApi ;
;
; Next forward LDR_MODULE ..............;
Next_LDRw: ;
mov esi, [esi] ; we go forwards
mov edx, edi ; edx->lp Dll name
cmp ebx, esi ; If current module = 1st module -> Dll is Not Loaded
mov eax, [esi+ebp] ; eax-> BaseDllName or FullDllName (Unicode) address
jne FindNextCharw ;
;
; The module is not loaded in memory and;
; we will try LoadLibrary to load it....;
;
cmp dword ptr [esp+1*4],-1 ; If it is forwarded
je Forwarded_Dll ; copy dll name in the stack and call oadLibrary
xor ebx, ebx ; ebx = 0
invoke LoadLibrary, edx ; call API
add ebx, eax ; ebx-> BaseDllName address or zero
je End_NotFound ; No such dll -> exit with ebx=0-> error
; End of Get module base address........;
;
GetNextApi: ;
mov edx, [ebx+3Ch] ; edx-> beginning of PE header
mov esi, ebx ; ebp-> current dll base address
mov edi, [ebx+edx+78h] ; edi-> RVA of ExportDirectory -> 78h
mov ecx, [ebx+edx+7Ch] ; ecx-> RVA of ExportDirectorySize ->7Ch
add esi, [ebx+edi+20h] ; esi-> AddressOfNames ->20h
add edi, ebx ; ebx-> current dll base address
movd MM5, edi ; MM5-> edi-> ExportDirectory
mov ebp, [esp+1*4+(4*4+2*4)] ; ebp->proc name address or ordinal value
add ecx, edi ; ecx= ExportDirectory address + ExportDirectorySize
mov eax, [edi+18h] ; eax = num of API Names-> nMax NumberOfNames->18h
test ebp, 0ffff0000h ; is it proc name address or ordinal value?
mov [esp+0*4], ecx ; [esp+0*4] = ExportDirectory address + ExportDirectorySize
je GetByOrdinal ;GetProcAddress by Ordinal
;
; Binary search ........................;GetProcAddress by Name
movd MM7, esp ; save the stack here
movzx ecx, byte ptr [ebp] ; ebp->proc name address
lea edi, [esi+4] ; cl-> 1st character of the proc name
mov esp, ebx ; esp-> current dll base address
neg edi ; set carry flag
movd MM6, edi ; MM6= -(esi+4]
Bin_Search: ;
;cmova esi, edx ; see Note 1
sbb edi, edi ; edi->mask -1 or 0
xor esi, edx ; mix esi and edx
and esi, edi ; esi=esi or esi=0
mov ebx, esp ; ebx-> current dll base address
xor esi, edx ; esi=esi or esi=edx
shr eax, 1 ;
je End_ZeroIndex ;
IndexIsZero: ;
add ebx, [esi+4*eax] ;
lea edx, [esi+4*eax+4] ;
cmp cl, [ebx] ; ebx-> API Names Table
jne Bin_Search ;
; End Binary search ....................;
;
; Compare next bytes of two strings.....;
lea edi, [ebp+1] ;
@@: ;
mov ch, [edi] ; comparing bytes
add ebx, 1 ;
cmp ch, [ebx] ; ebx-> API Names Table
jne Bin_Search ;
add edi, 1 ;
test ch, ch ;
jne @b ;
;
; Extract the index from EDX to get proc address
movd esi, MM5 ; esi-> ExportDirectory
movd eax, MM6 ; eax-> -(AddressOfNames+4)
mov edi, [esi+24h] ; edi->AddressOfNameOrdinals ->24h
mov ecx, esp ; ecx-> current dll base address
add ecx, [esi+1Ch] ; ecx-> AddressOfFunctions->1Ch
add eax, edx ; edx-> [esi+4*eax+4]
shr eax, 1 ; eax->index-> eax*2 for word table
add edi, esp ; esp-> current dll base address
movzx eax, word ptr [eax+edi] ; eax = Ordinal number for this index
mov ebx, esp ; ebx-> current dll base address
add ebx, [ecx+eax*4] ; ebx-> proc address
movd esp, MM7 ; restore the stack
;.......................................;
Is_it_Forwarded: ; check if proc address is inside export directory
cmp esi, ebx ; esi-> ExportDirectory
jnl EndProc ;
cmp ebx, [esp+0*4] ; [esp+0*4] = ExportDirectory address + ExportDirectorySize
jl Forwarded ;
;.......................................;
EndProc: ;
mov edi, [esp+2*4+0*4] ; restoring registers
mov eax, ebx ; eax->proc address or zero
mov ebp, [esp+2*4+1*4] ;
mov esi, [esp+2*4+2*4] ;
mov ebx, [esp+2*4+3*4] ;
add esp, 2*4+4*4 ;
ret 2*4 ;
;.......................................;
End_ZeroIndex: ;
jc IndexIsZero ; if it is 1st time zero->return,
movd esp, MM7 ; else (2nd time zero)-> restore the stack
End_NotFound: ; and exit
xor ebx, ebx ; ebx=0 -> flag not found
je EndProc ;
;.......................................;
GetByOrdinal: ;
cmp ebp, [esi].IMAGE_EXPORT_DIRECTORY.NumberOfFunctions
jnl End_NotFound ; esi-> ExportDirectory
sub ebp, [esi].IMAGE_EXPORT_DIRECTORY.nBase
mov eax, ebx ; eax-> current dll base address
add eax, [esi].IMAGE_EXPORT_DIRECTORY.AddressOfFunctions
add ebx, [eax + ebp*4] ; ebx-> proc address
jne Is_it_Forwarded ;
;.......................................;
Forwarded_Dll: ;
; Copy dll name in the stack............;
xor eax, eax ; eax->index = 0
sub esp, 2048 ; room for dll name in the stack
xor ebx, ebx ; ebx=0
@@: ;
mov cl, [edx+eax] ; edx->lp Dll name->source
add eax, 1 ;
mov [esp+eax-1], cl ; esp->lp target buffer
test cl, cl ;
je @f ;
cmp cl, "." ;
jne @b ;
mov [esp+eax-1], ebx ; ebx=0
@@: ;
invoke LoadLibrary, esp ; call API
add esp, 2048 ; restore the stack
add ebx, eax ; ebx-> BaseDllName address or zero
jne GetNextApi ;
je End_NotFound ; No such dll -> exit with ebx=0-> error
;.......................................;
Forwarded: ;
mov eax, ebx ; eax->proc address
; Call the proc "recursively"...........;
@@: ;
cmp byte ptr [eax], "." ; looking for "."
lea eax, [eax+1] ;
jne @b ;
cmp byte ptr [eax], "#" ; Is it forwarded by ordinal? Ex: "ntdll.#12"
je @f ;
@GetProc: ;
mov dword ptr [esp+1*4], -1 ; set flag -> it is forwarded
mov [esp+1*4+(4*4+2*4)], eax ; eax->offset of proc name or ordinal value
mov [esp+2*4+(4*4+2*4)], ebx ; ebx->lp Dll name
jmp GetStart ; start it again with new proc name and Dll name and flag
@@: ;
; A2Dw..................................;
lea edx, [eax+1] ;
xor eax, eax ;
@@: ;
movzx ecx, byte ptr [edx] ;
add edx, 1 ;
test ecx, ecx ;
je @GetProc ;
lea eax, [eax+4*eax] ;
lea eax, [ecx+2*eax-30h] ; eax=(eax*10 +ecx-30h)
jne @b ;
; End A2Dw..............................;
GetProcAddr endp ;
;.......................................;
OPTION PROLOGUE:PrologueDef ;
OPTION EPILOGUE:EpilogueDef ;
;.......................................;
End Start
;.......................................;
Notes: ;
1.Original code with branch: ;
Bin_Search: ;
jl @f ;
mov esi, edx ; if cl >[ebx] then esi == edx
@@: ;
shr eax, 1 ; else esi == esi
mov ebx, esp ;
je End_ZeroIndex ;
... ;
... ;
cmp cl, [ebx] ;
jne Bin_Search ;
;
Avoiding branch - Alternative A: ;
Bin_Search: ; best code but works with new CPUs
cmova esi, edx ; if cl >[ebx] then esi == edx
shr eax, 1 ; else esi == esi
mov ebx, esp ;
je End_ZeroIndex ;
... ;
... ;
cmp cl, [ebx] ;
jne Bin_Search ;
;
Avoiding branch - Alternative B: ;
Bin_Search: ; CPU compatible code
;cmova esi, edx ; if cl > [ebx] then esi == edx, else esi == esi
sbb edi, edi ; edi->mask -1 or 0
xor esi, edx ; mix esi and edx
and esi, edi ; esi=esi or esi=0
mov ebx, esp ; ebx-> current dll base address
xor esi, edx ; esi=esi or esi=edx
shr eax, 1 ;
je End_ZeroIndex ;
... ;
... ;
cmp cl, [ebx] ;
jne Bin_Search ;
;.......................................;
Regards,
Lingo
[attachment deleted by admin]
Lingo,
Thats nice and interesting code.
I have a question and that is how often do you think people need to get the address of a proc?
If it's not a lot of the time the program is running, then do you think that making some other API faster
would be a better challenge and a bigger reward to you and the community?
Whilst I havent measured your code for the performance difference I am assuming there is one.
I wondering why you don't asked your questions before here: :lol
http://www.masm32.com/board/index.php?topic=5774.0
So, if I answer your "question" the probability for moving this thread to The Colosseum
or to Recycle Bin becomes actually high...
Why?
I don't want to say that but all things depend on the level of the people
If they don't understand the art, beauty and the power of assembly and use
different OLD high level macros, classes and other similar constructions
just to make their code "more readable" and "easy for newbie"...
Example :
Most of the people here code their programs with "standard" C like
assembly code
Why?
Because it is the "standard" here and if someone want to change it a bit
in direction to more sophisticated and fast code moderators (some of them)
will delete his message and will say "You don't respect other people"
They are emotional and can't understand that I respect all the people as human beings but not their "assembly" code, nor their "In the name of the god/freedom/democracy/fatherland/etc..I will kill you", nor their " It became necessary for me to remove some posts from this thread. The decision was not an easy one ...", nor their...
So, this site may be good just for newbie in assembly rather than advanced coders
It is not a surprise for me the fact some advanced coders like bitrake,the_svin, nexo,
Mirno,Tomas, etc don't post their code here
The situation in some advanced C/C++ sites is opposite
If you try to mix assembly with C/C++ they will kill you with the arguments like:
"Why you make your code so unreadable and CPU dependable with assembly" and sometimes I agree with them because my best language is C/C++...
but it is other story....
With other words the way of thinking here is against "pure" assembly "in the name of
newbie" , and it one of the a reason for some people to stay newbie forever...
There is some light at the end of the tunnel with new 64bit assembly, it is a big
fun for me, but it is another story as well...
This is what we have the LAB for, development of fast code, if its fast, someone will find a use for it. :bg
Lingo,
It looks good, I will try and put some time in to have a play with it a bit later if I can get in front.
LATER :
Lingo,
See if I have done justice to your algo, I modified the front end code for a simple benchmark against LoadLibrary() GetProcAddress() in one block and you GetProcAddr() in the other as it receives the name of the string rather than the library handle.
It comes in a little faster on my Northwood core PIV.
This is what I get.
765 API timing
703 Lingo timing.
I have only posted in the changes to your front code for the timings.
.data
;szDllName db "c:\windows\system32\Kernel32.dll",0
szDllName db "Kernel32.dll",0
;szDllName db "Ws2_32.dll",0
;szProcName db "WriteFile",0,0,0,0
;szProcName db "WSAStartup",0,0,0,0
szProcName db "HeapAlloc",0,0,0,0
pKernel dd szDllName
hLib dd 0
.code ;
Start: ;
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"SetSystemTime"
fn MessageBox,0,str$(eax),"GetProcAddress Method",MB_OK
fn GetProcAddr,"SetSystemTime",pKernel
fn MessageBox,0,str$(eax),"GetProcAddr Method",MB_OK
push esi
mov esi, 5000
invoke GetTickCount
push eax
@@:
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"LoadLibraryA"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"LoadLibraryW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"FreeLibrary"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"GetModuleHandleA"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"GetModuleHandleW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"LoadResource"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"CreateDirectoryExA"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"CreateDirectoryExW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"DeleteFileA"
invoke LoadLibrary,pKernel
mov hLib, eax
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"DeleteFileW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"GetCommandLineA"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"GetCommandLineW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"PeekConsoleInputA"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"PeekConsoleInputW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"SetSystemTime"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"SetVolumeLabelA"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"SetVolumeLabelW"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"WaitForSingleObjectEx"
invoke LoadLibrary,pKernel
mov hLib, eax
fn GetProcAddress,hLib,"WinExec"
sub esi, 1
jnz @B
invoke GetTickCount
pop ecx
sub eax, ecx
fn MessageBox,0,str$(eax),"timing API",MB_OK
mov esi, 5000
invoke GetTickCount
push eax
@@:
fn GetProcAddr,"LoadLibraryA",pKernel
fn GetProcAddr,"LoadLibraryW",pKernel
fn GetProcAddr,"FreeLibrary",pKernel
fn GetProcAddr,"GetModuleHandleA",pKernel
fn GetProcAddr,"GetModuleHandleW",pKernel
fn GetProcAddr,"LoadResource",pKernel
fn GetProcAddr,"CreateDirectoryExA",pKernel
fn GetProcAddr,"CreateDirectoryExW",pKernel
fn GetProcAddr,"DeleteFileA",pKernel
fn GetProcAddr,"DeleteFileW",pKernel
fn GetProcAddr,"GetCommandLineA",pKernel
fn GetProcAddr,"GetCommandLineW",pKernel
fn GetProcAddr,"PeekConsoleInputA",pKernel
fn GetProcAddr,"PeekConsoleInputW",pKernel
fn GetProcAddr,"SetSystemTime",pKernel
fn GetProcAddr,"SetVolumeLabelA",pKernel
fn GetProcAddr,"SetVolumeLabelW",pKernel
fn GetProcAddr,"WaitForSingleObjectEx",pKernel
fn GetProcAddr,"WinExec",pKernel
sub esi, 1
jnz @B
invoke GetTickCount
pop ecx
sub eax, ecx
fn MessageBox,0,str$(eax),"timing Lingo",MB_OK
pop esi
jmp ExitProcess
; push offset szDllName ; lp Dir name
; push offset szProcName ; lp Proc Name
; ;push 908 ; ordinal of Write File API
; call GetProcAddr ;
; jmp ExitProcess ;
;
;........................................;
Lingo,
I'm not sure if I have offended you or not, but if I have then sorry. It's not my intention.
I asked for a function that returns all the procs in one go rather than one at a time because I needed all the procs in
a single table and because the API allows me to find all procs without knowing their names etc.
Anyhow, sorry if I offended.
Rgs, James.
It looks to me that your code searches a list of currently loaded DLLs before trying LoadLibrary. I'm just wondering if this is based on an assumption that LoadLibrary either doesn't do this or is slow at it, or based on knowledge, ie. is this the performance gain or is the search for the function the performance gain?
Looks good. :bg
Cheers,
Zooba :U
Thank you Hutch, :wink
I appreciate your testing efforts
and wondering why Petroizki has
other timing results..
James,
I'm not sure what you need but
you can try included Iczelion's
PE Tutorial no.7
Zooba,
"It looks to me that your code searches a list of currently loaded DLLs
before trying LoadLibrary.
Correct!
"I'm just wondering if this is based on an assumption that
LoadLibrary either doesn't do this or is slow at it..
LoadLibrary does it a bit slowly
See Hutch's test..
Regards,
Lingo
[attachment deleted by admin]
Hutch,
after error correction I used your test program and
my results are:
timing API -> 586
timing lingo -> 47
My CPU is P4 3,60 GHz Prescott
Thanks again,
Lingo
Hutch,
When I change dll name from
Kernel32.dll to c:\windows\system32\Kernel32.dll
the execution time for the original APIs rises 5 times... :lol
See: gettest1.exe and gettest2.exe
I have made small speed improvements and have updated
the included files
Regards,
Lingo
Lingo,
I will give it a try, I have both a Prescott and a Northwood core PIV handy so I will see how it works. I just happen to have my main box as win2k as the directory where the other box uses winnt like normal so i was wary of hard coding the path.
Small speed improvements again :lol
Regards,
Lingo
Tested with your first attachment on an AMD Athlon64 3000+, XP Pro SP2:
Timing API: 547
Timing Lingo: 31
Wow, that's a very nice pice of code you got there lingo (not to mention slightly unreadable for ASM-noobs like myself). :U
I like the way you setup variables/registers on the stack yourself - does any one know where to find documentation about this? Thanks in advance.
On-topic:
The results on my AMD Athlon 64 X2 Dual Core were:
515
31
GetKernelBase proc
assume fs:nothing
mov eax, fs:[30h]
mov edx, 0B8h
mov ecx, [eax+30h]
test eax, eax
jns @F
mov ebx, [eax+34h]
test ecx, ecx
jnz XI_1
@@:
mov eax, [eax+0Ch]
sub edx, 0B0h
mov eax, [eax+1Ch]
mov ebx, [eax]
XI_1:
mov eax, [ebx+edx]
ret
GetKernelBase endp
E^cube, assume fs:nothing mov eax, fs:[30h] only works on 9x and from my experience i never got it to work on XP... Have i missed something AGAIN javascript:void(0);
Eek
Works fine on my windows sp2 machine
; GetK32AndNtDll
; Obtains the image base of both kernel32.dll
; and ntdll.dll. (tested on 2K and XP)
; returns:
; EBX = Image Base of NTDLL.DLL
; EAX = Image Base of Kernel32.DLL
GetK32AndNtDll:
assume fs:nothing
mov eax, fs:[30h] ; PEB base
mov eax, [eax+0Ch] ; PEB_LDR_DATA
mov eax, [eax+1Ch] ; Ntdll
mov ebx, [eax+8] ; Ntdll Image Base
mov eax, [eax] ; Kernel32
mov eax, [eax+8] ; Kernel32 Image Base
ret
Synfire, honestly, I'm just beginning to see what those numbers are all about TEB-PEB ect... Do you have a documented list. I been looking for at lease 4 days off and on and can't find it anywhere. donky posted one here but it's gone.
E^cube, It been awhile. It do work on my XP. I been using POASM and forget that I have to un-comment this code. I really based my views on what I have read before than tried something with the [30h] with no success.
; ifndef __POASM__
; assume fs:nothing
; endif
It's fast as he*l. I could not even see the first few letters as it search for strings before i get my usual crash on Olly. I use to swear by y0da kernel but I see yours a lot faster even by the human eye. No dis-respect to all the work and here but how about some timing results.
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html
E^cube,
I'm glad to see you like my fastest GetKernelBase proc
on NT/XP and W98: :lol
http://www.asmcommunity.net/board/index.php?topic=12560.0
Regards,
Lingo
Yeah I like all your code Lingo :U i wasn't trying to take credit for it, I posted it here in your thread because of its obvious relation. However in the future i'll be sure to mention the authors name anytime I post code that isn't mine, to make sure theres no confusion.
Pentium M Dothan 1,86@2,5 Ghz (Asus P4C800-E Dlx.)
timing API: 312
timing Lingo: 31