【逆向】逆向练习及相关总结

article/2025/11/2 9:33:03

文章目录

  • crakeme练习
    • crackme1
    • crackme2
    • crackme3
  • 解题步骤
  • 总结
    • 关键代码查找方法
    • 常见代码
    • C++类对象逆向分析
    • C++虚函数逆向分析
    • 系统dll文件的指令
    • kernel32.dll、user32.dll、ntdll.dll文件
    • TEB、PEB

crakeme练习

crackme1

学到的知识点:

  1. main函数查找方法:运行到EntryPoint -> 第一个call(一般在第三行) -> 第二个call(一般在第四行) -> 向下翻,找push,add,mov的三个连续call位置,选中间的call -> 找一群int3的位置上面的call。此时就进入了主函数。
  2. VS防止栈内存溢出的安全机制代码(一般放在函数开头):
push ebp
mov ebp,esp
sub esp,1C
mov eax,dword ptr ds:[448004]
xor eax,ebp
mov dword ptr ss:[ebp-4],eax
  1. [ebp-xxx]:一般表示局部变量;[ebp+xxx]:一般表示函数参数

第一个参数:[ebp+0x8]
第二个参数:[ebp+0xC]

  1. add esp, xxx:一般在call函数调用完之后,用于堆栈平衡,xxx一般为【参数所占字节数】,可以根据xxx的大小判断函数传入的参数的个数

注意:xxx要用16进制

crackme2

学到的知识点:

  1. 可以根据程序运行的情况(关键API)来下断点,然后返回到上层调用,从而找到关键代码。

例如:bp MessageBoxA;或者在od上按ctrl+G

crackme3

思路:

  1. 按照常用思路找到主函数
  2. 发现第一个关键函数:
    在这里插入图片描述经过分析发现该函数是用来判断输入的字符串是否是在’0’和’9’之间的字符
  3. 经过第一个关键函数,如果输入了0-9之外的字符,那么eax就会被设置为0,从而直接跳转到失败。如果输入的都是0-9之间的字符串,那么eax就会被设置为1,继续执行下面的代码。
  4. 发现第二个关键函数:
    在这里插入图片描述 进入查看这个函数的具体内容,发现以下代码段:
    在这里插入图片描述
    经过分析发现这段代码是将输入的字符当作数字依次与5异或,得出的结果为"16716724"。
  5. 这样就明确了思路,需要输入一段数字,使得每个数字与5异或之后,要和"16716724"这段数字对应相等:
    写出如下python脚本:
xor_string = '16716724'
xor_num = 5
int_list = [int(i) for i in xor_string]
result_str = ''
for num in int_list:result_str += str(num ^ xor_num)
print(result_str)
  1. 得到payload=43243271

解题步骤

  1. 运行程序,观察程序功能
  2. 明确目标
  3. 找到关键代码的位置:
    • 根据字符串查找
    • 根据程序运行时的特征,比如程序运行时弹出了一个MessageBoxA窗口,那么就可以在所有的MessageBoxA位置下断点:bp MessageBoxA。然后返回到上层调用,从而找到关键代码。

总结

关键代码查找方法

  • main函数查找方法:运行到EntryPoint -> 第一个call(一般在第三行) -> 第二个call(一般在第四行) -> 向下翻,找push,add,mov的三个call位置,选中间的call -> 找一群int3的位置上面的call。此时就进入了主函数。
  • 可以根据程序运行的情况(关键API)来下断点,然后返回到上层调用,从而找到关键代码。

例如:bp MessageBoxA;或者在od上按ctrl+G

常见代码

  • VS防止栈内存溢出的安全机制代码(一般放在函数开头):
push ebp
mov ebp,esp
sub esp,1C
mov eax,dword ptr ds:[448004]
xor eax,ebp
mov dword ptr ss:[ebp-4],eax
  • [ebp-xxx]:一般表示局部变量;[ebp+xxx]:一般表示函数参数

第一个参数:[ebp+0x8]
第二个参数:[ebp+0xC]

  • add esp, xxx:一般在call函数调用完之后,用于堆栈平衡,xxx一般为【参数所占字节数】,可以根据xxx的大小判断函数传入的参数的个数

注意:xxx要用16进制

C++类对象逆向分析

#include<stdio.h>
class Base {
public:Base() {printf("Base::Base()\n");}
};class Child : public Base {
public:Child() {printf("Child::Child()\n");}
};int main() {Child child;return 0;
}
  • this指针的构造
    在这里插入图片描述
    在这里插入图片描述
  1. 首先定义child,然后将child对象的地址放到ecx中
  2. 然后进入Child类中,将ecx的内容放到this指针中
  • 子类中调用父类构造函数的方法
    在这里插入图片描述

