PDA

View Full Version : Diassembly understanding question


b3n
July 15th, 2007, 05:09
Hello guys,

i wrote a little program in Visual Studio 2005 (C++) and now i try to reverse it to get a better understanding of what kind of assembly code is generated by the compiler. Since i got the source and the output i know pretty good what is going on but i came to a point where i dont exactly know whats happening on the assembly side of the code. It would be nice if someone could tell me if im getting everything right or help me out where i currently get lost.

7: ImageLoader::ImageLoader(std::string filename, std::string sectionName)
8: {
100011A0 push 0FFFFFFFFh
100011A2 push offset __ehhandler$??0ImageLoader@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z (10018B62h)
100011A7 mov eax,dword ptr fs:[00000000h]
100011AD push eax
100011AE sub esp,8
100011B1 push esi
100011B2 push edi
100011B3 mov eax,dword ptr [___security_cookie (1001D01Ch)]
100011B8 xor eax,esp
100011BA push eax
100011BB lea eax,[esp+14h]
100011BF mov dword ptr fs:[00000000h],eax
100011C5 mov edi,ecx
100011C7 mov dword ptr [esp+10h],edi
100011CB mov dword ptr [esp+1Ch],1
100011D3 call dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (10019060h)]
100011D9 lea esi,[edi+28h]
100011DC mov ecx,esi
100011DE mov byte ptr [esp+1Ch],2
100011E3 call dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (10019060h)]

this bit of code is the constructor of one of my objects. the compiler has added some sort of checking code, i think that this is related to the .net runtime. i read about the security cookie and that it is used to detect buffer overflows, so i figured this is nothing i have to care about. the part that i do not understand are the calls at 100011D3 and 100011E3. Can someone tell me what these calls do or what they are used for? I found them in other parts of the disassembly too but i couldnt figure out what they purpose is in the other context either.

Thanks!

naides
July 15th, 2007, 09:57
Quote:
[Originally Posted by b3n;67179]. . . the part that i do not understand are the calls at 100011D3 and 100011E3. Can someone tell me what these calls do or what they are used for? I found them in other parts of the disassembly too but i couldnt figure out what they purpose is in the other context either.
Thanks!


It is calling the std::string method that allocates (in the heap) the two string type parameters, which are themselves objects that your ImageLoader object takes: filename and sectionName.

Because filename and sectionName are objects their creation is not simple, transparent or logical . . .


Once you take the pathway of high level, object oriented programming, the simplest task becomes an incomprehensible mess and a bureaucratic nightmare at the ASM level. The objects, your secretaries, take care of all the gory details, but don't even think about asking them how they do it.
If you want the conveniences of high level programming, stay up there in your Penthouse and don't think about the details of every day life

_gd_
July 15th, 2007, 12:59
You've misread the method name, try leaving out the template instantiation:
std::basic_string<>::basic_string<>
So, what's actually going on is that two std::basic_string<> (std::string in this case) objects are being constructed.
Furthermore we can see that the objects belong to ImageLoader, because ecx is initialized to memory within ImageLoader in both cases.
Most likely, your code assigns 'filename' and 'sectionName' to std::string members of ImageLoader.

I'll agree that C++ constructs such as these can appear hairy and bloated.
However, there's a method to the madness and you'll no doubt come accross them often, whether you choose to program in C++ or not.
Spending the time to familiarize yourself with them will pay off in the end, imo.
If you at some time wan't to write C++ programs in an restricted environment(driver/embedded/etc.), you'll most likely need a firm grasp of what the compiler generates.

b3n
July 15th, 2007, 15:28
thanks for the responses guys, it was very helpful!

LLXX
July 15th, 2007, 15:30
Quote:
[Originally Posted by naides;67186]Once you take the pathway of high level, object oriented programming, the simplest task becomes an incomprehensible mess and a bureaucratic nightmare at the ASM level. The objects, your secretaries, take care of all the gory details, but don't even think about asking them how they do it.
Agree 100%. C -> Asm isn't too bad though, but C++ code is very inefficient. (And they keep saying compilers are getting better... I've seen quite the contrary. VC98 actually generates smaller code than the newer compilers.)

...and OP, you should've tried the Google. "C++ disassembly understanding" gives some useful results.

_gd_
July 15th, 2007, 19:53
Quote:
[Originally Posted by LLXX;67192]Agree 100%. C -> Asm isn't too bad though, but C++ code is very inefficient.

That's just plain nonsense.
First off, C++ fully implements C, so, much C++ code is C code.
In fact the first C++ compiler generated C code as an intermediary language, for wider platform support.
Second, I'm tired of hearing this ignorant myth. So I'll comment on some of the most commonly used C++ features, I'll ignore productivity benefits as I'm certain it'd fall on deaf ears.

