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);
    }
    

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

    PROFIT…

    ===

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

    Z2l0aHViLmNvbS93b25nZmVpL2FjLXBsdWdpbg==

    aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1SZUg1U0tmUEtTOA==

    Далее автор упоролся по хардкору и решил не просто доработать некую очень полезную программу, а отреверсить ее полностью :D
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 2

      +2
      Я ничего не понял, автор наркоман.
      Скрытый текст
      image

        0

        А это вообще законно?! о_0

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое