2 * KERNEL32 thunks and other undocumented stuff
4 * Copyright 1997 Marcus Meissner
16 #include "stackframe.h"
17 #include "selectors.h"
23 /***********************************************************************
25 * Win95 internal thunks *
27 ***********************************************************************/
29 /***********************************************************************
30 * Generates a FT_Prolog call.
32 * 0FB6D1 movzbl edx,cl
33 * 8B1495xxxxxxxx mov edx,[4*edx + xxxxxxxx]
34 * 68xxxxxxxx push FT_Prolog
37 static void _write_ftprolog(LPBYTE thunk
,DWORD thunkstart
) {
41 *x
++ = 0x0f;*x
++=0xb6;*x
++=0xd1; /* movzbl edx,cl */
42 *x
++ = 0x8B;*x
++=0x14;*x
++=0x95;*(DWORD
*)x
= thunkstart
;
43 x
+=4; /* mov edx, [4*edx + thunkstart] */
44 *x
++ = 0x68; *(DWORD
*)x
= (DWORD
)GetProcAddress32(GetModuleHandle32A("KERNEL32"),"FT_Prolog");
45 x
+=4; /* push FT_Prolog */
46 *x
++ = 0xC3; /* lret */
47 /* fill rest with 0xCC / int 3 */
50 /***********************************************************************
51 * FT_PrologPrime (KERNEL32.89)
53 void WINAPI
FT_PrologPrime(DWORD startind
,LPBYTE thunk
) {
54 _write_ftprolog(thunk
,*(DWORD
*)(startind
+thunk
));
57 /***********************************************************************
58 * Generates a QT_Thunk style call.
61 * 8A4DFC mov cl , [ebp-04]
62 * 8B148Dxxxxxxxx mov edx, [4*ecx + (EAX+EDX)]
63 * B8yyyyyyyy mov eax, QT_Thunk
66 static void _write_qtthunk(LPBYTE start
,DWORD thunkstart
) {
70 *x
++ = 0x33;*x
++=0xC9; /* xor ecx,ecx */
71 *x
++ = 0x8A;*x
++=0x4D;*x
++=0xFC; /* movb cl,[ebp-04] */
72 *x
++ = 0x8B;*x
++=0x14;*x
++=0x8D;*(DWORD
*)x
= thunkstart
;
73 x
+=4; /* mov edx, [4*ecx + (EAX+EDX) */
74 *x
++ = 0xB8; *(DWORD
*)x
= (DWORD
)GetProcAddress32(GetModuleHandle32A("KERNEL32"),"QT_Thunk");
75 x
+=4; /* mov eax , QT_Thunk */
76 *x
++ = 0xFF; *x
++ = 0xE0; /* jmp eax */
77 /* should fill the rest of the 32 bytes with 0xCC */
80 /***********************************************************************
81 * ThunkConnect32 (KERNEL32)
82 * Connects a 32bit and a 16bit thunkbuffer.
98 UINT32 WINAPI
ThunkConnect32( struct thunkstruct
*ths
, LPSTR thunkfun16
,
99 LPSTR module16
, LPSTR module32
, HMODULE32 hmod32
,
104 struct thunkstruct
*ths16
;
106 dprintf_thunk(stddeb
,"ThunkConnect32(<struct>,%s,%s,%s,%x,%lx)\n",
107 thunkfun16
,module32
,module16
,hmod32
,dllinitarg1
109 dprintf_thunk(stddeb
," magic = %c%c%c%c\n",
115 dprintf_thunk(stddeb
," length = %lx\n",ths
->length
);
116 if (lstrncmp32A(ths
->magic
,"SL01",4)&&lstrncmp32A(ths
->magic
,"LS01",4))
118 hmm
=LoadModule16(module16
,NULL
);
121 thkbuf
=(SEGPTR
)WIN32_GetProcAddress16(hmm
,thunkfun16
);
124 ths16
=(struct thunkstruct
*)PTR_SEG_TO_LIN(thkbuf
);
125 if (lstrncmp32A(ths16
->magic
,ths
->magic
,4))
128 if (!lstrncmp32A(ths
->magic
,"SL01",4)) {
129 if (ths16
->length
!= ths
->length
)
131 ths
->x0C
= (DWORD
)ths16
;
133 dprintf_thunk(stddeb
," ths16 magic is 0x%08lx\n",*(DWORD
*)ths16
->magic
);
134 if (*((DWORD
*)ths16
->magic
) != 0x0000304C)
136 if (!*(WORD
*)(((LPBYTE
)ths16
)+0x12))
140 if (!lstrncmp32A(ths
->magic
,"LS01",4)) {
141 if (ths16
->length
!= ths
->length
)
143 ths
->ptr
= (DWORD
)PTR_SEG_TO_LIN(ths16
->ptr
);
144 /* code offset for QT_Thunk is at 0x1C... */
145 _write_qtthunk (((LPBYTE
)ths
) + ths
->x1C
,ths
->ptr
);
146 /* code offset for FT_Prolog is at 0x20... */
147 _write_ftprolog(((LPBYTE
)ths
) + ths
->x20
,ths
->ptr
);
154 /**********************************************************************
155 * QT_Thunk (KERNEL32)
157 * The target address is in EDX.
158 * The 16 bit arguments start at ESP+4.
159 * The number of 16bit argumentbytes is EBP-ESP-0x44 (68 Byte thunksetup).
162 VOID WINAPI
QT_Thunk(CONTEXT
*context
)
166 THDB
*thdb
= THREAD_Current();
168 memcpy(&context16
,context
,sizeof(context16
));
170 CS_reg(&context16
) = HIWORD(EDX_reg(context
));
171 IP_reg(&context16
) = LOWORD(EDX_reg(context
));
172 EBP_reg(&context16
) = OFFSETOF( thdb
->cur_stack
)
173 + (WORD
)&((STACK16FRAME
*)0)->bp
;
175 argsize
= EBP_reg(context
)-ESP_reg(context
)-0x44;
177 memcpy( ((LPBYTE
)THREAD_STACK16(thdb
))-argsize
,
178 (LPBYTE
)ESP_reg(context
)+4, argsize
);
180 EAX_reg(context
) = Callbacks
->CallRegisterShortProc( &context16
, argsize
);
184 /**********************************************************************
185 * WOWCallback16 (KERNEL32.62)(WOW32.2)
187 DWORD WINAPI
WOWCallback16(FARPROC16 fproc
,DWORD arg
)
190 dprintf_thunk(stddeb
,"WOWCallback16(%p,0x%08lx) ",fproc
,arg
);
191 ret
= Callbacks
->CallWOWCallbackProc(fproc
,arg
);
192 dprintf_thunk(stddeb
,"... returns %ld\n",ret
);
196 /**********************************************************************
197 * WOWCallback16Ex (KERNEL32.55)(WOW32.3)
199 BOOL32 WINAPI
WOWCallback16Ex(
200 FARPROC16 vpfn16
,DWORD dwFlags
,DWORD cbArgs
,LPVOID pArgs
,
203 return Callbacks
->CallWOWCallback16Ex(vpfn16
,dwFlags
,cbArgs
,pArgs
,pdwRetCode
);
206 /***********************************************************************
207 * _KERNEL32_52 (KERNEL32.52)
208 * Returns a pointer to ThkBuf in the 16bit library SYSTHUNK.DLL.
211 LPVOID WINAPI
_KERNEL32_52()
213 HMODULE32 hmod
= LoadLibrary16("systhunk.dll");
215 dprintf_thunk(stddeb
, "_KERNEL32_52: systhunk.dll module %d\n", hmod
);
219 return PTR_SEG_TO_LIN(WIN32_GetProcAddress16(hmod
,"ThkBuf"));
222 /***********************************************************************
223 * _KERNEL32_43 (KERNEL32.42)
224 * A thunkbuffer link routine
225 * The thunkbuf looks like:
227 * 00: DWORD length ? don't know exactly
228 * 04: SEGPTR ptr ? where does it point to?
229 * The pointer ptr is written into the first DWORD of 'thunk'.
230 * (probably correct implemented)
233 DWORD WINAPI
_KERNEL32_43(LPDWORD thunk
,LPCSTR thkbuf
,DWORD len
,
234 LPCSTR dll16
,LPCSTR dll32
)
240 hmod
= LoadLibrary16(dll16
);
242 fprintf(stderr
,"KERNEL32_43->failed to load 16bit DLL %s, error %d\n",dll16
,hmod
);
245 segaddr
= (DWORD
)WIN32_GetProcAddress16(hmod
,(LPSTR
)thkbuf
);
247 fprintf(stderr
,"KERNEL32_43->no %s exported from %s!\n",thkbuf
,dll16
);
250 addr
= (LPDWORD
)PTR_SEG_TO_LIN(segaddr
);
251 if (addr
[0] != len
) {
252 fprintf(stderr
,"KERNEL32_43->thkbuf length mismatch? %ld vs %ld\n",len
,addr
[0]);
257 *(DWORD
*)thunk
= addr
[1];
259 dprintf_thunk(stddeb
, "_KERNEL32_43: loaded module %d, func %s (%d) @ %p (%p), returning %p\n",
260 hmod
, HIWORD(thkbuf
)==0 ? "<ordinal>" : thkbuf
, (int)thkbuf
, (void*)segaddr
, addr
, (void*)addr
[1]);
265 /***********************************************************************
266 * _KERNEL32_45 (KERNEL32.44)
267 * Another 32->16 thunk, the difference to QT_Thunk is, that the called routine
268 * uses 0x66 lret, and that we have to pass CX in DI.
269 * (there seems to be some kind of BL/BX return magic too...)
271 * [doesn't crash anymore]
273 VOID WINAPI
_KERNEL32_45(CONTEXT
*context
)
278 THDB
*thdb
= THREAD_Current();
280 dprintf_thunk(stddeb
,"KERNEL32_45(%%eax=0x%08lx(%%cx=0x%04lx,%%edx=0x%08lx))\n",
281 (DWORD
)EAX_reg(context
),(DWORD
)CX_reg(context
),(DWORD
)EDX_reg(context
)
283 stacksize
= EBP_reg(context
)-ESP_reg(context
);
284 dprintf_thunk(stddeb
," stacksize = %ld\n",stacksize
);
286 memcpy(&context16
,context
,sizeof(context16
));
288 DI_reg(&context16
) = CX_reg(context
);
289 CS_reg(&context16
) = HIWORD(EAX_reg(context
));
290 IP_reg(&context16
) = LOWORD(EAX_reg(context
));
292 curstack
= PTR_SEG_TO_LIN(STACK16_PUSH( thdb
, stacksize
));
293 memcpy(curstack
- stacksize
,(LPBYTE
)ESP_reg(context
),stacksize
);
294 ret
= Callbacks
->CallRegisterLongProc(&context16
,0);
295 STACK16_POP( thdb
, stacksize
);
297 dprintf_thunk(stddeb
,". returned %08lx\n",ret
);
298 EAX_reg(context
) = ret
;
301 /***********************************************************************
302 * _KERNEL32_40 (KERNEL32.40)
303 * YET Another 32->16 thunk, the difference to the others is still mysterious
304 * target address is EDX
308 VOID WINAPI
_KERNEL32_40(CONTEXT
*context
)
313 THDB
*thdb
= THREAD_Current();
315 dprintf_thunk(stddeb
,"_KERNEL32_40(EDX=0x%08lx)\n",
318 stacksize
= EBP_reg(context
)-ESP_reg(context
);
319 dprintf_thunk(stddeb
," stacksize = %ld\n",stacksize
);
320 dprintf_thunk(stddeb
,"on top of stack: 0x%04x\n",*(WORD
*)ESP_reg(context
));
322 memcpy(&context16
,context
,sizeof(context16
));
324 CS_reg(&context16
) = HIWORD(EDX_reg(context
));
325 IP_reg(&context16
) = LOWORD(EDX_reg(context
));
327 curstack
= PTR_SEG_TO_LIN(STACK16_PUSH( thdb
, stacksize
));
328 memcpy(curstack
-stacksize
,(LPBYTE
)ESP_reg(context
),stacksize
);
329 ret
= Callbacks
->CallRegisterShortProc(&context16
,0);
330 STACK16_POP( thdb
, stacksize
);
332 dprintf_thunk(stddeb
,". returned %08lx\n",ret
);
333 EAX_reg(context
) = ret
;
337 /***********************************************************************
339 * A thunk setup routine.
340 * Expects a pointer to a preinitialized thunkbuffer in the first argument
342 * 00..03: unknown (pointer, check _41, _43, _46)
345 * 06..23: unknown (space for replacement code, check .90)
347 * 24:>E800000000 call offset 29
348 * 29:>58 pop eax ( target of call )
349 * 2A: 2D25000000 sub eax,0x00000025 ( now points to offset 4 )
350 * 2F: BAxxxxxxxx mov edx,xxxxxxxx
351 * 34: 68yyyyyyyy push KERNEL32.90
355 * 3E ... 59: unknown (space for replacement code?)
356 * 5A: E8xxxxxxxx call <32bitoffset xxxxxxxx>
358 * 60: 81EA25xxxxxx sub edx, 0x25xxxxxx
360 * 67: 68xxxxxxxx push xxxxxxxx
361 * 6C: 68yyyyyyyy push KERNEL32.89
364 * This function checks if the code is there, and replaces the yyyyyyyy entries
365 * by the functionpointers.
366 * The thunkbuf looks like:
368 * 00: DWORD length ? don't know exactly
369 * 04: SEGPTR ptr ? where does it point to?
370 * The segpointer ptr is written into the first DWORD of 'thunk'.
374 LPVOID WINAPI
_KERNEL32_41(LPBYTE thunk
,LPCSTR thkbuf
,DWORD len
,LPCSTR dll16
,
377 HMODULE32 hkrnl32
= GetModuleHandle32A("KERNEL32");
382 /* FIXME: add checks for valid code ... */
383 /* write pointers to kernel32.89 and kernel32.90 (+ordinal base of 1) */
384 *(DWORD
*)(thunk
+0x35) = (DWORD
)GetProcAddress32(hkrnl32
,(LPSTR
)90);
385 *(DWORD
*)(thunk
+0x6D) = (DWORD
)GetProcAddress32(hkrnl32
,(LPSTR
)89);
388 hmod
= LoadLibrary16(dll16
);
390 fprintf(stderr
,"KERNEL32_41->failed to load 16bit DLL %s, error %d\n",dll16
,hmod
);
393 segaddr
= (DWORD
)WIN32_GetProcAddress16(hmod
,(LPSTR
)thkbuf
);
395 fprintf(stderr
,"KERNEL32_41->no %s exported from %s!\n",thkbuf
,dll16
);
398 addr
= (LPDWORD
)PTR_SEG_TO_LIN(segaddr
);
399 if (addr
[0] != len
) {
400 fprintf(stderr
,"KERNEL32_41->thkbuf length mismatch? %ld vs %ld\n",len
,addr
[0]);
403 addr2
= PTR_SEG_TO_LIN(addr
[1]);
405 *(DWORD
*)thunk
= (DWORD
)addr2
;
407 dprintf_thunk(stddeb
, "_KERNEL32_41: loaded module %d, func %s(%d) @ %p (%p), returning %p\n",
408 hmod
, HIWORD(thkbuf
)==0 ? "<ordinal>" : thkbuf
, (int)thkbuf
, (void*)segaddr
, addr
, addr2
);
413 /***********************************************************************
415 * QT Thunk priming function
416 * Rewrites the first part of the thunk to use the QT_Thunk interface
417 * and jumps to the start of that code.
420 VOID WINAPI
_KERNEL32_90(CONTEXT
*context
)
422 dprintf_thunk(stddeb
, "_KERNEL32_90: QT Thunk priming; context %p\n", context
);
424 _write_qtthunk((LPBYTE
)EAX_reg(context
),*(DWORD
*)(EAX_reg(context
)+EDX_reg(context
)));
425 /* we just call the real QT_Thunk right now
426 * we can bypass the relaycode, for we already have the registercontext
428 EDX_reg(context
) = *(DWORD
*)((*(DWORD
*)(EAX_reg(context
)+EDX_reg(context
)))+4*(((BYTE
*)EBP_reg(context
))[-4]));
429 return QT_Thunk(context
);
432 /***********************************************************************
434 * Another thunkbuf link routine.
435 * The start of the thunkbuf looks like this:
437 * 04: SEGPTR address for thunkbuffer pointer
440 VOID WINAPI
_KERNEL32_46(LPBYTE thunk
,LPSTR thkbuf
,DWORD len
,LPSTR dll16
,
447 hmod
= LoadLibrary16(dll16
);
449 fprintf(stderr
,"KERNEL32_46->couldn't load %s, error %d\n",dll16
,hmod
);
452 segaddr
= (SEGPTR
)WIN32_GetProcAddress16(hmod
,thkbuf
);
454 fprintf(stderr
,"KERNEL32_46-> haven't found %s in %s!\n",thkbuf
,dll16
);
457 addr
= (LPDWORD
)PTR_SEG_TO_LIN(segaddr
);
458 if (addr
[0] != len
) {
459 fprintf(stderr
,"KERNEL32_46-> length of thkbuf differs from expected length! (%ld vs %ld)\n",addr
[0],len
);
462 *(DWORD
*)PTR_SEG_TO_LIN(addr
[1]) = (DWORD
)thunk
;
464 dprintf_thunk(stddeb
, "_KERNEL32_46: loaded module %d, func %s(%d) @ %p (%p)\n",
465 hmod
, HIWORD(thkbuf
)==0 ? "<ordinal>" : thkbuf
, (int)thkbuf
, (void*)segaddr
, addr
);
468 /**********************************************************************
470 * Check if thunking is initialized (ss selector set up etc.)
471 * We do that differently, so just return TRUE.
474 BOOL32 WINAPI
_KERNEL32_87()
476 dprintf_thunk(stddeb
, "_KERNEL32_87: Yes, thunking is initialized\n");
480 /**********************************************************************
482 * One of the real thunking functions. This one seems to be for 32<->32
483 * thunks. It should probably be capable of crossing processboundaries.
485 * And YES, I've seen nr=48 (somewhere in the Win95 32<->16 OLE coupling)
488 DWORD WINAPIV
_KERNEL32_88( DWORD nr
, DWORD flags
, FARPROC32 fun
, ... )
491 DWORD
*args
= ((DWORD
*)&fun
) + 1;
493 dprintf_thunk(stddeb
,"KERNEL32_88(%ld,0x%08lx,%p,[ ",nr
,flags
,fun
);
494 for (i
=0;i
<nr
/4;i
++) { dprintf_thunk(stddeb
,"0x%08lx,",args
[i
]); }
495 dprintf_thunk(stddeb
,"])\n");
499 case 4: ret
= fun(args
[0]);
501 case 8: ret
= fun(args
[0],args
[1]);
503 case 12: ret
= fun(args
[0],args
[1],args
[2]);
505 case 16: ret
= fun(args
[0],args
[1],args
[2],args
[3]);
507 case 20: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4]);
509 case 24: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5]);
511 case 28: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5],args
[6]);
513 case 32: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5],args
[6],args
[7]);
515 case 36: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5],args
[6],args
[7],args
[8]);
517 case 40: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5],args
[6],args
[7],args
[8],args
[9]);
519 case 44: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5],args
[6],args
[7],args
[8],args
[9],args
[10]);
521 case 48: ret
= fun(args
[0],args
[1],args
[2],args
[3],args
[4],args
[5],args
[6],args
[7],args
[8],args
[9],args
[10],args
[11]);
524 fprintf(stderr
,"_KERNEL32_88: unsupported nr of arguments, %ld\n",nr
);
529 dprintf_thunk(stddeb
," returning %ld ...\n",ret
);
533 /**********************************************************************
534 * KERNEL_619 (KERNEL)
535 * Seems to store y and z depending on x in some internal lists...
537 WORD WINAPI
_KERNEL_619(WORD x
,DWORD y
,DWORD z
)
539 fprintf(stderr
,"KERNEL_619(0x%04x,0x%08lx,0x%08lx)\n",x
,y
,z
);
543 /**********************************************************************
544 * AllocSLCallback (KERNEL32)
546 * Win95 uses some structchains for callbacks. It allocates them
547 * in blocks of 100 entries, size 32 bytes each, layout:
549 * 0: PTR nextblockstart
551 * 8: WORD sel ( start points to blockstart)
555 * 18: PDB *owning_process;
558 * We ignore this for now. (Just a note for further developers)
559 * FIXME: use this method, so we don't waste selectors...
561 * Following code is then generated by AllocSLCallback. The code is 16 bit, so
562 * the 0x66 prefix switches from word->long registers.
565 * 6668x arg2 x pushl <arg2>
567 * EAx arg1 x jmpf <arg1>
569 * returns the startaddress of this thunk.
571 * Note, that they look very similair to the ones allocates by THUNK_Alloc.
574 AllocSLCallback(DWORD finalizer
,DWORD callback
) {
575 LPBYTE x
,thunk
= HeapAlloc( GetProcessHeap(), 0, 32 );
579 *x
++=0x66;*x
++=0x5a; /* popl edx */
580 *x
++=0x66;*x
++=0x68;*(DWORD
*)x
=finalizer
;x
+=4; /* pushl finalizer */
581 *x
++=0x66;*x
++=0x52; /* pushl edx */
582 *x
++=0xea;*(DWORD
*)x
=callback
;x
+=4; /* jmpf callback */
584 *(PDB32
**)(thunk
+18) = PROCESS_Current();
586 sel
= SELECTOR_AllocBlock( thunk
, 32, SEGMENT_CODE
, FALSE
, FALSE
);
591 FreeSLCallback(DWORD x
) {
592 fprintf(stderr
,"FreeSLCallback(0x%08lx)\n",x
);
595 /**********************************************************************
596 * KERNEL_358 (KERNEL)
597 * Allocates a code segment which starts at the address passed in x. limit
598 * 0xfffff, and returns the pointer to the start.
601 _KERNEL_358(DWORD x
) {
604 fprintf(stderr
,"_KERNEL_358(0x%08lx),stub\n",x
);
608 sel
= SELECTOR_AllocBlock( PTR_SEG_TO_LIN(x
) , 0xffff, SEGMENT_CODE
, FALSE
, FALSE
);
609 return PTR_SEG_OFF_TO_SEGPTR( sel
, 0 );
612 /**********************************************************************
613 * KERNEL_359 (KERNEL)
614 * Frees the code segment of the passed linear pointer (This has usually
615 * been allocated by _KERNEL_358).
618 _KERNEL_359(DWORD x
) {
619 fprintf(stderr
,"_KERNEL_359(0x%08lx),stub\n",x
);
620 if ((HIWORD(x
) & 7)!=7)
622 SELECTOR_FreeBlock(x
>>16,1);
626 /**********************************************************************
627 * KERNEL_472 (KERNEL)
628 * something like GetCurrenthInstance.
631 _KERNEL_472(CONTEXT
*context
) {
632 fprintf(stderr
,"_KERNEL_472(0x%08lx),stub\n",EAX_reg(context
));
633 if (!EAX_reg(context
)) {
634 TDB
*pTask
= (TDB
*)GlobalLock16(GetCurrentTask());
635 AX_reg(context
)=pTask
->hInstance
;
638 if (!HIWORD(EAX_reg(context
)))
639 return; /* returns the passed value */
643 /**********************************************************************
644 * KERNEL_431 (KERNEL.431)
647 BOOL16 WINAPI
KERNEL_431(LPSTR fn
,WORD x
) {
648 IMAGE_DOS_HEADER mzh
;
653 hf
= OpenFile32(fn
,&ofs
,OF_READ
);
654 if (hf
==HFILE_ERROR32
)
656 if (sizeof(mzh
)!=_lread32(hf
,&mzh
,sizeof(mzh
))) {
660 if (mzh
.e_magic
!=IMAGE_DOS_SIGNATURE
) {
661 fprintf(stderr
,"file has not got dos signature!\n");
665 _llseek32(hf
,mzh
.e_lfanew
,SEEK_SET
);
666 if (sizeof(DWORD
)!=_lread32(hf
,&xmagic
,sizeof(DWORD
))) {
671 return (xmagic
== IMAGE_NT_SIGNATURE
);
674 HANDLE32 WINAPI
WOWHandle32(WORD handle
,WOW_HANDLE_TYPE type
) {
675 fprintf(stderr
,"WOWHandle32(0x%04x,%d)\n",handle
,type
);
676 return (HANDLE32
)handle
;