Aimsun ANGApp is a class available to python scripting. It is a very interesting class that TSS provides to its general users, and can be very powerful if the user really appreciates how it works.
In the past a few days, I have been cleaning up my previous codes, while working on a project proposal that may require Aimsun micro + meso. And for some reason, one code snippet that has worked previously crashes on ANGApp.close … …
This time I am pretty hesitant to write to TSS for help – though I am sure they are going to help, as always TSS is very kind and prompt in supporting their users – sometimes a little delay if they are busy. And for these couple of days, I know they are being working their tails off on the new release of Aimsun 6.1.0 – couldn’t be more busier.
So I decided not to bother them and find out myself.
All rightee ……the question is, what is really going on inside ANGAPP.close()? And why there is a crash? Let’s take a partial look at the code segment:
OK, save the current state of ESI by pushing it to stack. This is a no brainer.
.text:00B620E0 push esi
Note, ECX always holds the “this” pointer for C++ class. So, after mov ESI, ECX, ESI stores the starting address of the current ANGApp instance
.text:00B620E1 mov esi, ecx
Now, since ESI+0 is the starting address of the subject ANGApp instance, ESI + 8 must be the address for some private member-variable, and by looking at the instruction at 00B620EA , we can immediately get the hint that it must be the address for a GGui* pointer . Now, what about ESI + 4? Note the fact that ANGApp inherits from QObject, thus ESI+4 actually is the address for a protected pointer of type QObjectData. What about ESI+0xC? Hey, are you as curious as I am? That is a good question! That is actually the address for a pointer of type GKModel *.
.text:00B620E3 mov ecx, [esi+8]
IS the GGui* NULL?
.text:00B620E6 test ecx, ecx
.text:00B620E8 jz short loc_B620FC
If the GGui* is not NULL then call GGui.getActiveModel
.text:00B620EA call ds:__imp_?getActiveModel@GGui@@QBEPAVGKModel@@XZ ;
.text:00B620F0 mov ecx, [esi+8]
Now after invoking GGui.getActiveModel, EAX holds the returned pointer for GKModel
.text:00B620F3 push eax
Then GGui.closeDocument is called with the returned GKModel pointer from above instruction
.text:00B620F4 call ds:__imp_?closeDocument@GGui@@QAE_NPAVGKModel@@@Z
Okaydokay. So far so good! Now restore ESI.
.text:00B620FA pop esi
.text:00B620FC ; —————————————————————————
[ESI + 0xC] is the address that holds the pointer of type GKModel*
.text:00B620FC mov ecx, [esi+0Ch]
Now ECX holds the pointer to GKModel
.text:00B620FF test ecx, ecx
.text:00B62101 jz short loc_B62112
Here is the tricky part. [ECX +0] is the address of the v-table of GKModel class
.text:00B62103 mov eax, [ecx]
Now EDX stores the address of the v-table of GKModel class
.text:00B62105 mov edx, [eax]
Index 1 means the first virtual function in the v-table. Note, GKModel inherits from GKObject. Therefore, the first virtual function in the v-table is the destructor ~GKObject()
Now call the destructor
.text:00B62109 call edx
Reset the GKModel* pointer to zero.
.text:00B6210B mov dword ptr [esi+0Ch], 0
This is perfect code logically showing no problem at all! And it turns out that the crash was caused by a corrupted memoy I artificially manipulated which screw up ANGApp’s initernal data!