城市直播房产教育博客汽车
投稿投诉
汽车报价
买车新车
博客专栏
专题精品
教育留学
高考读书
房产家居
彩票视频
直播黑猫
投资微博
城市上海
政务旅游

深入理解计算机系统第三章程序的机器级表示(上)

4月4日 火凤派投稿
  简介
  这个Lab属于第三章程序的机器级表示,本章主要介绍了汇编的各种指令以及程序运行时栈和寄存器的变化。通过C的各种语法引入了对应的汇编指令,使得更易理解各种基本汇编指令以及C语法的底层实现。
  大部分汇编指令很直白,与常用的高级语言的语法都能对应,所以很快就可以熟悉上手;switch语句比较复杂,多了一个跳转表需要处理;浮点数有自己的寄存器和指令,在函数调用的时候需要多加注意。
  本章整体能够比较流畅地看下来,每一部分讲解完成之后都会有对应的练习题帮助加深理解和记忆,感觉比大学时候轻松很多。(可能是本章依旧比较简单,没有深入太多;也有可能大学学过了,还残留着部分知识;更有可能是心态变化了,课程没有那么繁重,每天只需要用2小时学习即可)疑惑点回顾
  本章遇到几个印象深刻的疑惑点,先记录一下,部分还没有找到答案。栈帧对齐
  第一个疑惑点在函数调用这块(P173),主要是在被调用之前需要将栈帧按16字节对齐。longQ(longx);longP(longx,longy){longuQ(y);longvQ(x);}
  以上C代码生成的汇编代码如下(隐去了与本处无关的部分):P:pushqrbppushqrbxsubq8,rspmovqrdi,rbpmovqrsi,rdicallQPLTmovqrax,rbxmovqrbp,rdicallQPLTaddqrbx,raxaddq8,rsppopqrbxpopqrbpret
  可以发现汇编代码第4行(subq8,rsp)在栈中分配了8个字节(这一行书上的注释为:对齐栈帧),没有对应的C代码,同时后面也没有使用。所以当时就十分好奇为什么会生成这样的代码,即使有注释还是不明白是什么意思。
  经过本地各种尝试和搜索后了解到:这样是为了将栈帧按16字节对齐。函数调用时,rsp要整除16才能成功,即在调用函数P(callP)之前,rsp必定是16的整数倍。运行callP时会将返回地址入栈(P165)(但这一部分仍然算在调用者的栈帧内)。这时进入了被调用的函数P开始位置,但此时rsp模16余8,而在P保存寄存器rbp和rsx后(入栈16字节数据),rsp模16仍然余8,而后续在调用函数Q前再无出入栈的操作,所以为了按16字节对齐,需要再在栈上分配8字节的空间(subq8,rsp)。
  后面在数据对齐部分(P191)也讲到这一知识点:任何内存分配函数(alloca,malloc,calloc,realloc)生成的块的起始地址都必须是16的倍数大多数函数的栈帧边界都必须是16字节的倍数(有一些例外)数组分配
  第二个疑惑点在缓冲区溢出这块(P195),表现形式和第一个疑惑点一样,都是在栈上多分配了一些空间,但这次表现不同,没找到原因(在这里花费好几小时,仍然没有找到答案,先暂时搁置)。chargets(chars);voidecho(){1。数组的大小合法的任何数也不影响这种模式2。数组的类型换成其他基本类型也不影响这种模式charbuf〔8〕;gets(buf);}
  以上C代码生成的汇编代码如下(隐去了与本处无关的部分):echo:subq24,rspleaq8(rsp),rdicallgetsPLTaddq24,rspret
  可以发现虽然仍旧是按照16字节对齐,但是多分配了16字节的空间,依照前面的对齐要求,此处我们分配了8字节数组后已经对齐,应该是不需要再多分配空间了。
  经过本地各种组合尝试后发现:只有定义了数组会这样(与数组类型无关),基本都会在数组末尾的前面留下一部分不使用的空间。猜测可能与GCC的栈破坏检测特性有关(P199),但不关闭这个特性,仍旧会在金丝雀值的前面有不使用的空间。
  不关闭栈破坏检测的汇编代码如下(隐去了与本处无关的部分):echo:subq24,rspmovqfs:40,raxmovqrax,8(rsp)xorleax,eaxmovqrsp,rdicallgetsPLTmovq8(rsp),raxxorqfs:40,raxjne。L4addq24,rspret浮点数类型转换
  P208处讲解了GCC生成的浮点数类型转换的汇编代码有两行,而没有使用一条汇编指令直接进行转换。作者也在书中说明不清楚为什么这样处理:
  我们不太清楚GCC为什么会生成这样的代码,这样做既没有好处,也没有必要在XMM寄存器中把这个值复制一遍。准备
  可以在官网〔1〕下载bomblab相关的程序。
  本次需要使用的程序依旧需要在Docker中运行,将本地Lab的目录挂载进容器中即可:dockerruntiv{PWD}:csappubuntu:18。04
  进入容器后需要安装gdb:aptgetupdateaptgetyinstallgdb
  然后就可以愉快的开始闯关了。闯关
  本次Lab给了待使用的bomb程序及其对应的main部分,查看main。c文件,可以发现总共有6个字符串需要输入,必须全部正确才能正确通过这关。BombLabwriteup〔2〕中给了提示,我们可以使用gdb,objdumpt,objdumpd,strings辅助我们通过这关。(这些也在main。c文件开头介绍过)
  首先,我们运行gdbbomb开始调试,进入后可以输入help来获取不同指令的帮助文档,也可以查看书本P194进行相关操作。
  然后调试获取所需的6个字符串。获取第一个字符串
  main。c中给出了第一个字符串将在phase1中进行校验,所以我们需要在phase1入口处打上断点,然后运行程序使其进入phase1函数:在函数phase1入口处打上断点(gdb)breakphase1Breakpoint1at0x400ee0运行bomb(gdb)runStartingprogram:csappbomblabbombwarning:Errordisablingaddressspacerandomization:OperationnotpermittedWelcometomyfiendishlittlebomb。Youhave6phaseswithwhichtoblowyourselfup。Haveaniceday!程序提示输入第一个字符串,我们暂时还不知道,所以随便输入一个idealismxxm程序停在了第一个断点处:函数phase1入口Breakpoint1,0x0000000000400ee0inphase1()
  此时我们需要使用disas查看函数phase1的汇编代码:获取phase1函数的汇编代码(gdb)disasDumpofassemblercodeforfunctionphase1:0x400ee00:sub0x8,rsp0x400ee44:mov0x402400,esi0x400ee99:callq0x401338stringsnotequal0x400eee14:testeax,eax0x400ef016:je0x400ef7phase1230x400ef218:callq0x40143aexplodebomb0x400ef723:add0x8,rsp0x400efb27:retqEndofassemblerdump。
  查看汇编代码,我们发现内部调用了stringsnotequal来比较两个字符串是否相等,该函数接收两个参数,分别放在edi(我们输入的字符串的起始地址)和edi(待比较字符串的起始地址)中,如果这两个字符串不等,则会调用explodebomb引爆炸弹。
  mov0x402400,esi表明比较字符串的起始地址为0x402400,所以我们只需要查看这个地址开始的一段字符串即可获取第一个字符串。然后我们打印这个地址开始的100个字节对应的字符串:(gdb)print(char)0x4024001001BorderrelationswithCanadahaveneverbeenbetter。00000000Wow!Youvedefusedthesecretstage!00flyers
  可以获取第一个字符串为:BorderrelationswithCanadahaveneverbeenbetter。获取第二个字符串
  我们在上一步获取了第一个字符串,此时我们可以开始获取第二个字符串。让我们重新运行程序(gdbbomb),在phase2入口处打上断点,然后运行程序使其进入phase2函数:在函数phase2入口处打上断点(gdb)breakphase2Breakpoint1at0x400efc运行程序(gdb)runStartingprogram:csappbomblabbombwarning:Errordisablingaddressspacerandomization:OperationnotpermittedWelcometomyfiendishlittlebomb。Youhave6phaseswithwhichtoblowyourselfup。Haveaniceday!输入上一步获得的字符串BorderrelationswithCanadahaveneverbeenbetter。Phase1defused。Howaboutthenextone?由于还不知道第二个字符串,所以随便输入以便进入函数phase2idealismxxmBreakpoint1,0x0000000000400efcinphase2()
  此时我们需要使用disas查看函数phase2的汇编代码:获取phase2函数的汇编代码(gdb)disasDumpofassemblercodeforfunctionphase2:0x400efc0:pushrbp0x400efd1:pushrbx0x400efe2:sub0x28,rsp0x400f026:movrsp,rsi0x400f059:callq0x40145creadsixnumbers省略暂时不关注的代码。。。Endofassemblerdump。
  可以发现最开始分配了28个字节空间,然后调用了函数readsixnumbers,这个函数的参数是rdi(输入的字符串)和rsi(刚刚分配的空间)。根据现有条件判断应该是要从输入的字符串中读取6个数字,并存储在我们刚刚分配的空间中。
  接下来我们就需要给函数readsixnumbers打断点,继续运行至函数readsixnumbers入口处,查看其如何将6个数读取出来:在函数readsixnumbers入口处打上断点(gdb)breakreadsixnumbersBreakpoint2at0x40145c继续运行(gdb)continueContinuing。Breakpoint2,0x000000000040145cinreadsixnumbers()获取readsixnumbers函数的汇编代码(gdb)disasDumpofassemblercodeforfunctionreadsixnumbers:0x40145c0:sub0x18,rsp0x4014604:movrsi,rdx0x4014637:lea0x4(rsi),rcx0x40146711:lea0x14(rsi),rax0x40146b15:movrax,0x8(rsp)0x40147020:lea0x10(rsi),rax0x40147424:movrax,(rsp)0x40147828:lea0xc(rsi),r90x40147c32:lea0x8(rsi),r80x40148036:mov0x4025c3,esi0x40148541:mov0x0,eax0x40148a46:callq0x400bf0isoc99sscanfplt0x40148f51:cmp0x5,eax0x40149254:jg0x401499readsixnumbers610x40149456:callq0x40143aexplodebomb0x40149961:add0x18,rsp0x40149d65:retqEndofassemblerdump。
  初步观察汇编代码,发现是通过sscanf函数从我们输入的字符串中获取值,接下来就是判断这6个数字分别存在了什么位置。
  书中P120讲解了函数的前六个入参数分别存储在rdi,rsi,rdx,rcx,r8,r9中,P164讲解了从第7个入参开始,参数存储在栈顶。
  我们先判断函数sscanf的入参分别是什么:第一个入参(rdi):后续没有改动该寄存器的地方,那么第一个入参就是我们输入的字符串第二个入参(rsi):可以发现esi被指令mov0x4025c3,esi修改了,说明第二个参数仍然是字符串,结合函数定义可知该字符串是模式串,用于读取数字。我们使用(gdb)print(char)0x4025c320查看其开始的20个字符,可得模式串为:dddddd第三个入参(rdx):可以发现rdx被指令movrsi,rdx修改了,说明第三个参数是相对传入指针偏移量为0的位置第四个入参(rcx):可以发现rcx被指令lea0x4(rsi),rcx修改了,说明第四个参数是相对传入指针偏移量为4的位置第五个入参(r8):可以发现r8被指令lea0x8(rsi),r8修改了,说明第五个参数是相对传入指针偏移量为8的位置第六个入参(r9):可以发现r9被指令lea0xc(rsi),r9修改了,说明第六个参数是相对传入指针偏移量为12的位置第七个入参((rsp)):可以发现(rsp)被指令(lea0x10(rsi),rax,movrax,(rsp))修改了,说明第七个参数是相对传入指针偏移量为16的位置第八个入参(0x8(rsp)):可以发现0x8(rsp)被指令(lea0x14(rsi),raxmovrax,0x8(rsp))修改了,说明第八个参数是相对传入指针偏移量为20的位置
  由此可知,传入指针指向int〔6〕的数组的起始地址(假设以arr代表该数组),输入串是六个以空格分割的32位有符号数字(int),并且这个六个数字将会按顺序存在arr数组中。
  此时我们再看函数phase2完整的汇编代码:(gdb)disasDumpofassemblercodeforfunctionphase2:0x400efc0:pushrbp0x400efd1:pushrbx0x400efe2:sub0x28,rsp0x400f026:movrsp,rsi0x400f059:callq0x40145creadsixnumbers0x400f0a14:cmpl0x1,(rsp)0x400f0e18:je0x400f30phase2520x400f1020:callq0x40143aexplodebomb0x400f1525:jmp0x400f30phase2520x400f1727:mov0x4(rbx),eax0x400f1a30:addeax,eax0x400f1c32:cmpeax,(rbx)0x400f1e34:je0x400f25phase2410x400f2036:callq0x40143aexplodebomb0x400f2541:add0x4,rbx0x400f2945:cmprbp,rbx0x400f2c48:jne0x400f17phase2270x400f2e50:jmp0x400f3cphase2640x400f3052:lea0x4(rsp),rbx0x400f3557:lea0x18(rsp),rbp0x400f3a62:jmp0x400f17phase2270x400f3c64:add0x28,rsp0x400f4068:poprbx0x400f4169:poprbp0x400f4270:retqEndofassemblerdump。
  调用完readsixnumbers函数后,我们已经将读取的六个数字按顺序存储在数组arr中,分别对应栈中的值:(rsp),0x4(rsp),0x8(rsp),0xc(rsp),0x10(rsp),0x14(rsp)。接下来我们依次判断每个值是多少:第一个数(arr〔0〕:(rsp)):这个数通过cmpl0x1,(rsp)进行判断,紧接着运行跳转指令je0x400f30phase252判断是否跳过引爆炸弹的函数,表明当(rsp)的值等于1时不会触发爆炸,所以第一个数为:1第二个数(arr〔1〕:0x4(rsp)):通过第一个数的判断后,成功跳转到0x400f3052处,这里的代码类似:intaarr1;intbarr7;,然后跳转到0x400f1727处,连续三条指令表明判断a〔1〕a〔1〕是否等于a〔0〕(即判断arr〔1〕arr〔0〕arr〔0〕是否成立),成立时跳过引爆炸弹的函数,表明当0x4(rsp)的值等于2时不会触发爆炸,所以第二个数为:2第三个数(arr〔1〕:0x4(rsp)):通过第二个数的判断后,成功跳转到0x400f2541处,连续三条指令表明先执行了类似aa1的操作,然后判断ab是否成立(即判断a是否指向了arr6),成立时会接着运行并跳转至0x400f3c64处(表明通过了所有判断,字符串合法);不成立时会紧接着跳转至0x400f1727处,继续前一步中的判断。所以这里其实是一个循环,只有当前数是前一个数的2倍时才不会引爆炸弹,所以第三个数为:4
  综上可知六个数分别为:1,2,4,8,16,32,对应的字符串为:12481632。
  重新运行程序,依次输入前两个字符串,然后发现输出了Thatsnumber2。Keepgoing!,表明我们第二个字符串也成功获取到了。获取第三个字符串
  我们在通过前面的步骤成功获取了前两个字符串,此时我们可以开始获取第三个字符串。让我们重新运行程序(gdbbomb),在phase3入口处打上断点,然后运行程序使其进入函数phase3,并获取phase3汇编代码:。。。Breakpoint1,0x0000000000400f71inphase3()获取函数phase3的汇编代码(gdb)disasDumpofassemblercodeforfunctionphase3:0x400f430:sub0x18,rsp0x400f474:lea0xc(rsp),rcx0x400f4c9:lea0x8(rsp),rdx0x400f5114:mov0x4025cf,esi0x400f5619:mov0x0,eax0x400f5b24:callq0x400bf0isoc99sscanfplt0x400f6029:cmp0x1,eax0x400f6332:jg0x400f6aphase3390x400f6534:callq0x40143aexplodebomb0x400f6a39:cmpl0x7,0x8(rsp)0x400f6f44:ja0x400fadphase31060x400f7146:mov0x8(rsp),eax0x400f7550:jmpq0x402470(,rax,8)0x400f7c57:mov0xcf,eax0x400f8162:jmp0x400fbephase31230x400f8364:mov0x2c3,eax0x400f8869:jmp0x400fbephase31230x400f8a71:mov0x100,eax0x400f8f76:jmp0x400fbephase31230x400f9178:mov0x185,eax0x400f9683:jmp0x400fbephase31230x400f9885:mov0xce,eax0x400f9d90:jmp0x400fbephase31230x400f9f92:mov0x2aa,eax0x400fa497:jmp0x400fbephase31230x400fa699:mov0x147,eax0x400fab104:jmp0x400fbephase31230x400fad106:callq0x40143aexplodebomb0x400fb2111:mov0x0,eax0x400fb7116:jmp0x400fbephase31230x400fb9118:mov0x137,eax0x400fbe123:cmp0xc(rsp),eax0x400fc2127:je0x400fc9phase31340x400fc4129:callq0x40143aexplodebomb0x400fc9134:add0x18,rsp0x400fcd138:retqEndofassemblerdump。
  0x400f4300x400f6534:和上一步类似,就不赘述了,主要是通过sscanf获取输入字符串的两个32位有符号整数,假设分别存储在a(0x8(rsp))和b(0xc(rsp))中。如果没有成功获取两个整数,那么就会引爆炸弹,无法继续运行。
  0x400f6a390x400f6f44:主要是判断a7是否成立,成立则可继续运行;不成立则跳转至0x400fad106引爆炸弹,无法继续运行。
  0x400f71460x400f7550:主要是通过a的值计算如何跳转,即通过07计算跳转的位置,很明显是switch语句的汇编代码。那么0x402470就是跳转表的起始位置,我们运行printx0x40247016查看跳转表的相关数据:查看0x402470开始的16个字节(gdb)printx0x402470161{0x400f7c,0x0,0x400fb9,0x0,0x400f83,0x0,0x400f8a,0x0,0x400f91,0x0,0x400f98,0x0,0x400f9f,0x0,0x400fa6,0x0}
  按照顺序对应一下可以发现a对应的跳转位置及对应的代码效果关系如下:
  a
  跳转位置
  相关代码将c赋值为
  0hr0x400f7c57
  0xcf(207)
  1hr0x400fb9118
  0x137(311)
  2hr0x400f8364
  0x2c3(707)
  3hr0x400f8a71
  0x100(256)
  4hr0x400f9178
  0x185(389)
  5hr0x400f9885
  0xce(206)
  6hr0x400f9f92
  0x2aa(682)
  7hr0x400fa699
  0x147(327)
  switch语句执行完毕后,将运行0x400fbe1230x400fc4129,主要是判断bc是否成立,不成立则引爆炸弹,不再继续运行;成立则通过跳过引爆炸弹的操作,成功通过校验。
  综上,第三个字符串有8个合法值:0207,1311,2707,3256,4389,5206,6682,7327。
  我们重新运行程序,在输入第三个字符串时,随意输入上面8个字符串中的一个,控制台都会输出Halfwaythere!告诉我们第三个字符串成功获取。
