01
:
8B
7D
08
mov edi, [ebp
+
8
]
02
D7 mov edx, edi
03
33
C0 xor eax, eax
04
83
C9 FF
or
ecx,
0FFFFFFFFh
05
: F2 AE repne scasb
06
C1
add ecx,
2
07
: F7 D9 neg ecx
8A
45
0C
mov al, [ebp
0Ch
09
FA mov edi, edx
10
: F3 AA rep stosb
11
C2 mov eax, edx
先贴出这个练习的代码。主要考察的是 scas和stos指令的用法。首先根据[ebp+8]和[ebp+0ch]可以判断出这应该是一个被调用函数的实现代码,但这对于我们分析这段代码没关系。下面对每行代码进行分析。
scas
stos
Line 1 mov edi, [ebp+8]
mov edi, [ebp+8]
取地址为 ebp+8 的 double word 存入 edi 中。若此代码段为被调用函数,则此条语句为取第一个参数到 edi。现在看不出来这个 double word 到底是什么。
Line 2 mov edx, edi
mov edx, edi
把 edi 的值复制到 edx 中。
Line 3 xor eax, eax
xor eax, eax
将 eax 置零。
Line 4 or ecx, 0FFFFFFFFh
or ecx, 0FFFFFFFFh
设置 ecx = -1,做什么用处现在还看不出来。
Line 5 repne scasb
repne scasb
这是最关键的一步。首先看 scas指令,它进行如下操作:
scasb
scasw
scasd
由于以上特性,所以此指令经常与 REP 指令一起出现,例如此条指令就是将 edi 指向的 double word 与 eax 比较,如果不等,则 edi = edi + 4, 直到相等。
REP
Line 6 add ecx, 2
add ecx, 2
Line 7 neg ecx
neg ecx
之所以把这俩放到一起,是因为单看第六行指令很难理解,所以就继续往下看,发现对 ecx 取相反数了。首先由第五行的 scasd 指令可知 ecx 的值从 -1 又减小了从开始字符到遇到第一个 \0 时的长度。所以可以猜测这段代码用来计算字符串长度。
所以,如果 edi 指向的字符串为 \0,则 ecx 完成第五行指令后的值为 -2。则完成第六行和第七行指令后的值分别为 0, 0。同理,如果 edi 指向的字符串为 hello\0,则上述值分别为 -7, -5, 5。
\0
hello\0
故,此时 ecx 内存放的是此字符串的长度。
Line 8 mov al, [ebp+0Ch]
mov al, [ebp+0Ch]
将地址为 ebp+0Ch 的 byte 存入 al 中。若此代码段为被调用函数,则此条语句为取第二个参数到 al。
Line 9 mov edi, edx
mov edi, edx
这里将第二行存入 edx 的值取出来,即将 edi 恢复成字符串头地址。
Line 10 rep stosb
rep stosb
继续先看 stos指令,它进行如下操作:
stosb
stosw
stosd
所以这条指令就是把从字符串头开始位置,长度为 ecx(从第七行可知 ecx 为字符串长度)的内存块写入寄存器 al 的值。相当于 memset()。
Line 11 mov eax, edx
mov eax, edx
将字符串首地址作为返回值返回。
这段代码的 C 语言表示为:
char
* replace_with_x(
*des,
x) {
int
count = -1;
*ret = des;
while
(*des !=
'\0'
) {
count -= 1;
des += 1;
}
count += 2;
count = -count;
des = ret;
(count != 0) {
*des = x;
return
ret;
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法