Rhino 2.0 Beta 8_9_2001

How to Tame a Charging Rhino

 
Date 8/22/01

by Sojourner

Published by +Tsehp oct 2001

 
 
There is a crack, a crack in everything. That's how the light gets in.
 
Rating
()Beginner (x)Intermediate ( )Advanced ( )Expert
 



 

Introduction
Finally I can give you a decent introduction to a fine program that has been developing over the last, say 4-5 years. Rhino inititally started off in beta testing that long ago as a win based modeling program. trueSpace and 3ds Max had already been out along with Strata 3d, AutoCad, of course, and there were some others at that time, but Rhino was touted as an inexpensive, easy to use alternative that was strictly a modeler. It has improved up to our present time with this new release. I expect that soon they will finally go live with version 2.

 


Tools required

w32dasm 8.x--your choice of flavors --Try Protool's site-- http://www.protools.cjb.net/

hex editor needed- UltraEdit 8.xx or whatever you want to use- www.ultaedit.com

Restorator 2.5 or any resource editor you wish-- you'll have to look around to find this baby or get something from protools: http://www.protools.cjb.net/

You will in all likelihood collect other tools as you go along, even as I have. I want to thank those people who have contributed to the scene in this way.

Target's URL/FTP

ftp://ftp.mcneel.com/pub/rhino/2.0/beta/

Just go to this site and then download what you need.

To Do List
What to do - well, this new version has a few things we need to be aware of.
1. Nullify the need to check and be sure we have version 1.1 on our system

2. Destroy the expiration

3. Allow unlimited saving
Essay

Ah, it's great to be writing again. I've decided that I can't publish ahead of time what I'll be working on, because frankly I just don't know myself. Things change you know? What had priority yesterday doesn't have today. Anyway let's get BTW on this newest release from McNeel. It is nice. Many improvements over some of the earlier beta 2 releases. Don't try to read this without having downloaded and installed it. Interestingly enough, you will notice right off the bat that our little program wants an install serial. I must say these are difficult to track down, but I happen to have a few. Also, in the newest release of oscar from pc they include some, although not what you need this time. McNeel tends to change the codes from release to release, but they often repeat later on. Go figure. If you actually need the install codes for this version of Rhino 2 and Flamingo, then please email me below. I will send them to you for this version only.

Since you already downloaded and installed Rhino Beta 2, let's go ahead and disassemble it. This is a fairly large executable. 9632 kb, so it may take a few minutes for w32dasm to do it's thing. Off the wall, I have learned that w32dasm disassembly speed is mostly a function of your cpu speed, so the faster your comp brain, well, the faster your disassembly takes place. No charge for this nugget. When you run this program Rhino tells you that it will expire on a certain date. This is of paramount importance to us, because it is one of the things in our "To Do" list that we must deal with.

Part 1
Now let's take care of the number one problem listed above. Do a string search and look for the first bad message you encountered after you ran your Rhino for the first time. "This beta requires Rhino version 1.1 to be installed." There is only one occurence. The questions you should be asking yourself are, "Can I safely jump around this guy?" "Should I just nop the call that calls this routine?" In the first case, yes, we can safely jump around our problem. In the second case it would be bad business to try to nop the call because the opening of the program depends on this procedure. Check it out yourself. That is why we must do the first. We must jump around the bad message because it will shut our program off, as if we had nopped the call. Here is some necessary code. Check out what we need to change here-- the code at 0050A4A0 7527 jne 0050A4C9 needs to be changed to EB27--that way we will force the jump over the bad message and forced exit. Now you may ask why we don't just change the value in ecx to always be something other than zero. The short answer is we don't need to because just a little bit of code later at :0050A4D2 E8992E0100 call 0051D370, this call takes us directly to an xor ecx, ecx, hence, no need to worry about what ever is there anyway. But it was a good question nevertheless.

* Referenced by a CALL at Address:
|:0052AAA5
|
:0050A480 81EC38030000 sub esp, 00000338
:0050A486 56 push esi
:0050A487 57 push edi

* Possible Ref to Menu: MenuID_00CD, Item: "Exit"
|
:0050A488 6A01 push 00000001
:0050A48A E881FDFFFF call 0050A210
:0050A48F 83C404 add esp, 00000004
:0050A492 85C0 test eax, eax
:0050A494 7528 jne 0050A4BE
:0050A496 E8A5E70100 call 00528C40
:0050A49B 8B4810 mov ecx, dword ptr [eax+10]
:0050A49E 85C9 test ecx, ecx
:0050A4A0 7527 jne 0050A4C9 <-- HERE
:0050A4A2 E81941F3FF call 0043E5C0
:0050A4A7 85C0 test eax, eax
:0050A4A9 751E jne 0050A4C9
:0050A4AB 6A10 push 00000010

* Possible StringData Ref from Data Obj ->"Rhinoceros"
|
:0050A4AD 683008C600 push 00C60830

* Possible StringData Ref from Data Obj ->"This beta requires Rhino version "
->"1.1 to be installed."

|
:0050A4B2 68D44AC600 push 00C64AD4
:0050A4B7 50 push eax

* Reference To: USER32.MessageBoxA, Ord:01BEh
|
:0050A4B8 FF15B017BC00 Call dword ptr [00BC17B0]

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0050A494(C), :0050A4FF(C), :0050A65E(C), :0050A72D(C), :0050A735(C)
|:0050A777(C), :0050A784(C), :0050A7DB(C)
|
:0050A4BE 5F pop edi
:0050A4BF 33C0 xor eax, eax
:0050A4C1 5E pop esi
:0050A4C2 81C438030000 add esp, 00000338
:0050A4C8 C3 ret

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0050A4A0(C), :0050A4A9(C)
|
:0050A4C9 6A0A push 0000000A
:0050A4CB 6A04 push 00000004
:0050A4CD 6838BAD300 push 00D3BA38
:0050A4D2 E8992E0100 call 0051D370
:0050A4D7 8B842450030000 mov eax, dword ptr [esp+00000350]
:0050A4DE 6838BAD300 push 00D3BA38
:0050A4E3 50 push eax
:0050A4E4 E8D7740F00 call 006019C0
:0050A4E9 83C414 add esp, 00000014

* Reference To: COMCTL32.InitCommonControls, Ord:0011h
|
:0050A4EC FF152810BC00 Call dword ptr [00BC1028]

* Reference To: WINMM.timeGetTime, Ord:0098h
|
:0050A4F2 FF15D417BC00 Call dword ptr [00BC17D4]
:0050A4F8 E8A3FCFFFF call 0050A1A0
:0050A4FD 85C0 test eax, eax
:0050A4FF 74BD je 0050A4BE
:0050A501 E89AB5FAFF call 004B5AA0
:0050A506 85C0 test eax, eax
:0050A508 751D jne 0050A527
:0050A50A 50 push eax

Part 2 - Destroy the expiration
Well, that was nice. Now at least our little Rhino will run without needing that nasty old version 1.1. We still need to do away with our time lmitation. As you should know there are several ways and api calls that deal with time. I encountered a new one this week, as a matter of fact, and I will speak of it in my next tutorial. So, our next foremost question is "What kind of time protection do we have to deal with?" Our leading messagbox tells us the this is a time limited trial, but even more than that, that it ends on a certain date, allegedly. Do you remember when it will end? Could some time in September be right? So, maybe we can start looking for 00015180H. Would we? I don't think so in this case, because we're looking at a certain date, not a certain number of days, because the program won't know when you are going to install it. It would be uneconomical programmatically to try to set that up. A hard-coded date would suffice generally. As a matter of history, Rhino betas have used a hard-coded date for years. Now that doesn't mean things can't change. They certainly could. But in this case, they haven't. Let's search through our string listings and we'll run across this "Beta Expired." Nice huh? And again, it is the only listing. Truly simplifies life, doesn't it? As you can see there are two ways to easily get here. Check the code listing on these calls.

* Referenced by a CALL at Addresses:
|:0050A240 , :0050A29B
|
:0050A3E0 6A00 push 00000000

* Possible StringData Ref from Data Obj ->"Beta Expired"
|
:0050A3E2 68404AC600 push 00C64A40

* Possible StringData Ref from Data Obj ->"This beta version of Rhino has "
->"expired."

|
:0050A3E7 681849C600 push 00C64918
:0050A3EC 6A00 push 00000000

* Reference To: USER32.MessageBoxA, Ord:01BEh
|
:0050A3EE FF15B017BC00 Call dword ptr [00BC17B0]
:0050A3F4 C3 ret

Code that calls the expiration
We will trace into the code now to see if we can follow the flow through the code woods. I traced the code at 0050A221 to see what was in [esp+0E] and at 0050A225 with [ esp+0C]. [esp+0E] contains the month and [esp+0C] contains the year.

:0050A210 83EC10 sub esp, 00000010
:0050A213 8D442400 lea eax, dword ptr [esp]
:0050A217 53 push ebx
:0050A218 56 push esi
:0050A219 57 push edi
:0050A21A 50 push eax

* Reference To: KERNEL32.GetLocalTime, Ord:011Bh
|
:0050A21B FF158C11BC00 Call dword ptr [00BC118C]
:0050A221 8B7C240E mov edi, dword ptr [esp+0E] <-- Month
:0050A225 8B74240C mov esi, dword ptr [esp+0C] <--Year
:0050A229 81E7FFFF0000 and edi, 0000FFFF
:0050A22F 81E6FFFF0000 and esi, 0000FFFF
:0050A235 33DB xor ebx, ebx
:0050A237 E8F4000000 call 0050A330 <-- Registry checks
:0050A23C 85C0 test eax, eax <-- I have not found this to be anything other than zero--A-OK
:0050A23E 7411 je 0050A251 <-- Could force the jump if it makes you more comfortable, EB11
:0050A240 E89B010000 call 0050A3E0 <--HERE-calls expiration
:0050A245 5F pop edi
:0050A246 5E pop esi

* Possible Ref to Menu: MenuID_00CD, Item: "Exit"
|
:0050A247 B801000000 mov eax, 00000001
:0050A24C 5B pop ebx
:0050A24D 83C410 add esp, 00000010
:0050A250 C3 ret

Code continuation:
We definitely need to end up here at 0050A251. This is key. Make the changes I have noted below. We need the right year in esi, so we'll load it in ourselves. Nop those jumps, except for the last one, as noted. Remember what I asked above about the month being September? Well, at 0050A25B this is checked. I know that September is 9, so why the A = 10? Because you can go up to month 9 but not over it. We need our prog to work anytime, right? Time marches on everyone. Fix it.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0050A23E(C)
|
:0050A251 81FED1070000 cmp esi, 000007D1 <--Changes here BED107000090, mov esi, 07d1 (2001)
:0050A257 7F26 jg 0050A27F <--Changes here 9090, nop, nop
:0050A259 7511 jne 0050A26C <--Changes here 9090, nop,nop
:0050A25B 83FF0A cmp edi, 0000000A <--month 10, October
:0050A25E 7D1F jge 0050A27F <--Changes here 9090, nop,nop
:0050A260 83FF07 cmp edi, 00000007
:0050A263 7D16 jge 0050A27B <--Changes here EB16, jmp 0050A27B

* Possible Ref to Menu: MenuID_00CD, Item: "Exit"
|
:0050A265 BE01000000 mov esi, 00000001
:0050A26A EB1A jmp 0050A286

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0050A259(C)
|
:0050A26C 81FED1070000 cmp esi, 000007D1
:0050A272 7D0B jge 0050A27F

* Possible Ref to Menu: MenuID_00CD, Item: "Exit"
|
:0050A274 BE01000000 mov esi, 00000001
:0050A279 EB0B jmp 0050A286

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0050A263(C)
|
:0050A27B 33F6 xor esi, esi <--Follow the flow
:0050A27D EB07 jmp 0050A286

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0050A257(C), :0050A25E(C), :0050A272(C)
|

