Object Oriented Programming is a Dirty Rotten Low Down Trick: A Look at How C++ Works

  Рет қаралды 174,587

Creel

Creel

Күн бұрын

Пікірлер: 745
@pyromen321
@pyromen321 2 жыл бұрын
A long time back, I debugged a buffer overflow that was corrupting a vtable pointer. Oh boy, that was a mess. Basically, execution would jump seemingly randomly and I’d end up seeing a nonsensical and corrupted backtrace. It’s fun when your program crashes inside a function that you know for sure is never called.
@WhatsACreel
@WhatsACreel 2 жыл бұрын
Hahaha! Oh wow, yeah, that would be a problem! Those bugs that seem to just do random things are soooo hard to fix!! Major props for finding that one :)
@inferior2884
@inferior2884 2 жыл бұрын
I think modern technologies such as ASAN would be able to find these types of errors, although you did mention it was a while back, so ASAN might have not existed.
@pyromen321
@pyromen321 2 жыл бұрын
@@inferior2884, Sadly, ASAN only catches overflows where you smash through the end or beginning of a buffer. If you write hundreds or thousands of bytes after the end of your buffer (depending how big of a red zone you have ASAN configured for), it won’t be detectable.
@Patashu
@Patashu 2 жыл бұрын
This is definitely my least favourite part about C/C++. When things go wrong finding the first thing that went wrong is a nightmare.
@phitc4242
@phitc4242 2 жыл бұрын
oh dang I think I have ptsd of that
@bundlesofun9568
@bundlesofun9568 2 жыл бұрын
Had to actually stop and think about why I love this particular programming channel so much and it hit me. You don't talk philosophy, just computing, and it's so unbelievably refreshing to hear someone not spend hours lecturing me on why I'm a terrible programmer and just get right down to business. Wonderfully informing as always, keep up the good work!
@wrong1029
@wrong1029 Жыл бұрын
He's jolly and that's very rare in this field.
@calengo454
@calengo454 Жыл бұрын
oh, I personally love people telling me why I'm a horrible programmer as well as a bit of philosophy. people really have different tastes, don't they?
@anonmouse-zr9cn
@anonmouse-zr9cn Жыл бұрын
Who is this philosophical programmer and where can I find him
@dennismuller1141
@dennismuller1141 2 жыл бұрын
The fact that methods receive the invisible this pointer internally shows a concept which I started to understand by coding in Python, where you have to specify the self parameter in method heads explicitly. It also shows that C is not that far away from an object oriented programming language, you can do the basic stuff with structs and functions that take a struct as their first argument, just like the this pointer. Thanks for the great video!
@WhatsACreel
@WhatsACreel 2 жыл бұрын
It's sooo close to C! I guess the original name, "C with Classes", is pretty much what it still is today! Cheers for watching :)
@MrGooglevideoviewer
@MrGooglevideoviewer 2 жыл бұрын
fully on the money mate!.. I think that (although maybe someone who knows a little more than I can chime in to confirm here), but I think the only difference that C++ OOP would provide over C OOP (using struct pointers as you mentioned) is exceptions. Technically there is probably a bit more again (like constructors/destructors etc.), but I think you can manually 'hard-code' something equivalent but to guarantee the destructor to be called during an exception I thought was something that C++ could achieve with C not..... -- I'm sure I've either read or been told this in the past somewhere but I've never explored how that is achieved in practise. I imagine that exception handling would be done through catching either software/hardware interrupts and preparing a set of functions to call for the 'message handler' that would be invoked during the particular trap for that particular exception call?? Jeez the more I write the more I realise I don't know shit.... now that I've written it all, even though it's a pointless monologue It seems a waste to delete 😆😆
@tymoteuszkazubski2755
@tymoteuszkazubski2755 2 жыл бұрын
C++ was first implemented as a compilation step for C
@graealex
@graealex 2 жыл бұрын
You can do everything in C that you can do in C++, it just takes ten times the lines of code, and you get none of the benefits.
@igormorgado
@igormorgado 2 жыл бұрын
@@graealex because C++ has no real benefits. ;-)
@williamdrum9899
@williamdrum9899 2 жыл бұрын
"Wait, it's just pointers?" "Always has been" Funny you mention the alignment table at 5:07. I've actually been wondering this: On a CPU that has strict alignment rules (like 68000, ARM, or most RISC CPUs) can you get more bang for your buck by doing this with your structs: int foo; char a; char b; short y; int bar; instead of: char a; int foo; char b; short y; int bar;
@WhatsACreel
@WhatsACreel 2 жыл бұрын
That's a great question! I reckon that would work a treat! Definitely worth a try anyway. Thanks for watching :)
@rustycherkas8229
@rustycherkas8229 2 жыл бұрын
I faced this problem years ago with "complex message" sources that were to compile to both a Windows DLL (for an 'unpacked' VB GUI with VB 'records') and a 'packed struct' QNX database app... Yes, floating doubles, longs, and ints to the top of a struct, with ragged chars and char arrays gathered at the bottom DID work just fine. No padding required. The declaration of the structs' elements appeared 'jumbled' in the source code, but cpus don't care where the bits are stored; that;s a human hang-up...
@Kniffel101
@Kniffel101 2 жыл бұрын
Yes, you should get more out of it, especially if you have like 10,000 instances of the struct in an array, since you'll have less wasted bytes per cache line.
@syirogane
@syirogane 2 жыл бұрын
Recently, I was able to take a struct from about 180 bytes to 128 bytes by reshuffling 32-bit fields vs 64-bit fields and using a union for one block of 4 64-bit pointers. Also, just today, I found I could sneak a field into another for free due to alignment padding.
@skilz8098
@skilz8098 2 жыл бұрын
Yeah but isn't some of this also "Compiler Vender Implementation defined?" MSVC vs GCC vs Clang vs Intel? The language standard is vague as it doesn't force one to comply to a strict way of doing things. It's a guideline that needs to be followed so that the same written source code produces the same expected or desired results. In other words, they don't care how you do it as long as it provides the expected results. I'm just wondering if this could be different between various compiler vendors? Also doesn't the calling conventions have an effect too?
@TheEclecticDyslexic
@TheEclecticDyslexic Жыл бұрын
Wrote a compiler for a little language when I was in university. The language was made specifically for the class, and changed as new ideas were taught in the class. All of this lines up pretty much how I expected. If you want to learn how memory is handled in something like C++ write a compiler by hand for a small language and it all becomes pretty clear. There are lots of books you can find to follow on this stuff, but i had the benefit of an awesome teacher.
@ThickpropheT
@ThickpropheT Жыл бұрын
We too studied compilers in one class. Man, what a fun class that was.
@theseangle
@theseangle 4 ай бұрын
Man y'all had awesome unis. In my uni we learn Figma instead of Graphics Programming in the "Computer Graphics" class 🤦‍♂️
@jokerpb4778
@jokerpb4778 2 жыл бұрын
I love this guy's videos. He is revealing all the magic computers have these days.
@MrRobertX70
@MrRobertX70 Жыл бұрын
I think that it makes perfect sense that an object-oriented paradigm would be implemented using the simplest and fasted code that gets the job done.
@khatharrmalkavian3306
@khatharrmalkavian3306 2 жыл бұрын
It's a moment to remember when you finally realize that C++ is just C with one scoop of syntactic sugar and ten scoops of bureaucracy trying to fix that first scoop.
@khatharrmalkavian3306
@khatharrmalkavian3306 2 жыл бұрын
I still prefer C++, though.
@WhatsACreel
@WhatsACreel 2 жыл бұрын
Ha! Me too :)
@jbird4478
@jbird4478 2 жыл бұрын
The first C++ compiler actually just generated C code. To be fair though, there are a lot of things C++ adds which are pretty much impossible to do in C. But object oriented programming is not one of them. There are plenty of oo libraries in C.
@Sauvenil
@Sauvenil 2 жыл бұрын
@@jbird4478 What I wonder, is would it be worth learning C++ since I've already learned and use C? The things you gain - how useful are they actually in real world applications?
@jbird4478
@jbird4478 2 жыл бұрын
@@Sauvenil Yes. It is probably the most used language next to C, so it is useful. My advice would be to stay clear from anyone that teaches "C++ design principles" or "the right way" to program in C++ though. C++ is most useful as a procedural language like C, but with added features. It's also easy to learn if you already know C by just incorporating one C++ concept at a time in a program. For instance, if you find you have quite some duplicate code you could try to write that as C++ templates instead. Or replace some struct with a class. That way you'll learn C++ without falling in the trap of these over-designed ideas like OOP. And some things - like operator overloading - you can just completely ignore. There are a few small compatibility issues between C and C++ (like casting void*) but you'll resolve those quickly enough.
@reirei_tk
@reirei_tk 2 жыл бұрын
The real world use case for code like this is job security.
@bkucenski
@bkucenski 2 жыл бұрын
The most important thing you can do with an indispensable employee is fire them immediately. At the end of the day, the inputs and outputs and business logic are known so anything can be rewritten by a competent developer.
@andersjjensen
@andersjjensen 2 жыл бұрын
It's also very useful when you're stuck with a mission critical binary for which the source code is long gone. Pull it apart with HexRays, hack in what you need, and link it by hand. C++ is gorgeously stupid when it comes to linking order, so you can convince it to fudge anything for you before main() is even called.
@andersjjensen
@andersjjensen 2 жыл бұрын
@@bkucenski The most important thing in business is fiscal responsibility. Firing an indispensable employee gains you one salary. Rewriting and debugging someone's 20 years career can easily cost you 10 times that... because, as it turns out, the program itself quite often IS "the documentation" of all the decisions and discoveries of special cases that has happened over it's development time.
@nilionth
@nilionth 2 жыл бұрын
im learning so much internally about programming as a whole. ive never really understood what happens in the background, only what ive been taught in school, all horseshit. thank you, you're a great teach
@johnheaney3349
@johnheaney3349 2 жыл бұрын
Circa 1990 I was writing 65816 assembly for the Apple IIGS and was really just getting interested in C++, which was just becoming mainstream. I was using MASM, which has a very robust macro facility and wrote a set of macros to create vtables and make virtual function calls so that I could do object oriented programming in assembly language. It was so much fun!
@mikicerise6250
@mikicerise6250 2 жыл бұрын
Heh. Reminds me when I was first tasked with modifying a consted variable in my introductory C++ class. People looked at me like I was a homicidal maniac when I presented my inline assembly solution and proclaimed, "well, if you want me to ignore the compiler, nothing better than a bit of assembly!" Apparently, I was only meant to const_cast that poor devil. 😂
@arthurmoore9488
@arthurmoore9488 2 жыл бұрын
What's fun is that modifying a const variable is by definition undefined behavior. So, the compiler can happily ignore anything that modifies a const_cast variable. Meanwhile, the assembly solution might not even be pointing to a real memory address since the variable was optimized out. Segfault due to out of bounds write, or write to read only memory.
@bothieGMX
@bothieGMX 2 жыл бұрын
Furthermore. Once you introduce undefined behavior, the entire program run has undefined behavior, even the part LEADING to the occurrence of undefined behavior. The assembly version would definitively get a real memory address, because some way or the other you would provide a pointer, but that pointer can as you pointed out point to ro memory. And of couse, all other uses of that const object could still use an inline copy of the value and hence not be affected by that neat little assembly trick.
@kzinti2
@kzinti2 2 жыл бұрын
@@arthurmoore9488 That is false. The compiler cannot ignore anything that modifies a const_cast'ed variable. The value cannot be optimized out. Memory is not saved by using const variables. What you say is true of constant expressions (i.e. constexpr), not const variables. Modifying const variables is not undefined behaviour. The C++ spec is pretty clear about what const does and doesn't do. It is purely a compile-time construct used to help the programmer catch programming mistakes. const does not in any way impact code generation and runtime behaviour.
@DrSaav-my5ym
@DrSaav-my5ym 2 жыл бұрын
wait......so why the hell were you in a intro to C++ class if you already freaking knew assembly, wtf?
@kzinti2
@kzinti2 2 жыл бұрын
@@DrSaav-my5ym What does knowing assembly have to do with knowing C++? They are completely different. C++ is far more complex than assembly. Sure the later requires you to understand your CPU's architecture and functionality, but assembly is not complex.
@zeekjones1
@zeekjones1 2 жыл бұрын
I went to school for visual C++ over a decade ago. _Definitely a bit rusty_ The main thing that stuck with me is my teacher complaining about my loops not terminating. _I can modify code, but can't write from scratch anymore_ Never even messed with memory directly, I could imagine his face if I did. I'm of a mindset that I gotta break something to figure out how it works, and this seems a good starting point to get back into coding.
@maxron6514
@maxron6514 2 жыл бұрын
Ye
@BoardGameMaker4108
@BoardGameMaker4108 2 жыл бұрын
Definitely recommend learning one component at a time, if you miss something that can make even the simplest thing impossible. Javidx9 is a great KZbinr for learning C++ kzbin.info He makes simple games in the windows console, like Tetris.
@Kenjuudo
@Kenjuudo 2 жыл бұрын
@@BoardGameMaker4108 I second that. javidx9 is great!
@nicolaskeroack7860
@nicolaskeroack7860 2 жыл бұрын
"my loops not terminating" so basically you mean that you were getting inside of a loop, with no way of getting out? So it's just that you coded a loop that make no sense and will never be true?
@zeekjones1
@zeekjones1 2 жыл бұрын
@@nicolaskeroack7860 I had strict conditions in them where they would only work as intended. It was more like I repurposed them into instances, where the technical close for the loop would be the end of the page.
@fredgotpub871
@fredgotpub871 2 жыл бұрын
Good to actually see why classes were design for: let the compiler deal with the Vtable. Thanks!
@dylanconway7190
@dylanconway7190 2 жыл бұрын
Awesome video. I never knew the vtable pointer was placed at the beginning of the class, but thinking of it now it makes a lot of sense
@WhatsACreel
@WhatsACreel 2 жыл бұрын
Cheers mate, it is interesting how simple it all is under the hood! Thanks for watching :)
@thewhitefalcon8539
@thewhitefalcon8539 Жыл бұрын
Not always the beginning
@Ximaz-
@Ximaz- Жыл бұрын
Thanks for this video. In the firs tpart of the video, it shown me that I was thinking right about struct in C. The fact that you should be able to cast a struct and change it's attributes as you will. :) Even though it's C++ with OO, struct are kind of OO but in C. It works pretty the same.
@matthias916
@matthias916 2 жыл бұрын
currently in the process of researching how oo works so I can apply it to my own programming language, this video helped a ton, keep it up!
@NonTwinBrothers
@NonTwinBrothers 2 жыл бұрын
Now THAT'S what I call Forbidden C++
@Eavolution_
@Eavolution_ 2 жыл бұрын
move over javidx9 and your gotos, we've found the real chest of forbidden c++
@artekmeister
@artekmeister 2 жыл бұрын
I amaze myself that I understand this video perfectly having learned all this stuff over 25 years ago. (Although I haven't used it very much.) Great video. Well done. Gut gemacht.
@AaronBonBarron
@AaronBonBarron Жыл бұрын
This has got to be the most Aussie programming channel on the internet. Incredible.
@HominisLupis
@HominisLupis 2 жыл бұрын
Creel, it's a real pleasure to watch your videos and partake in your years of (especially low level) language and implementation details. Thank you.
@Bentley070
@Bentley070 2 жыл бұрын
This is an excellent introduction! Would be really cool to see an advanced version of this that examines the implementation of multiple inheritance, virtual inheritance, and dynamic_cast in the presence of the other two.
@vercolit
@vercolit 2 жыл бұрын
Very good video! It really shows how simple C++ needs to be to retain its speed compared to C and assembly.
@cmasupra
@cmasupra 2 жыл бұрын
I've programmed in C, C++, Java, a couple assembly languages, Visual Basic, C#, Racket, some other languages, and Python. It wasn't until I got to Python and saw the "self" parameter at the start of all member functions that I had an epiphany about how OOP is done behind the scenes. This video did 2 things in addition to that epiphany. 1: It confirmed my epiphany. 2: When you showed polymorphism, it showed me that the designers who came up with OOP must have been geniuses to purposely design in polymorphism behind the scenes.
@sakari_n
@sakari_n 2 жыл бұрын
Well yeah. If you want to understand a programming language or some feature of it. It is a good idea to disassemble some code you made. That allows you to see what is going on concretely.
@spicynoodle7419
@spicynoodle7419 2 жыл бұрын
I reached most of your older videos at least twice a couple of weeks ago. I was craving that Creel and I can finally scratch it with this brand new video!
@maevrik
@maevrik 2 жыл бұрын
Visual Studio C++ compiler allows you to see the class memory layout at compile time using the compiler switch /d1reportAllClassLayout. The real mess starts when you have multiple layers of inheritance with non pure virtual classes. You get a nice interleave of function pointers and data that may or may not be aligned depending on user #pragma. It's crucial to understand this for reverse engineering applications from compiled binaries.
@geiger21
@geiger21 2 жыл бұрын
This was very interesting! Although I knew about this "magic" I didn't know how exactly it looks in memory etc. Maybe some deep dive into the templates next?
@WhatsACreel
@WhatsACreel 2 жыл бұрын
Great idea! I did record bits on templates and constructors and destructors, but I cut it out coz it was too long! Maybe I can put it in another vid at some point. Cheers for the suggestion and cheers for watching :)
@geiger21
@geiger21 2 жыл бұрын
@@WhatsACreel can't wait to see said video then ;) cheers!
@Cammymoop
@Cammymoop 2 жыл бұрын
Seconded
@TheTruthAboutLemmings
@TheTruthAboutLemmings 2 жыл бұрын
@@WhatsACreel There's no such thing as 'too long'
@MrWorshipMe
@MrWorshipMe 2 жыл бұрын
The fact that the first parameter of a member function is the this pointer is useful to know if one wants to pass stateful free functions. You use bind_front on that member function and its instance, thereby creating a free function with a state that you can pass along to other functions who'd be agnostic to the fact there's a class behind it.
@bkucenski
@bkucenski 2 жыл бұрын
I had always thought of C as a strictly typed language until I read the book Expert C Programming: Deep C Secrets and it pointed out it was loosely typed and having more experience I realized, that yeah, you can always get to the bytes and do what you want.
@andersjjensen
@andersjjensen 2 жыл бұрын
Yeah, that one was an eye opener to me too. And the book that finally made me understand paged virtual memory.
@outlander234
@outlander234 2 жыл бұрын
I am a complete hobbyist gamedev so I learned enough of programming just so I can work with the engines and being able to read people's code but years ago I had a thirst to really learn how computers actually work so I went on a journey to learn about architecture, assembly language, C language, C++, C# etc. And I had quite a bit of "Aha!" moments one of which was "Hey its all about pointers and data and hoping around data?"Also another aha was realizing you actually working in tandem with compiler, that's the main thing in programming, that's what a language is actually, compiler puts it together in a set way the designers of that language intended and it gets translated to machine code in the end. Absolutely loved how C was just enough step up from assembly that it didn't obscure true nature of programming. As I learned about the community I never could understand how people favored some languages over the others, still cannot to this day hear somebody say "This is better than that..." Like wtf do you even know it all works?? Its all the same under the hood but of course you are going to use the one its supported in whatever framework you work in. One time I watched this one programmer video how they would hire people coming with CS degrees but no work experience and some didn't know the difference between stack and heap... Sure developing some non demanding android app you don't need to know anything about that even how OOP works but if you are developing anything that requires above average performance you will need to know how it actually all works under the hood.
@FelixHdez
@FelixHdez 2 жыл бұрын
Well of course it's all the same under the hood, it still makes sense to prefer some languages over others, there's syntax, readability, and frameworks like you mention, etc.
@mielole
@mielole 2 жыл бұрын
Vtables were explained to me before but now I understand that the vtables are per-class and not per-instance, I couldn't understand that before, so thank you for putting that in and insisting on it!
@besusbb
@besusbb 2 жыл бұрын
Thanks Creel, this is an extremely good and interesting video.
@lx2222x
@lx2222x 2 жыл бұрын
Thank you so much for making the video! I before was modding games, so I knew that before. But I never would have thought of creating custom VTables. Before I was modifying the addresses of the VTable, but this allows for even better stuff, like function swapping for only one instance (for ex. Localplayer). I would like to see a part 2 that covers virtual inheritance and dynamic casting :D
@sbqp3
@sbqp3 2 жыл бұрын
Awesome video, thanks for sharing! Regarding getting the address of a variable that you discuss around 10:30, you can use the "immediate" window and type "&a", that will give you the address and data. Another trick is using the watch window and enter an expression like "&b,bb", which will show the address of b in binary format.
@vertexbz14
@vertexbz14 2 жыл бұрын
Great stuff! No bs and to the point, nice. I think the higher level the language is, more tricks it uses - objective c is also very interesting in this manner (class/instance structure, dynamic linking)
@terriblecoughing4767
@terriblecoughing4767 2 жыл бұрын
Nice to see you again, man
@WhatsACreel
@WhatsACreel 2 жыл бұрын
You too Mr. Coughing :)
@dekutree64
@dekutree64 Жыл бұрын
Another fun point not mentioned is that single inheritance of member variables is implemented like struct DerivedStruct{BaseStruct base; int derivedVar;}; Thus any DerivedStruct pointer simultaneously points to a BaseStruct, and can be passed to functions that take a BaseStruct pointer. And if BaseStruct has a vtable pointer as its first member, then so does DerivedStruct. You just point it to DerivedStruct's vtable, and then virtual calls from inside BaseStruct functions will actually jump to DerivedStruct functions. Very easy to do in regular C. C++ just automates a lot of it.
@sabitkondakc9147
@sabitkondakc9147 2 жыл бұрын
You never fail to amaze me Creel, thanks for your time.
@jynx0riZ0r
@jynx0riZ0r 2 жыл бұрын
All good examples, why C and C++ are great. Thanks for the overview. ;-)
@damian_smith
@damian_smith Жыл бұрын
It's good to see how much you're enjoying MacGyvering the language mechanisms!
@TheEVEInspiration
@TheEVEInspiration 2 жыл бұрын
This brings me so back to the 90s :)
@shoesoft
@shoesoft 2 жыл бұрын
14:14 That's the cool thing about it. You can f.e. store an array of base class objects and call a function that is overridden in each unique child class without that child class. Or you can also easily override certain methods, knowing that your custom class will do special things at common events. In Unreal Engine f.e. there is an Actor base class and you can inherit from it and override events like BeginPlay, BeginDestroy, Tick and so on.
@lepidoptera9337
@lepidoptera9337 Жыл бұрын
Yes, it is so cool that every hacker will thank you for giving them a named pointer to one of their malicious functions. ;-)
@amortalbeing
@amortalbeing 2 жыл бұрын
This was absolutely amazing to watch:) thanks a gazillion times and keep up the great job :)
@alexwolski3344
@alexwolski3344 2 жыл бұрын
Awesome video! Really demystifies OOP. Does this mean that upcasting is type punning? Can you use type punning to downcast?? (assuming classes are the same size)
@WhatsACreel
@WhatsACreel 2 жыл бұрын
You certainly could! Not recommended of course, to cast from parent to child, but there's nothing in C++ stopping us from treating any block of RAM as anything we like! Well, nothing but the compiler trying to warn us that we're doing something stupid! Hahaha, cheers for watching :)
@JohnnyWednesday
@JohnnyWednesday 2 жыл бұрын
@@WhatsACreel - There are cases in my engine where an abstract list of TParent items (for example, nodes in the scenegraph) might be cast to multiple different TChild descendants based upon their class (IE identify and then cast a TParent to a TPortal for another render pass) - is that the kind of thing you refer to when you say it's not recommended? What pitfalls are there with this kind of access pattern?
@andersjjensen
@andersjjensen 2 жыл бұрын
@@JohnnyWednesday It's just a rule of thumb to not do it, but when you know you need to, then you need to. And there are several kinds of instances where it's the only way to get things to run fast.
@anobodyscontentstream5347
@anobodyscontentstream5347 2 жыл бұрын
I thought this was gonna be a boring old news video about C++ magic, but I really liked that demo where you created a function in assembly alongside the C++ and how you dug to find the address value. Nice stuff!
@anobodyscontentstream5347
@anobodyscontentstream5347 2 жыл бұрын
And commenting to early… the vtable thing is awesome. The vtable pointer is in data segment that is writable… mind-blown!
@steveokinevo
@steveokinevo 2 жыл бұрын
Another amazing video Chris man, love messing under the hood with oop for funzies, another quick way I use for getting those variable addresses, open up memory viewer in debug and type &variable_name in address bar that will throw up the memory address of var and it's contents. Great content as always mate, noice one.
@Syntax753
@Syntax753 2 жыл бұрын
What a superb reference to a Norwegian masterpiece :) That's put a smile on my face for the rest of day. The content was excellent too btw :D
@Syntax753
@Syntax753 2 жыл бұрын
Spoiler: kzbin.info/www/bejne/oKDJf4WVjNCieac&ab_channel=discoveryplusNorge
@fondueeundof3351
@fondueeundof3351 Жыл бұрын
@21:00 Would Function1 have access to private members of *this_ptr?
@sonatab2646
@sonatab2646 2 жыл бұрын
This is fascinating, and incredibly evil! Theoretically you can use this to build add-on or plugin packages that have control over functions and data that it has no business interacting with. Not authorized to touch core functionality at your business? Who cares! Video game allows for mods but no modifications to the engine? Sorry UE4, we now have an unstable and horribly awful workaround! Want to call a class function without a member? No problem! Basically: "Thanks, I hate it!"
@greenaum
@greenaum 2 жыл бұрын
Theoretically, it shouldn't work, memory protection should keep data away from code that doesn't have permission to access it. But operating systems just do protection on a per-program basis, so within a program it's your playground. IRL is it much of a problem? If you're writing a program, and you have access to some functions, you're "trusted" enough to access even the ones you weren't explicitly given access to. A C program can access anything in it's own memory space. You're not "supposed" to but the machine won't stop you. I wonder if this "weird ugly internal names" trick would let you mess about with the inner parts of a game engine you bought to make a product around? Maybe run it through a decompiler and see if those names crop up, or at least useful memory addresses. You might use the same trick, find the address of something you're allowed to have, then add a few to the pointer and see what's there. In fact you might run a loop where a pointer accesses the entire memory space and dumps it during program execution. Just to see what you find. I bet those weirdo ugly names have a format you could look out for. That's assuming all of that stuff doesn't get ripped out at compile time, when Unreal compile the version of the engine they ship to customers. Still, it happens that sometimes people leave the debugging info in. Not every time but sometimes enough to be useful.
@instrate4307
@instrate4307 2 жыл бұрын
Waiting for this man once coming out as a teacher in my university. That are what I wanted to hear and study on our lectures. (KPI Ukraine)
@ZeriAi
@ZeriAi 2 жыл бұрын
A very entertaining, educational and overall fun video Many thanks for making it
@fdc4810
@fdc4810 Жыл бұрын
Hey Creel, thanks for the excellent video! Can I ask at 11:03, why the first parameter of the function is at rcx register ? And what happens if the number of parameters of a function is greater than the number of registers of the CPU ? Does it do loops to load parameters onto the registers in several batches ?
@ThusHeComplained
@ThusHeComplained Жыл бұрын
It loads them in this order: RCX, RDX, R8, R9, and if there are more, it loads the rest onto the stack
@fdc4810
@fdc4810 Жыл бұрын
@@ThusHeComplained So say I need to read the fifth parameter, which is on the stack, which register does it load to before accessed by the ALU ?
@alphalunamare
@alphalunamare Жыл бұрын
14:50 "Dog Fish" made my day! :-) I really enjoyed this under the hood look at things, it makes things make more sense. I can follow pointer manipulation and intent far easier than posh words. :-)
@codingmarco
@codingmarco 2 жыл бұрын
Great video! For me, it always helps to understand how the language works under the hood.
@R4ngeR4pidz
@R4ngeR4pidz 2 жыл бұрын
Could you clear up some confusion I had? At 17:13 it states "in the same order as defined in the class" is this actually supposed to say defined or should it be "declared" instead?
@MrHaggyy
@MrHaggyy Жыл бұрын
Can you use the vtable pointer change for a self programming code? Like get one or two tables with default functions. At runtime write your acustomed vtable in RAM, point the original table to it and get a custom behavior?
@mohandeszalatan4576
@mohandeszalatan4576 2 жыл бұрын
That was so awesome! Thanks for the video Chris. Cheers :)
@AndrewCodeDev
@AndrewCodeDev 2 жыл бұрын
Creel's throwing down the gauntlet! Nah, great stuff as always man, cheers!
@RedstoNeman0
@RedstoNeman0 2 жыл бұрын
well damn, that just shattered most courses where I was told that those OOP practices helped with security inside a program lol, pointers seem obvious but I really thought at least some form of protection from outside writes was implemented
@SkenonSLive
@SkenonSLive Жыл бұрын
The tricks used in this video are basically Java Reflection in C++. Obviously C++ does not support this officially but because of its low level nature you can still hack it together. Java kind of normalized these operations and was judged for it, especially in the early days 😁. It does break a students brain for a bit but it can be useful to achive higher levels of abstraction. Now the JVM offers an option to turn this off, never tried to bypass it, wondering if it can be done?
@alphalunamare
@alphalunamare Жыл бұрын
Are you sure? Aren't the 'tricks' as you call them just inherent mechanisms of the way C++ has been constructed/designed to achieve desirable safety features in order to predicate against the manifest lunacy of C Hackers? Rules are not made to be broken they are made to keep the system safe etc. Just because you can doesn't mean you should and mature programmers appreciate C++ on fast machines over C on comparatively slow machines in decades gone by because the bugs are less likely and when they do occur then they are easier to fix. The luxury of powerful hardware gives us the utility of Safe programming languages, something one could only dream of in the '70s say. So it is nothing at all to do with Java in any shape or form. This video is pretty much akin to looking 'under the hood' and seeing how the engine works not the Car or its glossy paintwork. I found it easier to understand than someone dissing me because I didn't know that polymorphism had stuff all to do with Polyphemus. :-)
@coder2k
@coder2k 2 жыл бұрын
I really love the way you explain that stuff! Thanks for spreading the knowledge in such an accessible way! :)
@nosehad5486
@nosehad5486 Жыл бұрын
Can't we use mprotect to change the VTable? I am using it to change my functions on runtime.
@furyzenblade3558
@furyzenblade3558 2 жыл бұрын
Super interesting video! Thank you for making those videos
@fortytwo8388
@fortytwo8388 2 жыл бұрын
Love this video, such a good illustration. Question: at 20:50, what about adding an extra parameter to Function1, that is not in the class function? We read from the stack then?
@WhatsACreel
@WhatsACreel 2 жыл бұрын
I think it would cause the CPU to read the stack as though the parameter had been passed. It's probably not going to read anything useful there, hahaha! Mind you, the first 4 parameters will be passed in RCX, RDX, R8 and R9, so you'd have to pass more than 4 to test it. There's certainly a lot of hacking trickery which involve examining the stack. If we need to be cautious in a sensitive function, then definitely clear the local variables from the stack before returning. The whole stack is read/write, so there's nothing stopping us from reading all the data from the previous function calls. That's pretty much what a debugger reads when it hits a breakpoint. Anywho, cheers for the interesting question, I'll have to try that some day, and thanks for watching :)
@fortytwo8388
@fortytwo8388 2 жыл бұрын
@@WhatsACreel thank you for the answer. Now this got me interested how a debugger actually is implemented. Your videos really inspire me, as they give real starting points to look deeper into topics I only have kind of abstract knowledge about.
@elegantcastle00
@elegantcastle00 2 жыл бұрын
Your teaching ways are really awesome !
@ashrasmun1
@ashrasmun1 2 жыл бұрын
I love your humour and the educational value you provide 😊😊
@sannekaribo4253
@sannekaribo4253 2 жыл бұрын
You just keep blowing my mind
@Je3f0o
@Je3f0o 2 жыл бұрын
Amazing video, interesting topics to watch as always.
@RigelNarcissus
@RigelNarcissus 2 жыл бұрын
2:40 I believe type punning is always undefined behavior in both C and C++, because it violates the strict aliasing rule. C and C++ compilers are allowed to operate under the assumption that two pointers of different types will never be aliased. So not just bad practice, but literally illegal in the rules of the language :p I think you can do type punning in C safely using a union, but in C++, its illegal to access a union member that is not the "active" member (ie, the member that was assigned to last) This stuff is all so very complicated and its unfortunately very easy to invoke UB with C++ :(
@sakari_n
@sakari_n 2 жыл бұрын
Undefined behavior is not something anyone should respect. It is utter BS. Undefined behavior was invented by spiteful compiler writers who were not loved is children. And it is impossible to write a serious program in C++ that does not have undefined behavior anyway.
@RigelNarcissus
@RigelNarcissus 2 жыл бұрын
@@sakari_n It's not BS, nor is it done out of spite. It's done because its very useful for enabling various more aggressive compiler optimizations. That's like, the entire reason it exists. All behavior that is currently undefined *could* be defined, but the compiler would be forced to do less aggressive optimizations and/or more runtime checks. Invoking UB is as much of a logic error as violating the preconditions of a function. It *might* work, but the implementer of said function could start utilizing those preconditions more aggressively, and your code may break under those conditions. And yeah, avoiding UB in C++ is unfortunately very difficult, and that's a flaw with the language itself :(
@sakari_n
@sakari_n 2 жыл бұрын
@@RigelNarcissus It is BS and if you think otherwise you are wrong.
@sakari_n
@sakari_n 2 жыл бұрын
@@RigelNarcissus Oh yeah just ignore my previous response I was pretty drunk last night. Yeah that is like the point with UB that it lets the compiler to do more optimizations, but the issues comes from thinking that the whole behavior is undefined when it is usually clear what the optimal behavior is for the target platform. So it should not be treated is completely undefined, but as a set of possible behaviors to that platform. Anyway I need to go drink my hangover away.
@mudi2000a
@mudi2000a 2 жыл бұрын
@@sakari_n the issue is actually the naming, they should have called it implementation specific behavior. But I guess they went with undefined behavior to discourage the usage.
@gammyhorse
@gammyhorse Жыл бұрын
Hello Creel, very interesting video. Please, can you share how did you get syntax highlight for the assembly code in Visual Studio?
@blazed-space
@blazed-space Жыл бұрын
Amazing channel, you produce awesome quality content
@aredrih6723
@aredrih6723 2 жыл бұрын
There some extra fun with multiple inheritance of class with a vtable that end up with 2 vtableptr. And in case of virtual inheritance, the offset to the subtype being stored in the vtable. Lot of fun edge case that are not meant to be messed with and could blow up when playing with them. ^^
@georganatoly6646
@georganatoly6646 2 жыл бұрын
I remember the first time I mucked about with classes, I'm a C/Assembly programmer, I thought objects were gimmicky namespace hacks, basically programmatic alternative to macros that would serve the same purpose (this was my first impression, my current understanding has obviously evolved), in my view the only 'real' valid application of OO principles is if there's a meaningful layer of abstraction between the programmer and the underlying assembly, and I don't mean libraries or frameworks, so then platforms like Java or .Net/C# -- I also don't like how colleges seem to favor OO over procedural and functional styles, going so far as to teach OO based data structs and algorithms, which the students may be shocked to find aren't as directly applicable as they might first assume, such as in functional programming languages or in compute/space critical applications where everything must be as flat/contiguous as possible -- -- anyway, really enjoy videos on higher level concepts from these kinds of perspectives!
@DennisVlaanderen
@DennisVlaanderen 2 жыл бұрын
I'm currently in college, we get a course into OO (Java), Declarative (specifically Data driven T-SQL) and Functional (free language choice) programming each. They much rather give us the toolsets to know which is which and the benefits and points of attention for all of them, rather than deep diving into a single language for 3 years. There's also a specific major we can opt in for specifically focus more on data-analysis, web-development or embedded development. So luckily there does seem to be a shift in course-structure to move away from fixed OO.
@arthurmoore9488
@arthurmoore9488 2 жыл бұрын
As someone who makes my living dealing with bad code, and has dealt with more than one Physics PHD's code, I actually appreciate things being taught as OO and wish those PHDs knew it... It turns out that when they are trained to treat everything as CSV files where each column is a separate 1D array that leads to really bad programming practices. What's fun is that pandas actually does that under the hood using numpy, but because it exposes the entire table in terms of a DataFrame, it's so much easier to work with. The key takeaway should be that just because the low level does something efficient, like hidden `this` to a name mangled function, doesn't mean that we should be constrained to doing it by hand. Better to teach students the basics, then go back and explain all the "magic" under the hood to make it work efficiently.
@georganatoly6646
@georganatoly6646 2 жыл бұрын
@@arthurmoore9488 a bit of an aside, but I think there's a fair case for the argument that we all make a living dealing with bad code lol
@NicosLeben
@NicosLeben 2 жыл бұрын
19:10 Shouldn't these three functions have a single dummy parameter which functions as 'this'? Or is this only needed in x86 programming?
@dshuffman32
@dshuffman32 2 жыл бұрын
The Creel has returned... nice!
@MRooodddvvv
@MRooodddvvv 2 жыл бұрын
That vtable override trick is what i had to do for overriding some methods in precompiled proprietary DLL to make it interract with data on network rather than just accessing file localy.
@heaslyben
@heaslyben 2 жыл бұрын
"There's better ways to call three functions." Ha! This was interesting and good fun, thanks!
@nazstreamsini4870
@nazstreamsini4870 2 жыл бұрын
this video is a must watch if you are into game hacking and also help you reverse engineer routine and i guess, with this knowledge, we can implement those oop feature in C if we wanted to
@roynevo367
@roynevo367 2 жыл бұрын
About the vtable content being hardware-protected, does this apply specifically to msvc or is it something which is defined by the language standard?
@lepidoptera9337
@lepidoptera9337 Жыл бұрын
It depends on your compiler and operating system. And even if the compiler manufacturer claims such a thing or something similar still doesn't mean it's true. If you can hack the MMU, then absolutely everything, everywhere, at all times, is on the table. Don't forget... C/C++ have an asm statement. ;-)
@SpellsOfTruth
@SpellsOfTruth 2 жыл бұрын
While I understand that Terry Davis is very controversial, the one thing he was spot on about was "A nword came up with this idea... std::cout
@WhatsACreel
@WhatsACreel 2 жыл бұрын
I'd have to agree. It's all overriding the shift operators. Very odd syntax, really. I don't know a lot about Terry Davis, I have heard of his Temple OS, certainly an interesting story, I imagine he had a great many fantastic ideas! Cheers for watching, and have a good one :)
@SpellsOfTruth
@SpellsOfTruth 2 жыл бұрын
@@WhatsACreel Thanks for the interesting videos!
@VACatholic
@VACatholic 2 жыл бұрын
For your first example, if you're #include ing the header file from outside, you can, in your main function, do #define private public, and then you can just access the members.
@bbowling4979
@bbowling4979 2 жыл бұрын
That sir is just pure evil and ingenious and I love it!
@arthurmoore9488
@arthurmoore9488 2 жыл бұрын
@@bbowling4979 Reason number whatever that C++ is trying to go towards modules. No more #defines from random headers cluttering up code, and none of this nonsense either. Next thing you know you'll be doing `#define BEGIN {` and `#define END }`. Also, if I am ever asked to work on a code base and find this I will immediately ask for a raise!
@bbowling4979
@bbowling4979 2 жыл бұрын
@@arthurmoore9488 That should count as either hazardous pay or cruel and unusual punishment!
@devnull1013
@devnull1013 2 жыл бұрын
#define true false
@IndellableHatesHandles
@IndellableHatesHandles 2 жыл бұрын
It's still better than no typing at all or optional typing. Languages like Python and JS are nightmares to code in when compared with typed languages. I didn't realize that until I started using C# and C to program things. Having the peace of mind of knowing that if something compiles, it'll run, unless you did something careless with memory management is priceless as a more advanced programmer. Hopefully, the game dev industry moves towards Rust at some point so that us game devs can be even more at ease while coding.
@thatcrockpot1530
@thatcrockpot1530 2 жыл бұрын
Cool vid! I'd love to see more of this.
@stephenpaul7499
@stephenpaul7499 2 жыл бұрын
Your illustration skills are impressive 24:48
@wfjhDUI
@wfjhDUI 2 жыл бұрын
I'm not 100% who the target audience is for a talk like this but an interesting motivating exercise might be going in the other direction and showing one would implement object-oriented programming in C++ if keywords `struct` and `class` didn't exist.
@CPP_malloc
@CPP_malloc 2 жыл бұрын
Would be nice, if you start making full series on a complete topic like developing an engine for Ray tracing, or OS development series.
@WhatsACreel
@WhatsACreel 2 жыл бұрын
Thanks for the suggestion. I do have some larger projects in the works. But I probably won't show much about the backend. It's just not very interesting. Forms coding and buttons and that. I would love to share the interesting parts of those projects at some point, tho. Well, we'll see how we go. Thanks for watching :)
@williamdrum9899
@williamdrum9899 2 жыл бұрын
That would be cool. I don't understand how 3D polygons work at all
@sagivalia5041
@sagivalia5041 2 жыл бұрын
OS development or even driver development sounds really interesting
@jokerpb4778
@jokerpb4778 2 жыл бұрын
Yeah ray tracing would be great
@ricos1497
@ricos1497 2 жыл бұрын
@@jokerpb4778 Ray Tracing would be a fantastic name for a super hero.
@hhurtta
@hhurtta 2 жыл бұрын
So the title should actually be: How to Break C++ OOP with Dirty Rotten Low Down tricks? Great video though, loved it!
@raptoress6131
@raptoress6131 2 жыл бұрын
I feel like I'm getting in on some juicy secrets.
@WhatsACreel
@WhatsACreel 2 жыл бұрын
It's definitely fun to look at how C++ does things! Marvelous language!! Cheers for watching :)
@CjqNslXUcM
@CjqNslXUcM Жыл бұрын
Did anyone actually think there was some kind of special, magical way the CPU understands objects? I always thought it was just a weird abstraction.
@Fine_Mouche
@Fine_Mouche 2 жыл бұрын
In whichs langages we can fusionne/merge / [inherit from] 2 uppers/parents classes ? Because in most OOP, you can inherit from only one.
@cemlynwaters5457
@cemlynwaters5457 Жыл бұрын
This is a fantastic video!
@bradmccoy1747
@bradmccoy1747 2 жыл бұрын
I like the content; I might have to watch this again. I have a version of assembler that I have been creating for a few years now and I added some basic OOP to it this past winter. I didn't know how everyone else was doing it so I just made it up. And it turns out that I created a block of instance data variables and stored them in a section within the exe separate from the static data. Then when a new instance gets created the default instance data is copied into the upper memory instance block. But I did not do any vtable logic whatsoever in my version. The function calls are all statically linked to the code space where the function rests. Yes - it works at the simple level. But without vtables - i doubt that it can do any of that fancy polymorphic inheritance. Maybe next winter I will add the vtable concept and expand it. But this video gives me a great indicator that I am on the right track and also lays out what I may do next. Thanks for the under the hood examination!
@tornoutlaw
@tornoutlaw 2 жыл бұрын
Minor correction, the "fast inverse square root" you cite as an example for type punning was implemented in Quake 3 Arena, I think, not Quake 4.
@WhatsACreel
@WhatsACreel 2 жыл бұрын
Oh, you are right!!! Times like this I wish we still had annotations :( Hahaha, well spied :)
@Manuel-j3q
@Manuel-j3q 2 жыл бұрын
Was about to write same thing Thank you Ctrl + F
@shk253
@shk253 2 жыл бұрын
The master himself, from the land of sunshine and snakes!
@WhatsACreel
@WhatsACreel 2 жыл бұрын
And echidnas! I saw one yesterday hahaha! Sooooo cool! Thanks for watching :)
@DominikGuzowski
@DominikGuzowski 2 жыл бұрын
I had Dr. Brady for Microprocessor Systems. He's a huge fan of talking about weather if I do say so myself, and also hating on Windows 😂
@frozendude707
@frozendude707 2 жыл бұрын
12:28 Geez mate, have furry pron reference memes become so popular that even you reference them? 🤣 I guess you are a man of much and many a culture. Thanks for the vid, great as always!
@EER0000
@EER0000 2 жыл бұрын
Great video, thanks a lot!
@sakari_n
@sakari_n 2 жыл бұрын
There is one additional difference with normal C functions and member methods in C++ that you did not mention. The implicit pointer that the compiler adds for the address of the class object is passed differently than any other parameter in some other platforms like Win32 x86 (32 bit). The this pointer is passed in different register than the normal C function parameters. I am not sure would you consider unions type punning , but C/C++ union give a good basis for creating discriminated unions out of them. Discriminated unions probably the best way to do polymorphic data. Although discriminated unions have overlapping data of different types they are not a bad practice and I use them in every big C or C++ project I have worked on, like yesterday both at the project I worked on at work and on another project I worked on at home. Discriminated unions are very nice and I will use them later today. Oh and also there is this trick for Visual Studio if you want to inspect some function in release mode. You can use __declspec(noinline) to tell the compiler to not inline a function, but still your function may have interesting optimizations that only work with the known parameters for the function.
@elliott8175
@elliott8175 2 жыл бұрын
He did talk about "this" being in a different register, and that which one depends on the OS. Type punning with unions is defined behavior in C, but sadly not in C++.
@sakari_n
@sakari_n 2 жыл бұрын
@@elliott8175 Hmm. I see. Looks like I missed that part about this register. But still even if unions are not being well defined in C++ they can be safely used since C++ compiler that would not work properly with unions would be useless compiler. And language specs are kinda over rated anyway. I used to spergout about that stuff, but really languages are a tool for getting a job done. Getting too hung up on the manual for the tool is waste of time. The compiler vendors have to make compilers that work, so even if they could technically break something that is used on current software they do not. And in the end if some compiler vendor would make the nonsensical decision to break software intentionally you can just move you code into a C file and compile it with the rest of the C++ code.
@elliott8175
@elliott8175 2 жыл бұрын
@@sakari_n I agree that what the compilers actually do trumps what the standard says that they should. But in practice I think this more applies to compiler bugs. Sometimes there's a terrible problem with one compiler that they don't fix for a while, so you end up avoiding nicer syntax to make your code more cross-platform and backward compatible - even though the standard says it should be fine. The other way around though... there's always a way to do it that doesn't break the standards (and is still efficient). I don't do type punning anymore (I was going to say "hardly ever", but I really never do it at all), but for people who might need that level of control, you can safely cast to a char* (it think they introduced bit_fields or something for this in c++20 to make it a bit nicer). Also, a compiler that doesn't support type punning with unions doesn't make unions useless for that compiler - unions are for occupying less memory when you have multiple data fields that's always "xor". That was their main purpose.
@sakari_n
@sakari_n 2 жыл бұрын
@@elliott8175 Yeah the point of my comment was that what the compiler does is more important than the standard and the funnily worded thing about compiler being useless if they do not support unions. Is about the same thing there are unions everywhere in old code bases of lot of software and if a compiler would not support unions all of these code bases would be uncompilable with that compiler. Anyway about the C/C++ discriminated unions and syntax. I think using discriminated unions gives you clearer and much easier to understand code then using inheritance with C++ classes. And the longer I have programmed the more I think clear and easy to understand code has become more and more important thing to try to do.
@PlaXer
@PlaXer 6 ай бұрын
first day I that learned about pointers I finally understood what the "self" parameter was for
@skyeplus
@skyeplus 2 жыл бұрын
Couple of notes on private member access and type punning: 1. C++ is allowed by the standard to optimize out variables of the class which are not in use. If you get a pointer to a member of the class as an argument, e.g. in a function void foo(int* x) {...}, and at the call place you have foo(&a->x), then the rest of object `a` may not exist at all. You'll have a UB if you calculate pointer of `a` from the pointer of `x` and attempt to access members other than `x`. 2. Const members could be optimized out by compiler, e.g. compiler might inline values of const value where it's used. Attempts to monkey-patch them might fail in peculiar ways. See std::launder on how to reinitialize memory for objects with const members with use of placement `new`.
@kzinti2
@kzinti2 2 жыл бұрын
1) No, the C++ compiler is not allowed to remove variables from your class or structures even if it can prove the they are not in use. What's interesting is that I don't think it is even possible for a compiler to prove such a thing. 2) const members can not be optimized out. Same as #1: the compiler it not allowed to change your classes/structures. A const variable inside a function could be optimized out, but this is also true of non-const variables. In this context, const doesn't do anything. const is really just a compile-time tool helping the programmer to catch programming errors. It doesn't impact code generation in any way (and cannot do so for various reasons).
Object-Oriented Programming is Embarrassing: 4 Short Examples
28:03
Brian Will
Рет қаралды 2,1 МЛН
We Attempted The Impossible 😱
00:54
Topper Guild
Рет қаралды 56 МЛН
REAL or FAKE? #beatbox #tiktok
01:03
BeatboxJCOP
Рет қаралды 18 МЛН
How Strong Is Tape?
00:24
Stokes Twins
Рет қаралды 96 МЛН
Emulating a CPU in C++ (6502)
52:28
Dave Poo
Рет қаралды 1 МЛН
Master Pointers in C:  10X Your C Coding!
14:12
Dave's Garage
Рет қаралды 334 М.
Object Oriented Programming vs Functional Programming
18:55
Continuous Delivery
Рет қаралды 763 М.
The Flaws of Inheritance
10:01
CodeAesthetic
Рет қаралды 979 М.
Top 10 Craziest Assembly Language Instructions
15:19
Creel
Рет қаралды 462 М.
31 nooby C++ habits you need to ditch
16:18
mCoding
Рет қаралды 839 М.
Naming Things in Code
7:25
CodeAesthetic
Рет қаралды 2,3 МЛН
Assembly Language Misconceptions
18:13
Creel
Рет қаралды 105 М.