Are people able to hijack compiled libraries with their own implementations? - c++

I have some legacy code that passes around a pointer to an internal data structure that the library uses to manipulate and check state variables. Thinking about refactoring this code, I became curious if creating a bunch of classes that are meant to only be used internally in my library would cause security concerns. Could others link in their own implementations of these methods? Say I have a Config class that reads an encrypted license and sets settings correctly. Would it be possible to manipulate this class somehow?
Here's some code I wrote to try initial feasibility
shared.cpp
#include "shared.h"
Config::Config() : max_clients()
{
}
Config::~Config()
{
}
int Config::getMaxClients() const
{
return max_clients;
}
void Config::setMaxClients(int num)
{
max_clients = num;
}
I compiled this into a shared library with
g++ -c shared.cpp -fpic
g++ -shared -o libshared.so shared.o
Once compiled we can see what other people can see about our Config class in our library.
$ nm -C libshared.so
0000000000201030 B __bss_start
0000000000201030 b completed.7696
w __cxa_finalize
0000000000000780 t deregister_tm_clones
0000000000000810 t __do_global_dtors_aux
0000000000200e20 t __do_global_dtors_aux_fini_array_entry
0000000000201028 d __dso_handle
0000000000200e58 d _DYNAMIC
0000000000201030 D _edata
0000000000201038 B _end
00000000000008f8 T _fini
0000000000000850 t frame_dummy
0000000000200e18 t __frame_dummy_init_array_entry
0000000000000a50 r __FRAME_END__
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000000910 r __GNU_EH_FRAME_HDR
0000000000000720 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000007c0 t register_tm_clones
0000000000201030 d __TMC_END__
U operator delete(void*, unsigned long)
00000000000008de T Config::setMaxClients(int)
000000000000085a T Config::Config()
000000000000085a T Config::Config()
00000000000008a0 T Config::~Config()
0000000000000882 T Config::~Config()
0000000000000882 T Config::~Config()
00000000000008cc T Config::getMaxClients() const
0000000000200e48 V typeinfo for Config
0000000000000908 V typeinfo name for Config
0000000000200e28 V vtable for Config
U vtable for __cxxabiv1::__class_type_info
There's probably a lot more analysis I don't know about here.
mal.cpp
With this information we could guess a bit about return types on setMaxClients and create our own implementation.
#include <iostream>
class Config {
public:
Config() {}
void setMaxClients(int num);
};
void Config::setMaxClients(int num)
{
std::cout << "do something bad" << std::endl;
}
I compiled this file with
g++ -c mal.cpp -fpic
g++ -shared -o libmal.so mal.o
main.cpp
class Config {
public:
Config();
virtual ~Config();
int getMaxClients();
void setMaxClients(int num);
};
int main()
{
Config config;
config.setMaxClients(4);
return 0;
}
Finally, I compiled main.cpp with linking my mal library first then shared like so
g++ main.cpp -lmal -lshared
This produced a binary which when run I get
$ LD_LIBRARY_PATH=. ./a.out
do something bad
*** stack smashing detected ***: <unknown> terminated
[1] 7842 abort (core dumped) LD_LIBRARY_PATH=. ./a.out
Yes it crashed but I'm not more experienced to know where to go next. Is this not a security concern because you wouldn't actually be able to change any of the classes fields since you don't know the member variables? Or is there a concern here that requires best practices in C++?

Your replacement class should ideally be the same size as the original class. The config in main.cpp has a virtual dtor, whereas the one in mal.cpp does not. This will cause the latter to be 8bytes larger (assuming 64bit OS), and will invariably lead to a problem on destruction of the class.
If this is a real concern, a simple next step is to perform a checksum on the dll, and validate that at runtime (quit if checksum does not match). It wont help a great deal for someone who really cares though. If you know enough about asm, you can usually patch a binary exe with some no-ops until your check returns true.

Related

ldd does not show the dependency which make then needs

