逆向知識十三講,匯編中數組的表現形式,以及還原數組
講解數組之前,要了解數組的特性
1.數據具有連續性
2.數據類型相同
比如:
int Ary[3] = {0,1,2};
我們可以看出,上面定義的數組,數據是連續的,其中每個數據類型大小都是int類型(類型也是一樣的)
匯編中識別數組:
1.地址連續
2.帶有比例因子尋址 (lea reg32,[xxx + 4 *xxxx])
一丶一維數組在匯編中的表現形式
首先說下數組尋址公式,便於下面講解
公式: 數組首地址 + sizeof(type) * n
偽代碼:
int Ary[3] = {1,2,3};
Ary[N] = 1;
sizeof(type) : 這個是求數組元素的類型的,比如上面是int類型數組,我們求數組元素的類型 sizeof(Ary[0]);
n: n的取值是下標運算,比如我們要求第數組中的第一項,(元素為2,從零開始),
代入公式: Ary + sizeof(Ary[0]) * 1
= Ary +4 * 1
= Ary + 4 取內容則是元素2了.
看例子:
高級代碼:
int main(int argc, char* argv[]) { int Ary[3] = {0,1,2}; int i = 0; scanf("%d",&i); Ary[i] = 3; //這句話會產生數組尋址公式 return Ary[i]; }
Debug下的匯編代碼:
之截圖重要代碼
紅色區域還有下面的add esp,8屬於scanf上面的代碼,給數組初始化等等,重要代碼屬於粉紅框內的
1. 局部變量賦值給ecx
2.[ebp + ecx * 4 + var_c],寫入了3,其中 ebp + var_c 是數組首地址, 4是sizeof(type), ecx則是n值.
由此代入我們的數組尋址公式
Ary + sizeof(type) * n
= [ebp + Var_c + 4 * ecx]
只不過比例因子尋址會變化,轉為公式是一樣的,其中sizeof()求出的值變為了常量.
如果喜歡匯編的這種表達形式,可以把數組公式變換一下,
變為:
Ary + (n * sizeof(type))匯編是這種的,其實是一樣的.
Release下的匯編
Release下也是一樣的,可能和Debug匯編不一樣,但是其本質也就是數組尋址公式一樣的.
Ary + sizeof(type) * n
Ary+ (n*sizeof(type))
在這里可能大家會有疑問,為什么esp + var_c是數組首地址,而不用+18h?
因為在vc6.0下,是esp尋址,而這個18h只是做調整,IDA中顯示成這樣是想告訴我們,我要用到Var_C,但是因為我是esp尋址,所以我要調整一下才能找到var_c
而在高版本下,則會直接ebp尋址.不重要,知道就好.
二丶二維數組在匯編下的表現形式
數組尋址公式是一樣的,但不同,
1.sizeof(type)變了. type的取值變為的自己的低維
2.不光求高維,低維也要求
現在的數組尋址公式變為了:
int ary[M][C];
數組首地址 +sizeof(type[C]) * i + sizeof(type) * j; i和j是下標運算的值, 比如 ary[3][4] = 1, 3是i,j是4, 不要和MC搞混,MC是數組定義的時候的值.
其中的sizeof(type[C])變為了二維數組的低維了.
如有一個數組為:
int Ary[2][3] = {{1,2,3},{4,5,6}};
我要求4所在的位置,
我們打印的時候要輸入 ary[1][0] 可以打印出4
那么我們可以通過手來計算出其位置
得出:
Ary + sizeof(type[C]) * i + sizeof(type) * j
簡化公式:
Ary + C * sizeof(Type)*i + sizeof(type) * j 在Debug下會到這一步
簡化公式:
ary + sizeof(type) * (i * C + j); 在Release下會優化為這一步,因為發現了公因子 sizeof(type)了,可以提出來
代入公式得到:
ary + 4 * 1 * 3 + 0
= ary + 12
也就是說數組首地址 + 12 就得出4所在的地址位置.
+12在高級語言中,因為要%4對齊,所以我們要/4
所以得出 12 / 4 = 3,那么如果是指針指向數組的首地址,那么只需要+3即可取得數組的元素4,這也是一維數組訪問二維數組元素的公式.
代碼:
總結一下:
首先要知道數組的尋址公式,因為維數組多了一維,所以要求出高維還要求出低緯度.而其中的type取值是取自己的低維
公式; 數組首地址 + sizeof(type[C]) * i + sizeof(type)*j 重要,必須了解
舉例子了解Debug下的匯編和Release下的數組尋址公式的區別
高級代碼:
int main(int argc, char* argv[]) { int Ary[2][3] = {{1,2,3},{4,5,6}}; int i = 0; int j = 0; scanf("%d%d",&i,&j); Ary[i][j] = 9; //會產生數組尋址公式 return 0; }
Debug下的匯編
通過我們的數組尋址公式得出
1.edx 是獲取i的值
2. edx * C 相當於我們的數組中的尋址公式 sizeof(type[C]) *i 的值.
3.lea的時候 求出數組首地址 + sizeof(type[c])的值.
4.ecx得出j的值
5.eax + 4 * ecx 相當於數組首地址 + sizeof(type)*j
致此熟悉數組尋址公式看匯編代碼很簡單了.
所以Debug下的數組公式會變成
數組首地址 + sizeof(type[C]) * i + sizeof(type] * j
Release下的匯編
上面說過,在Release下會優化我們的原始的公式為
數組首地址 + sizeof(type) * (C * i + j)的形式
我們代入到匯編中查看.
1.eax 得出i的值
2.edx得出數組首地址的值
3.ecx的出 數組首地址 + i * 2 的值
4.add eax,ecx 重新寫會eax,eax = 數組首地址 + i * 2 + i 那么可以簡化為數組首地址 + i * 3即可.
5.運用數組尋址公式 [esp + var18 + 4 * eax] esp + var18得出數組首地址 + i *3 + 4
因為我們的j的取值是0,所以在Release下不是我們想象的 數組首地址 + i * 3 *4 + 0,+0優化掉了.
三丶三維數組在匯編中的表現形式
其實二維數組就介紹了高維數組怎么求了,以不變應萬變.
有一個三維數組
int Ary[M][C][H]
下標操作:
ary[i][j][k] = 1;
數組尋址公式為:
Ary + sizeof(type[C][H]) * i + sizeof(type[H])*j + sizeof(type)*k 在Debug下原模原樣
在Release下會優化公式為:
Ary + sizeof(type)*c*h*i + sizeof(type)*h*j + sizeof(type)*k
發現公因數繼續優化
Ary + sizeof(type) * (c*h*i + h*j + k)
發現了兩個h
繼續簡化
Ary + sizeof(type) * (h*(c*i + j) + k);
所以上面就是最終公式
高級代碼:
int main(int argc, char* argv[]) { int Ary[2][3][4] = {NULL}; int i = 0; int j = 0; int k = 0; scanf("%d%d%d",&i,&j,&k); Ary[i][j][k] = 9; //會產生數組尋址公式 return 0; }
Debug下的反匯編代碼:
公式先貼出來:
Ary + sizeof(type[C][H]) * i + sizeof(type[H])*j + sizeof(type)*k
代入公式看匯編
1.eax = i的值
2. eax * 30 ,相當於求 sizeof(type[C][H]) * i
3.求出數組首地址+eax,也就是求出了 Ary[M]的位置,給Ecx賦值
4.求出j的值
5.左移4位,相當於2^4次方也就是16 這一步相當於求 sizeof(type[H])的值
6.ary[M] + sizeof(type[H])的值得出了 ary[M][C]的值
7.求出k的值
8.數組尋址公式 ary[M][C]的值 + 4 * k的值.
在Debug下代入公式即可.
Release下的匯編
上面說了,Release下匯編會優化,也就是我們的公式會優化.
優化為:
Ary + sizeof(type) * (h*(c*i + j) + k);
根據公式代入即可.
四維數組,高維數組,數組公式同上,只不過注意兩點
1.sizeof(type) type類型比如是自己的低維
2.要加一條新的公式
比如
int ary[A][B][C][D];
下標分別為
i j k l
數組公式為:
Ary + sizeof(type[B][C][D]) * i + sizeof(type[C][D]) * j + sizeof(tyep[D]) * j + sizeof(type)*k
自己優化一下即可
總結:
數組尋址公式要熟悉最簡單的數組尋址公式,因為更高緯度也是從上面公式,只不過type變化了,
會了數組尋址公式,可以說你用指針指向任何一個高維數組的值,取值使用即可.因為在高維在內存中也是線性存儲,也就是一維數組的表現形式.