* Objects.
** Construction
Without using inheritance, this is the equivilant of allocating a struct and calling a function to initialize the struct (usually inlined).
With inheritance, there's the added cost of calling a chain of functions (one per class in the inheritance tree) to initialize the struct (usually inlined).
Depending on the specifics, allocation can be as cheap as making room on the stack or as expensive as allocating heap memory.
However, the allocation can be overloaded, so often used classes can be constructed from a pool, making them inexpensive to create.

** Destruction
Again inheritance only defines the chain of functions to call (usually inlined).
Freeing the memory depends on where the object is located.

** Copying.
Copying comes at one added call (usually inlined) or if undefined as a bitwise copy of the object memory.

** General.
Construction, destruction and copying is user defined, allowing the user to effectively disable these constructs.
As a programmer you can also help the compiler producing more optimized code by writing small 'inlinable' constructors/destructors/copy-constructors, effectively removing the overhead.
It is entirely possible to write an integer class with the exact same performance characteristics of the native 'int' type...

* Polymorphism.
Is essentially the same as storing a table of function pointers.
The added cost is a table lookup and function pointer call.
DirectX as a prominent example is exposed entirely using this feature, as is many other technologies in wide use, yet I don't hear any programmers crying about the "massive" performance penalty

* Templates.
The most potentially expensive feature.
And also the area where compilers have improved the most.
Templates can produce excessive copies of nearly identical functions, leading to an inflation in memory usuage, the performance impact is minimal, though.

* Exception handling.
C++ Exceptions has the added cost of destructing any objects allocated at the scope they're thrown, constructing an exception object and finding a compatible exception handler.


Bottomline is there's nothing inherently inefficient about C++ and the bits which could accumulate into a problem can be controlled by the programmer.
As such, inefficiency is not a trait of the language but of the programmer, which could also be said of ASM and C.

