攻防世界 Windows_Reverse1

第一次遇见地址偏移间接操作,记录一下
wp的思路方法不是我的,我只是做记录理思路

题目


分析过程

PE查壳,有一个UPX壳

 用工具脱壳

 IDA看看main函数

两个关键参数:input(v6改名) 和 flag(v4改名)

光看反汇编函数,这两个参数怎么都联系不起来

想着地址说不定连在一起呢?

-00000404 input           db ?

-00000804 flag            db ?

好吧,地址都联系不起来

通过学习才知道有地址偏移间接操作

 

看看main函数的汇编

sub  esp, 804h

esp寄存器存放的是栈指针,栈指针减804h,可以将esp+804h看作ebp基指指针

堆栈指针及相关寄存器
堆栈是操作系统中,最为常见的一种数据结构。严谨的说,堆栈是包括堆和栈两种数据结构的,但是我们通常说堆栈,其实就是指栈。在栈中,最重要的两个指针是 SP(栈指针) 和 BP(基指指针)。

SP(Stack Pointer),栈指针,在 32 位系统中,ESP(Extended SP) 寄存器存放的就是栈指针。在 64 位系统中,表现为 RSP 寄存器。SP 永远指向系统栈最上面一个栈帧的栈顶。所以 SP 是栈顶指针
BP(Base Pointer),基指指针,在 32 位系统中,EBP(Extended BP)寄存器存放的就是基指指针。在 64 位系统中,表现为 RBP 寄存器。BP 指向栈帧的底部,一般称之为栈底指针
上述定义相信你会在大多数博客见到,但是这些指针及寄存器的作用到底是什么呢?SP,指针即地址,存放栈顶指针,目的就是,下一次对栈操作的时候,系统可以及时找到栈的当前位置。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/song_lee/article/details/105297902

 后面的红框可以看作在 基址esp+804h 的基础上 加上中间代码的指令字节长度,其实就是取input和flag

指令字长度=操作码长度+地址码长度(源操作数地址长度和目的操作数地址长度)

汇编指令长度与寻址方式有关,规律或原则如下:

一、没有操作数的指令,指令长度为1个字节

二、操作数只涉及寄存器的的指令,指令长度为2个字节

    如:mov bx,ax

三、操作数涉及内存地址的指令,指令长度为3个字节

    如:mov ax,ds:[bx+si+idata]

四、操作数涉及立即数的指令,指令长度为:寄存器类型+1

    8位寄存器,寄存器类型=1,如:mov al,8;指令长度为2个字节

    16位寄存器,寄存器类型=2,如:mov ax,8;指令长度为3个字节

五、跳转指令,分为2种情况:

1、段内跳转(指令长度为2个字节或3个字节)

jmp指令本身占1个字节

段内短转移,8位位移量占一个字节,加上jmp指令一个字节,整条指令占2个字节

如:jmp short opr

段内近转移,16位位移量占两个字节,加上jmp指令一个字节,整条指令占3个字节

如:jmp near ptr opr

2、段间跳转,指令长度为5个字节

如:jmp dword ptr table[bx][di]

  jmp far ptr opr

  jmp dword ptr opr 

注意:形如“jmp 1234:5678”的汇编指令,是在Debug中使用的汇编指令,汇编编译器并不认识,如果在源程序中使用,那么在编译时便会报错。

input 给了 eax

flag 给了 ecx

 

看看sub_401000函数是否能把两个参数联系起来

有ecx,有eax,可以联系起来

v1 是一个 _BYTE 类型的指针,并且注释指出 ecx 寄存器可能被用作 v1 的存储位置

 

v4 = input - v1

注意,我们是将 &input 传入这个函数,&input表示取input变量的地址

这行代码表示将input的地址减去v1的地址,v4是地址差--->因此v1+v4表示input的地址

 

*v1 = byte_402FF8[(char)v1[v4]];

先看看括号里面 v1[v4] :其实这个可以看作是v1+v4,也就是input的地址——v1不断+1,可以看作是一种遍历input的新操作

数组格式:参照地址[偏移量] 

本质:通过参照地址+偏移量来遍历数组

 再看看括号外面byte_402FF8[...]:可以看作将遍历到的 input中的单个字符的ascii值 作为偏移量在byte_402FF8数组中寻址

 

那看看byte_402FF8数组

 中间还有一些乱七八糟的东西,最后有两个疑似关键的字符串

ASCII表显示字符是从32开始的,可显示字符编号范围是32-126(0x20-0x7E),共95个字符。

我们是将input的单个字符的ascii值作为偏移量,这个ascii值最小从32开始,32之前的都取不到

 敲计算器可得,地址是从0x403018开始,从0x403078结束,也就是说,只查后面两个字符串

注意!写脚本的时候不能把前面丢掉,如果你把前面的丢掉,原本在32索引位置的字符串就变成0索引了,就查不到了

综上,sub_401000函数的作用就是将输入的input单个字符的ascii值作为偏移量,作为偏移量在byte_402FF8数组中寻址,结果给*v1,也就是放到ecx寄存器里,最后给了flag


逆向脚本

 1 key1=[
 2 255, 255, 255, 255, 255, 255, 255, 255,  78, 230,
 3    64, 187, 177,  25, 191,  68, 255, 255, 255, 255,
 4   255, 255, 255, 255, 254, 255, 255, 255,   1,   0,
 5     0,   0, 126, 125, 124, 123, 122, 121, 120, 119,
 6   118, 117, 116, 115, 114, 113, 112, 111, 110, 109,
 7   108, 107, 106, 105, 104, 103, 102, 101, 100,  99,
 8    98,  97,  96,  95,  94,  93,  92,  91,  90,  89,
 9    88,  87,  86,  85,  84,  83,  82,  81,  80,  79,
10    78,  77,  76,  75,  74,  73,  72,  71,  70,  69,
11    68,  67,  66,  65,  64,  63,  62,  61,  60,  59,
12    58,  57,  56,  55,  54,  53,  52,  51,  50,  49,
13    48,  47,  46,  45,  44,  43,  42,  41,  40,  39,
14    38,  37,  36,  35,  34,  33,  32,   0,   0,   0,
15     0,   0
16 ]
17 key2="DDCTF{reverseME}"
18 flag=""
19 for i in key2:
20     flag+=chr(key1[ord(i)])
21 print("flag{"+flag+"}")

flag

 flag{ZZ[JX#,9(9,+9QY!} 

posted @ 2024-04-17 15:37  demo41  阅读(30)  评论(0编辑  收藏  举报