原创 菜农要求给67楼穿裤子~~~答出:MOV A,R7“尿童即可毕业,85分吧

2009-3-11 20:00 2563 4 4 分类: MCU/ 嵌入式

原帖出处: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点评的很精彩~~~不过还没参透“反逆向技术”的“真谛”~~~

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
4
关闭 站长推荐上一条 /3 下一条