我認爲最重要的一章是第三章。本章主要學習的就是彙編語言,信息安全的核心思惟方式就是「逆向」,反彙編就是直接的逆向工程。因此我這篇博客寫的是第三章的內容總結。html
歷史:數組
程序編碼:安全
gcc -o mstore mstore.c gcc -Og -S mstore.c gcc -Og -c mstore.c
兩種抽象:
- 指令集結構ISA:是機器級程序的格式和行爲,定義了處理器狀態、指令的格式,以及每條指令對狀態的影響。
- 機器級程序使用的存儲器地址是虛擬地址,看上去是一個很是大的字節數組,其實是將多個硬件存儲器和操做系統軟件組合起來。oop
機器級代碼學習
控制測試
一、條件碼編碼
CF:進位標誌 ZF:零標誌 SF:符號標誌 OF:溢出標誌
二、訪問條件碼操作系統
SET指令根據t=a-b的結果設置條件碼;
能夠條件跳轉到程序的某個其餘部分;
能夠有條件的傳送數據。翻譯
三、跳轉指令及其編碼設計
jump指令
直接跳轉:後面跟標號做爲跳轉目標 間接跳轉:*後面跟一個操做數指示符 其餘跳轉指令
除了jump指令外,其餘跳轉指令都是有條件的。有條件跳轉是指根據條件碼的某個組合,或者跳轉或者繼續執行下一條指令。
四、循環
循環結構的三種形式
do-while:先執行循環體語句,再執行判斷,循環體至少執行一次。 while: 把循環改爲do-while的樣子,而後用goto翻譯 for: 把循環改爲do-while的樣子,而後用goto翻譯 彙編中用條件測試和跳轉組合實現循環的效果。大多數彙編器根據do-while形式來產生循環代碼,其餘的循環會首先轉換成do-while形式,而後再編譯成機器代碼。
五、switch語句
跳轉表是一種很是有效的實現多重分支的方法,是一個數組,表項i是一個代碼段的地址,這個代碼段實現當開關索引值等於i時程序應該採起的動做。
long mult2(long,long); void multstore(long x,long y,long *dest){ long t= mult2(x,y); *dest = t; }
#include<stdio.h> void multstore(long,long,long*); int main(){ long d; multstore(2,3,&d); printf("2*3 --> %ld\n",d); return 0; } long mult2(long a,long b){ long s=a*b; return s; }
gcc -Og -o prog ms1.c mstore.c objdump -d prog
store_prod: movq %rdx, %rax (y-->%rax)(設爲b0) cqto (將%rax符號拓展位八字,拓展的y的高位數據保存在%rdx裏,此值爲y的高位:b1) movq %rsi, %rcx (x-->%rcx )(設爲a0) sarq $63, %rcx (拓展x的符號位爲64位,並保存到%rcx,此值爲x的高位:a1) imulq %rax, %rcx (k1 = b0*a1) imulq %rsi, %rdx (k2 = a0*b1) addq %rdx, %rcx (k1+k2) mulq %rsi (128位無符號全乘法,計算 %rsi * %rax ,結果的高64位存儲在:%rdx,低64位存儲在:%rax) addq %rcx, %rdx (k1+k2+%rdx) movq %rax, (%rdi) (存儲最終的低64位結果到內存) movq %rdx, 8(%rdi) (存儲最終的高64位結果到內存) ret
等價的C代碼爲(((y-z)<<63)>>63)^((y-z)*x)
p = x * y = (x1*W + x0) * (y1*W + y0) = (x1*y1*W*W) + W(x1*y0+x0*y1) + x0*y0
公式中x1y1WW超過了128位,並且未超出128位部分始終全爲0,所以能夠去掉.因而公式變成了p=W(x1y0+x0y1) + x0y0,而後能夠繼續轉化,注意這裏的x0y0是極可能會超出64位的,假設x0y0的超出64位的部分爲z1,未超出64位的部分爲z0.那麼公式能夠變成以下:
p = W(x1y0+x0y1+z1) + z0
1
很明顯,須要將x1y0+x0y1+z1放到最終結果的高位,即(%rdi),z0放到最終結果的低位,即8(%rdi)
而後仔細翻譯下各個語句
store_prod: movq %rdx, %rax # %rax = y0. cqto # 有符號運算,所以用cqto,這裏會自動關聯%rdx和%rax分別表示高位和低位,假如y是負數,那麼%rdx全部位都是1(此時值是-1),不然,%rdx全爲0, %rdx = y1. movq %rsi, %rcx # %rcx = x0. sarq $63, %rcx # 將%rcx向右移63位,跟%rdx的含義同樣,要麼是-1,要麼是0, %rcx = x1. imulq %rax, %rcx # %rcx = y0 * x1 imulq %rsi, %rdx # %rdx = x0 * y1 addq %rdx, %rcx # %rcx = y0 * x1 + x0 * y1 mulq %rsi # 無符號計算 x0*y0,並將x0*y0的128位結果的高位放在%rdx,低位放在%rax,所以這裏%rdx = z1, %rax = z0. addq %rcx, %rdx # %rdx = y0*x1+x0*y1+z1 movq %rax, (%rdi) # 將%rax的值放到結果的低位 movq %rdx, 8(%rdi)# 將%rdx的值放到結果的高位,能夠發現跟上面用數學公式推理的結果徹底一致!!!! ret
loop: movl %esi, %ecx # %ecx=n; movl $1, %edx # %edx=1; --> mask movl $0, %eax # %eax=0; --> result jmp .L2 .L3: movq %rdi, %r8 # %r8=x; andq %rdx, %r8 # %r8=x&%rdx; -->x&mask orq %r8, %rax # %rax=%rax | (x&%rdx); -->result |= x & mask salq %cl, %rdx # %rdx=%rdx<<(n&0xFF); -->mask<<=(n&0xFF) .L2: testq %rdx, %rdx jne .L3. # if %rdx!=0 goto L3. -->mask!=0 rep; ret
A.
%rdi, %r8 --> x %esi, %ecx --> n %rdx --> mask %rax --> result
B.
result = 0; mask = 1;
C.
mask != 0
D.
mask<<=(n&0xFF)
E.
result |= x & mask
F.
long loop(long x, int n) { long result = 0; long mask; for(mask = 1;mask != 0;mask = mask << (n&0xFF)){ result |= x & mask; } return result; }
傳送指令會對條件分別求值,因而假如xp爲空指針,那麼這裏產生對空指針讀數據的操做,顯然是不能夠的。因而這裏不能存在*xp,能夠用指針來代替,最後判斷出值以後,再進行讀取數據,所以這裏0也必須賦予一個地址,因而須要加個變量來存儲0這個數字。所以答案能夠是:
long cread_alt(long *xp) { int t=0; int *p = xp ? xp : &t; return *p; }
這個題就是純翻譯彙編,沒有什麼可講的。
case MODE_A: result = *p2; action = *p1; *p2 = action; break; case MODE_B: result = *p1 + *p2; *p1 = result; break; case MODE_C: *p1 = 59; result = *p2; break; case MODE_D: result = *p2; *p1 = result; result = 27; break; case MODE_E: result = 27; break; default: result = 12;
<switch_prob>: 400590: 48 83 ee 3c sub $0x3c, %rsi # 說明下面的數都要加上60 400594: 48 83 fe 05 cmp $0x5, %rsi 400598: 77 29 ja 4005c3 <switch_prob+0x33> # 若是大於65,跳到4005c3那一行 40059a: ff 24 f5 f8 06 40 00 jmpq *0x4006f8(,%rsi,8) # 跳到跳轉表對應的位置,假設跳轉表對應數組a[x],那麼分別跳到a[0x4006f8+8*(n-60)]的位置 4005a1: 48 8d 04 fd 00 00 00 lea 0x0(,%rdi,8),%rax # 60和62會跳到這個位置 4005a8: 00 400593: c3 retq 4005aa: 48 89 f8 mov %rdi, %rax # 63會跳到這個位置 4005ad: 48 c1 f8 03 sar $0x3, %rax 4005b1: c3 retq 4005b2: 48 89 f8 mov %rdi, %rax # 64會跳到這個位置 4005b5: 48 c1 e0 04 shl $0x4, %rax 4005b9: 48 29 f8 sub %rdi, %rax 4005bc: 48 89 c7 mov %rax, %rdi 4005bf: 48 0f af ff imul %rdi, %rdi # 65會跳到這個位置 4005c3: 48 8d 47 4b lea 0x4b(%rdi), %rax # 大於65和61會跳到這個位置 4005c7: c3 retq
根據上面的分析過程可得答案以下:
long switch_prob(long x, long n){ long result = x; switch(n):{ case 60: case 62: result = x * 8; break; case 63: result = result >> 3; break; case 64: result = (result << 4) - x; x = result; case 65: x = x * x; case 61: # 也能夠去掉這行 default: result = x + 0x4b; } }
store_ele: leaq (%rsi, %rsi, 2), %rax # %rax = 3 * j leaq (%rsi, %rax, 4), %rax # %rax = 13 * j leaq %rdi, %rsi # %rsi = i salq $6, %rsi # %rsi * = 64 addq %rsi, %rdi # %rdi = 65 * i addq %rax, %rdi # %rdi = 65 * i + 13 * j addq %rdi, %rdx # %rdx = 65 * i + 13 * j + k movq A(, %rdx, 8), %rax # %rax = A + 8 * (65 * i + 13 * j + k) movq %rax, (%rcx) # *dest = A[65 * i + 13 * j + k] movl $3640, %eax # sizeof(A) = 3640 ret
A.
&D[i][j][k] = XD + L(i * S * T + j * T + k)
B.
由A題目中的公式以及彙編至第9行第10行計算出來的可得:
S * T = 65 T = 13 S * T * R * 8 = 3640
很容易能夠計算出來
R = 7 S = 5 T = 13
*3.65
.L6: movq (%rdx), %rcx # t1 = A[i][j] movq (%rax), %rsi # t2 = A[j][i] movq %rsi, (%rdx) # A[i][j] = t2 movq %rcx, (%rax) # A[j][i] = t1 addq $8, %rdx # &A[i][j] += 8 addq $120, %rax # &A[j][i] += 120 cmpq %rdi, %rax jne .L6 # if A[j][i] != A[M][M]
A.
從2~5行裏沒法區分A[i][j]和A[j][i],只能從第6和7行來看,A[i][j]每次只移動一個單位,因此每次+8的寄存器%rdx就是指的A[i][j]。
B.
由於寄存器%rdx是A[i][j],因此另外一個寄存器%rax是A[j][i]。
C.
A[j][i]每次移動一行的距離,因此可得公式:8 * M = 120,顯然,M=15。
sum_col: leaq 1(, %rdi, 4), %r8 # %r8 = 4 * n + 1 leaq (%rdi, %rdi, 2), %rax # result = 3 * n movq %rax, %rdi # %rdi = 3 * n testq %rax, %rax jle .L4 # if %rax <= 0, goto L4 salq $3, %r8 # %r8 = 8 * (4 * n + 1) leaq (%rsi, %rdx, 8), %rcx # %rcx = A[0][j] movl $0, %eax # result = 0 movl $0, %edx # i = 0 .L3: addq (%rcx), %rax # result = result + A[i][j] addq $1, %rdx # i += 1 addq %r8, %rcx # 這裏每次+8*(4n+1),說明每一行有4n+1個,所以NC(n)爲4*n+1 cmpq %rdi, %rdx jne .L3 # 這裏說明一直循環到3*n才結束,因此能夠說明一共有3n行,所以NR(n)爲3*n rep; ret .L4: movl $0, %eax ret
根據上述代碼中的分析,能夠得出
NR(n) = 3 * n NC(n) = 4 * n + 1
3.67
相對於%rsp的偏移量 | 存儲的值
---|---
%rsp+24| z
%rsp+16| &z
%rsp+| y
%rsp | x
3.68
首先,結構體str2類型的最長單位是long,因此按照8位對齊,str1一樣,也是按照8位對齊.
再來看彙編代碼:
setVal: movslq 8(%rsi), %rax # 說明str2的t從第8位開始的,由於按照8位對齊,所以sizeof(array[B])小於等於8 # 由於下邊的t是int類型,只佔4個字節,爲了避免讓t與array共佔8個字節,因此sizeof(array[B])大於4,所以可得5<=B<=8. addq 32(%rsi), %rax # 說明str2的u從第32位開始的,所以t與s佔了24個字節,能夠將2個s放在t的一行,佔滿8個字節,剩下的s佔據兩行,所以可得7<=A<=10. movq %rax, 184(%rdi) # 說明str1的y從第184位開始的,所以184-8<A*B*4<=184
根據彙編代碼推出的三個公式:
5<=B<=8 7<=A<=10 184-8<A*B*4<=184
能夠算出惟一解爲:
A=9 B=5
<test>: mov 0x120(%rsi), %ecx # 這句話是訪問bp的first,說明first與a一共佔了288個字節 add (%rsi), %rcx # %rcx = n lea (%rdi, %rdi, 4), %rax # %rax = 5 * i lea (%rsi, %rax, 8), %rax # %rax = &bp + 40 * i mov 0x8(%rax), %rdx # ap->idx = %rax + 8 # 這兩句代表了&bp->a[i]的地址計算公式,即&bp+8+40i,所以能夠說明,a的總大小是40 # +8說明first本身佔8個字節,按照的8位對齊,所以a的第一個元素確定是8個字節的. movslq %ecx, %rcx # 在這裏將n進行了類型轉換,int型轉換成了long型,所以說明ap裏的x數組必定是long型 mov %rcx, 0x10(%rax, %rdx, 8) # 這句說明了ap->x[ap->idx]的地址計算公式是&bp + 16 + idx * 8 # +16說明了包含了first以及idx,說明idx是a的第一個元素,根據上面得出的第一個元素確定是8個字節的結論,說明idx是long類型. # 再由於一共佔大小40,因此x數組的元素個數爲(40 - 8) / 8 = 4 retq
注意是union類型
A.
e1.p 0 e1.y 8 e2.x 0 e2.next 8
B.
16
C.
這一問比較有難度,邏輯性也很強,仍是建議儘可能可以本身推出來.下面來仔細推一下,這題就很差從頭開始一句句的推了, 須要跳躍性的推理(什麼鬼).
1 proc: 2 movq 8(%rdi), %rax 3 movq (%rax), %rdx 4 movq (%rdx), %rdx 5 subq 8(%rax), %rdx 6 movq %rdx, (%rdi) 7 ret
先來看proc的C代碼,等式右邊中間有個減號,所以,能夠去彙編裏找到第5行的subq,因此2~4行就是賦值的被減數.
第3行和第4行代碼分別加了兩次星號,所以能夠說明是((A).B)結構,根據第二行,由於是偏移量+8,取得是第二個值,e1.y不是指針,所以只能是e2.next,因而A爲e2.next;同理,B說明也是指針,沒有偏移量,是取得第一個值,所以只能是e1.p.因此被減數就推出來了爲((up->e2.next).e1.p)
再看第5行,減數的偏移量是相對於%rax+8,上一條步驟中,%rax是(up->e2.next),取第二個值,並且彙編代碼中並未加星號,所以說明不是指針,那麼只能e1.y,所以減數是(up->e2.next).e1.y
最後只剩等式左邊,來看第6行,偏移量爲0說明取得第一個值,且從C代碼中看未加星號,所以不是指針,因此只能是e2.x.
根據上述推理,能夠得出C代碼爲:
void proc(union ele *up){ up->e2.x = *(*(up->e2.next).e1.p) - *(up->e2.next).e1.y; }
- [20155232](http://www.cnblogs.com/lsqsjsj/p/8052671.html) - 結對學習內容 - 最重要的一章的學習 - 分享學習體會
xxx xxx