子类会在自己的构造函数内部添加一段call父类构造函数的代码,添加该代码的位置为子类构造函数中的所有命令前

#include<stdio.h>
class Base {
public:~Base() {printf("Base::~Base()\n");}
};class Child : public Base {
public:~Child() {printf("Child::~Child()\n");}
};int main() {Child child;return 0;
}
  • 子类中调用父类析构函数的方法
    在这里插入图片描述

子类会在自己的析构函数内部添加一段call父类析构函数的代码,添加该代码的位置为子类析构函数中的所有命令后

  • 当编译器认为构造函数是不必要(没有执行指令)的时候,它是不会创建构造函数的
#include<stdio.h>
class CObj {
private:int a = 1;int b = 2;
public:void set(int n1, int n2) {a = n1;b = n2;printf("set(int a, int b)\n");}
};int main() {CObj obj;obj.set(20, 30);return 0;
}
  • 类中成员变量及其赋值方法
    在这里插入图片描述
  1. 首先将obj的地址通过ecx赋值给this指针([this]),然后在传给eax
  2. 然后把1赋值给[eax],即源代码中的int a=1
  3. 再把2赋值给[eax+4],即源代码中的int b=2
  • 调用成员函数修改成员变量
    在这里插入图片描述
    在这里插入图片描述
  1. 将参数传入
  2. 执行成员函数,首先将[this](对象的地址)赋值给eax
  3. 再将参数n1赋值给ecx
  4. 然后将ecx放到[eax]中,即对象的内存中
  5. b = n2同理

总结:

  • 构造函数调用过程:先调用子类构造函数,进入子类构造函数内部,先去调用父类的构造函数,之后再去执行自己的代码
  • 析构函数调用过程,先调用子类析构函数,进入子类析构函数内部,先去执行自己的代码,之后再去调用父类的析构函数
  • 函数成员变量的访问:都是通过this指针+偏移的形式去访问。调用成员函数的时候,编译器会默认传this指针,放到我们的ecx寄存器。

C++虚函数逆向分析

#include <stdio.h>
#include <Windows.h>class CObj
{
public:int a = 1;virtual void show(){printf("虚函数show\n");}void fun1() {printf("成员函数fun1\n");}
};void show2()
{printf("外部函数show2\n");
}int main() {CObj obj;CObj* pObj = &obj;pObj->show();return 0;
}

虚函数会在对象的首地址(低地址)存放一个虚函数表指针(虚函数表用于存放所有的虚函数地址),该指针存放的是虚函数表的首地址。

  • 执行虚函数的过程
    在这里插入图片描述
  1. 将[pObj]的值(即Obj对象的地址)存放到eax中
  2. 去除Obj对象的前四个字节放到edx中,即将虚函数表指针放到edx中
  3. 将[edx]的值(虚函数表的前四个字节,即第一个虚函数的地址)存放到eax中
  4. call eax,即调用虚函数
  • 根据虚函数调用的原理,将外部函数show2来替换虚函数show,即要使得pObj->show()调用show2函数。修改代码如下:
#include <stdio.h>
#include <Windows.h>class CObj
{
public:int a = 1;virtual void show(){printf("虚函数show\n");}void fun1() {printf("成员函数fun1\n");}
};void show2()
{printf("外部函数show2\n");
}int main()
{DWORD OldProtect = 0;LPVOID Addr = 0;CObj obj;CObj* pObj = &obj;// 获取虚函数表__asm{mov eax, dword ptr[pObj]; //取对象首地址mov eax, [eax]; //取虚函数表指针,放到eax中mov Addr, eax; // Addr存放虚函数表指针push eax; //保存eax,防止VirtualProtect改变eax}if (Addr) VirtualProtect(Addr, 0x4, PAGE_READWRITE, &OldProtect);// 修改[eax]的读写权限,使得[eax]的内容可以更改__asm{pop eaxmov edx, show2;		//将show2函数的地址存放到edx中mov [eax], edx; //将show2函数地址替换虚函数表中的第一个虚函数(即show函数)}if (Addr) VirtualProtect(Addr, 0x4, OldProtect, &OldProtect); // 将读写权限修改回来pObj->show();return 0;
}

系统dll文件的指令

系统dll文件加载的地址一般都是0x7xxxxxxx。

