PDB — это не только ценный мех

от автора

Представьте себе, вам необходимо доработать некую очень полезную программу без SDK, но по счастливому стечению обстоятельств рядом завалялся PDB файл.

(Беременным и детям не читать!)

Скажу сразу, выход есть (Ваш КО). То что комитет не в состоянии осилить десятками лет (рефлексия не нужна), ужасный M$ разработал/раздобыл 100 лет назад, а именно DIA SDK. В комплекте есть DIA2Dump.exe который порадует глаз любого художника. Остается доработать его напильником…

Для начала нам нужны кошечки.

ООП баян:

#define NOINLINE __declspec(noinline)  class IDrawable { public:    virtual void draw() = 0; };  class Shape : public IDrawable { public:    NOINLINE Shape(int ix, int iy);    NOINLINE virtual ~Shape();    virtual void draw();    inline void setXY(int ix, int iy) { x = ix, y = iy; }    NOINLINE void someWork();    int x;    int y; };  class Circle : public Shape { public:    NOINLINE Circle(int ix, int iy, int ir);    virtual ~Circle();    void draw();    int r; };  Shape::Shape(int ix, int iy) { x = ix; y = iy; } Shape::~Shape() { } void Shape::someWork() { printf("someWork\n"); } void Shape::draw() { }  Circle::Circle(int ix, int iy, int ir): Shape(ix, iy), r(ir) { } Circle::~Circle() { } void Circle::draw() { printf("Circle\n"); }  int main() {     auto c = new Circle(0, 0, 10);     c->draw();     c->someWork();     delete c;      getchar();     return 0; } 

Компилируем -> victim.exe / victim.pdb

Автор своими кривыми ручками немного доработал DIA2Dump (pdb-ripper на гитхабе, Achtung г*вн*код!!!) и теперь он выдает кое-что пригодное к использованию:

DIA2Dump.exe -rip -printCppProxy -m -g -d -rd -names "Shape;Rectangle;Circle" victim.pdb > victim.h 
IDrawable

//UDT: class IDrawable @len=8 @vfcount=1 	//_VTable 	 	//@intro @pure @virtual vtpo=0 vfid=0 @loc=optimized @len=0 @rva=0 	//_Func: public void draw(); 	 	//@loc=optimized @len=0 @rva=0 	//_Func: public void IDrawable(IDrawable * _arg0);  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public void IDrawable(const IDrawable & _arg0);  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public void IDrawable();  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public IDrawable & operator=(IDrawable * _arg0);  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public IDrawable & operator=(const IDrawable & _arg0);  //UDT;  class IDrawable { public: 	void* _vtable; 	 	inline void draw() {  		typedef void (IDrawable::*_fpt)();  		auto _f=xcast<_fpt>(get_vfp(this, 0));  		return (this->*_f)();  	} 	 	inline IDrawable * ctor() { return this; } 	inline void dtor() {} }; 

Shape

//UDT: class Shape @len=16 @vfcount=2 	//_Base: class IDrawable @off=0 @len=8 	 	//@loc=optimized @len=0 @rva=0 	//_Func: public void Shape(const Shape & _arg0);  	 	//@loc=static @len=20 @rva=4208 	//_Func: public void Shape(int ix, int iy);  	 	//@intro @virtual vtpo=0 vfid=1 @loc=static @len=11 @rva=4304 	//_Func: public void ~Shape();  	 	//@virtual vtpo=0 vfid=0 @loc=static @len=3 @rva=4336 	//_Func: public void draw();  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public void setXY(int _arg0, int _arg1);  	 	//@loc=static @len=12 @rva=4320 	//_Func: public void someWork();  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public Shape & operator=(const Shape & _arg0);  	 	//@intro @virtual vtpo=0 vfid=1 @loc=optimized @len=0 @rva=0 	//_Func: public void * __vecDelDtor(unsigned int _arg0);  	 	//_Data: this+0x8, Member, Type: int, x 	//_Data: this+0xC, Member, Type: int, y //UDT;  class Shape : public IDrawable { public: 	int x; 	int y; 	 	inline Shape * ctor(int ix, int iy) {  		typedef Shape * (Shape::*_fpt)(int, int);  		auto _f=xcast<_fpt>(_drva(4208));  		return (this->*_f)(ix, iy);  	} 	 	inline void dtor() {  		typedef void (Shape::*_fpt)();  		auto _f=xcast<_fpt>(get_vfp(this, 1));  		(this->*_f)();  	} 	 	inline void draw_impl() {  		typedef void (Shape::*_fpt)();  		auto _f=xcast<_fpt>(_drva(4336));  		return (this->*_f)();  	} 	 	inline void draw() {  		typedef void (Shape::*_fpt)();  		auto _f=xcast<_fpt>(get_vfp(this, 0));  		return (this->*_f)();  	} 	 	inline void someWork() {  		typedef void (Shape::*_fpt)();  		auto _f=xcast<_fpt>(_drva(4320));  		return (this->*_f)();  	} }; 

Circle

