Maparian Software presents /-----------------------------------------\ | Reverse-engineering TI-83 Plus software | | | | By Matthias Valvekens AKA Mapar007 | \-----------------------------------------/ DISCLAIMER: THE AUTHOR HEREBY DISCARDS ANY LIABILITY FOR ALL DAMAGE CAUSED BY (MIS)USE OF THE INFORMATION CONTAINED IN THIS GUIDE. IT IS SOLELY INTENDED FOR EDUCATIONAL PURPOSES. The following series of dots must fit on one line: ----------------------------------------------------------------------------------------------------------------------------------------------------- /------------------------\ | Part One: Introduction | \------------------------/ 0. Preface ----------- You probably have met the problem: you've just downloaded an app, tried it out, and it's just great. However, there's this annoying credit screen that keeps popping up, or this key binding that's simply not logical, or you just want to cheat at a game... And you don't have the source code! Then, one of the solutions is what this tutorial is all about: reverse-engineering. It is what the word says: the contrary of engineering. Engineering is going from a concept over coding to a (hopefully) working application. Reverse-engineering is going from the application binary to (or as close as possible to) the source code. In short: you interpret the program binary in order to understand it. Ready? Then let's go! 1. Requirements ---------------- - Familiarity with the Z80 assembly language and TI's operating system/hardware (duh) - A good emulator/debugger+ROM file: WabbitEmu (Windows, revsoft.org) or TilEm (Linux, lpg.ticalc.org). You probably already have a ROM if you really are a TI calculator coder. ;-) - A good assembler: I use Spasm (obtainable from revsoft.org) - A good disassembler: IDA is of course the best, but it's expensive, I suggest Dan Weiss's (Linux users: run through wine). For raw disassembly (zero interpretation), there's z80dis, the Z80 version of the GameBoy disassembler. - For OS analysis: no need to disassemble it yourself, use this file: http://www.brandonw.net/calcstuff/OS2.43Disassembly.zip - Your brains, a lot of time and DETERMINATION A useful source of documentation: wikiti.brandonw.net /-------------------------\ | Part Two: The Real Work | \-------------------------/ 1. Stripping/dumping executables --------------------------------- Without the raw binary, there's nothing to reverse-engineer... This paragraph explains what you need to do to convert your 8xp/8xk/8xu into a usable, raw binary file. First off, if you're going to analyse an OS, use your ROM file or dump the page you want from your calculator (or emulator), it's really the easiest solution, trust me. In my opinion, dumping from your emulator is the easiest solution for apps, stripping is the easiest one for programs. There probably are quite a few 8xp strippers out there on ticalc.org, but if you want my horrible written-in-1-minute java program, drop me an e-mail. Note: I don't know if WabbitEmu is capable of saving variables with virtual link, but TilEm certainly is: you just go to the link menu and "transmit" it as if you were sending a program to your friend with a link cable. A save dialog then pops up, and you can store your variable whereever you want. As you probably know, the OS loads applications in the following way: the first page of the first app is stored on page 69h, the next app/page on 68h, etc. TilEm will print a message to the terminal when it loads an application: 'Loaded app "name.8xk" to page xx'. This tells you where you have to look for it. The next step is: dumping the page of the app into an appvar. You can use this little program: #include "ti83plus.inc" page equ $67 .org $9D93 .db $BB,$6D ld hl,name rst 20h bcall(_ChkFindSym) jr nc,err ld hl,$4000 bcall(_EnoughMem) jr c,err ld hl,name rst 20h ld hl,$4000 bcall(_CreateAppVar) inc de inc de ld a,page ld hl,$4000 ld bc,$4000 bcall(_FlashToRam) res 5,(iy) ret err: ld hl,serr bcall(_PutS) res 5,(iy) ret name: .db $15,"PDUMP",0 serr: .db "Error",0 Simply equate "page" to any page you want. As I already stated, you shouldn't dump individual pages for OS analysis, use a ROM image, since it's hard to guess on which page a certain routine will appear (exception for bcalls, see later on). When you have your dump, read on to the next topic... 2. Disassembling ----------------- It means what you probably think it means: dis-assembling, un-assembling etc... It's the process of converting a binary file into "readable" Z80 code. The dumb, easy part is automated by most people (exception for masochists). The tool used for this stuff is called a disassembler (duh). The HARD part, however, is correcting the misinterpretations of the disassembler, such as code that is regarded as data, and so on. Even the most intelligently designed disassemblers make mistakes, and you should NEVER rely only on those results. When dealing with our favorite TI-83+, there's the first major problem to face: cross-page calls. As far as I know, none of the disassemblers that currently exist have support for this. You could have the following piece of code somewhere in RAM: (don't try it, the values are random) label: di in a,(6) push af ld a,$77 out (6),a call $4638 pop af out (6),a ei ret I'm not going to break down these silly pieces of code, you should be able to understand them. (if you don't, there's asmin28 and WikiTI for documentation) Most disassemblers will (AFAIK all of them) bluntly regard the code at 4638h in their current address space as the call destination, which might cause all sorts of havoc on the final results. That's why I never disassemble applications, I only use a debugger. For OS analysis this is nearly impossible, though, you should use Brandon Wilson's file referred to in the first section. Note: at the time of writing, I am working on a disassembler that might handle this. If I can get it done with all the key features I intend it to have, I'll release it. Problem No 2 is functions which use arguments passed on via the code stream (like the OS's bcall routine). IDA should be able to fix this with some configuration, but... As you all know, bcall(xxxx) is really just rst 28h \ .dw xxxx . The OS does some messing with the stack, calls the right vector, and makes sure the call returns AFTER the label word. The more primitive disassemblers would produce this: Original code: push bc ld b,0 bcall(_WriteAByte) ;WriteAByte equ $8021 pop bc ret Assembled version: (by line) c5 0600 ef2180 c1 c9 Disassembled version: (z80dis) PUSH BC LD B,0h RST 28h LD HL,C180h RET After some hours of utter frustration and transpiration, depending on the size, of course, you should get an acceptable disassembly. Still a long way to go till the "inspiration" part, though... 3. Execution tracing --------------------- Execution tracing is the "art" of finding out where a certain operation (like evaluating a point in a graph) is performed, not by reading the source, but by executing the program with a debugger, breakpointing and so on. If you do it well, this is one of the easiest parts of RE. As a first requirement, you should open up your favorite text editor and brainstorm about the routines the piece of code you are tracing is going to use. It's pointless to include stuff like _PutS or _LdHLInd since these are used far too much. You should try to guess the less common bcalls, like _EraseFlashPage or _UCLineS, you won't encounter those every few lines of code. When you have the bcall addresses (include file), you can't start tracing yet. What's the catch? To avoid compatibility problems, TI has created a very clever (but _slightly_ slow) system for the ROM calls. The bcall equates (which are fixed for every OS) are pointers to a vector table, which has the actual address and page of the routine. Those are properties that change with the OS version. The vector tables (yes, there are two), are stored on the following pages: 83+: 1Bh, 1Fh (boot calls) 83+ SE/84+ SE: 7Bh, 7Fh (boot calls) 84+: 3Bh, 3Fh (boot calls): it's generally safe to assume SE on the 84+ (and so does the OS), not sure about 83+, though, but I guess you can. I'll use the SE pages for reference through the guide. (this also goes for other OS pages) Oh yeah, in case you didn't know: the last page of Flash ROM (1Fh/3Fh/7Fh respectively) is where the boot code resides. Even with the most dreadful exploits, it's not modifiable in software, at least not as far as I can tell. The rules are the following: If bit 14 is set and bit 15 is reset ($4xxx), the vector is on page $7B. If bit 15 is set and bit 14 is reset ($8xxx), the vector is on page $7F and leads to a boot code call. (Most of the low-level Flash-related routines are stored in the boot code to avoid messing with them) The actual vector consists of 3 bytes: .dw addr .db page Not as hard as you think it is! An example: We have bcall(_PutS). _PutS equates to $450A. It's an address of the form $4xxx, so we'll be looking on page $7B, at address $450A. A quick check with Calcsys yields the following 3 bytes (OS 2.43): F1 5A 01. Accounting for endianness, our bcall resolves to address 5AF1h on page 01. That's it! Another one: _WriteAByte equates to $8021, this means it's stored in the boot code. But, since $8021 doesn't map to a Flash address (assuming _normal_ memory mapping mode, you hair-splitters), we have to subtract $4000 from it. This yields the following vector (boot code 1.02 on a 84+): 9A 4C 3F. So we have our routine at $4c9a on page 3Fh. You don't have to do all this pointer-following yourself, if you have a calculator with Calcsys at hand, you can use the DR/DISROM/ROM command to get the address and page of a given ROM call equate. Now comes the real tracing. When you've got some interesting bcalls, run your emulator, load whatever program you wish, and start up the debugger (F11 for TilEm). Now you press G, type the address you want, and press F2 to put a breakpoint on it. This is going to stop the emulator when it reaches that address. At this moment the page doesn't really matter since the chance is very small that you'll come across this exact same address on another page. Then you click run/close the debugger, invoke the operation you want. (Push-to-test, graph plot,...) The debugger window pops up with the little PC arrow pointing at your breakpoint. Fine, but what now? That's trivial if you know how the Z80 calling system works (which you should!): breakpoint the next RET, continue execution, take the top address from the stack, go to the location it points to, and go on till you arrive in the code of your application, at a place where you can orientate. You'll also be able to read the Z80's virtual address space, processor registers, hardware stack etc., it's all in your debugger window. Note: Some emulators, like TilEm, have more ways of breakpointing, where you can breakpoint on reading a certain address, writing to it, reading writing to a certain IO port or range of bytes,... It's in the "Handle Breakpoints" menu. 4. Finishing the job --------------------- By now you should have a good bit of data about the program you're analyzing. Now, if you want to modify the thing with you newly acquired knowledge, unless you are great enough to reconstruct a recompilable source file(i.e. with labels and so on), you'll have to go into patching. That, however, is beyond the scope of this guide. /-----------------------------\ | Part Three: Additional info | \-----------------------------/ 1. Good resources ------------------ (you probably know most of them) - dragonfire.unitedti.org/asmin28: the best and most widely used course in assembly programming for the 83+. - brandonw.net: occasionally there is some really interesting stuff on this site... - unitedti.org: the biggest TI calculator forum. - ti-wereld.nl: TI calculator forum for Dutch-speaking people like myself. - wikiti.brandonw.net: a wiki for most software and hardware related documentation for all TI calcs. 2. Contact information ----------------------- I hope you enjoyed reading this guide, and that it has actually been useful to somebody... You can always reach me at mapar007a {AT} gmail {DOT} com, but make sure to include something like "TI calcs" in the title. Any additions/mistakes I made/comments are welcome. Also, feel free to point out the grammar, spelling and style errors in the text, I'm not a native speaker of English. About myself: I'm a soon-to-be-15-year-old Belgian student. I've got about 2 years of experience in the domain of calculator programming, and I've been doing assembly for about one year and a half. For the future, I'm planning computer science studies at university. I haven't really accomplished anything notable yet, unless you count that Push-to-Test patch I wrote last year. I'm mostly active on the UnitedTI forum, and sometimes on Cemetech and RevSoft. If you're lucky you might meet me on the #tcpa channel, but as we probably live in vastly different time zones... At any rate, I wish you, the reader, success in your calculator programming "career". Regards from the small spot on the West-European map, Mapar007 1st October 2009 Changelog: ---------- Second release: (8 Oct. 09) - Added linebreaks. - Corrected some minor things.