kernel32.dll、user32.dll、ntdll.dll文件

  • kernel32.dll:所有进程无论窗口还是控制台程序,都会引用kernel32.dll
  • user32.dll:窗口程序专用,封装了所有跟窗口操作相关的API
  • ntdll.dll:无论是kernel32.dll还是user32.dll文件都会去调用ntdll.dll

TEB、PEB

  • TEB:线程环境块,说白了就是一个结构体,该结构体保存了线程中的各种信息

  • PEB:进程环境块,存放进程相关信息

    结论:

    • FS:TEB
    • FS:[0x30] == PEB // 其中保存了LDR的结构体
    • FS:[0x3c] == LDR // 其中保存了InitalizationOrderModuleList指针的结构体
    • FS:[0x3c+0x1c] == InitalizationOrderModuleList //初始化排序的dll

    指令:

    mov esi, FS:[0x30] // PEB地址
    mov esi, [esi+0xc] // LDR地址
    mov esi, [esi+0x1c] // InitalizationOrderModulelist地址

    InitalizationOrderModulelist结构体:

    struct _LIST_ENTRY{_LIST_ENTRY *Flink; // 下一个结构体指针_LIST_ENTRY *Blink; // 上一个结构体指针
    }
    

    InitalizationOrderModulelist保存的dll文件顺序:第一个是ntdll.dll,第二个是kernel32.dll或者kernelbase.dll

    mov esi, [esi] // Flink,第二个dll文件的信息


http://chatgpt.dhexx.cn/article/jummEch4.shtml

相关文章

JavaScript-js数组去重

1&#xff0c;利用Set()方法 let list [1,2,1,2,4,4,5] let Newlist Array.from(new Set(list)) console.log(Newlist)//输出[ 1, 2, 4, 5 ]2&#xff0c;新建一个数组&#xff0c;逐一保存原数组中的值&#xff0c;判断新数组中是否已有该数值&#xff0c;无则保存&#xff…

常用的JS数组去重方法大全

写在前面&#xff1a; 我们要想使用数组去重&#xff0c;那就必须对数组有一定的了解&#xff0c;关于JS数组的一些方法以及使用&#xff0c;可参考&#xff1a; ①JavaScript 内置对象之-Array ②ES5新增数组方法 ③浅谈JavaScript中ES6新增的Set和Map结构以及Array.from方法 …

js去重都有哪些方法?

1、去重方法一 arr.splice 2、去重方法二 借助新数组&#xff0c;判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中(原数组长度不变但被按字符串顺序排序) 3、创建一个新数组&#xff0c;判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中 4、借助i…

JS中数组去重的五种方法