I have the program which needs v2xmvtest.so. When i try to build it via make i get undefined reference to *
Seems like that function from libssl1.0. (If i install it, it's built fine)
But i do not see the place where these function are used. More than that, when i try ldd v2xmvtest.so it does show only libvssl1.1 dependency.
Summary:
Is there a way to find out where those finctions from libvssl1.0 are used in the program ? (i have source code of the v2xmvtest.so and try to search, but there no any of these)
I need a description why ldd does not show me libssl1.0 dependency, but during linkning it's needed
Thank you!
Libraries are not required to fully define used symbols. For instance you can have lib.cpp:
int foo();
int bar(int x)
{
return x + foo();
}
and it compiles even foo is actually not defined yet:
g++ lib.cpp -shared -o lib.so
Then you can have main.cpp:
#include <iostream>
int bar(int);
int foo()
{
return 10;
}
int main()
{
std::cout << bar(42) << std::endl;
}
which defines foo and compiles successfully with lib.so:
g++ main.cpp lib.so -o main
The foo function in lib.so is expected just to be provided when the application is linked and is not specified where exactly from, even not necessary from a library.
You can check for undefined symbols in a library using nm tool:
nm -C -u lib.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U foo()
w __cxa_finalize##GLIBC_2.17
w __gmon_start__
And finally you can force gcc to ensure no undefined symbols are used by the object files by compiling with -Wl,-z,defs linker flag:
g++ lib.cpp -shared -Wl,-z,defs -o lib.so
...
lib.cpp:(.text+0x24): undefined reference to `foo()'

What is nm telling me regarding the vtable in this program compiled with gcc on Linux?

I changed a Base class to be abstract (i.e. I made one of its methods pure virtual) and recompiled it. When I went to link it with the derived class, the linker complained about the vtable. I investigated things with nm, but I am not sure exactly what nm was telling me. I fixed things simply by removing the *.o files and recompiling the Derived class, but I would like to understand what exactly was going on with the vtable here.
My original code was this:
Base.h
class Base {
public:
virtual void doSomething();
};
Base.cpp
#include <iostream>
#include <Base.h>
void Base::doSomething() {
std::cout << "Base::doSomething()" << "\n";
}
Derived.h
#include <Base.h>
class Derived : public Base {
public:
Derived();
void doSomething() override;
};
Derived.cpp
#include <iostream>
#include <Derived.h>
Derived::Derived() {
std::cout << "Derived::Derived() constructor" << "\n";
}
void Derived::doSomething() {
std::cout << "Derived::doSomething()" << "\n";
}
The Makefile contained the following:
CXXFLAGS = -std=c++14 -pedantic -Wall -Werror -I ./
default: build
clean:
rm -f proggy
rm -f *.o
proggy: proggy.o Base.o Derived.o
g++ -o proggy proggy.o Base.o Derived.o
Base.o: Base.cpp Base.h
Derived.o: Derived.cpp Derived.h
I then ran make and all was well. For the record, I ran nm at this point too, as follows:
$ nm -C Derived.o | grep Base
00000000 W Base::Base()
00000000 W Base::Base()
00000000 n Base::Base()
U typeinfo for Base
U vtable for Base
where I see that typeinfo for Base is undefined, but it all seems happy enough with this.
Also, in Base.o there is this at this stage:
nm -C Base.o | grep Base
000000d4 t _GLOBAL__sub_I__ZN4Base11doSomethingEv
00000000 T Base::doSomething()
00000044 R typeinfo for Base
0000004c R typeinfo name for Base
00000038 R vtable for Base
I then altered Base.h and Base.cpp as follows to make the class abstract:
Base.h
class Base {
public:
virtual void doSomething() = 0; // pure virtual
};
Base.cpp
#include <iostream>
#include <Base.h>
// void Base::doSomething() {
// std::cout << "Base::doSomething()" << "\n";
// }
And then when I ran make I got this error:
$ make proggy
g++ -std=c++14 -pedantic -Wall -Werror -I ./ -c -o Base.o Base.cpp
g++ -o proggy proggy.o Base.o Derived.o
Derived.o:(.rodata+0x5c): undefined reference to `typeinfo for Base'
Derived.o: In function `Base::Base()':
Derived.cpp:(.text._ZN4BaseC2Ev[_ZN4BaseC5Ev]+0x48): undefined reference to `vtable for Base'
collect2: error: ld returned 1 exit status
Makefile:17: recipe for target 'proggy' failed
make: *** [proggy] Error 1
where c++filt tells me the following:
$ c++filt _ZN4BaseC2Ev
Base::Base()
So I then ran nm as follows and it told me this:
$ nm -C Derived.o | grep Base
00000000 W Base::Base()
00000000 W Base::Base()
00000000 n Base::Base()
U typeinfo for Base
U vtable for Base
I can see that typeinfo for Base is undefined, but then it was at the very start too, so I thought this was not a concern, but it is. Note also that there was no mention of Base in Base.o now.
$ nm -C Base.o | grep Base
i.e. nothing was found by this nm and grep command.
Finally, I removed all the *.o files and ran make again and all was well again. I then ran nm to see what nm reports and it reports the following:
$ nm -C Derived.o | grep Base
00000000 W Base::Base()
00000000 W Base::Base()
00000000 n Base::Base()
00000000 V typeinfo for Base
00000000 V typeinfo name for Base
00000000 V vtable for Base
What does this all mean?
My questions are:
What is nm telling me here and what went wrong earlier with the
vtable?
What did derived need that it didn't have?
Why did recompiling the derived class fix things and what did this
fix regarding the vtable?
From the log
$ make proggy
g++ -std=c++14 -pedantic -Wall -Werror -I ./ -c -o Base.o Base.cpp
g++ -o proggy proggy.o Base.o Derived.o
It means your makefile does not re-build Derived.o, and only re-build Base.o.
This of course causes the issue.
You need to fix the Makefile to add proper dependency for Derive.cpp to Base.h.
The vtable stores the addresses of the implemented virtual methods. If all methods of a class are pure-virtual and none are implemented, then no vtable needs to be generated yet*, because there is no way to instantiate such a class by itself (in debug mode the vtable may still be generated, pointing everything to a trap function).
When you compile Derived.cpp with Base.h having a non-pure virtual function, it references the vtable of Base.
When you subsequently change Base.h to have only pure virtual functions and rebuild Base.o, the vtable from Base.o disappears. At this point you need to rebuild Derived.o, otherwise it will keep on referencing the non-existing vtable.
When you rebuild Derived.o, the compiler sees that Base is a pure-virtual class and generates a vtable for it in Derived.o itself because it knows there isn't one in Base.o.
Another potential issue arises after reordering virtual functions in the base class. Then derived classes, if not rebuilt, can end up invoking the wrong functions in their parent class.
That's why it is important to get the dependency chain right to make sure dependent object files are rebuilt when necessary.
Derived.o: Derived.cpp Derived.h Base.h
* the gory details are compiler-dependent but the way GCC does it is: since it's impossible to instantiate a pure-virtual class, the vtable generation is actually postponed until there is at least one implementation, because only then it is actually possible to have an instance of the class. So the vtable is generated with every derived implementation and exported as a "weak" object (type V) to allow for potential duplicates to be merged at link time.

No .debug_str found in ELF file?

Im doing a project that works with ELF files. Right now Im using the following as a sample input -
class C {
public:
C();
C(int x, int y);
int getX();
private:
int x;
int y;
};
class SubC : public C {
int z;
};
int f() {return 0;}
C c;
SubC subC;
int i;
double d;
I then run
gcc test.cpp -g -c -o test.o
and I get test.o as expected. I then feed test.o into a library I found called peter-dwarf. My problem is that the library says "no section .debug_str found in test.o"
Am I doing something wrong during compilation? Or is the library not working?
Edit: should have been a -g in there
Use -g in gcc to generate the debug symbols. You may also refer to the documentation of debugging options of gcc here.
The -g alone might not include DWARF information if your system is configured in some way. There is a number of switches related to DWARF specifically, so if -g alone does not work, you may need to go there and mangle with other switches.
Probably you need to compile with debugging information enabled. Try:
gcc -g test.cpp -c -o test.o

Cannot link to shared library

I'm trying to compile a minimal shared library and link to it and have been failing for two hours now. Here is ALL the code:
// rect.h
class Rect{
private:
int width_, height_;
public:
Rect(int width, int height);
int width();
int height();
};
// rect.cpp
#include "rect.h"
Rect::Rect(int width, int height)
:width_(width), height_(height){}
int Rect::width() { return width_; }
int Rect::height() { return height_; }
// client.cpp
#include "rect.h"
#include <iostream>
int main() {
std::cout << Rect(1,2).width();
return 0;
}
And this is how I try to compile it:
$ g++ -shared -o librect.so rect.cpp
$ g++ -L. -lrect -Wl,-rpath,'.' client.cpp -o client
/tmp/cc0Xe7ms.o: In function `main':
client.cpp:(.text+0x1a): undefined reference to `Rect::Rect(int, int)'
client.cpp:(.text+0x26): undefined reference to `Rect::width()'
collect2: error: ld returned 1 exit status
The library compiles just fine and the Rect class is properly exported from what I can tell:
$ nm -D librect.so
0000000000201028 B __bss_start
w __cxa_finalize
0000000000201028 D _edata
0000000000201030 B _end
0000000000000738 T _fini
w __gmon_start__
00000000000005b8 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000000714 T _ZN4Rect5widthEv
0000000000000724 T _ZN4Rect6heightEv
00000000000006f0 T _ZN4RectC1Eii
00000000000006f0 T _ZN4RectC2Eii
The strangest thing is that this compiles fine and works on my work computer (Kubuntu 12.10 64bit) but fails to link properly on any other machine I've tried (4 in total, all 64-bit Ubuntu/Kubuntu 12.04 and 12.10)
I tried everything I could think of. Passing the verbose option to the linker shows that the librect.so is indeed found successfully.
Does anybody have a clue what the problem might be?
The libraries have to go after the local translation units:
g++ -L. -Wl,-rpath,'.' client.cpp -o client -lrect
# ^^^^^^
It has to do with how unresolved symbols are looked up by the linker; search the internet for a plethora of information on this if you're curious.

Variable dissapears in binary which is available in static lib

I have the problem mentioned. I create an object inside a static lib, it is there when I run nm on the static lib, but when i link the lib to a binary and run nm it has disappeared. I know this problem has been asked before, but I could not find any answers. It is important for me to be able to retain that variable in the binary because we are trying to implement version checks for static libs in a binary. Please help. Thank you. Example code follows:
test.h
#ifndef TEST_H
#define TEST_H
class Test
{
public:
Test();
};
extern Test* gpTest;
#endif
test.cpp
#include "test.h"
Test::Test()
{
gpTest = this;
}
Test test;
main.cpp
#include "test.h"
#include <iostream>
using namespace std;
Test* gpTest = NULL;
int main()
{
return 0;
}
BUILD
g++ -c test.cpp -o test.o
ar cr test.a test.o
g++ main.cpp -o app -L/home/duminda/intest/test.a
nm -C test.a
0000000000000054 t global constructors keyed to _ZN4TestC2Ev
000000000000002b t __static_initialization_and_destruction_0(int, int)
0000000000000016 T Test::Test()
0000000000000000 T Test::Test()
U __gxx_personality_v0
U gpTest
0000000000000000 B test
nm -C app
0000000000600e10 d _DYNAMIC
0000000000600fe8 d _GLOBAL_OFFSET_TABLE_
0000000000400754 t global constructors keyed to gpTest
0000000000400858 R _IO_stdin_used
w _Jv_RegisterClasses
0000000000400717 t __static_initialization_and_destruction_0(int, int)
U std::ios_base::Init::Init()##GLIBCXX_3.4
U std::ios_base::Init::~Init()##GLIBCXX_3.4
0000000000601050 b std::__ioinit
0000000000600df0 d __CTOR_END__
0000000000600de0 d __CTOR_LIST__
0000000000600e00 D __DTOR_END__
0000000000600df8 d __DTOR_LIST__
0000000000400968 r __FRAME_END__
0000000000600e08 d __JCR_END__
0000000000600e08 d __JCR_LIST__
0000000000601038 A __bss_start
U __cxa_atexit##GLIBC_2.2.5
0000000000601028 D __data_start
0000000000400810 t __do_global_ctors_aux
0000000000400670 t __do_global_dtors_aux
0000000000601030 D __dso_handle
w __gmon_start__
U __gxx_personality_v0##CXXABI_1.3
0000000000600ddc d __init_array_end
0000000000600ddc d __init_array_start
0000000000400770 T __libc_csu_fini
0000000000400780 T __libc_csu_init
U __libc_start_main##GLIBC_2.2.5
0000000000601038 A _edata
0000000000601058 A _end
0000000000400848 T _fini
00000000004005a0 T _init
0000000000400620 T _start
000000000040064c t call_gmon_start
0000000000601038 b completed.6096
0000000000601028 W data_start
0000000000601040 b dtor_idx.6098
00000000004006e0 t frame_dummy
0000000000601048 B gpTest
000000000040070c T main
If nothing in the program references an object-file from a library, then there is no reason for the linker to include that object-file in the executable. A linker is designed to pick only those parts from a library that are needed to resolve open dependencies.
When using static linking, the only options are:
Don't use libraries, but pass all the relevant object files explicitly to the linker
Make sure at least one of the object files from the application depends on the relevant object-file from the library.
Perhaps dynamic linking can be of assistance here, but I don't know for sure.
optimizing compiler/linker - you are not using gpTest in main() so it is not added to the code. If you place a reference/use inside the main() function it will reappear.
I found what I was looking for :D. It seems that linker flag --whole-archive is there to make sure all symbols in static lib regardless of whether they are accessed by app or not get into app. Though I still have trouble getting this to work, I guess I am on the right path.