听完黑洛大佬的建议之后,突然发现了一个比较严重的问题,C/C++都不了解,怎么可能轻易的去修改或者破解他们的写出来的程序,于是大致浏览了网上的资源,《完美C++》还在路上,先写一点
以下几个概念让我在刚开始学习的时候产生了很大的障碍,后来我决定先把他们啃掉,变成可以可以客串以下c++程序员的java程序员
1.指针
个人认为。理解指针最好的方法其实是CE中那两个找指针的教程,很直接
指针变量保存的是地址,而地址本质上是一个整数,然后作为一名基础很差(几乎都不知道程序是怎么跑起来的,JVM以下一脸懵逼)的java狗,看到这里就懵了
为什么要地址,我们从来都是面向对象
Object obj = new Object();
obj.run();
然后我并不想浪费时间去从正面阐述指针是咋来的是什么个东西,我想的是,为什么我要指针,然后我在知乎看到了这样一条答案
到这里,我想到了jdk8新出的函数指针,当时理解这个东西非常费劲也每太搞懂,现在从这个指针的角度出发来看上面的代码。
一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这个首地址其实就是上面回答中的运单
obj.run()这个过程实际上是让快递小哥拿着运单,把礼物送到运单上的地址
从这里可以看出Java和C++的差别,java在一个函数的运行时,除非利用反射或者jdk8的函数指针的机制或者一些ClassLoader的办法,从语言层面本身是很难去改变这个运单上的地址的。但是C++就不一样了,他可以运用指针去改变这张运单上的地址。这也就引出了另一个关键点,我们只要通过修改运单地址,就能改变程序原本执行的顺序达到我们的目的,这里让我想到了IAT HOOK
关于指针,由于接触的时间和理解问题,我上面的说法是非常粗略甚至可能是错误的,但是对于理解hook流程,应该是有一定帮助的
2.struct(结构体)
下面的大部分都是在看完这里之后的一些笔记
http://c.biancheng.net/view/2235.html
先来看一下c语言的struct
struct Student{
Student(char *name, int age, float score);
void show();
char *m_name;
int m_age;
float m_score;
};
和java的类不同之处在于 struct 只能包含成员变量,不能包含成员函数。而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。
归纳一下就变成
struct = java中没有成员函数的class
我之前以为,结构体是个非常非常神秘的东西,得出上面的结论之后,虽然很片面,但是对于我前期浏览到相关内容的时候,可以让我有一个大概的理解,可以让我顺利的略过他。之后肯定会得到纠正的
3.虚函数
先来看一个造了一百年的轮子,人类和老师类,下面的代码即使是一个java程序员,看起来也是相当舒服的,真的是黑洛说的好像回家的感觉
以下案例来自
http://c.biancheng.net/view/2294.html
侵删
#include <iostream>
using namespace std;
//基类People
class People{
public:
People(char *name, int age);
void display();
protected:
char *m_name;
int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是个无业游民。"<<endl;
}
//派生类Teacher
class Teacher: public People{
public:
Teacher(char *name, int age, int salary);
void display();
private:
int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是一名教师,每月有"<<m_salary<<"元的收入。"<<endl;
}
int main(){
People *p = new People("王志刚", 23);
p -> display();
p = new Teacher("赵宏佳", 45, 8200);
p -> display();
return 0;
}
C++中的派生类其实就是Java中的子类,但是这段代码的输出确是这样的
王志刚今年23岁了,是个无业游民。 赵宏佳今年45岁了,是个无业游民。
这个就很尴尬了,第二行的输出不应该是"赵宏佳今年45岁了,是一名教师,每月有8200元的收入"么。看来,同样的写法,Java会自动根据函数名去重载父类的方法,C++却并不会,难道C++不能重载父类函数么,显然并不是,这个时候就要引入虚函数的概念了
只要该上面的display()函数前面加上*virtual*就可以实现重载
#include <iostream>
using namespace std;
//基类People
class People{
public:
People(char *name, int age);
virtual void display(); //声明为虚函数
protected:
char *m_name;
int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是个无业游民。"<<endl;
}
//派生类Teacher
class Teacher: public People{
public:
Teacher(char *name, int age, int salary);
virtual void display(); //声明为虚函数
private:
int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是一名教师,每月有"<<m_salary<<"元的收入。"<<endl;
}
int main(){
People *p = new People("王志刚", 23);
p -> display();
p = new Teacher("赵宏佳", 45, 8200);
p -> display();
return 0;
}
这时候的输出就是
王志刚今年23岁了,是个无业游民。 赵宏佳今年45岁了,是一名教师,每月有8200元的收入
===2019.3.5更新
写完上面这些之后,我决定hook一下自己的代码,我把char *换成了string,下面是完整的代码
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <string>
using namespace std;
//基类People
class People {
public:
People(string name, int age);
virtual void display(); //声明为虚函数
protected:
string m_name;
int m_age;
};
People::People(string name, int age) : m_name(name), m_age(age) {}
void People::display() {
cout << m_name << "今年" << m_age << "岁了,是个无业游民。" << endl;
}
//派生类Teacher
class Teacher : public People {
public:
Teacher(string name, int age, int salary);
virtual void display(); //声明为虚函数
private:
int m_salary;
};
Teacher::Teacher(string name, int age, int salary) : People(name, age), m_salary(salary) {}
void Teacher::display() {
cout << m_name << "今年" << m_age << "岁了,是一名教师,每月有" << m_salary << "元的收入。" << endl;
}
void getUd(People *peple) {
peple->display();
}
int main() {
cout << "函数地址:" << &getUd << endl;
People *p = new People("王志刚", 23);
getUd(p);
cout << "*p地址:" << p << endl;
string s;
cin >> s;
getUd(p);
p->display();
p = new Teacher("赵宏佳", 45, 8200);
p->display();
return 0;
}
上面的代码中增加了getUd这个函数,这个就是用来hook的
首先,用Detour进行Hook,上面我偷偷打印了函数的地址,然后用注入dll的方法进行hook
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <windows.h>
#include <string>
#include <iostream>
#include "../include/detours.h"
using namespace std;
typedef int (__cdecl *pFunc) (int);//要hook函数的指针,从ida里面复制出来的
pFunc FuncToDetour = (pFunc)(0x011316C7); //设置要hook函数的地址
/*
int __cdecl sub_11316C7(int a1)
{
return sub_1139840(a1);
}
*/
int __cdecl MyFunc_getUd(int i)
{
cout << "get Point";
MessageBox(NULL, "get Point", "msg", MB_OK);
int ret = FuncToDetour(i);
return ret;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)FuncToDetour, MyFunc_getUd);
DetourTransactionCommit();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
/*
这里需要注意的地方是typedef int (__cdecl *pFunc) (int);这个函数原型
*/ 上面的被hook的原型是我直接ida反编译F5弄出来的,在这过程中遇到的障碍是我之前一直想用免费版的IDAPro5,但是他在win10上好像并不能F5,于是我就换成了IDA7.0才弄出来的,直接根据上面偷偷打印的地址跳转过去,然后复制出来。还有在上面的hook函数的时候,如果原型不正确或者没有继续调用原函数FuncToDetour,是会报错的。报错如下
==========小插曲,在找资料的时候,我又发现了一个新的库frida,不知道为啥,一看python就觉得搞这个有戏,官网一查还真有windows的demo。按照我目前的水平,只能以API为单位进行hook,如果要hook任意地址,要么重写DetourAttach的源码,要么直接在代码里面插入难看的如下内容
然后把寄存器都存到变量里面,hook结束再还原回去。这个就有点难受了,看完Frida的Demo我感觉有戏,试了一下代码还真可以,
import frida
import sys
def on_message(message, data):
print("[%s] => %s" % (message, data))
session = frida.attach('ConsoleApplication1.exe')
script = session.create_script('''
var f1 = ptr('0x013816C7');
Interceptor.attach(f1, {
onEnter:function(args){
console.log("hook success",args[0]);//第一个值的指针地址
console.log("registor",this.context.eax);//寄存器内容
}
});
''')
script.on('message', on_message)
script.load()
用OD附加之后观察之后,发现他是在hook的函数头添加了jmp指令并用nop填充。虽然运行起来感觉性能有点慢,可以明显感觉到顿一下(ps:应该不可能是我机器的原因),但是这种方式搞起来还是蛮爽的,瞬间又回到了熟悉的世界
附上看雪的Frida官方手册
https://zhuanlan.kanxue.com/article-352.htm
ps:ida能f5能反编译出伪代码是清风告诉我的,之前没说是他告诉我的被他骂臭不要脸,社会,社会~
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-3-6 10:34
被flameonyou编辑
,原因: 添加hook内容