数组去重的几种方法(JavaScript版&#xff09; 前言&#xff1a;你需要知道JavaScript中的 splice函数 的用法 splice函数介绍&#xff1a; splice函数用法&#xff1a;splice函数介绍看不懂没关系&#xff0c;怎么用一看例子一目了然 第一种&#xff1a;双重for循环去重 原…

js数组中对象去重的方法

一个数组中含有对象&#xff0c;并且去除数组中重复的对象 id相同的&#xff0c;保留第一个&#xff0c;其它的删除 let arr [{ id: 0, name: "张三" },{ id: 1, name: "李四" },{ id: 2, name: "王五" },{ id: 3, name: "赵六" },{…

js实现数组去重的方式(7种)

目录 JS数组去重的方式1.利用Set()Array.from()2.利用两层循环数组的splice方法3.利用数组的indexOf方法4.利用数组的includes方法5.利用数组的filter()indexOf()6.利用Map()7.利用对象 JS数组去重的方式 例&#xff1a;将下面数组去除重复元素&#xff08;以多种数据类型为例…

js数组去重(9种方法),你都会了吗?

以下共有九种数组去重的方式和详解&#xff08;包含对象数组去重&#xff09;&#xff1a; 1.利用Array.from(new Set)去重&#xff1a; // 1.利用set去重 // Set是es6新增的数据结构&#xff0c;似于数组&#xff0c;但它的一大特性就是所有元素都是唯一的&#xff0c;没有…

解决Linux没有ens33

#临时关闭 systemctl stop NetworkManager # 永久关闭网络管理命令 systemctl disable NetworkManager #开启网络服务 systemctl start network.service

【ubuntu虚拟机】ens33未出现在ifconfig问题

事情发生与2023年4月12日&#xff0c;windows上安装了docker-desktop&#xff0c;奈何wsl不好用&#xff0c;便卸载了&#xff0c;之后我的虚拟机ubuntu无法联网&#xff0c;于是开始解决之旅 事故原因 ifconfig查了一下&#xff0c;没有ens33网卡&#xff0c;于是用ip addres…

Centos开机后ens33网卡失效连不上网

今天使用xshell连接虚拟机一直失败 打开虚拟机ip addr 发现如下问题 解决方案 1.尝试重启网关 ifup ens33 &#xff08;无法解决&#xff09; # ifup ens332.停止网卡&#xff0c;设置disable后重新启动 &#xff08;依旧失败&#xff09; # systemctl stop NetworkManager…

centos7网络配置没有ens33文件

注意&#xff1a;此教程中的centos是已经能够上网的状态&#xff01; 问题来源 需要使用centos7来搭建一些服务器&#xff0c;由于我用的VMware总是自己变ip并且不能设置&#xff0c;所以只能从centos系统配置文件下手 。 从网上教程来看&#xff0c;centos7 的网卡名叫 ens33&…

Linux 初始化网络配置ens33

配置文件路径&#xff1a; [rootnode2 ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno #IPV6INITyes #IPV6_AUTOCONFyes #IPV6_DEFROUTEyes #IPV6_PEERDNSyes #IPV6_PEERROUTESyes #IPV6_FAILURE_FATALno…

怎么修改Linux网络名为ens33,Centos7更改ens33网卡名称为eth0

用ifconfig查看网卡名称为ens33&#xff0c;现在要将它修改成eth0 [rootlocalhost ~]# ifconfig ens33: flags4163 mtu 1500 inet 192.168.1.3 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 2409:8a02:9014:8440:20c:29ff:fe95:cc8f prefixlen 64 scopeid 0x0 inet6 f…

linux怎么修改ens33文件,linux 修改centos7的网卡ens33修改为eth0

Linux 操作系统的网卡设备的传统命名方式是 eth0、eth1、eth2等,而 CentOS7 提供了不同的命名规则,默认是基于固件、拓扑、位置信息来分配。这样做的优点是命名全自动的、可预知的,缺点是比 eth0、wlan0 更难读,比如 ens33 。 如果不习惯使用新的命名规则,可以恢复使用传统…

linux网卡ens33,如何解决Linux 系统下 ifconfig 命令无网络接口 ens33

今天我在做Redis的哨兵集群模式的时候&#xff0c;以前都是好的&#xff0c;也不知道从什么时候开始就无法连接Redis服务器了&#xff0c;就是运行如下命令&#xff0c;没有效果&#xff1a;redis-server redis.conf&#xff0c;然后在通过命令查看redis的状态&#xff0c;始终…

centos找不到ens33

输入这行命令进入配置网络地址&#xff0c;却显示是新文件。 解决方法 先进入前面的文件夹cd /etc/sysconfig/network-scripts/ 然后再ls 发现我们的是ens192 cd ifcfg-ens192进入修改 最后 systemctl restart network 再ip add 查看

Ubuntu20.04 设置ens33网卡

简介&#xff1a;记录一下Ubuntu20.04 设置ens33网卡。起因&#xff1a;在虚拟机启动情况下&#xff0c;直接关闭主机。导致重新启动虚拟机后ens33网卡无法生成ip。 1.进入 /etc/netplan/ 目录 2.修改目录下文件 3.执行命令 netplan apply 再重启执行 ifconfig 就能看到 ens3…

ens33网卡IP地址丢掉了

背景 有的时候&#xff0c;vmware创建centos7的虚拟机上开始用的好好的&#xff0c;后来&#xff0c;ens33的ip地址就不见了。 试图启动ens33&#xff0c;发现无法启动 解决办法 chkconfig NetworkManager off chkconfig network on service NetworkManager stop service ne…

CentOS:ens33没有IP地址

CentOS&#xff1a;ens33没有IP地址 1.环境版本 2.问题异常 启动CentOS操作系统后&#xff0c;经过ip a命令&#xff08;或ifconfig&#xff09;查看无IP地址&#xff1a; 3.解决步骤 root角色修改配置文件&#xff1a; # vi /etc/sysconfig/network-scripts/ifcfg-ens33将O…

linux虚拟机ifcfg-ens33网卡配置,静态IP、网关、子网掩码、DNS地址的配置

使用ifconfig命令显示命令找不到&#xff0c;可以yum install net-tools安装。 1、使用ifconfig查找虚拟机现有的网络配置 其中lo是本地网络&#xff0c;ens33是主要配置的网络 2、查找网络配置所需要的资料&#xff1a;ip地址范围、网关、子网掩码、DNS地址 ip地址范围&…