* Possible Ref to Menu: MenuID_00CD, Item: "Exit"
|
:0050A27F BE01000000 mov esi, 00000001
:0050A284 8BDE mov ebx, esi

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0050A26A(U), :0050A279(U), :0050A27D(U)
|
:0050A286 8B442420 mov eax, dword ptr [esp+20] <-- None of this affects the outcome
:0050A28A 85C0 test eax, eax
:0050A28C 7409 je 0050A297
:0050A28E 85DB test ebx, ebx
:0050A290 7405 je 0050A297
:0050A292 E819000000 call 0050A2B0

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0050A28C(C), :0050A290(C)
|
:0050A297 85F6 test esi, esi <--Important test- everything flows to here, esi needs to be zero. Follow from 0050A27B.
:0050A299 7405 je 0050A2A0 <-- Since we have made esi zero already--it jumps
:0050A29B E840010000 call 0050A3E0 <--Here - Calls expiration

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0050A299(C)
|
:0050A2A0 8BC6 mov eax, esi
:0050A2A2 5F pop edi
:0050A2A3 5E pop esi
:0050A2A4 5B pop ebx
:0050A2A5 83C410 add esp, 00000010
:0050A2A8 C3 ret

This is getting to be fun now. We can almost enjoy our program fully.

Part 3. Allow unlimited saving
You see, there is a problem with our program if we leave it in its current state. After the known expiration date if you try to save a file the Rhino 2 Beta will shut down automatically without consulting you or leaving you any messages to deal with. Very naughty of them to abuse the end-user in such a way. We need to fix that. I cheated a little here, but just a little. I love w32dasm but it has some limitations in the way it runs that softice doesn't have. I set breakpoints in softice for the comdlg32.dll for GetSaveFileNameA, because I suspected that I could catch the bugger before my prog blanked out. Well, my trick didn't work, at all. Hmm, I had to think of what was going on. Well, you still have to use GetSaveFileNameA to do your file saves anyway, so I went back to work in w32dasm and started looking for occurences of this. Here they are, eight of them:

* Referenced by a CALL at Addresses:
|:004243B3 , :00424489 , :0047BB41 , :0047BC51 , :0049E731
|:00508C3E , :00574B9B , :00616F7A
|

* Reference To: comdlg32.GetSaveFileNameA, Ord:000Bh
|
:00B7BA34 FF25EC17BC00 Jmp dword ptr [00BC17EC]

No go! I had to try another tack. I knew that the shutdown only occurred after the time had passed the September cut off, so maybe it was checking the local time. It was worth a try. Again I used softice to set a breakpoint for the GetLocalTime and I ran everything again like before. I created a model, then chose Save As and popped inside user32.dll at the GetLocalTime function. How nice!. I single stepped back into my program and found out I was inside of one of the .dlls used by Rhino, OpenNurbs_vc60.dll. What an interesting development. I continued single-stepping and soon found myself back into Rhino at 004C44C0 which is the return point from the call. See below. So, depending on what's in the [ebp+FFFFFF78] after the return from the FreeLibrary call will determine the course of our little history. As long as the value is 2001(07D1h) we'll be doing better. Sadly, that won't be enough, as there are several other needed checks that have to be accomplished. When it's all said and done we can jump this entire mess to a predefined point, which is at 004C452D. We need to zero out eax before leaving our code jungle and this will do it. But-how did I figure out the jump code? EB31. Because I have a handy dandy little tool called Jumpgen from Muad'Dib and noptical. you might be able to find this on Protool's site, see above for the url link, or maybe ImmortalDescendant's website--I really don't remember where I got it, but it is nice and saves lots of time.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004C44AD(C)
|
:004C44B6 8D8D78FFFFFF lea ecx, dword ptr [ebp+FFFFFF78]
:004C44BC 51 push ecx
:004C44BD FF55F0 call [ebp-10] <--Interesting!!- this is a call to GetLocalTime
:004C44C0 83C404 add esp, 00000004
:004C44C3 8D55BC lea edx, dword ptr [ebp-44]
:004C44C6 52 push edx