Quote:
[Originally Posted by LLXX;67192](And they keep saying compilers are getting better... I've seen quite the contrary. VC98 actually generates smaller code than the newer compilers.)

I challenge you to find one speed comparison between VC98 and VC2005 in favour of VC98.

Quote:
[Originally Posted by LLXX;67192]...and OP, you should've tried the Google. "C++ disassembly understanding" gives some useful results.

How about you get on the cluetrain and follow your own advice.

LLXX
July 15th, 2007, 22:33
Quote:
First off, C++ fully implements C, so, much C++ code is C code.
...with other features (the ++). I wasn't saying a C++ compiler produces more inefficient output, I was saying that using C++ features results in more inefficient output.
Quote:
DirectX as a prominent example is exposed entirely using this feature, as is many other technologies in wide use, yet I don't hear any programmers crying about the "massive" performance penalty
That's because DX was what M$ invented, so if you use it you have to use it... I'm not saying it was the best though.
Quote:
Bottomline is there's nothing inherently inefficient about C++ and the bits which could accumulate into a problem can be controlled by the programmer.
Of course the programmer can do things like use specific compiler switches and the like to maximise compiler-side optimisation, but there is a limit to efficiency imposed by the language, which becomes lower when ++ features are in use.
Quote:
As such, inefficiency is not a trait of the language but of the programmer, which could also be said of ASM and C.
The programmer is partially responsible, but so is the language.
Quote:
I challenge you to find one speed comparison between VC98 and VC2005 in favour of VC98.
Was I comparing speed?

Just looking at the OP, it's hard to say there's nothing wrong with C++ when the code has to call a function for every string object allocated and deallocated, when the usual C-style strings as arrays would've been sufficient. There is more code output, more code which has to be stored in the binary, and more code that has to be executed. How is that not inefficient?

Adding layers of abstraction will increase inefficiency. No exceptions.

_gd_
July 16th, 2007, 00:12
Quote:
[Originally Posted by LLXX;67198]...with other features (the ++). I wasn't saying a C++ compiler produces more inefficient output, I was saying that using C++ features results in more inefficient output.

If that's what you meant then I'm not gonna nitpick about it.
But you've missed my point entirely. The overhead associated with using C++ features is insignificant, as long as you know what you're doing.
Exception handling isn't cheap in C either but that doesn't make it inefficient..

Quote:
[Originally Posted by LLXX;67198]That's because DX was what M$ invented, so if you use it you have to use it... I'm not saying it was the best though.

Ehh, what kind of broken logic is that? No one is forcing game developers to use DirectX.
There's a number of opensource alternatives which together cover the featureset of DirectX.
At any rate, if late binding is good enough for something as performance critical as games, then it can't be all bad.

Quote:
[Originally Posted by LLXX;67198]Of course the programmer can do things like use specific compiler switches and the like to maximise compiler-side optimisation, but there is a limit to efficiency imposed by the language, which becomes lower when ++ features are in use.

There's no limit, there's best case scenarios (which is widening every time a new version of msvc or gcc hits the street) and worst case scenarios.

Quote:
[Originally Posted by LLXX;67198]The programmer is partially responsible, but so is the language.

Right, and assembler is inefficient because it allows the programmer to create cache-misses.

Quote:
[Originally Posted by LLXX;67198]Was I comparing speed?

I have no idea, nor do I particularly care.
I'm certain it'll just end with claims that 500kb of shared copy-on-write memory mattters on todays 1GB+ systems.

Quote:
[Originally Posted by LLXX;67198]Just looking at the OP, it's hard to say there's nothing wrong with C++ when the code has to call a function for every string object allocated and deallocated, when the usual C-style strings as arrays would've been sufficient.

That's the price of using msvc's implementation of std::string, that's the choice of the author, not the compiler, you keep overlooking this distinction.

Quote:
[Originally Posted by LLXX;67198]There is more code output, more code which has to be stored in the binary, and more code that has to be executed. How is that not inefficient?

That all depends on how much of the code is actually used.
Quite often there's a fair deal of dead code in statically linked executables.
That has nothing to do with the language nor the compiler.

Quote:
[Originally Posted by LLXX;67198]Adding layers of abstraction will increase inefficiency. No exceptions.

Code:
class class1 {
int a_;
public:
class1(int a) : a_(a) {}
~class1() {}
class1(const class1& copy) : a_(copy.a_) {}
int get_a() const { return a_; }
};
class class2 : public class1 {
public:
class2(int a) : class1(a) {}
~class2() {}
class2(const class2& copy) : class1(copy.get_a()) {}
int get_b() const { return this->get_a(); }
};
class class3 : public class2 {
public:
class3(int a) : class2(a) {}
~class3() {}
class3(const class3& copy) : class2(copy.get_b()) {}
int get_c() const { return this->get_b(); }
};
int main(int argc, char* argv[]) {
class3 cls(argc);
class3 cls2(cls);
return cls2.get_c();
}

Compiles (MSVC2005 Release mode) to: mov eax, dword ptr [esp+4]; ret
That's 3 layers of abstraction, I'd try 4 but I'm affraid it might melt my CPU!

Anyway, all I'm asking is that you do the research before running your mouth about something you clearly no very little about.

b3n
July 16th, 2007, 05:12
Hello guys, its me again with another question. I found some object or structure initialization code in my program. Since im using a library where i dont have the source code to im not exactly sure whats going on.

My first question is, is it possible to create an object without invoking the new operator? I found this kind of code in the file:

.text:1000A694 mov esi, [esp+20h+arg_0]
.text:1000A698 push esi
.text:1000A699 call constructor_?

inside this call some initialization is performed:

.text:1000BEEB xor eax, eax
.text:1000BEED mov [esi+20h], di
.text:1000BEF1 mov [esi+22h], di
.text:1000BEF5 mov [esi+24h], di
.text:1000BEF9 mov [esi+26h], di
.text:1000BEFD mov [esi+28h], di
.text:1000BF01 mov [esi+2Ah], di
.text:1000BF05 mov [esi+2Ch], di
.text:1000BF09 mov [esi+2Eh], di
.text:1000BF0D mov [esi+30h], di
.text:1000BF11 mov [esi+32h], di
.text:1000BF15 mov [esi+34h], di
.text:1000BF19 mov [esi+36h], di
.text:1000BF1D mov [esi+38h], di
.text:1000BF21 mov [esi+3Ah], di
.text:1000BF25 mov [esi+3Ch], eax
.text:1000BF28 mov [esi+40h], eax
.text:1000BF2B mov [esi+44h], di
.text:1000BF2F mov [esi+46h], di
.text:1000BF33 mov [esi+48h], eax
.text:1000BF36 mov [esi+4Ch], eax
.text:1000BF39 mov [esi+50h], eax

this is just a part of the initialization code to keep the post in a reasonable size. my assumption is that esi holds the reference to an object or structure (is there a way to tell the difference?). and then the values added to esi are used as an index to the individual fields. both di and eax are zero at the time the code is run. is it possible to tell by the kind of register used to initialize the fields to tell about their size, like word or dword? i would think that the fields initialized with DI are a smaller size than the ones with EAX. Can someone confirm this?

I didnt come accross any new operator call so im not sure if im dealing with an object or a structure...

fr33ke
July 16th, 2007, 09:54
DI is 2 bytes (the lower half of EDI), EAX is 4 bytes.

Here's an ASCII graph of how a register is broken down:
Code:

+----+----+----+----+
| | EAX | | 4 bytes. Equivalent to EDI
+----+----+----+----+

+----+----+----+----+
| | | AX | 2 bytes. Equivalent to DI
+----+----+----+----+

+----+----+----+----+
| | | AH | AL | Both 1 byte. No equivalent for EDI in x86 asm.
+----+----+----+----+

Each box represents 8 bits or 1 byte
NOTE: There is no EAH or EAL

naides
July 16th, 2007, 09:55
Quote:
[Originally Posted by b3n;67207]

My first question is, is it possible to create an object without invoking the new operator? I found this kind of code in the file:

new() operator is high level, C. When you create an instance of an object in C++, the compiler, behind the scenes, allocates the memory for the object in the heap, using windows API such as heapAlloc and others.

So the answer is yes, OOP does the allocation for you without explicit use of new() or free() operators


this is just a part of the initialization code to keep the post in a reasonable size. my assumption is that esi holds the reference to an object or structure

Right. HeapAlloc returned a pointer to the object/structure which in this case is held in esi.

(is there a way to tell the difference?).

The difference between Structure and object could be substantial at high level language, but is non-existent at low level.

Think that in C++ a structure CAN have and DOES have methods, so the difference between object and structure is a small technicality (All structure associated data and methods are by default, public).

Moreover, what gets stored (initialized in the case of the object you put as an example) in the heap is the DATA associated with the Object. The code for the Object methods is usually located somewhere in the code segment of the app, not allocated dynamically. (There are exceptions, dynamically generated code, or inlined code which is allocated along with the data in the heap, but that is another story)


and then the values added to esi are used as an index to the individual fields. both di and eax are zero at the time the code is run. is it possible to tell by the kind of register used to initialize the fields to tell about their size, like word or dword?


You are right di is word, eax is dword. Also looking at the offsets from ESI, you see that the next variable after di initialization is 2 bytes above, while the next after EAX is 4 bytes above. Some decompilers would have explicitly showed that to you:

.text:1000BF21 mov wordptr [esi+3Ah], di
.text:1000BF25 mov dwordptr [esi+3Ch], eax

i would think that the fields initialized with DI are a smaller size than the ones with EAX. Can someone confirm this?

I didnt come accross any new operator call so im not sure if im dealing with an object or a structure...



In blue.

Take a look at a book by Kris Kaspersky: Hacking disassembly uncovered,

available in the web for free. He goes into some detail doing the sort of exercises you are doing right now.

b3n
July 16th, 2007, 09:58
Thanks naides, again your answer helped me a lot!

_gd_
July 16th, 2007, 10:36
Quote:
[Originally Posted by b3n;67207]My first question is, is it possible to create an object without invoking the new operator?

Yes. Consider this code:
Code:
class test {
int x_;
public:
test() : x_(5) {}
~test() {}
};
test instance1;
int main(int argc, char* argv[]) {
test instance2;
}

'instance1' is pre-allocated in the data section and 'instance2 is allocated on the stack, the same as with native type.
In addition the use of operator new can be obscured if the class overloads it.

Quote:
[Originally Posted by b3n;67207]my assumption is that esi holds the reference to an object or structure (is there a way to tell the difference?).

As naides mentioned there's practically no difference.
However, it's useful to think of structs as data holders and classes as data holders with methods, at least when you're looking at disassembly.
Methods are different from functions in that they take a hidden value as argument. The 'this' register is passed to methods through ecx in msvc generated code.
So whenever you see mov ecx, x; call y; or similar, you pretty much know you're dealing with a method. Mapping out a class is mostly an exercise in keeping track of the 'this' register.

Quote:
[Originally Posted by b3n;67207]is it possible to tell by the kind of register used to initialize the fields to tell about their size, like word or dword?

Some times you can, other times optimization eliminates individual member intializations in favour of something faster.
The constructor, copy-constructor and destructor are still great places to start though.

Quote:
[Originally Posted by b3n;67207]i would think that the fields initialized with DI are a smaller size than the ones with EAX. Can someone confirm this?

There's really no way to tell from just that code.
Try adding a new struct in IDA and add the fields as you think they're layed out, then revise the struct as you learn more about it.
Working like that will greatly reduce the complexity of otherwise incomprehensible code.

naides
July 16th, 2007, 17:37
I think I may expand a little bit in the rant I posted before.

Programming at abstract level, OOP etc, is closer to human language, to human logic, and allowing the machine to do all or most the dirty work for you: input validation, address tracking, bounds checking, etc,etc etc.

OOP code IS or at least CAN BE highly inefficient and unnecessarily complex, when looked from the machine code point of view. . .
But given the price of RAM memory, storage, and CPU speed, the price you pay for such comfort, fatter files, more complex code, is less and less, as time goes by.

I am with LLXX with the concept that whatever OOP can do, ASM programming can do, in a more compact fashion, faster, with the slimmest files possible, but hey, when you are my age, do you care so much if your waist is 28 inches anymore???

_gd_ surely realizes that analysis of the ASM code generated by OOP compilers (C++)can be a labyrinth, and that is OK.
Such code is not designed to be easily reverse engineered or analyzed at asm level. Only weirdos like the people in this board care about opening the belly of the toy-doll and learning the secrets on how she talks.

C++, and other high level languages intent is to to isolate the programmer from the intricacies of the inner machine workings, and those languages do eeeehh. . . . a barely acceptable job?

_gd_
July 16th, 2007, 19:58
The perceived inefficiency stems from the majority of C++/OOP programmers who either ignore size implications or are unaware of them.
And let's be honest, code anorexia isn't rational, in these days.
If you know the nuts and bolts of the language you're using, then it is possible to create efficient code, for whatever that's worth.

I started out programming in assembler, like most people around these parts, so I do understand where you're coming from.
However, as a reverse engineer I've benefited much more from learning different highlevel languages, than I ever did assembly.
You'll never be looking at neatly organized assembly when you're reversing, no interesting programs are produced that way (and haven't for a long time).
There's so many areas that's "closed" off to you if you cannot think like a programmer. Most of the people, insisting on using assembly, that I've met did not have a formal understanding of compute science and as such do not think as programmers.
In terms of writing tools to aid you, spending a month to write some utility in a assembly, as opposed to spending a few hours working out how to use some third party library, can be a show stopper.

Anyway, the reason that merely dismissing C++ as inefficient ticks me off, is that it's not an absolute truth and it's extremely counter productive.
As a community we'd be seeing alot more interesting tools, if newcomers were encouraged to learn a highlevel language.

Maximus
July 22nd, 2007, 16:05
Unfortunately the crash lost the answers, but let me answer to your posts.

First of all, your capability of recognizing my pattern as 'frustrated rookie' is ...messy, at last.
When I learned C++, there were no 'for dummies' series, there were no ANSI C++ at all.
My first bought, costly inter-platform C++ compiler was using a single cast because static/dynamic/reinterpret weren't yet really formalized and implemented in C++.
I am still forced to use it today (some project still C++), so it makes for more than ...21 days, unfortunately.
...as a friend of mine said "you do program C+ then? No, luckily I only teach it now" -I wish I could.

Since I do love 'hard' C++ guys, I made you 3 simple tests on 3 programming field (style,ANSI,C++), the first tricky 3 things I thought about (you know, a whole summer working due to deadline in October...):
1- a test to see your 'goodness' on Style, and your acquaintance with the secure programming techniques. Apart the fact that you took the worst possible 'solution' bugging more a bugged code, an answer that in practice says 'better keeping things implicit, I need to code less' is actually the highway to code exploits and alot of bugs. Things should always be explicit. Which is, by the way, something you find in the myriads of 'style manuals' around. They say that for a reason.
2- a question on a real ANSI C++ specification hole. Your answer? No, the compiler do that... mah... my compiler does the same? I Hope... Wait! ...why should I hope?
3- a very very tricky, commented code sequence for exploiting a weakness of the C++ and for having a little fun. Unfortunately, you missed that.

About code optimization... all the optimization books that discuss HLL optimization are mostly not worth the paper they are written on (unless they talk about algorithms, clearly). Sure, understand basic code optimization is important, but that is not real optimization.
You can really optimize code only when you are used to forecast what your compiler will output, because PCs execute assembler instructions, not C statements. The compiler's optimization is limited for the very simple reason they cannot break certain important rules (see compiler's code generation books), or they would risk to break your code.
Compilers do cause cache misses. The only real optimization books I really found worth their read-time are the Intel/AMD optimization manuals. Because when you need speed, you just isolate the segments that are really time-critical. And such optimization must be done in accordance to techniques related to the CPU architectures.

Well, I thank you anyway for having thought I were a 21-day old C++ programmer angered with C++ because I were unable to manage my code -and offered to help me. I have appreciated that anyway (no kidding).

Regards,
Maximus

edit----------------------
ps: welcome to RCE forums