MVS TOOLS AND TRICKS OF THE TRADE January 1994 Sam Golob MVS Systems Programmer sbgolob@cbttape.org Sam Golob is a Senior Systems Programmer THE FASCINATION OF THE DISASSEMBLER - PART ONE. It's amazing that a computer works. Even those of us who are programmers sometimes can't believe what we are seeing, when the computer is "strutting its stuff" while running some neat program. Did a person really program this? Or is there some "hidden spirit" that looks at what we've programmed, and then does wonders with it? Actually, the truth is that the computer really is only a machine. But the fascination produced by this machine never ceases. I find this fact itself to be worthy of study. Why are we so emotionally taken by the workings of a computer? This topic would really deserve expert treatment by someone as insightful as Rod Serling. What inner yearning of the human spirit is stirred, when one sees the outward workings of a computer? What do we feel? What does the computer awaken within us? I think that a small part of the answer lies in our conceptual inability to relate programming languages (which we think in) to the executable load modules that control the actions of the machine. We do our programming with something that is language-like, whereas the machine obeys a strange combination of binary bytes that constitute the load module's machine instructions. Today, we'll spend some time thinking about the connection between our language-like programs and the machine's binary instructions which result from "compiling" our programs. More specifically, we shall discuss the reverse of the normal process. We shall try to understand the machine's mysterious code by attempting to convert it back to our own assembly language, which we have a better shot at understanding. If we see assembly language code produced from what the machine is doing, our fascination and pure wonder might be converted to a measure of understanding. This reverse process is called disassembly, and programs which "de-compile" machine code, are called disassemblers. The Compilation Process. In order to understand the de-compilation process, which is our subject today, I have to say a few words about the compilation process. I'm speaking philosophically here. Probably ninety percent of our readership actually understands this mechanism in much more detail, so please don't feel that your intelligence is being insulted. Please let me go on. The compilation process is the bridge between our language-like computer instructions and the binary codes that make the computer jump through its hoops. On an IBM mainframe system, the initial translator from our programming language is called the compiler. The compiler looks at input source code, turning it into a card-image ordered collection of machine instructions and external references called an "object deck". While an object deck already contains actual machine instructions, it cannot itself run on a machine. (Let's forget about the LOADER program for now.) Another program called the linkage editor is necessary to "digest" this set of instructions further, in order to make it easier for the machine to "fetch" the program and run it. The output of the linkage editor, which takes input from one or more object decks, is called a load module. When we look at a load module with an unpracticed eye, it looks like a bunch of gibberish to us. Now to the meat. We systems programmers delve into a machine's internal workings, software-wise. When a problem occurs, the only evidence we usually have is a core dump that points to load modules and data areas. Our problem is to look at the machine's "gibberish" and to figure out what the machine was trying to do. It would of very much help to look at language instructions instead of binary machine code. Thus we have the need to reverse the compilation process and to generate a source code listing, arising from machine code. Obviously, it's easier to automate that job, rather than to do it by hand. Therefore, people have written "disassembler" programs. Our aim now is to concretely illustrate the usefulness of the disassembly process in a practical way. What's in it for us? I'll show you two "free" disassembler programs and one vendor product. During part one, we'll speak more generally. Subsequently, we'll bring in more detail. At the end of this, we should all have a much better feel for the joy and practicality of frequent "disassembling". Also, we'll forge a much better bond in our minds between the wondrous actions of the machine, and the practical desires of its programmers. The Aims of Disassembling. There are several clear purposes for disassembling machine code. These are: comparing load modules, fitting zaps to existing code, trying to see how code works, and changing how the code works when you don't have the source to work from. A fifth purpose is to satisfy auditors. For some reason (unknown to me) auditors who complain that an installation doesn't have source for some essential program, will be satisfied if they produce disassembled source. For a sixth application, one can employ disassembling to understand code from object decks that are shipped with PTFs. I'll give a few practical examples from my own work. But first, let me introduce you to the disassembler programs. To my knowledge, two disassembler programs are found on the CBT MVS Utilities Tape, which is distributed (but not created) by NaSPA. These programs can be found on Files 217 and 171 of the CBT tape. The vendor product disassembler is part of the extremely versatile (and comparatively inexpensive) PDSTOOLS product from SERENA in Burlingame, California. PDSTOOLS has an "object deck decoder" as well as a load module decoder. We'll see all of this next time. Meanwhile, look at Figure 1 which is an ISPF panel that was created by a member of my NaSPA chapter. That panel is part of a convenient interface to both disassemblers that will hopefully be on the CBT MVS Tape soon. Before we go on, I have to say that you shouldn't use a disassembler to violate software license agreements. However, many circumstances arise in a systems programmer's work, in which the understanding of the structure of some load module makes a world of difference in one's ability to solve the current problem. Therefore you need to have these tools in your arsenal, at the ready, just in case. Practical Examples. Now for a quick disassembler application to start with. Let's say we have two copies of a load module having different linkedit dates, from a software vendor. The question arises whether these two modules are really the same, or whether there are some different bytes in their code. My quick way to tell, is to disassemble both modules to two source files, and then to run a compare against these source files. This is an elegant, simple technique and it works very well. After verifying that the code is the same, all you have to do is to make sure that the two sets of load module attributes (reentrancy, reusability, etc.) match each other. The disassembler print files will display all the load module attributes. Or if you have PDSTOOLS, you can use its ATTRIB and MAP subcommands against each load module. Job finished. You can also use this technique to discover where a zap was done, by disassembling the zapped module and the unzapped one. A comparison of the two source decks will quickly reveal the location of the zap. An extension of this technique can often be used to figure out the logic behind a "late-model" load module. I have saved my old XA optional materials tapes (which contain non-OCO IBM source code for the operating system. These tapes contain base-level source for many system modules. No PTFs have modified them yet. By assembling this code for a given module, you'll get an idea of the logic behind how the module works. You have an honest assembly listing, on your own printer's paper, complete with comments. You are probably not running that code on your system, however. If the running code is not OCO, but it is at a later maintenance level, a comparison between a disassembly of the running code, with the assembly listing of the base code, will usually give you a nice idea about what is really going on with your running code. You can get to the bottom of many software problems in this way. Fitting zaps to a new version of a load module can become very easy through disassembly. It depends upon the circumstances, but I'll give you an example to show how it might be done. This example follows File 236, on CBT Tape version 354 or higher, so you can see some machine readable illustrations if you have a new CBT Tape. A friend of mine, who works as an SE for a hardware vendor, had a customer who modified the module IDCLC01, which produces TSO "LISTC LEVEL" displays on the tube. The purpose of the zap was to produce a shorter LISTC listing. This customer's zapping point was located in the first branch of nine similar compares and branches. IBM changed the logic of the compares and branches in a later release, and it became difficult to find the proper zapping location. See Figure 2, which shows the original DFP release 2.4 base code, followed by the later code, to which a similar zap was fitted, despite the logic change to the module. Again, the zap was a NOP to the first branch of nine similar branches that followed each other. Decoding of the later module with a disassembler, made it possible to easily discover the place to put the proper zap. And subsequent testing showed the zap to function correctly on the later module release. In our next installment, we'll continue our exposition of disassembly examples. However, for this time, I'd like to conclude with some talk about of how to run the public disassemblers. Running the Disassemblers. See Figure 3 for an illustration of the JCL to run both public disassemblers. You'll see that the JCL to run the File 217 disassembler is much different than the JCL to run the disassembler from File 171. The reason is that the disassembler from File 171 can use real macros to supply assembly labels, and it must call the assembler to assemble these macros. A good look at Figure 3 will make this obvious to you. Each disassembler also comes with a detailed set of instructions to show you how to make multiple passes and supply labels to the disassembled code. The File 171 disassembler can do it from real assembler macros, as we mentioned. However, we do not have space to discuss the topic of multiple passes in this installment, and we encourage you to explore that area yourselves, for now. Again, we hope to make the ISPF interface shown in Figure 1 available shortly. I hope this introduction gives you a push to start working with these disassemblers, and that this preparation will get you ready for our next installment. Good luck. See you next time. * * * * * * * * * * * * * * * * * * * * * * * FIGURE 1. This panel was developed by a member of our New Jersey NaSPA Chapter which is called SPONJ (Systems Programmers of New Jersey). It is a part of his ISPF interface to both public domain Disassemblers. Either Disassembler can be chosen from this panel. Printout from the Disassemblers is browsed as a result of this invocation. This interface will soon appear on the CBT MVS Tape, and thus will be available for public use, probably by the time you are reading this. ----------------------------- CBT DisAssemblers ------------------------------- COMMAND ===> Enter/Verify Parameters below: Choose your ===> 1 1 (File 171) - Nicest output, some bugs, but it DisAssembler looks the most like real Assembler 2 (File 217) - Updated version of the DISASM used by other dialog panel (D5) Data Set Name ---> 'SYS1.MODTEST' Member Name ===> SHOWMVS CSECT Name ===> SHOWMVS (Optional) - Defaults to member name. *---------------------------------------------------------------------------* *-- Warning! Do not Disassemble IBM Licensed Software! Warning! ----* *---------------------------------------------------------------------------* Lines per Page ===> 55 Redisplay Last ===> NO - YES to reshow previous listing * * * * * * * * * * * * * * * * * * * * * * * FIGURE 2. Illustration of fitting a zap to higher level code. Original zap, from DFP Release 2.4 base code assembly. C @02,FDTPTR+80(,@15) BNE @RF01262 <---- change branch to nop C @02,FDTPTR+8(,@15) 4770 to 4700 BNE @RF01262 C @02,FDTPTR+96(,@15) BNE @RF01262 C @02,FDTPTR+100(,@15) BNE @RF01262 C @02,FDTPTR+104(,@15) BNE @RF01262 - - - - nine occurrences of similar code - - - - DFP 3.3.1 level. Note the logic change, but the similar pattern. L R14,88(,R2) 58E0 2058 LTR R14,R14 12EE BNZ 3731(,R9) (Zap to 4700) 4770 9E93 <--- zap location L R15,16(,R2) 58F0 2010 LTR R15,R15 12FF BNZ 3731(,R9) 4770 9E93 L R0,104(,R2) 5800 2068 LTR R0,R0 1200 BNZ 3731(,R9) 4770 9E93 L R1,108(,R2) 5810 206C LTR R1,R1 1211 BNZ 3731(,R9) 4770 9E93 L R14,112(,R2) 58E0 2070 LTR R14,R14 12EE BNZ 3731(,R9) 4770 9E93 - - - - nine occurrences of similar code - - - - The new zap was discovered through disassembly and a search for nine similar instruction patterns containing a branch. This zap was tested. It works just like the original zap did. * * * * * * * * * * * * * * * * * * * * * * * FIGURE 3. JCL to run the disassemblers from the CBT MVS Tape. JCL for the disassembler on File 217. //SKJCSCD JOB (A006,SYTM,99,99),S-GOLOB,TIME=1440, // CLASS=Q,MSGCLASS=V,NOTIFY=&SYSUID //* //DISASM EXEC PGM=DISASM,REGION=0K <--- FILE 217 DISASMBLR //STEPLIB DD DISP=SHR,DSN=SYS2.SBG.USRLLIB //SYSPRINT DD SYSOUT=* //SYSABEND DD SYSOUT=* //SYSLIB DD DISP=SHR,DSN=SKJCSC.A.LOAD //SYSPUNCH DD DISP=SHR,DSN=SKJCSC.DISASM.PUNCH(LAA001) //SYSIN DD * LAA LAA /* JCL for the disassembler on File 217. Note the changes needed because this program must call the assembler to obtain labels from actual system macros. //SKJCSCR JOB (A006,SYTM,99,99),S-GOLOB,TIME=1440, // CLASS=Q,MSGCLASS=V,NOTIFY=&SYSUID //* //DISASM EXEC PGM=DISASMR,REGION=0K <--- FILE 171 DISASMBLR //STEPLIB DD DISP=SHR,DSN=SYS2.SBG.USRLLIB //*----------------------------------------------*// //*-- UNNECESSARY DD'S --*// //*----------------------------------------------*// //ABNLIGNR DD DUMMY //SYSABEND DD SYSOUT=* //*----------------------------------------------*// //*-- ASSEMBLER DD'S --*// //*----------------------------------------------*// //SYSPRINT DD DSN=&&PRT,DISP=(NEW,PASS),UNIT=SYSDA,SPACE=(TRK,(15,15)), // DCB=(RECFM=FBM,LRECL=121,BLKSIZE=12100) //SYSIN DD DSN=&&IN,DISP=(NEW,PASS),UNIT=SYSDA,SPACE=(TRK,(15,15)), // DCB=(RECFM=FB,LRECL=80,BLKSIZE=3120) //SYSLIB DD DISP=SHR,DSN=SYS1.MACLIB // DD DISP=SHR,DSN=SYS1.MODGEN // DD DISP=SHR,DSN=SYS1.AMODGEN //SYSUT1 DD UNIT=SYSDA,SPACE=(CYL,(1,1)) //SYSPUNCH DD DUMMY //*----------------------------------------------*// //*-- DIS-ASSEMBLER DD'S --*// //*----------------------------------------------*// //DISDEBUG DD SYSOUT=* //DISPRINT DD SYSOUT=* //DISPUNCH DD DISP=SHR,DSN=SKJCSC.DISASM.PUNCH(LAA002) //DISMOD DD DISP=SHR,DSN=SKJCSC.A.LOAD //* 1 1 2 2 3 3 4 4 5 5 6 6 7 //* +....0....5....0....5....0....5....0....5....0....5....0....5....0.. //DISIN DD * LINES 55 MODULE LAA CSECT LAA /* //