投诉 评论 转载

大漠里的厮杀一个没权没势的老头竟能感天动地、调动鬼神,他的力量是打哪来的呢?王有福最近很闹心,他的儿媳李秀怀胎已经十一个月了,可孩子就是生不出来。一个月前,李秀快到预产期了,高……小儿常见的药诊有哪些并非所有的人吃药后都会发生过敏反应,药疹会发生在有过敏体质的个体。一般的药疹是发病不是立刻显现,而是具有潜伏期。第一次用药往往不引起皮疹,但已使机体处于一种致病状态,平均期为7……秋水读后感三篇秋水读后感(一)《秋水》中,秋水时至,百川灌河河伯欣然自喜,以天下之美为尽在己至于北海望洋向若叹曰:闻道百,以为莫己若者,我之谓也《尚书》曰:满招损,谦得益人们无论……秋天的树叶牡丹雍容华贵,桂花芳香扑鼻,但是我却爱秋天火红的枫叶。有一句诗说的好:停车坐爱枫林晚,霜叶红于二月花,就是秋天最美的一道风景线。远看,枫树上那火红的枫叶就像一团熊熊……纪律作风集中教育活动个人剖析日,市局开展了专卖纪律作风集中教育活动,并下发了整风肃纪活动的实施方案。方局长就集中教育活动做了专门的动员讲话。其目的意义在于全面动员部署,统一思想认识,进一步严明纪律、整顿作……呋喃丹用途呋喃丹为白色结晶,无臭味,可溶于多种有机溶剂,难溶于二甲苯、石油醚、煤油。那么呋喃丹用途有哪些呢?下面小编来介绍一下吧。呋喃丹是一种广谱高效的内吸性杀虫、杀螨和杀线虫剂。……吃到造句用吃到造句大全121,英格:我们不能没吃到“麻辣鸭头”就走!122,然后大家开始组成三人的分粥委员会及四人的评选委员会,互相攻击扯皮下来,粥吃到嘴里全是凉的。123,赵小姐姓赵是……难忘这段美好时光再有几个月就要毕业了,你有什么想说的吗?你还记得我们初入校园时,满心的好奇,不停地张望着这个新的环境。跟随着父母进入我们一年级的教室,当初的我们是那么羞涩一年级时,……游戏攻略护盾是什么与太平宝术有什么用?三国吧兄弟割草游戏是一款三国主题的动作冒险类游戏,游戏带来经典的割草玩法和刺激的动作对决,玩家将化身三国英雄不断对抗强大的敌人,潮水般的敌人不断袭来,玩家需要制定各种作战策略,……男人女人谁睡了睡在两性博弈的世界,到底谁是狼,谁是羊有的时候还真难分辨。很多男人会不由自主的像我一样把自己认为是狼,是主动出击的,可是如果不是女人发出来某些信号,你这狼根本就不会有机会,而最后……大方向提升转化率你知道了嘛关于转化,账户层级有一些要素,对转化有着至关重要的影响。很多时候,我们跑太快,以至于不能一步一步扎实地走。1、精准关键词SEM的重点方向是提高曝光,吸引点击,提高曝……深入理解计算机系统第三章程序的机器级表示(上)简介这个Lab属于第三章程序的机器级表示,本章主要介绍了汇编的各种指令以及程序运行时栈和寄存器的变化。通过C的各种语法引入了对应的汇编指令,使得更易理解各种基本汇编指令以……
给女生的25条极简护肤建议千篇一律的步行街,靠什么挣钱?同样是天玑8100,红米K50和realmeGTNeo3谁更先进的新数学模型描述了光与物质之间的高能相互作用吃得少,活得久?饭量越小,衰老速度越慢?听听医生怎么说洗头时的四个小窍门,简单易操作,学会了发量越来越多荣耀奉上的耳机甜点Earbuds3i真无线蓝牙耳机评测山海怪谈录解密墓地怪声比亚迪汉dmi价格出来了,老车主这次感觉没背刺挺爽的景甜的背后是谁安徽省化工行业协会一行前往鲁西集团中新化工参观交流常按少海穴,疼痛不再来

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找