* Reference To: KERNEL32.FreeLibrary, Ord:00B4h
|
:004C44C7 FF154C13BC00 Call dword ptr [00BC134C]
:004C44CD 8B8578FFFFFF mov eax, dword ptr [ebp+FFFFFF78] <--Year is loaded here
:004C44D3 25FFFF0000 and eax, 0000FFFF
:004C44D8 898574FFFFFF mov dword ptr [ebp+FFFFFF74], eax
:004C44DE 8B8D7AFFFFFF mov ecx, dword ptr [ebp+FFFFFF7A]
:004C44E4 81E1FFFF0000 and ecx, 0000FFFF
:004C44EA 898D5CFFFFFF mov dword ptr [ebp+FFFFFF5C], ecx
:004C44F0 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C44FA 7E05 jle 004C4501 <-- Here, need to change to EB31, jmp 004C452D
:004C44FC E98E000000 jmp 004C458F

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004C44FA(C)
|
:004C4501 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C450B 750B jne 004C4518
:004C450D 83BD5CFFFFFF0A cmp dword ptr [ebp+FFFFFF5C], 0000000A
:004C4514 7C02 jl 004C4518
:004C4516 EB77 jmp 004C458F

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004C450B(C), :004C4514(C)
|
:004C4518 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C4522 750D jne 004C4531
:004C4524 83BD5CFFFFFF07 cmp dword ptr [ebp+FFFFFF5C], 00000007
:004C452B 7D04 jge 004C4531
:004C452D 33C0 xor eax, eax <--Jumps to here-Why? We need to kill eax, then we can get out.
:004C452F EB7F jmp 004C45B0

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004C4522(C), :004C452B(C)
|
:004C4531 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C453B 7D04 jge 004C4541
:004C453D 33C0 xor eax, eax
:004C453F EB6F jmp 004C45B0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004C453B(C)
|
:004C4541 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C454B 750D jne 004C455A
:004C454D 83BD74FFFFFF07 cmp dword ptr [ebp+FFFFFF74], 00000007
:004C4554 7C04 jl 004C455A
:004C4556 33C0 xor eax, eax
:004C4558 EB56 jmp 004C45B0

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004C454B(C), :004C4554(C)
|
:004C455A 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C4564 7E10 jle 004C4576
:004C4566 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C4570 7D04 jge 004C4576
:004C4572 33C0 xor eax, eax
:004C4574 EB3A jmp 004C45B0

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004C4564(C), :004C4570(C)
|
:004C4576 81BD74FFFFFFD1070000 cmp dword ptr [ebp+FFFFFF74], 000007D1
:004C4580 750D jne 004C458F
:004C4582 83BD5CFFFFFF0A cmp dword ptr [ebp+FFFFFF5C], 0000000A
:004C4589 7D04 jge 004C458F
:004C458B 33C0 xor eax, eax
:004C458D EB21 jmp 004C45B0

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004C44FC(U), :004C4516(U), :004C4580(C), :004C4589(C)
|
:004C458F C70598B2D30000000000 mov dword ptr [00D3B298], 00000000
:004C4599 E882CA0400 call 00511020
:004C459E 8B1590B9D300 mov edx, dword ptr [00D3B990]
:004C45A4 52 push edx

* Reference To: USER32.DestroyWindow, Ord:008Eh
|
:004C45A5 FF157017BC00 Call dword ptr [00BC1770]

* Possible Ref to Menu: MenuID_00CD, Item: "Exit"
|
:004C45AB B801000000 mov eax, 00000001

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004C4493(U), :004C44B1(U), :004C452F(U), :004C453F(U), :004C4558(U)
|:004C4574(U), :004C458D(U)
|
:004C45B0 5F pop edi
:004C45B1 8BE5 mov esp, ebp
:004C45B3 5D pop ebp
:004C45B4 C3 ret

Whoa, was that fun or what? I believe we have finished our foray into the deep, dark code woods. It was nice, but we're finished for the day. We have spent some time doing this and learning. Actually took me a few hours to go through all this stuff for you. You cannot leave your brain behind when you are working on these progs for yourself. You must think, even as I have shown you a thought process or two when I am going through these things. If something doesn't work the way we think it should, then why didn't it work? What am I missing? Where did I go wrong? Sometimes we need to call it quits for the day and go do something else. There is life beyond electrons and pixels.

Final Notes

This was a simple lesson, but certainly having to look to figure things out. Until later.
If you have any questions please feel free to contact me at jomamameister@yahoo.com

Oh Duh
I wont even bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one.