//UDT: class Circle @len=24 @vfcount=2 	//_Base: class Shape @off=0 @len=16 	 	//@loc=optimized @len=0 @rva=0 	//_Func: public void Circle(const Circle & _arg0);  	 	//@loc=static @len=34 @rva=4352 	//_Func: public void Circle(int ix, int iy, int ir);  	 	//@virtual vtpo=0 vfid=1 @loc=optimized @len=0 @rva=0 	//_Func: public void ~Circle();  	 	//@virtual vtpo=0 vfid=0 @loc=static @len=12 @rva=4464 	//_Func: public void draw();  	 	//@loc=optimized @len=0 @rva=0 	//_Func: public Circle & operator=(const Circle & _arg0);  	 	//@intro @virtual vtpo=0 vfid=1 @loc=optimized @len=0 @rva=0 	//_Func: public void * __vecDelDtor(unsigned int _arg0);  	 	//_Data: this+0x10, Member, Type: int, r //UDT;  class Circle : public Shape { public: 	int r; 	 	inline Circle * ctor(int ix, int iy, int ir) {  		typedef Circle * (Circle::*_fpt)(int, int, int);  		auto _f=xcast<_fpt>(_drva(4352));  		return (this->*_f)(ix, iy, ir);  	} 	 	inline void dtor() {  		typedef void (Circle::*_fpt)();  		auto _f=xcast<_fpt>(get_vfp(this, 1));  		(this->*_f)();  	} 	 	inline void draw_impl() {  		typedef void (Circle::*_fpt)();  		auto _f=xcast<_fpt>(_drva(4464));  		return (this->*_f)();  	} 	 	inline void draw() {  		typedef void (Circle::*_fpt)();  		auto _f=xcast<_fpt>(get_vfp(this, 0));  		return (this->*_f)();  	} }; 

Что тут интересного?

//UDT: class IDrawable @len=8 @vfcount=1  //@intro @pure @virtual vtpo=0 vfid=0 @loc=optimized @len=0 @rva=0 //_Func: public void draw(); 	 // @intro --> introducing virtual function (метод объявлен впервые) // @pure virtual --> очевидно // @vtpo --> virtual table pointer offset (крестопроблемы, может быть несколько VT) // @vfid --> virtual function id in VT // @loc=optimized --> вырезано за ненадобностью // @rva=0 --> relative virtual address 

В случае вызова виртуального метода все просто, адрес уже лежит в таблице по индексу метода:

class IDrawable { 	// без VT никуда 	void* _vtable; 	 	// this->_vtable[func_id]() 	inline void draw() {  		typedef void (IDrawable::*_fpt)(); 		auto _f=xcast<_fpt>(get_vfp(this, 0));  		return (this->*_f)();  	} };  // THIS IS SPARTA!!! __forceinline void* get_vtp(void* obj) {  	return *((void**)obj);  } __forceinline void* get_vfp(void* obj, size_t id) {  	return *((void**)((uint8_t*)get_vtp(obj) + id * sizeof(void*)));  } 

Для невиртуальных методов необходимо извлечь RVA из PDB файла и ткнуть носом компилятор:

//UDT: class Shape @len=16 @vfcount=2  //@loc=optimized @len=0 @rva=0 <--- тут ничего не выгорит, компилятор заинлайнил метод //_Func: public void setXY(int _arg0, int _arg1);   //@loc=static @len=12 @rva=4320 <--- ВОТ ОН, ЗДОРОВЕННЫЙ ЯЗЬ //_Func: public void someWork();   class Shape : public IDrawable { 	inline void someWork() {  		typedef void (Shape::*_fpt)();  		auto _f=xcast<_fpt>(_drva(4320));  		return (this->*_f)();  	} }; 

Что такое _drva? Мы знаем только RVA (смещение) функции относительно базы, а нужен полноценный виртуальный адрес:

void* _image_base = GetModuleHandleA(nullptr); // hello DYNAMICBASE  __forceinline void* _drva(size_t off) {  	return ((uint8_t*)_image_base) + off;  } 

Зачем xcast? Методы в плюсах вызываются по __thiscall и необходимо доступно объяснить компилятору кто тут самый умный. По факту кастуем void* в указатель на метод, this передается скрыто первым параметром:

typedef void (Shape::*_fpt)();  auto _f=xcast<_fpt>(_drva(4320));  (this->*_f)(); 		 // NUCLEAR MINEFIELD!!! template<typename TOUT, typename TIN> __forceinline TOUT xcast(TIN in) {     union     {         TIN in;         TOUT out;     }     u = { in };     return u.out; } 

Далее нужно внедрить наш собственный код в процесс victim.exe используя стандартные техники которые тут обсуждаться не будут. Смысл один — некий код будет выполнен в адресном пространстве victim.exe.

void injected_func() { 	void* _image_base = GetModuleHandleA(nullptr); 	Circle* obj = (Circle*)malloc(sizeof(Circle)); 	obj->ctor(0, 0, 999); 	((IDrawable*)obj)->draw(); 	obj->dtor(); 	free(obj); } 

Остается хукнуть удобный метод/функцию где есть доступ к нужным объектам… Или найти глобальные переменные 😀

PROFIT…

===

Как эта фигня используется в реальности:

Z2l0aHViLmNvbS93b25nZmVpL2FjLXBsdWdpbg==

aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1SZUg1U0tmUEtTOA==

Далее автор упоролся по хардкору и решил не просто доработать некую очень полезную программу, а отреверсить ее полностью 😀

ссылка на оригинал статьи https://habr.com/ru/post/507124/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *