我认为最重要的一章是第三章。本章主要学习的就是汇编语言,信息安全的核心思惟方式就是“逆向”,反汇编就是直接的逆向工程。因此我这篇博客写的是第三章的内容总结。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