主要看了周壑老师的课写的笔记!很久之前写的了!搬运一下 呜呜呜!
奥里给我是周壑老师粉丝!!!!记得B站给周壑老师点关注!
主要的模拟环境是capstone和unicorn
capstone: https://github.com/aquynh/capstone
unicorn: https://github.com/unicorn-engine/unicorn
VMProtect 3.5.0: https://down.52pojie.cn/Tools/Packers/
正常测试结果:
最开始的时候我们将优化关掉,之后黑盒子测试的时候会将优化打开看看加了vmp都有什么区别的
样本代码:
如果我们开启优化的话我们可以看到像strcmp这样的函数将会内嵌到main中:
但是如果我们不启用优化 就可以看到是进行call然后外平栈很传统的一个方式:
我们将样本加vmp壳:
我们拖到xdbg进行调试,定位到main以后看一下
这里的push call相当于一个进入虚拟机的一个标志,相当于假call,我们在call下面进行断点的时候会发现,无法断下来,因为已经跑飞了
测试就用CE来测试我们这个是假的call,可以看到我们程序跑起来了后我们这里没有断下来切这个位置是cc
我们现在需要模拟环境,所以用模拟的环境来跑我们的vmp
因为我们当前的EIP在.vmp0的节中,还有我们当前的线程堆栈的状态,我们需要dump下来,一个大小是0x8A000一个是5000
需要修改我们的当前的寄存器的状态在我们的模拟环境中,和当前dump下来内存的大小和名字地址
因为我们的模拟器是while(1)的,所以我们跑的时候一定会出现一定的异常比如说我们的scanf需要进内核但是我们没有内存映射所以就会产生异常而中断下来,这里面模拟器的一些参数比较有用:
这里可以看到我们模拟成功了,和我们这边的参数是一样的,address: 0x485b76相对应
比较有用的就是我们这里的detail
detail->x86->op_count : 表示我们有多少个操作数
operands:
detail中的regs_read这几个,代表了隐式读,隐式写
隐式读,隐式写:举个例子,push ebp我们看到这个指令都知道我们要对ebp进行压栈的操作,所以对ebp有个读的操作,这个操作叫做显示读,但是我们也会对esp进行读写的操作,所以对于esp有个隐式读隐式写的操作,那么这个操作的作用就是有什么我们会对eflag也就是标志寄存器进行隐式读写的操作,这个到后面进行污点分析等等的有所用处
我们现在让他跑起来,可以看到这里报了一个读到了一个没有映射地址的地方我们过去看一下0x473594
这里稍微提一下eflags的TF位,我们的TF位是的变动和我们的断点的机制有关这个xdbg我们下断点跑起来的时候我们的TF位会变成1如果我们单步的话就不会有这个问题的 (可以自己稍微尝试一下),这里可以看到我们的403370是我们输入的buffer,4020108是我们的scanf的参数%s,47295d是我们的返回地址,4010c0是我们scanf的地址
我们既然知道这些,我们就可以模拟scanf,因为像系统的一些dll等等的系统库,不可能vmp将所有的都加上了vmp所以我们当调用他们的时候是没有vmp的且模拟器会中断,需要自己模拟类似于scanf的这些函数去进行继续执行
模拟scanf代码:我们当前的eip在0x473594我们断下来,让我们的buffer中的值等于我们想要的值,修改我们的eip是我们的返回地址,esp当前+8,修改到我们调用完scanf的地址即可,因为我们用到了data和rdata的位置我们需要dump下来
我们继续跑 可以看到断下来的位置是我们代码中的strcmp:
这里有个小知识点,就是我们经过call等东西,eax,ecx,edx是易变寄存器,他们经过call的时候是可以改变的,很大几率而其他的寄存器一般在发生call之后的时候不会改变,叫不易改变寄存器
模拟strcmp代码:
跑起来后:
当然我们也可以模拟代码中修改eax的值变成0看看结果:
我们指令进行单步执行的时候,可以知道我们的jmp不会打扰我们的执行跳转 ( jmp 立即数 ) 可以把这个指令当作!啥也不是!没错直接给他干掉 其他的像ret啦,ja啦,je啦,jmp寄存器啦,call啦都是不一定的,所以我们将他们打印出来
这里面的ret都相当于
因为新版本的符合一个叫做寄存器轮转的问题所以他可能jmp ebp,jmp edi等等的
这里的ja 都是一个地址,这个是因为他会判断自己的VM_STACK 虚拟栈,栈式虚拟机实现起来方便,膨胀倍数高,是虚拟机保护的首,虚拟栈就是临时进行数据交换,VMProtect 的 EBP 寄存器就是虚拟栈的栈顶指针,如果感觉膨胀到一定的时候需要提升栈的空间防止虚拟机空间溢出,所以会有很多ja的指令去判断,是不是到了规定的大小的值
我们如果分析虚拟机的时候需要去处理一下局部混淆的问题,我们看一下我们call进去的虚拟机的样子指令,可以看到有一堆莫名其妙的指令,那么这些指令大部分都是我们的混淆指令:
像一个虚拟机中用特别多的jmp指令,我们就可以把这些jmp的指令当作不存在,因为jmp执行的时候仅仅是跳转而且这些jmp imm的时候我们可以明确知道他跳转到了哪里,分析vmp你可以看到我们的jmp几乎百分之99都是这种类型的jmp imm所以我们把这些jmp当作不存在,将跳转过去的代码和当权的代码块当作同一个代码块进行分析,需要ret call jcc等等的这些指令的时候我们当作是代码块的结束的位置
因为我们现在是先分析局部混淆所以我们就先对这个局部混淆的位置进行分析,一直到0x40bc54,我们把这个代码都打出来从头:
这里的局部混淆有个特点的总结,就是我们将所有的jmp都去掉,我们对寄存器的写操作,第二次写会覆盖第一次写的操作,所以第一次的写的命令就是混淆,这里有个小技巧就是我们假设要看ecx寄存器我们就可以在xdbg用按h点击ecx即可:
这里可以看到我们的ecx已经被ds:[esi]内存地址进行赋值了,我们的rcr的命令就无效了没有用了就相当于混淆指令可以去掉,但是上面的对ecx进行读取的操作所有不知道是不是混淆需要继续去分析的
手动大概去除混淆的代码:
我们生成字节码去测试一下生成一个新的内存空间
我们先分配一个页的内存
用ce去获取字节码赋值到内存区域修改call再跑起来
直接f9看是否成功满足我们说的原理,可以看到是对的,如果还想继续很细致的恢复去混淆的话,就需要自己调试的时候细致去分析了
但是如果想自动化去除混淆的话,需要细分寄存器的读写和eflags的读写规则(lea指令是不访问内存的)
([mem]操作数中有对base和index的隐式读)
假设例子:
由于本人excel不知道怎么了,开启了发飙模式,用不了,直接用简陋的语言叙述来表示了
//未完成的计划:
(代码还没有实现,但是根据capstone应该可以实现出很好的去除混淆代码的方式)
这封图很好的解释了函数调用界面的类型的方式(只针对与vmp来说)
测试代码:
因为我们分析可以发现进入虚拟机的标识就是push call 的标识 我们可以用这个call来定位分析我们的进入虚拟机的位置
00401068这个地址是我们的printf的系统的那边的函数:
那么主要的流程就是:
还会有个比较有意思的事情就是:
正常我们的cmp函数入口的位置是0x401100,但是我们f9他永远都不运行,因为入口的位置也是虚拟化中的一部分
我们也可以通过ebp来找我们想要找的进入虚拟机的位置,假设我们现在再scanf之后的位置看一下ebp:
scanf的进入
进入虚拟机
进入虚拟机
再出虚拟机
满足了上面流程图的特点
我们首先主要dump,自定义比较函数的cmp的内存的位置到printf,因为在正常的比较函数中会有我们相应的一个jcc的一个跳转的形式,我们通过黑盒测试去模拟出一个相应的指令的方式,这是原始代码的一个形式
主要的模拟就是我们的这个je加了vmp是什么样子
中途自己测试的时候发现一个好玩的事情,我们的模拟器的TF位置是因为我们下断点之后就会断下来,所以TF位置会置1,但是我们的正常的执行流程的下来TF的位置没有变成1,如果我们把eflags加上了TF位置的值,我们模拟器会自动认为,该句有断点所以只执行当前的一句代码,就不会向下执行了
因为是bool类型只跟我们al有关,所以将al置1的时候就是fail,如果置0的时候就是ok,所以我们可以确定当前的jcc的跳转的格式在我们的虚拟机的代码之中
如果我们不确定jcc的类型在虚拟机里都有什么跑的时候,我们打印一下看一下,可以发现大部分都是ja做虚拟机栈看是否溢出的一个保护措施
所以我们就要去想虚拟机中是怎么去实现je这一个效果的方法,自己模拟一下代码来实现一下:
原理(使用pushfd的情况):
不使用pushfd的情况:
但是我们的vmp没有这么极限,他还是使用了pushfd的操作的,但是在vmp里是没有减法的,这个eflag的标志位的改变是比较复杂的
结果标志位: Z , S...
过程标志位: C , O...
虽然说假设我们知道结果可以判断一个Z或者S位的东西,但是我们的过程标志是有可能发成改变的,假设0+1 = 0xFFFFFFFF + 2
针对于一个无符号的( 0 - 2^32 )我们溢出的时候一般都看C位,有符号的就是看O位了,在有无符号的情况下,NOT(a) + b 产生的C/O位与a - b产生的C位总是保持一致的
VMP中 eflag实现的过程:
因为上面确定的原则所以如果想要产生真正的eflags我们需要进行两次的pushfd的操作,第一次去取过程化的eflags的操作,第二次去取结果化的eflags的操作,两次的结果进行OR就是我们需要的真正的eflags,所以我们可以对pushfd这个指令进行追踪,这里对eflags的保存还有一个指令就是lahf
所以我们把pushfd和lahf都打出来,但是在vmp中一般的lahf都是做混淆的,很少使用大部分的情况就是pushfd
比如说这样的情况我们lahf做完操作之后对我们的eax进行重新的赋值,那么lahf就没有意义了
输出的结果:
看到这么多的pushfd主要是因为有加减运算的那些,就会产生一次pushfd,但是我们只关心产生z位的那个pushfd
测试一下,是对的,vmp主要根据pushfd进行操作
通过二分法可以定位一下影响我们zf的那个pushfd在哪里,修改后的结果:所以pushfd可以控制结果的流程
因为像pushfd很多,我们如果出现很多的情况的时候,就不能这么二分法慢慢去找,很浪费时间,所以我们可以根据规则,比如我们pushfd之后我们需要 ,与操作,移位的操作,假设这个指令是and eax,0x40 但是我们不知道第二个操作数他是立即数还是寄存器还是内存,或者说他是第一个操作数还是第二个操作数我们是未知的,所以我们要写一个规则通用的:
可以看到现在的这个位置ecx相当于我们说的一个zf(0x40)的位置的一个判断,eax是我们的eflags
shr eax,cl cl是0x6
开启优化的版本:
dump scanf到printf的位置,因为我们的cmp的自定义的函数已经内敛到我们的main中所以定位不到入口和出口的位置
下断跑一下,看一下加了优化后的流程,总结一下流程图第一次,但是我们不知道0x421d22的分支上是否还有别的jcc
我们可以把我们的程序的流程指令的个数都打印出来,我们如果遇见像分支一类的情况下,我们就可以发现指令的个数会有明显的一个改变的形式,其实这个东西也不是很重要,因为如果我们作为正向的开发肯定原则不会这么去写代码,一定会加上一些加密或者是其他的原则让这种方式不会成功
但是还是要看一下测信道攻击是什么情况:
如果第一个不是正确的数值路径就会变少:
所以可以通过这种方式来侧信道攻击从而达到一个确定最后的结果是什么的情况,但是这种问题很容易被加密的算法等等所干掉,让这种办法不可以进行,因为计算机的运算速度不满足(只是说一下可以有这个方式)
被污染的数据,在代码的执行指定的时间点上面,因为输入的数据的不同而可能产生不同取值的数据
简要的来说,因为我们的输入去导致了一个数值的修改,我们的这个数值就叫做被污染,那么这个污染的数值如果对另一个数值进行写的操作,那么另一个数值也就被污染了,如果这个数值被其他数值写操作了,那么这个数值就解除污染,这个有点像游戏分析中的数据追踪,去获取谁访问了该数值,然后追踪找到最后的基址的方式是差不多的
污染传播的一般原则:
但是比如这些原则来说针对于汇编的一些指令,有些是不满足这个规则的,比如说xchg eax,ecx 我们对两个寄存器都进行了一个读的操作,并且两个寄存器都有写操作,按照我们的原则来说我们的两个寄存器就都会进行污染,但实际上其实就是一个寄存器被污染了,所以像这些指令来说,我们需要拿出来单独的做规则
假设一个例子代码
我们的push指令,push eax为例子,我们对eax进行读的操作,对堆栈的内存进行写的操作,要写不同的指令的污点的代码
污点分析最好的一个好处就是因为我们对一个污染源做完操作之后他一定会有一个终止的位置,使用的最佳的位置,比如说我要比较cmp举例子到最后的测试的时候看到大部分都是进行要给 movzx xxx,xxx 这样的指令去写入寄存器 (最后一次的时候) 进行比较
char buf[
1204
];
void main()
{
while
(
1
)
{
scanf(
"%s"
, buf);
if
(!strcmp(buf,
"123"
))
printf(
"ok\n"
);
else
printf(
"fail\n"
);c
}
}
char buf[
1204
];
void main()
{
while
(
1
)
{
scanf(
"%s"
, buf);
if
(!strcmp(buf,
"123"
))
printf(
"ok\n"
);
else
printf(
"fail\n"
);c
}
}
|
名称 |
值 |
类型 |
▶ |
[0x00000000] |
{type=X86_OP_IMM (0x00000002) reg=0x4cc49084 imm=0x000000004cc49084 ...} |
cs_x86_op |
case
0x473594
:
{
DWORD val
=
0x00343332
;
uc_mem_write(uc,
0x403370
, &val,
4
);
regs.regs.r_eip
=
0x47295d
;
uc_reg_write(uc, UC_X86_REG_EIP, ®s.regs.r_eip);
regs.regs.r_esp
+
=
8
;
uc_reg_write(uc, UC_X86_REG_ESP, ®s.regs.r_esp);
}
case
0x473594
:
{
DWORD val
=
0x00343332
;
uc_mem_write(uc,
0x403370
, &val,
4
);
regs.regs.r_eip
=
0x47295d
;
uc_reg_write(uc, UC_X86_REG_EIP, ®s.regs.r_eip);
regs.regs.r_esp
+
=
8
;
uc_reg_write(uc, UC_X86_REG_ESP, ®s.regs.r_esp);
}
case
0x472ecc
:
{
regs.regs.r_eax
=
1
;
err
=
uc_reg_write(uc, X86_REG_EAX, ®s.regs.r_eax);
regs.regs.r_eip
=
0x4854c1
;
err
=
uc_reg_write(uc, X86_REG_EIP, ®s.regs.r_eip);
regs.regs.r_esp
+
=
8
;
err
=
uc_reg_write(uc, X86_REG_ESP, ®s.regs.r_esp);
}
case
0x472ecc
:
{
regs.regs.r_eax
=
1
;
err
=
uc_reg_write(uc, X86_REG_EAX, ®s.regs.r_eax);
regs.regs.r_eip
=
0x4854c1
;
err
=
uc_reg_write(uc, X86_REG_EIP, ®s.regs.r_eip);
regs.regs.r_esp
+
=
8
;
err
=
uc_reg_write(uc, X86_REG_ESP, ®s.regs.r_esp);
}
!strcmp(insn
-
>mnemonic,
"ret"
)||(!strcmp(insn
-
>mnemonic,
"jmp"
) && insn
-
>detail
-
>x86.operands[
0
].
type
!
=
X86_OP_IMM)||!strcmp(insn
-
>mnemonic,
"call"
)||(insn
-
>mnemonic[
0
]
=
=
'j'
&& insn
-
>detaicl
-
>regs_read_count
=
=
1
&& insn
-
>detail
-
>regs_read[
0
]
=
=
X86_REG_EFLAGS)
!strcmp(insn
-
>mnemonic,
"ret"
)||(!strcmp(insn
-
>mnemonic,
"jmp"
) && insn
-
>detail
-
>x86.operands[
0
].
type
!
=
X86_OP_IMM)||!strcmp(insn
-
>mnemonic,
"call"
)||(insn
-
>mnemonic[
0
]
=
=
'j'
&& insn
-
>detaicl
-
>regs_read_count
=
=
1
&& insn
-
>detail
-
>regs_read[
0
]
=
=
X86_REG_EFLAGS)
push reg
ret
Emulate i386 code
00485B76
push
0x4cc49084
00485B7B
call
0x441d33
00441D33
pushfd
00441D34
stc
00441D35
clc
00441D36
push edi
00441D37
rcl edi,
0x6b
00441D3A
xchg edi, edi
00441D3C
push edx
00441D3D
btr edi, edi
00441D40
push ecx
00441D41
btr di, ax
00441D45
rol edi,
0xc
00441D48
bswap cx
00441D4B
push eax
00441D4C
push esi
00441D4D
push ebx
00441D4E
clc
00441D4F
push ebp
00441D50
bswap si
00441D53
bts eax, ebx
00441D56
mov ecx,
0
00441D5B
cbw
00441D5D
push ecx
00441D5E
mov bl,
0x99
00441D60
clc
00441D61
mov esi, dword ptr [esp
+
0x28
]
00441D65
btr edi, edx
00441D68
not
bp
00441D6B
bts edi, eax
00441D6E
ror esi,
1
00441D70
movzx eax, bp
00441D73
bsf eax, esi
00441D76
lea esi, [esi
-
0x1394580a
]
00441D7C
bswap esi
00441D7E
sal al,
0xe6
00441D81
sbb bx, bp
00441D84
xor esi,
0x5bf674ed
00441D8A
movsx ebp, cx
00441D8D
btc ebx, ebx
00441D90
not
esi
00441D92
adc bp,
0x66ec
00441D97
stc
00441D98
bswap esi
00441D9A
and
ebp,
0xa934b31
00441DA0
stc
00441DA1
lea esi, [esi
+
ecx]
00441DA4
lahf
00441DA5
mov ebp, esp
00441DA7
lea esp, [esp
-
0xc0
]
00441DAE
rcl ebx, cl
00441DB0
mov edi, ecx
00441DB2
mov ebx, esi
00441DB4
xadd di, cx
00441DB8
mov eax,
0
00441DBD
xor edi, eax
00441DBF
sub ebx, eax
00441DC1
or
edi,
0x1a7d74b8
00441DC7
rcl di,
0xe5
00441DCB
lea edi, [
0x441dcb
]
00441DD1
rcr ecx, cl
00441DD3
mov ecx, dword ptr [esi]
00441DD5
stc
00441DD6
add esi,
4
00441DDC
xor ecx, ebx
00441DDE
jmp
0x42fdc3
0042FDC3
bswap ecx
0042FDC5
jmp
0x47eef0
0047EEF0
dec ecx
0047EEF1
stc
0047EEF2
neg ecx
0047EEF4
jmp
0x40bc45
0040BC45
add ecx,
0x2941083
0040BC4B
clc
0040BC4C
test dl,
0x4c
0040BC4F
xor ebx, ecx
0040BC51
add edi, ecx
0040BC53
push edi
0040BC54
ret
Emulate i386 code
00485B76
push
0x4cc49084
00485B7B
call
0x441d33
00441D33
pushfd
00441D34
stc
00441D35
clc
00441D36
push edi
00441D37
rcl edi,
0x6b
00441D3A
xchg edi, edi
00441D3C
push edx
00441D3D
btr edi, edi
00441D40
push ecx
00441D41
btr di, ax
00441D45
rol edi,
0xc
00441D48
bswap cx
00441D4B
push eax
00441D4C
push esi
00441D4D
push ebx
00441D4E
clc
00441D4F
push ebp
00441D50
bswap si
00441D53
bts eax, ebx
00441D56
mov ecx,
0
00441D5B
cbw
00441D5D
push ecx
00441D5E
mov bl,
0x99
00441D60
clc
00441D61
mov esi, dword ptr [esp
+
0x28
]
00441D65
btr edi, edx
00441D68
not
bp
00441D6B
bts edi, eax
00441D6E
ror esi,
1
00441D70
movzx eax, bp
00441D73
bsf eax, esi
00441D76
lea esi, [esi
-
0x1394580a
]
00441D7C
bswap esi
00441D7E
sal al,
0xe6
00441D81
sbb bx, bp
00441D84
xor esi,
0x5bf674ed
00441D8A
movsx ebp, cx
00441D8D
btc ebx, ebx
00441D90
not
esi
00441D92
adc bp,
0x66ec
00441D97
stc
00441D98
bswap esi
00441D9A
and
ebp,
0xa934b31
00441DA0
stc
00441DA1
lea esi, [esi
+
ecx]
00441DA4
lahf
00441DA5
mov ebp, esp
00441DA7
lea esp, [esp
-
0xc0
]
00441DAE
rcl ebx, cl
00441DB0
mov edi, ecx
00441DB2
mov ebx, esi
00441DB4
xadd di, cx
00441DB8
mov eax,
0
00441DBD
xor edi, eax
00441DBF
sub ebx, eax
00441DC1
or
edi,
0x1a7d74b8
00441DC7
rcl di,
0xe5
00441DCB
lea edi, [
0x441dcb
]
00441DD1
rcr ecx, cl
00441DD3
mov ecx, dword ptr [esi]
00441DD5
stc
00441DD6
add esi,
4
00441DDC
xor ecx, ebx
00441DDE
jmp
0x42fdc3
0042FDC3
bswap ecx
0042FDC5
jmp
0x47eef0
0047EEF0
dec ecx
0047EEF1
stc
0047EEF2
neg ecx
0047EEF4
jmp
0x40bc45
0040BC45
add ecx,
0x2941083
0040BC4B
clc
0040BC4C
test dl,
0x4c
0040BC4F
xor ebx, ecx
0040BC51
add edi, ecx
0040BC53
push edi
0040BC54
ret
00441D33
pushfd
00441D36
push edi
00441D3C
push edx
00441D40
push ecx
00441D4B
push eax
00441D4C
push esi
00441D4D
push ebx
00441D4F
push ebp
00441D56
mov ecx,
0
00441D5D
push ecx
00441D5E
mov bl,
0x99
00441D61
mov esi, dword ptr [esp
+
0x28
]
00441D6E
ror esi,
1
00441D76
lea esi, [esi
-
0x1394580a
]
00441D7C
bswap esi
00441D84
xor esi,
0x5bf674ed
00441D90
not
esi
00441D98
bswap esi
00441DA1
lea esi, [esi
+
ecx]
00441DA5
mov ebp, esp
00441DA7
lea esp, [esp
-
0xc0
]
00441DB2
mov ebx, esi
00441DB8
mov eax,
0
00441DBF
sub ebx, eax
00441DCB
lea edi, [
0x441dcb
]
00441DD3
mov ecx, dword ptr [esi]
00441DD6
add esi,
4
00441DDC
xor ecx, ebx
0042FDC3
bswap ecx
0047EEF0
dec ecx
0047EEF2
neg ecx
0040BC45
add ecx,
0x2941083
0040BC4C
test dl,
0x4c
0040BC4F
xor ebx, ecx
0040BC51
add edi, ecx
0040BC53
push edi
0040BC54
ret
00441D33
pushfd
00441D36
push edi
00441D3C
push edx
00441D40
push ecx
00441D4B
push eax
00441D4C
push esi
00441D4D
push ebx
00441D4F
push ebp
00441D56
mov ecx,
0
00441D5D
push ecx
00441D5E
mov bl,
0x99
00441D61
mov esi, dword ptr [esp
+
0x28
]
00441D6E
ror esi,
1
00441D76
lea esi, [esi
-
0x1394580a
]
00441D7C
bswap esi
00441D84
xor esi,
0x5bf674ed
00441D90
not
esi
00441D98
bswap esi
00441DA1
lea esi, [esi
+
ecx]
00441DA5
mov ebp, esp
00441DA7
lea esp, [esp
-
0xc0
]
00441DB2
mov ebx, esi
00441DB8
mov eax,
0
00441DBF
sub ebx, eax
00441DCB
lea edi, [
0x441dcb
]
00441DD3
mov ecx, dword ptr [esi]
00441DD6
add esi,
4
00441DDC
xor ecx, ebx
0042FDC3
bswap ecx
0047EEF0
dec ecx
0047EEF2
neg ecx
0040BC45
add ecx,
0x2941083
0040BC4C
test dl,
0x4c
0040BC4F
xor ebx, ecx
0040BC51
add edi, ecx
0040BC53
push edi
0040BC54
ret
9c
57
52
51
50
56
53
55
b9
00
00
00
00
51
b3
99
8b
74
24
28
d1 ce
8d
b6 f6 a7
6b
ec
0f
ce
81
f6 ed
74
f6
5b
f7 d6
0f
ce
8d
34
0e
8b
ec
8d
a4
24
40
ff ff ff
8b
de b8
00
00
00
00
2b
d8
3e
8d
3d
cb
1d
44
00
8b
0e
83
c6
04
33
cb
0f
c9
49
f7 d9
81
c1
83
10
94
02
f6 c2
4c
33
d9
03
f9
57
c3
9c
57
52
51
50
56
53
55
b9
00
00
00
00
51
b3
99
8b
74
24
28
d1 ce
8d
b6 f6 a7
6b
ec
0f
ce
81
f6 ed
74
f6
5b
f7 d6
0f
ce
8d
34
0e
8b
ec
8d
a4
24
40
ff ff ff
8b
de b8
00
00
00
00
2b
d8
3e
8d
3d
cb
1d
44
00
8b
0e
83
c6
04
33
cb
0f
c9
49
f7 d9
81
c1
83
10
94
02
f6 c2
4c
33
d9
03
f9
57
c3
char buf[
1204
];
bool
mystrcmp(char
*
p1, const char
*
p2)
{
while
(
*
p1 &
*
p2)
{
if
(
*
p1 !
=
*
p2)
return
false;
p1
+
+
;
p2
+
+
;
}
if
(
*
p1 ||
*
p2)
return
false;
return
true;
}
void main()
{
while
(
1
)
{
scanf(
"%s"
, buf);
if
(mystrcmp(buf,
"123"
))
printf(
"ok\n"
);
else
printf(
"fail\n"
);
}
}
char buf[
1204
];
bool
mystrcmp(char
*
p1, const char
*
p2)
{
while
(
*
p1 &
*
p2)
{
if
(
*
p1 !
=
*
p2)
return
false;
p1
+
+
;
p2
+
+
;
}
if
(
*
p1 ||
*
p2)
return
false;
return
true;
}
void main()
{
while
(
1
)
{
scanf(
"%s"
, buf);
if
(mystrcmp(buf,
"123"
))
printf(
"ok\n"
);
else
printf(
"fail\n"
);
}
}
00449668
call
0x46af7e
;进入虚拟机
0043FC17
call
0x46af7e
;scanf后进入虚拟机
00467D9E
call
0x47608d
;mystrcmp后进入虚拟机
00449668
call
0x46af7e
;进入虚拟机
0043FC17
call
0x46af7e
;scanf后进入虚拟机
00467D9E
call
0x47608d
;mystrcmp后进入虚拟机
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-10-26 16:28
被L0x1c编辑
,原因: