原创
菜农要求给67楼穿裤子~~~答出:MOV A,R7“尿童即可毕业,85分吧
原帖出处:http://bbs.21ic.com/club/bbs/list.asp?Page=1&boardid=11&t=3251912
xwj 发表于 2009-3-11 11:12 侃单片机 ←返回版面 |
67楼: “红杏出墙装疯拳”详解--第三招: |
闲话少说,马上开始! /*--------------------------------------------- HotTask51软中断函数 void __HotASM_ISR__(unsigned char isrNum) 入口: R7 D4D3 中断向量组号 D2D1D0 中断向量号 出口: R7 isrNum 结果: 跳入对应的中断地址 isrNum * 8 + 3 -----------------------------------------------*/ HotASM (__HotASM_ISR__)//(unsigned char isrNum) --定义个CODE区的数组,相当于CODE const unsigned char __HotASM_ISR__ {//DPTR保存的是汇编数组函数的首地址__HotASM_ISR__ #define lable__HotASM_ISR__Exit \ (\ + 3 * size_RL_A()\ + size_ORL_A()\ + size_CLR_BIT()\ + 2 *size_PUSH_REG()\ + size_CLR_A()\ ) --上面这段是用来计算跳转偏移量的,把需要跳过的指令字节数统计出来 asm_CJNE_R7(0x20, size_SJMP() - 1),//MOV a,R7 --和0x20比较后跳到下一句,影响CY:A>=0x20时C=0;A<0x20时C=1(借位) asm_MOV_A(0xEF),//MOV a,0xef// --这句很晕人,老x看到这里很疑惑:是MOV a,0xef呢,还是MOV a,#0xef? --因为这里差个“# ”的话那结果可就差远了,于是老x只好挖掘Hotel老给俺的HotC51Demo(别人没有哦) --找到了:#define HotASM_CODE_MOV_A_DATA (HotASM_REG)0x74//操作码 #define asm_MOV_A_DATA(DATA) HotASM_CODE_MOV_A_DATA, (HotASM_REG)DATA #define asm_MOV_A(DATA) asm_MOV_A_DATA(DATA) --哦,是操作码0x74,原来是MOV A,#DATA 74, DATA ,立即数进A --??? 这句有什么用呢??? asm_JNC(lable__HotASM_ISR__Exit),//非法软中断号>(0~0x1f) --判断上上句影响的CY,A>=0x20时直接退出(lable__HotASM_ISR__Exit标号) asm_RL_A(), --A<0x20时执行 asm_RL_A(), asm_RL_A(), asm_ORL_A(0x03), --A= A<<3 |0x03,之前A为0xEF...这不对头啊,这样跳出去那岂不变成固定的了, --实际应该是A= R7<<3 |0x03才对,这里肯定有鬼! --呵呵,老x后面再说:-) asm_CLR_ACC_2(), --什么意思???又要翻定义: --#define asm_CLR_ACC_X(BIT) asm_CLR_SREG_X(HotASM_ACC, BIT) --#define asm_CLR_ACC_2() asm_CLR_ACC_X(HotASM_BIT2) --哦,原来是CLR ACC.2,清除ACC的BIT2 asm_PUSH_ACC(),//压入中断地址低8位 --压栈ACC asm_CLR_A(), asm_PUSH_ACC(),//压入中断地址高8位 --压栈0x00 //lable__HotASM_ISR__Exit: asm_RET()//跳入对应的中断地址 isrNum * 8 + 3 --可变地址跳转 };
--好了,注释完了,我们来烟酒下老Hotel到底高了什么鬼吧 --嗯,关键就是这一句了: asm_MOV_A(0xEF),//MOV a,0xef// --这是一个可以双重语意的语句! 由于MCS51系列的CPU,指令可以是不等长的,可以是一个字节,也可以是多个字节,而按不同方式断句,任何一个二进制代码都可以当成指令来执行哦 asm_MOV_A(0xEF),//MOV a,0xef// 这句的二进制代码是74EF,而asm_CJNE_R7(0x20, size_SJMP() - 1)是R7不等于0x20时跳转偏移2-1 (#define size_SJMP() 2//字节数) 所以,执行CJNE后会跳到下一句74EF的中间!去执行代码EF!!! 查下MCS51指令表:MOV A,Rn E8|E9|EA|EB|EC|ED|EE|EF 12 1 EF对应的就是MOV A,R7! 嗯,这就对了嘛,Keil的第一个参数就是从R7传递的嘛 所以呢,最后在RET哪里就会弹出到R7<<3 |0x03的地址,正常的中断程序就放在那个地方嘛;-)
哎哎哎,那位同学,别急着走啊,还没讲完呢! 同学们,考虑问题要周到啊,CJNE是不相等时跳转,那么,万一要是r7==0x20,相等了怎么办呢?要是你现在就走了,那你将来就可能因为考虑不周全而经常写出有BUg的程序哦~~~
所以嘛...以老Hotel那从小吃人肉培养出的超人精力,以老Hotel那高达250的非人类智商,当然不会犯这样的低级错误啦~ 老Hotel早就预料到这种情况了。 我们来看: 相等时,顺序执行asm_MOV_A(0xEF)--注意,这次可不是从中间执行一半了哦! 然后:A>=0x20时C=0,这里A=0x20,那就是CY=0 然后在下一句asm_JNC(lable__HotASM_ISR__Exit)跳出去了,什么都没执行。
老师,那个asm_CLR_ACC_2()好像没什么用啊? 哦,这个啊,根据老x手上的HotC51Demo,那里面是这样写的: HotASM (__HotASM_ISR__)//(unsigned char isrNum) {//DPTR保存的是汇编数组函数的首地址__HotASM_ISR__ #define lable__HotASM_ISR__Error (-(size_CPL_C() + size_MOV_A() + size_JNC())) asm_CJNE_R7(0x20, size_CPL_C() +size_MOV_A() - 1),//MOV a,R7 //lable__HotASM_ISR__Error: asm_CPL_C(), asm_MOV_A(0xEF),//MOV a,0xef// asm_JNC(lable__HotASM_ISR__Error),//非法软中断号>(0~0x1f) asm_RL_A(), asm_RL_A(), asm_RL_A(), asm_ORL_A(0x03), asm_CLR_ACC_2(), asm_PUSH_ACC(),//压入中断地址低8位 asm_CLR_A(), asm_PUSH_ACC(),//压入中断地址高8位 asm_RET()//跳入对应的中断地址 isrNum * 8 + 3 };
大家可以看到,CJNE后执行了asm_CPL_C(),将CY取反了,所以呢后面的JNC当然就是不跳转了 顺序执行3次左移,OR0x03,这时ACC中的0xEF就变成了0x7f,再把ACC.2清零,就变成了0x7B, 因此R7=0x20是就会跳到0x007B这个地址了,而这个地址上只有条LJMP UserISR0f的跳转指令,所以最后执行的是UserISR0f中断服务程序。
这个程序中,JNC的跳转偏移lable__HotASM_ISR__Error是负的,也就是当A>0x20时往上面跳,跳到asm_CPL_C()这句开始执行,所以R7>0x20时也会执行和R7 =0x20时一样的操作 也就是说R7 >=0x20是就执行错误处理,一律跳到0x007B处,再跳到UserISR0f处的中断服务程序,从而实现“//非法软中断被拦截到0x0f”。
asm_CPL_C(),将CY取反这条指令也是设计得很巧妙的,既处理了不同状态,也和JNC配合避免了往回跳转的多次重入,没有的话就会变成个死循环了哦。
好了, 这节课基本上讲完了, 这个精灵鬼怪的老顽童,脑子实在是太好使了, 大家看他的程序时必须得时时留神哦,否则一不小心就会被他耍的团团转啊...
都说天才和疯子只有一步之遥,智商太高的人要么是天才,要么就是疯子 以老Hotel那高达250的非人类智商,正常时当然肯定还是天才啦 所以他平时说的话、写的程序大家看不懂或者很难看懂也是正常的,毕竟离疯子只有一步之遥嘛
看来老x还真是有先见之明啊,一开始这个贴就没取错题目哦 “红杏出墙装疯拳”--没错!就是天才老顽童边玩红杏出墙边装疯玩出来的拳法嘛~~~嘎嘎嘎嘎...
什么? 不是装疯?? 是“红杏出墙疯状拳”??? 老Hotel本来就是....
停停停停停! 这话你可不能乱说哦,这...这要是让老Hotel听到了,那后果可真是太严重了... 老Hotel算账就去找你啊,可千万别找俺老x。。。 这....这光想想就够吓人的了,老x我好怕怕.... 我...我闪~~~~
|
|
HotC51 发表于 2009-3-11 20:01 侃单片机 ←返回版面 |
70楼: 菜农要求给67楼穿裤子~~~答出:MOV A,R7“尿童即可毕业,85分吧 |
首先,asm_MOV_A(0xEF),应该为mov a,#0xef. 开始俺定义asm_MOV_A_DATA(0xEF),因为宏不支持"#"号,所以用DATA替代。 后来发现太罗嗦,就变为asm_MOV_A(0xEF)。
第2个关键就是asm_CLR_ACC_2(),即clr acc.2.
因为非法的ISRNum在0~0x1f,当然执行指令mov a,r7.
>=0x20都为非法ISRNum,故都执行mov a,#0xef. 移位后都会变为0x7f,这不满足ISR*8+3,故必须变为0x7b,所以clr acc.2
实际最为精彩的是asm_CJNE_R7(0x20, size_CPL_C() +size_MOV_A() - 1) 里的“-1”,这条“语句”“一箭三雕”~~~
1.反逆向技术: 因为一条指令“双意”,这个分支要打印2次,试想每条分支都采用此技术~~~ 反汇编者就被饿死了~~~
一会就有人找俺“逆向”,俺说:“若采用了俺的技术,俺不玩”~~~
2.合法语句执行mov a,r7 3.非法语句执行mov a,#0xef
当然还有那个=0x20继续和>0x20回跳后的cpl c.
还有最后那个asm_RET()//
它不是本函数的ret语句!!!本函数就不存在ret语句~~~
这个函数只是为了保存“断点”而生存~~~ 它的ret在哪里???
在ISR()最后那个reti就是本函数的“rei”~~~
因为: asm_PUSH_ACC(),//压入中断地址低8位 asm_CLR_A(), asm_PUSH_ACC(),//压入中断地址高8位 asm_RET()//跳入对应的中断地址 isrNum * 8 + 3 “变魔术”的跳入ISR()~~~
菜农从来就事论事~~~
这次xwj点评的很精彩~~~不过还没参透“反逆向技术”的“真谛”~~~
|
|
文章评论(0条评论)
登录后参与讨论