Demos can be more than they seem
Gettysburg is a very good strategic game and if you'll reverse it a little on your own you'll be able to build -and play- some very interesting scenarii (it's great tactical fun -for instance- to give enormous might to one particular corp in both camps). Enjoy!
A while back I saw a little snippet about Gettysburg on Fravia's page, and I was intrigued - most game demos are ridiculously limited, with huge chunks of the program code and/or data missing - but it seemed that Gettysburg might be complete. I quite like strategy games, and I happened to see a magazine with the Gettysburg demo on its cover CD, so I thought I'd give it a go, and sure enough, the results were quite interesting. Although the demo only allows you to play three scenarios, the data files for the rest of the scenario files (gettyXX.scn, where XX is the number of the scenario) seem to be there, almost complete...
Well, this game was apparently written by Sid Meir of Civilisation fame... but I wonder who wrote the protection?
First make a dead listing of the target, lee.exe, using W32Dasm (or IDA if you desire), as we will use this quite a lot. I strongly suggest that you configure SoftICE to use the Universal Video Driver (check the corresponding box in Display Adapter Setup). This requires DirectX 5.0 (which is installed along with Gettysburg anyway if I remember correctly) and displays SoftICE in a window on the screen rather than in full-screen mode. SoftICE seems to lock up occasionly (on my computer at least) when stepping through full-screen graphical programs (e.g. games using DirectX) if the Universal Video Driver is not being used, so if the same thing happens to you give it a try. Anyway, let's get started. Browsing through the string references in the dead listing, we come across an interesting reference: * Referenced by a Jump at Address:004237A7(C) | :00423806 55 push ebp * Possible StringData Ref from Data Obj ->"PICKSCENARIO" | :00423807 68845A4A00 push 004A5A84 * Possible StringData Ref from Data Obj ->"menu" | :0042380C 68945A4A00 push 004A5A94 :00423811 E85ADA0100 call 00441270 :00423816 83C40C add esp, 0000000C Setting a breakpoint at 00423806 and running Gettysburg, we find that it snaps whenever we go to play a scenario - thus we confirm that we are indeed on the right track. Paging down through the dead listing we see a call to USER32!SetRect before another very interesting string reference catches our eye: * Referenced by a Jump at Address:004238A6(C) | * Possible StringData Ref from Data Obj ->"(CD) " | :004238DE BF9C5A4A00 mov edi, 004A5A9C This is of interest to us because all of the disallowed scenarios have the prefix "(CD)" before their names in the scenario menu - so this further confirms our suspicions that we are headed in the right direction. Scrolling down a little more we come across the following code, putting any lingering doubts out of our minds: * Referenced by a Jump at Address:00423A90(C) | :00423979 E8323E0400 call 004677B0 :0042397E A1183E4D00 mov eax, dword ptr [004D3E18] :00423983 85C0 test eax, eax :00423985 750E jne 00423995 :00423987 83FD18 cmp ebp, 00000018 ; there are 24 scenarios :0042398A 7D09 jge 00423995 ; in Gettysburg... :0042398C 0FBE8D085A4A00 movsx ecx, byte ptr [ebp+004A5A08] :00423993 EB02 jmp 00423997 * Referenced by a Jump at Addresses:00423985(C), :0042398A(C) | :00423995 8BCD mov ecx, ebp * Referenced by a Jump at Address:00423993(U) | :00423997 B867666666 mov eax, 66666667 :0042399C 6A01 push 00000001 :0042399E F7E9 imul ecx :004239A0 C1FA02 sar edx, 02 :004239A3 8BC2 mov eax, edx :004239A5 C1E81F shr eax, 1F :004239A8 03D0 add edx, eax :004239AA 8BC1 mov eax, ecx :004239AC 80C230 add dl, 30 :004239AF B90A000000 mov ecx, 0000000A :004239B4 88542419 mov byte ptr [esp+19], dl :004239B8 99 cdq :004239B9 F7F9 idiv ecx :004239BB 80C230 add dl, 30 :004239BE 8854241A mov byte ptr [esp+1A], dl :004239C2 8D542414 lea edx, dword ptr [esp+14] :004239C6 52 push edx :004239C7 E8140B0000 call 004244E0 :004239CC 83C408 add esp, 00000008 :004239CF 85C0 test eax, eax :004239D1 0F84B5000000 je 00423A8C :004239D7 83FD01 cmp ebp, 00000001 ; check to see if :004239DA 7C05 jl 004239E1 ; the selected scenario :004239DC 83FD03 cmp ebp, 00000003 ; is an allowed one :004239DF 7E09 jle 004239EA The above code loops through each of the 24 (25 if you count from 1) scenario files, prefixing the scenario name with "(CD)" if it is not one of the three allowed ones. This code snippet is only called once (the first time you view the scenario menu), and only affects the way the scenario names are displayed (e.g. changing the jle instruction at 004239DF will *not* allow you to play any scenario, but will rather cause all the scenario names to be displayed without the "(CD)" prefix, even those that are not allowed). However, because we are +crackers we can "feel" that we are on the right track, and we know that the protection is around, so we keep looking for a little longer...and we see that the below code is executed for each scenario as well, after the previous check (and after adding the "(CD)" prefix to the scenario name, if appropriate): :00423A48 8B8424C4000000 mov eax, dword ptr [esp+000000C4] :00423A4F 8B4808 mov ecx, dword ptr [eax+08] :00423A52 8D8C0CC4000000 lea ecx, dword ptr [esp+ecx+000000C4] :00423A59 E8720E0500 call 004748D0 :00423A5E A1842C5100 mov eax, dword ptr [00512C84] :00423A63 8A15B43E4D00 mov dl, byte ptr [004D3EB4] ; load number :00423A69 3BE8 cmp ebp, eax :00423A6B 8895202C5100 mov byte ptr [ebp+00512C20], dl ; store it :00423A71 7519 jne 00423A8C The memory around the location [004D3EB4] shows the name of the scenario that is currently being processed, so setting a breakpoint at 00423A5E we can see that each scenario seems to given a number (from 1 to 3) that is stored at the location [ebp+00512C20], where ebp is the number of the current scenario from 0 to 24 (0x18). This number does turn out to be of importance to us later, but for now we will continue our exploration. Using the dead listing to trace the path of execution after the above code, we soon come to the following: * Referenced by a Jump at Address:00424056(C) | :00424079 33F6 xor esi, esi :0042407B 8D8C24BC000000 lea ecx, dword ptr [esp+000000BC] :00424082 8935448B4B00 mov dword ptr [004B8B44], esi :00424088 89352C8B4B00 mov dword ptr [004B8B2C], esi :0042408E E8AD100500 call 00475140 :00424093 A1842C5100 mov eax, dword ptr [00512C84] :00424098 8B0D183E4D00 mov ecx, dword ptr [004D3E18] :0042409E 83F801 cmp eax, 00000001 :004240A1 8BE8 mov ebp, eax :004240A3 7C05 jl 004240AA :004240A5 83F803 cmp eax, 00000003 ; allowed scenario? :004240A8 7E63 jle 0042410D ; jump if good guy! This check is almost identical to the earlier one that decided whether or not to add the "(CD)" prefix to the scenario name, except that this one is executed every time we select a scenario (using SoftICE we can confirm this) - so this is the main "good guy/bad guy" jump! If the scenario you selected was one of the first three, the scenario will load and you can play, otherwise you are returned to the opening screen ("Can't do that, General!"). The alert reader will quickly conclude that the jle instruction at 004240A8 needs to be changed to a jmp to allow any scenario to be played. This is true, but only partly. If you patch the program at the above location you will be able to play *some* of the disallowed scenarios, but not all. So there is obviously a second check further down the line - using SoftICE we trace past the above check (after patching it) firstly with a previously disallowed scenario that we can now play and then with a disallowed scenario that we still can't play. This allows us to hunt down the second check, shown below: * Referenced by a Jump at Address:00424198(C) | :004241F7 80B8202C510001 cmp byte ptr [eax+00512C20], 01 :004241FE 745D je 0042425D ; jump good guy! :00424200 8D8C24540B0000 lea ecx, dword ptr [esp+00000B54] Well, well, well. The value that was stored for each scenario in memory location [scenario number+00512C20] at line 00423A6B is important, it seems. If the number is 1 the scenario will run (once you've patched the check, of course), but if it was 2 or 3 (the other possible values) it will not. I must confess that I'm not entirely sure what the different values mean (could they be the diffent days over which the battle was fought?)...but in any case, if you change the je at 004241FE to a jmp you will be able to play *any* scenario (assuming you've already patched the check at 004240A8 of course). The only caveat is that if you select one of the previously disallowed scenarios you will occasionly start with an empty battlefield, and no troops to command. I haven't yet been able to acertain exactly why this is, but I suspect it may have something to do with the fact that apart from the three allowed scenarios, the scenario files are not complete - presumably the program tries to randomise the missing data if it can't find it, and sometimes messes up. Of course, if you have the scenarios from the full game they will run perfectly, no problems at all. Alternatively you could download one of the scenario editors available and make your own scenarios, which would also run perfectly... Good Hunting, +ReZiDeNt July 1998 Greetz to: Fravia+, for first arousing my interest in this target and then reminding me to write this essay, all +HCUker's and those on the VisAsm team and of course +ORC, without whom there would be no +HCU...
I have to say that the protection on this demo is quite pitiful...in fact, I'm inclined to think they *wanted* it to be cracked, for whatever reason - they even included all the scenario files, just to make things easier! However, at least it goes to show that you can't take anything for granted when it comes to protections, just because it's a "demo" don't assume that it isn't complete. In fact, I'm beginning to wonder about all those other strategy game demos I have on old CDs...perhaps they would be interesting to study as well :-)