`Cisco v. SSL, |PR2015-01754
`Page 1 of 72
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 1 of 72
`
`
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 2 of 72
`
`
`
`Windows 95 System Programming SECRETS
`Published by
`IDG Books Worldwide, Inc.
`An International Data Group Company
`919 East Hillsdale Boulevard, Suite 400
`Foster City, CA 94404
`
`‘-
`Copyright
`Copyright © 1995 by IDG Books Worldwide. All rights reserved. No part of this book (including
`interior design, cover design, and illustrations) may be reproduced or transmitted in any form, by
`any means (electronic, photocopying, recording, or otherwise), without the prior written
`permission of the publisher.
`
`Library of Congress Catalog Card No.: 95-75057
`
`ISBN 1-56884-318-6
`
`Printed in the United States of America
`
`First Printing, November, 1995
`
`10 9 8 7 6 5 4 3 2 1
`
`Distributed in the United States by IDG Books Worldwide, Inc.
`
`Limit of Liability/Disclaimer of Warranty
`The author and publisher of this book have used their best efforts in preparing this book. IDG
`Books Worldwide, Inc., International Data Group, Inc., and the author make no representation
`or warranties with respect to the accuracy or completeness of the contents of this book or the
`material on this disk included with this book, and specifically disclaim any implied warranties
`of merchantability or fitness for any particular purpose, and shall in no event be liable for any
`loss of profit or any other commercial damage, including but not limited to special, incidental,
`consequential or other damages.
`
`Trademarks
`
`All brand names and product names used in this book are trademarks, registered trademarks,
`or trade names of their respective holders. IDG Books Worldwide, Inc., is 11ot associated with
`any product or vendor mentioned in this book.
`
`‘PROGIUIMHIERS
`
`]
`
`Published in the United States
`
`WORLDWIDE
`
`P
`
`R
`
`E » S V;
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 3 of 72
`
`
`
`CSCO-1041
`Cisco v. SSL, |PR2015-01754
`Page 4 of 72
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 4 of 72
`
`
`
`686
`
`Windows 95
`
`Doing this is only a function call away with the code I’ll be presenting. If
`you’re just interested in getting the code to intercept API function calls, skip
`ahead to the last section in this chapter (“Intercepting Functions in Your
`Own Programs”). However, if you’re interested in Win32 system—level
`programming and learning how this technique works, ‘read on.
`Why bother writing a simple API spy program when more powerful ones,
`such as BoundsChecker32, are available commercially? By writing you own
`API spy for Win32 programs, you can gain a thorough understanding of the
`Win32 operating system philosophy, and develop an in-depth knowledge
`about the important differences between the three Win32 implementations
`(Windows NT, Windows 95, and Win32s).
`On the surface, the subject of this chapter appears to be “How to build an
`API spy program.” However, my real goal is to present a set of real—world
`Win32 programming problems, and show how they can be solved. In the
`process, you should see many facets of the Win32 architecture. As you’ll
`soon realize, writing an API spy for Win32 programs forces you to come into
`Contact with such issues as address spaces, rnultithreading, dynamic linking,
`debugging mechanisms, process management, and thread control. In short,
`the program I’ll be building will give you a good tour of many core Wi1132
`concepts.
`Before jumping into details about the program, I need to list the API spy
`program specifications:
`
`1. For a given Win32 process, the program should log the function calls it
`makes to a given list of DLLs.
`2. The set of DLLS to be monitored should be extendible by the user via a
`configuration file.
`
`. If the parameters to a function are known, they can be specified in the
`configuration file, and their values will be logged along with the
`function name.
`
`4. The spy program must log the return Values of functions.
`. The spy program should be able to run on Windows NT, Windows 95,
`and Win32s.
`W
`
`. No modification to the program’s source code or executable file should
`be required.
`
`. Log output should go to a disk file, rather than being shown live.
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 5 of 72
`
`
`
`7" w;:;:;;.sw;.a2 Am Spy
`
`537
`
`When this program is completed (at the end of the chapter), it will be
`able to do several things: it will let you pick another program to spy on, it
`will run that program, and it will produce an ASCII text file with the logged
`information. After the chosen program terminates, you can View the text file
`with an editor or viewer of your choice.
`An important limitation of this spy program —- and one that needs to be
`pointed out very clearly — is that this program is a per-process API spy.
`Unlike programs such as the Win16 WinScopc spy program, my Win32 API
`spy doesn’t watch calls made by all processes iii the system. Rather, it
`watches only the calls made by a single process. In a system with separate
`address space for each process, writing a global API spy is a significant
`undertaking and beyond the scope of this chapter. (How’s that for truth in
`advertising?)
`
`INTERCEPTING THE FUNCTIONS
`
`The basic idea behind any sort of spy program is that the spy program
`inserts itself into the flow of control of the program being spied on. The spy
`program gets control before the intended target of the call is reached, and
`does whatever logging it needs to before transferring control to the original
`intended target of the call. The first problem we’re faced with is how to let
`our spy program gain control somewhere between the “spyee’s” call to a
`DLL function, and the execution of the function in the DLL.
`One approach to this problem that’s been used in the past is to make
`your own DLL that exports functions with the same names as the functions
`you want to intercept. For example, if you wanted to intercept calls to the
`GetProcAddress function in KERNEL32.DLL, you’d make your own DLL
`with an exported GetProcAddress function. By putting this DLL’s import
`library first in the list of import libraries, the linker will fix up calls to
`GetProcAddress to point to your interception DLL, rather than to
`KERNEL32.DLL. The interception DLL logs the information about the call
`before jumping to the real function (for example, GetProcAddress in
`KERNEL32.DLL). As an alternative to creating an import library, you
`could just alias the imported functions in your DEF file. However, both of
`these approaches share the common characteristic of requiring the linker to
`set up the API interception at link time — something that’s not an option if
`the program you’re spying on isn’t one you wrote.
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 6 of 72
`
`
`
`Windows 95 Systéniifl’riogi.rieiiIinjfi
`
`This method of intercepting calls to API functions is exactly what the
`Microsoft API parameter profiler in the Win32 SDK does. Included with
`this profiler are five DLLS: ZDI32.DLL, ZDVAPI32.DLL, ZERNEL32.DLL,
`ZRTDLLDLL, and ZSER32.DLL; these DLLS intercept calls to GDI32.DLL,
`ADVAPI32.DLL, KERNEL32.DLL, CRTDLLDLL, and USER32.DLL,
`respectively. Instead of linking with these DLLs, you run a program
`(APF32CVT) that modifies the EXE you want to spy on. The net effect is
`the same as if you had linked with the import libraries. At least no source
`code is required to use APF32CVT.
`For our purposes, there are two problems with this dummy DLL
`approach. First, it’s not easy to extend it to accommodate new DLLs. For
`each new API function you want to intercept, you need to modify the inter-
`ception DLL and rebuild it. You also have to relink or modify the EXE to
`be spied on. The second, and bigger, problem is that this approach requires
`changing the program to be spied on, which is a direct violation of one of
`our design specifications.
`Another approach to intercepting calls to API functions is to somehow
`modify the target of the call. By changing the initial portion of the function
`being called, a program can enable itself to get control before the body of
`the function is executed. There are two methods of modifying a function’s
`prologue code to transfer control elsewhere. The first and most obvious
`method is to place a breakpoint instruction (opcode OXCC) at the first byte
`in the function’s code. When the function is called, an interrupt handler
`installed by the spy program gets control and does its logging. The spy pro-
`gram then restores the original byte of. the function before making the CPU
`execute exactly one instruction (via the trap flag single—step mechanism). In
`its single—step exception handler, the API spy program then reinserts the
`breakpoint opcode so that subsequent calls to the function will be caught.
`Although some Win16 spy programs use breakpoints to intercept calls
`to DLL functions, trying to do something similar under Win32 would be
`more difficult. For starters, under Wi1132, one process can’t see another
`process’s exceptions unless it’s acting as a debugger to that process. Next,
`forcing every call to an API function to go through the Win32 structured
`exception—handling code could seriously impair performance. Also, the
`separate address spaces of processes under Win32 would force the spy
`program to use ReadProcessMemory to see the target applications’s function
`parameters. This is far clunkier than being able to read the memory directly.
`The second method of modifying a function’s prologue code to transfer
`control to the spying code is to insert 21 ]MP or CALL instruction at the
`start of the function. One problem with this approach is that in 32-bit code,
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 7 of 72
`
`
`
`genomes; in Spy
`
`539
`
`a JMP or a CALL instruction will take at least 5 bytes. If the DLL has func-
`tions less than 5 bytes apart (yes, this has happened!), patching in a JMP or
`CALL becomes impractical because the function that comes later in the
`code will start in the middle of a JMP or CALL instruction. Unlike break-
`points that can be handled by another process, patching in ]MPs and
`CALLs,requires your code to be running in the process context of the program
`being spied on. To run in any arbitrary process requires that your code be in
`a DLL. However, as you’ll see later, running your spy code in the context of
`the process being spied on isn’t such a bad idea. Still, patching ]MPs or
`CAHsmmflmpmmmmmgwmdmmamflnmmmgwmdwywmnm
`need to constantly switch between the original code and your JMP/CALL
`instructions.
`
`Having looked at and discarded two obvious approaches to interception
`(linking to a custom DLL and patching the API function’s code), let’s look
`at a third approach that’s not so obvious. Nothing in the rule book says that
`the target code in the API function has to be patched. It’s equally valid to
`modify the call to the API function. If the spy program can somehow find
`the CALLS to the API functions, it can modify the CALL to point to the spy
`program’s logging code. As in a previously discussed method, the spy pro-
`gram’s logging code will need to execute in the process context of the program
`being spied on. The “Injecting a DLL into Another Process” section will
`show how it’s possible to “inject” a DLL into the address space of a
`process. Here, we’re concentrating on the interception part of the problem.
`You might be thinking to yourself, “A program might have hundreds or
`even thousands of calls to API functions in just the system DLLS alone.
`How on earth can I hope to find all those CALL instructions?” Never fear,
`the manner in which Win32 EXES and DLLS dynamically link to each other
`makes this almost incredibly easy: All calls to a given API function end up
`traveling through the same spot in the executable file. By patching that one
`location to point at the spy’s logging code, you intercept all calls made by
`theED(Etothatfuncfion.
`To see how this works, let’s look at the actual code generated for three
`separate calls to the GetVersion() API function in KERNEL32.DLL. Let’s
`start with the following small C program:
`
`int ma1'n()
`I
`
`GetVers1'on( );
`GetVersion( ):
`GetVersion( );
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 8 of 72
`
`
`
`69O
`
`Windows
`
`From this program, the compiler generates the following assembler code:
`
`410052: CALL
`
`@@4Z@03C
`
`410057: CALL
`410050: CALL
`
`0@420@3C
`@@4Z0fl3C
`
`4Z@03C:
`
`JMP
`
`DWORD PTR [@@44@064]
`
`The important thing to notice here is that CALL instructions don’t call
`directly to the GetVersion() code in KERNEL32.DLL. Instead, each call
`transfers control to 21 ]MP statement elsewhere in the EXE’s code. That JMP
`instruction dereferences a DWORD in memory a11d jumps to that location.
`In the above example, the DWORD is at address 00440064. What’s the
`address stored in this DWORD? As you might suspect, it’s the address of
`GetVersion() in KERNEL32.DLL. All calls to API functions end up going
`through a JMP DWORD PTR [XXXXXXXX] thunk. For each function
`that an executable imports, there’s a c0rrespor1ding]MP DWORD PTR
`[XXXXXXXX]. Who generates these JMP thunks? In Microsoft compilers,
`the JMP thunks are code in the import libraries for the DLLs being linked
`to. In Borland C++, the linker (TLINK) generates the JMP thunks.
`The questions that naturally arise from this JMP thu11k mechanism are
`“Where is the DWORD with the function’s address found?” and “Who’s
`responsible for initializing it?” The DWORD containing the imported function’s
`address is found in what is known as the import address table (or IAT, for
`short). The IAT typically resides in the .idata (import data) section of each
`executable. For each DLL that an executable links to, there’s an associated
`array of DWORDS containing addresses of functions in the imported DLL.
`When the Win32 loader brings an executable into memory, it fills in the
`array of DWORD with the proper addresses, as shown in Figure 10-1. In
`the executable file prior to loading, each DWORD contains an offset to an
`ASCHZ string that names the desired function (for example Get‘/ersion).
`When the loader brings the executable into memory, it overwrites the array
`of names offsets with the actual addresses of the functions.
`Having seen how an executable imports functions from other DLLs, it’s
`easy to understand how a spy program can intercept and log those functions
`with a minimum of fuss and overhead. The spy program merely needs to
`find the array of function addresses in the executable’s imports section and
`overwrite those addresses with the addresses of its own logging routines. No
`actual code patching is required, so there’s no need to constantly switch
`between the original code and the code as modified by the spy program.
`The executable ends up calling the API spy’s code directly, so the only over-
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 9 of 72
`
`
`
`_¢5;i.i-47‘, uiéiiiiiiig uiwiinaz API Spy
`
`591
`
`head is that of the logging functions. After the spy code has logged the
`function’s data, it ]MPs to the original intended target of the JMP DWORD
`PTR [XXXXXXXX] thunk. Simple, no?
`
`Array of
`|MAGE_lMPOFlT_DESCHlPTOR
`structures
`
`MYEXE.EXE
`Other
`
`import Address Table
`(function pointers)
`{>
`
`USER32.DLL
`
`sections
`.re|oc
`
`Section
`
`.idata
`
`section
`Data
`section
`
`Code
`
`PE
`
`MZ
`header
`
`“USER32.DLL"
`
`"KEFiNEL32.DLL”
`
`GetMessage
`code
`
`-
`
`MessageBox
`code
`
`KERNEL32.DLL
`
`HeapAlloc
`
`code
`
`CreateProcess
`code
`
`Figure 10-!
`The jdoto section of the executable usually holds the DWORD containing the imported
`function's address, although the imports table can be located elsewhere.
`
`'"
`
`Even if spying isn’t your goal, you can use this trick of modifying the
`addresses in the imports section to selectively intercept APIs. For example,
`you might want to replace a function in a DLL with your own custom-written
`code. It’s easy to implement a function that takes a DLL name and function
`name and returns a pointer to the D\X/ORD in the imported data section that
`holds the functions’s address. Your code would then overwrite the DWORD
`with the address of your own custom function. If you want to chain on to
`the original address, simply save off the original DWORD value before
`overwriting it.
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 10 of 72
`
`
`
`Windows 95‘ ;c;,o...osscisaj.,j
`
`In the spy program I’ll be building in this chapter, I’ve chosen to inter-
`cept calls to API functions in the manner just described. In making this deci-
`sion, I’ve committed myself to running the spy’s logging code in the process
`context of the application being spied on. Since the design goals for the API
`spy program don’t allow relinking or modification of the target application,
`I need to somehow force the logging code into the target program’s address
`space. It also means that the majority of the API spy program’s code must
`reside in a DLL.
`
`INJECTING A DLL INTO ANOTHER PROCESS
`
`Now that we know how we’ll intercept calls to API functions, the next hurdle
`is to force the spying code into the target application’s address space. In 16-bit
`Windows, this wouldn’t be an issue since all programs share a common
`address space. In Win32, however, each process has its own address space, and
`its own set of loaded DLLs. Just because one process is using a DLL doesn’t
`mean that another process can use it. Each process that wants to use a DLL
`needs to load the DLL for its own use, either by implicitly linking to it, or by
`calling LoadLibrary(). Since the programs we’ll Want to spy on have no knowl-
`edge of our API spy DLL, we’ll need to resort to dirty tricks to force the DLL
`into their address space.
`There are at least three ways to inject a DLL into the context of an
`arbitrary process. Jeffrey Richter’s May 1994 Microsoft Systems journal
`article describes each approach in quite a bit of detail. Here, I’ll give a brief
`overview of the two methods that we won’t take advantage of, and then
`spend more time on the DLL injection method that the API spy program
`will actually use. The final method that Richter chooses for a general-purpose
`implementation is similar (but not identical) to the method I’ll use for
`our spy program. The key difference is that Richter’s method uses the
`CreateRemoteThread function, which isn’t available in Win32s or Windows
`95. My version of injecting the DLL is portable to all three platforms.
`The first and best known way to force a DLL into another pr0cess’s
`address space is to install a windows hook using SetWindowsHookEx(). If
`you specify an hThread for a different process, or if you request a systemwide
`hook, the operating system automatically loads the DLL containing the hook
`procedure into the address spaces of all processes affected by the hook.
`Installing a windows hook to force our API spy DLL to load is ineffective
`for two reasons. The first reason is that you have to have an existing
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 11 of 72
`
`
`
`ad Win32 API Spy
`
`693
`
`process to install the hook for. By the time this occurs, the target process has
`undoubtedly called API functions. The spy program would miss all the API
`function calls made by the target process up to that point. The second reason
`is that the hook DLL won’t actually be loaded in the target process until
`that process takes some action that causes the hoolccallback to be invoked.
`Trying to use hooks to force a DLL to load in another process context just
`doesn’t offer enough precision with regard to when the DLL loads.
`A second way to force a DLL into the address space of a process falls
`into the barely documented category. It seems there’s an obscure registry key
`value buried deep down in the registry hierarchy:
`
`HKEY_LOCAL_MACHINE\Softwa re\M1' crosoft\w1'ndows NT\CurrentVers1' 0n\H1 ndows\APP1N1T_DLLS
`
`By adding your DLL’s name to this key, the operating system automatically
`loads the DLL i11to the address space of each process as the process starts up.
`There are several reasons why this method isn’t suitable for a spy program.
`The primary reason is that this change to the registry won’t have an effect
`until the next time the system is booted. To spy on a program, you’d have to
`reboot the system first. Not feasible! Another downside to this approach is
`that the spy DLL will need to determine on a case by case basis if it wants
`to spy on the process it was just loaded for. For applications you don’t want
`to spy on, the API spy DLL should return 0 in its DllMain() procedure in
`response to the DLL_PROCESS_ATTACl-I notification. Returning 0 from
`DllMain tells the operating system that this DLL shouldn’t be loaded for
`this process. Yet another problem is that the operating system will try to
`load the DLL in every process, even in those hidden processes that you don’t
`interact with (like MPREXE.EXE). This slows down the entire system.
`The third way to inject a DLL into another process is the brute—force
`approach; this is the approach I’ll use in the API spy program. In an ideal
`world, we would somehow convey to the target process that it should call
`LoadLibrary to load our spy DLL, and that it should call LoadLibrary
`immediately upon starting up. While we can’t do this directly, there’s no
`reason why we ean’t trick the process into loading the DLL for us.
`Let’s look at an analogy to get a better feel for what I’m proposing.
`Suppose you wanted access to a vault that’s locked via a Voice-recognition
`lock. Only one person has the proper voice pattern to unlock the door, and
`you’re not that person. The person with the proper voice won’t willingly
`unlock the door for you. However, you could hypnotize the person, and
`while they’re in the trance, tell them to speak the words tolunlock the door.
`Before bringing them out of the hypnotic state, you tell them to forget
`everything that just happened.
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 12 of 72
`
`
`
`Windows 95
`
`Progirdiifi
`
`So, how does this apply to loading a DLL? If we can freeze (or hypnotize,
`if you prefer) the target process, we can modify the process’s memory and reg-
`isters so that it looks like the process is calling LoadLibrary of its own volition.
`After setting up the registers and memory properly, we unfreeze the program
`and let it execute. The end result is that the target process calls LoadLibrary,
`and the operating system obliges by loading the API spying DLL into the target
`process’s address space. After the LoadLibrary call returns, we freeze the target
`process again, restore the memory and registers to their original values, and
`let the process resume as if nothing happened.
`As you might imagine, the code to fake the target process into calling
`LoadLibrary is complex. It’ll be modifying the code of the target process, so
`the first step is to calculate which code page it will modify and save that
`page away for later restoration. The injection code also needs to modify
`registers in the target process, so it should save away a copy of all the original
`register values. Luckily, Win32 provides the GetThreadContext function,
`which retrieves all the register values for a given thread into a C structure.
`Next, my code creates a code snippet to call LoadLibrary from within
`the context of the target process. Included in this code snippet is an ASCII
`string with the name of the spy DLL (APISPY32.DLL). Immediately after
`the call to LoadLibrary in the code snippet is a breakpoint instruction that
`allows the loader program to gain control immediately after the LoadLibrary
`executes. Once the code snippet is created, I write it out to the first page of
`the target process with the WriteProcessMemory function. Immediately
`after, I’ll change the EIP register in the target process so that execution will
`resume at the beginning of my code snippet.
`After setting up the memory and registers just so, the API spy program
`lets the target process execute. If all goes according to plan, the process
`successfully executes the LoadLibrary code and returns to the breakpoint I
`set. W/hen it hits the breakpoint, the target process is temporarily frozen
`again. The spy program takes this opportunity to restore the original code
`page it saved away, and to restore the original register values (again, using
`SetThreadConteXt()). With everything back to the way it was originally
`(except for the addition of our API spy DLL to the process’s address space),
`the breakpoint handler lets the target process resume execution. I’ll come
`back to this method of forcing another process to load a DLL in more detail
`when I show the code for the program in “The APISPY32 Code” section.
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 13 of 72
`
`
`
`,;.,;.cw;.s2 API Spy
`
`595
`
`Usme THE DEBUG API TO
`
`CONTROL THE TARGET PROCESS
`
`When loading a DLL in another process’s context, it’s essential to have precise
`control over the child process’s execution. The Win32 debug API provides all
`the essential information we’ll need. In particular, we need to know exactly
`when the target process is about to execute the first instruction so that we can
`inject the spy’s DLL. We’ll also need the debug API to know when the target
`process terminates. In addition, when we’re performing surgery in the target
`process’s address space, we need to be sure that the process isn’t going to take
`off and start executing while we’re in the middle of it. Using the debugging
`API takes care of this problem. Whenever the debugged process reports some-
`thing to the debugger, all threads in the debuggee are suspended until the
`debugger tells the operating system to let the debuggee resume execution.
`If we were writing a spy program for 16-bit Windows programs, the
`TOOLHELP NotifyRegister and InterruptRegister functions would be just
`the ticket. The TOOLHELP NFY_STARTTASK notification would allow us
`to know when the new task is about to begin execution, but before it actually
`executes any of the task’s code. Unfortunately, the TOOLHELP model of
`notification callbacks assumes a single address space for all processes. The
`TOOLHELP notification callback model won’t work under the separate
`address spaces of NT and Windows 95, so we’ll need to use the closest
`equivalent, the Win32 debug API.
`Using the Win32 debug API to monitor the target process’s execution
`imposes a certain architecture on our API spy program. The API spy will
`consist of two components. The first component is the code that intercepts
`the API functions in the target process and logs them. This code must reside
`in a DLL since we’ll be injecting it into the address space of the process to
`be spied on. The second component of the API spy consists of a loader pro-
`gram that loads the process to be spied upon. After loading the program,
`the spy executable enters into a debugging loop, which consists primarily of
`calls to WaitFo_rDebugEvent() and ContinueDcbugEvent(). As debugging
`events are returned by WaitForDebugEvent(), the loader program examines
`the events and takes whatever action is necessary. The type of events that
`can be returned by WaitForDebugEvent()are
`
`EXCEPTION_DEBUG_EVENT
`CREATE_THRE/\D_DEBUG_EVENT
`CREATE_PROCESS_DEBUG_EVENT
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 14 of 72
`
`
`
`696
`
`Windows 95iSlysi'eii1i
`
`EXIT_THREAD_DEBUG,EVENT
`EXIT_P ROCESS_DEBUG_EVENT
`LOAD DLL,DEBUG_EVENT
`UN LOAD_DLL_DEBUG_EVENT
`0UTPUT_DEBUG_STRING_EVENT
`RIP_EVENT
`
`Incidentally, if you compare the Win32 debug events to the notifications
`returned by a Win16 NotifyRegister callback function, you’ll notice a striking
`similarity. Also, if you want a program that uses \X/aitForDebugEvent and
`displays all the possible information returned by it, check out the DEB sample
`program in the Win32 SDK.
`Once our loader program has handled the debug event notification, it
`calls ContinueDebugEvent() to inform the operating system that it’s okay
`for the debuggee to resume execution. By putting WaitForDebugEvent and
`ContineDebugEvent() in a loop, the loader can see all significant events in
`the life of the process being spied on.
`The most important debug event for our API spy program is the
`EXCEPTION_DEBUG_EVENT. Immediately before a process is about to
`begin execution, WaitForDebugEvent() returns an EXCEPTION_DEBUG_
`EVENT notification, with the exception being of type STATUS_BREAK—
`POINT. The API spy loader program takes this as its cue to force the spy
`DLL into the process’s address space in the manner I described earlier.
`When the LoadLibrary call returns to the breakpoint we inserted into the
`process’s code area, the loader program sees another STATUS_BREAK-
`POINT exception. The loader program uses this to know when it should
`restore the original registers and memory pages that we modified earlier.
`Once the loader program has executed the target process past the two
`breakpoint exceptions, its work is mostly done. However, the Win32 API
`apparently doesn’t offer a way for a debugger to tell the system that it
`doesn’t want to receive debug notifications anymore. Once you begin using
`the debug API on a process, that process will be suspended each time it
`generates a debug event. A debugger call to ContinueDebugEvent for each
`debug event is the only way to keep the debuggee process running. Because
`of this, the API spy loader program needs to spin around in a WaitFor—
`DebugEvent and ContinueDebugEvent loop until the target process terminates.
`Even though we only really need a couple of the debug events, we’re forced
`to receive them all. We can ignore any debug event that we’re not interested
`in, and call ContinueDebugEvent without any further processing. In
`pseudocode form, the API spy loader looks like this:
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 15 of 72
`
`
`
`Load Process to be spied on
`while ( TRUE )
`[
`
`NaitForDebugEvent()
`
`it ( dehud event is a breakpoint
`{
`
`)
`
`)
`if ( first breakpoint
`modify debuggee to make it load the spy DLL
`else if ( second breakpoint
`)
`restore original register and data pages of debuggee
`
`lse if ( debug event is an EXIT_PROCESS )
`break out of loop
`
`l e
`
`ContinueDebugEvent()
`
`BUILDING STUBS TO Loc; API FUNCTIONS
`
`At this point, we’Ve worked out the major architectural questions relating to
`the operating system:
`
`ll How API functions will be intercepted
`
`I How to load the spy DLL into the target process’s address space
`
`I How to precisely control the target process’s execution
`
`There are still other issues to deal with, but they’re not as directly
`related to operating system concerns. One such area is the code that will
`handle the redirected API function calls. While it would be tempting to try
`to make a single entry point for all the function calls we redirect to our spy
`DLL, that just isn’t feasible. There would be no way for a single entry point
`in the spy DLL to know which function call it’s currently logging. Instead,
`We’ll need to create a unique block of code for each function that We inter-
`cept. The word thunk is commonly used to describe short pieces of code
`that do some processing before transferring control elsewhere. While the
`blocks of code I’ll be creating could be called thunks, I’ll use the term stub
`to avoid ambiguity between my code and Window’s thunks. All the code
`stubs for our spy program will be similar, but will differ slightly. When each
`stub receives a redirected function call, it pushes information unique to that
`function onto the stack before calling a common routine to log the call.
`
`CSCO-1041
`Cisco v. SSL, IPR2015-01754
`Page 16 of 72
`
`
`
`698
`
`Windows 95 Syslem
`
`If all we needed to do was to intercept a known fixed set of functions, it
`would be easy to create some macros and generate all the code stubs at com-
`pile time. Since our specification dictates that this API spy be extendible, build-
`ing the stubs when we compile the spy program isn’t an option. Instead, we’ll
`need to dynamically create the stubs based on information in a configuration
`file. Luckily, under Win32 this isn’t hard.
`For each stub we need, we can simply allocate some memory and write
`the appropriate code into it. Under 16-bit Windows this would be harder,
`since we would need to somehow allocate memory in code segments, rather
`than in the data segments returned by memory allocation functions. Once
`we had proper code segments, we couldn’t just write our stub code into the
`memory block because writing into code segments isn’t allowed. To write
`to the code stubs, we’d have to use alias selectors or the TOOLHELP
`MemoryWrite() function. Under Win32 these issues don’t come up since both
`the code data segments map to the identical range of addresses. We can write
`out our code using regular flat model data pointers and later execute through
`that code.
`
`To build the stubs, the spy DLL reads an input file (APISPY32.API) that
`contains the following information about each function to be intercepted:
`
`I The DLL containing the function
`I The name of the function
`
`I Optional information about the function’s parameters
`
`For each function, the spy DLL builds a stub containing code and data,
`a11d which is of the form shown in Figur