vc+ mfc 方法怎么被调用_调call和偷功能时,VC中内联汇编容易产生的错误

 最近,有不少刚学习内联汇编的网友和同学跟我聊,他写的内联汇编代码总是出现一些莫名其妙的错误,看上去明明是对的,却怎么也执行不成功。

        甚至有的是,代码注入器都可以测试成功的汇编代码,复制到VC里不行了!感到很困惑。

那么,今天我们就一起来探讨一下这个问题,帮助刚学习内联汇编的同学和刚逆向入门的同学少走些弯路,少遇一些坑。

9aa420ffa3a0bdd1f19add6ffd902622.png

 我们先来看下什么是内联汇编?

内联汇编,指在C语言中插入汇编语言。

       内联汇编可以帮我们做什么?

1.可以帮我们在C语言中插入汇编代码。

2.可以帮我们调用目标程序的子程序(CALL)。

3.可以协助我们偷功能。

2和3  是可以做一些羞羞的事的,功能很强大

36fbd66862dfb9b2a04ec2b4599c1c25.png

我们先来看一个简单的例子

ef09b436802052a773af1b5c659a4c70.png

下面的反汇编窗口是一个小游戏的吃药函数

push的第一个参数是 push 0

push的第二个参数是 push edx  

edx 是表示的药品位置(当然正常情况 我们不会选择这样参数的函数调用,位置参数一定

是没有ID 参数调用方便的,这里只是随便举一个简单例子)

call004FACD0  只是为了取一个返回值eax

然后把返回值赋值给  参数ECX

即mov  ecx,eax

最后调用函数  call 00491C50

bef7423c1a70b3a8016585b8c0f1bf87.png

函数我们分析清楚了,就可以写代码了

首先先在代码注入器中编写代码

代码注入器中测试成功的代码(药品我们选择第一个位置的 ,edx 为0)

我们看到除了变化的参数我们需要给其赋值以外,其他的代码就照抄的

476ba18dc57871f7b4702356306e1189.png

然后我们到VC中进行内联汇编的编写

da83149edab853d4d61aea81eb993757.png

这里需要注意的只有两点

第一点,从反汇编窗口复制出来的数值都是16进制的,到VC里要加上个0x

例如  004F1CD0  写成  0x004F1CD0

第二点,call  立即数  语法是不合法的   需要用寄存器转一下

call0x00491C50   写成  mov  eax,0x00491C50   call eax 

上面的例子可以看出内联汇编写法很容易

__asm{},把自己想插入的汇编代码直接按照汇编的语法编写即可。

166544a265b4486eb4f95cd24dd59901.png

感觉完了?不,才开始=。=

以上只是最简单的情况,在我们编写的过程中可能存在很多错误,主要表现为三种错误。

第一种错误,编译错误

这种错误编译器会提示我们,这种错误没什么难度,如下

cd79724f4e404c0681a2b5597e6bab6a.png

不能直接call 立即数

7ca62dec48c837171b135c5fd6303849.png

标点符号要英文的

a99fa3e372d97d93a3dab976dd810dec.png

立即数不能作为左值

根据提示修改即可。

第二种,运行错误,当然编译器是不会管我们的,所以我们就要额外小心的使用内联汇编。

我们拿个例子看下

例如代码注入器测试成功的代码

694fbd190be93e61fbd421a317831ea9.png

我们按照规则修改成内联汇编代码如下

也编译通过了

9ccd65a805b31d604e1b73db8d5b15ab.png

但是这段内联汇编代码测试了好多次

调用一定是会崩溃程序的(代码注入器的完全没有问题)

为什么?

14f221605ee28b2e9794e6de8c3c2617.png

那我们可以选择逆向我们自己的代码来看看情况,由于我写的是MFC dll

需要注入到游戏中运行的,所以我们附加上这个小游戏 ,然后去看我们的代码

首先我们在自己的内联汇编中加一些特征  ,方便我们直接定位跳转过去。

9847c53c134022309da2818677a32471.png

这样的特征,正常代码 是打死也不会有的。

点开E

d48c4688965cafb321da81f3519a97e8.png

找到我们的模块 我们的DLL.dll

ee97eba4d056f72b7e9593655623726d.png

入口地址 37542D16

CTRL+G 跳过去

701dbb1275c1fe422515ab0cc02528b3.png

baf2ae8a4989dcd5a08b9c9941fc05b5.png

bd3ad5f275bdfc23e144231e91fc6dfa.png

CTRL + S 搜索我们的特征码

59582d1b64f1fac7207a312b3285e97e.png

找到了我们的内联汇编代码

89547241f728c05cd97d313ab584e186.png

我们来对比一下代码

52ed2ec3cfe77770e2556c052a94294a.png

78d5bdcac1db9172b2e7b2b34f9d06d7.png

是不是发现 我们代码 mov  ecx,[0x00D0DF1C]  的括号没有了

有的人会说,  这是你没有加  dword ptr 

那么我们加一下试试

76e15edc492a81ec90120a22c8e5f004.png

发现代码依然是这样的

78d5bdcac1db9172b2e7b2b34f9d06d7.png

那么怎么办?

我们怎么写才能不出错误

代码写成这样就可以了

34a0b38e14e0da52989b72c47eebcfac.png

这是根本看不出来的可能的错误,一个是靠我们已经有的经验解决

另外一个就是,我们出现问题 ,用上面的方法到游戏里定位问题,解决问题,永远是最好的方法。

第三种,可以预知的运行错误

d8761047f4ec72fba022a5f573e1f313.png

958be9e25cdb7cea081d47801be0b4ab.png

看上去没有什么错误,但是实际上这样运行是百分百会崩溃的。

原因很简单,汇编代码不合法!

有的同学会说哪里不合法啊?

我们看int a[3] = {7,键码,1}; 一个数组,在逆向的本质里,其实就是3个局部变量而已

[ebp-4],[ebp-8],[ebp-C]

而 a是这个数组的首地址  即ebp-C

那么我们看  下面的内联汇编中

push a 是否合法

push  a  等价于  push  ebp - C

那么当然是不合法的!

因为不合法 ,编译器 不会编译出现不合法的代码的

他强制将代码编程成  push [ebp-c]  这样参数含义完全变化,程序执行过程自然出现错误,导致崩溃也是必然的了。

想要解决

所以我们要改成以下代码

正确的方法一

我们 lea ebx,a[0]

相当于 lea ebx,[ebp-C]

然后再 push ebx

这样就合法了

如下图

c3ee787de3eba416b1b59df99eeba069.png

正确的方法二

把指针转换成  用局部变量存放 然后再PUSH

77c9c09ec1680372b111c529e517e97e.png

edfc89ed7f0bfef708d25410a1a2d10a.png

总之,使用内联汇编,一定要对其机制比较了解

否则稍不小心,会在上面浪费很多调试时间

59097bcbd281baf23f17ba677a0f40fe.png

 处于好奇心,我也百度了一下,看看是不是也有很多人出现过类似的问题,果然找到了几个,我已经对其进行了一一解答。简单截图2个具有代表性的问题 我们一起看一眼

3940c142f52ac9f14aafe05a2fc2e99a.png

上面的例子,就是我们第二种错误的情况

代码需要写成

push 0

moveax,0x1E22DD8

moveax,[eax]

push eax

push 0

这样就可以了

8656f2386b783d20579dc52dd203b3be.png

上面的例子,就是我们第三种错误的情况

编译器认为代码不合法,强行编译的错误

并不是内联汇编不把字符串名当指针看待,而是你的汇编代码不合法,

编译器为了能够编译,编译结果自然和你想的不是一样的了。

a 是字符串名,看上去 mov  ebx,a   就可以了。

但是  真要是编程出来了,你还认识这样的汇编代码吗? mov  ebx, ebp-xx ????

8123d7b58baba6cb32f9604274cd736f.png

他真要编译出来是不是你认都不敢认,没见过这样的代码啊!

所以他只能编译成合法的代码吧。 那就是mov  ebx,[ebp-xx], 也就是你所谓的,把指针硬当成变量看,编译器心里苦啊,是您逼迫我这样干的!

偷功能

偷功能其实和写call  没有什么本质的区别了,只是需要抄下来的代码更多一些

更确切的说是复制粘贴出来修改的代码更多一些。

61815911d2c9c32d97b3526ea67cf3d6.png

复制出来的汇编代码需要进行初步处理才能写到内联汇编中

例如常数全部要加上0x

例如,call xxxxx  都要用寄存器转一下

例如 mov  eax,[0x12345678] 这种代码也要转一下

等等

还有一个最重要的,就是跳转要修改

例如 jnz  12345678 他是要跳转到目的代码地址执行的,我们偷出来的代码已经不是原来的地址了

所以要通过标签修改跳转的地址。

跳转的写法

我们随便截取一段代码,来说明跳转怎么修改

be36bb981a8e91bd092618336a2ff5ff.png

内联汇编中的写法

__asm

{

     mov esi,[eax]

     cmp  esi,eax

    Je Label1  //         不能再写  je 0x66C66F

Label2:   //                 跳转过来的标签

    mov edi,esi

    mov esi,[esi]

    push 0

    lea ecx, [edi+8]

    mov eax,0x0066CC60

   call  eax

   push 1

   push edi

   mov ecx,ebp

   mov eax,0x0066cc20

   call eax

   cmp esi,[ebp]

   Jnz  Label2     //         不能再写  je 0x66C652

Label1: //                 跳转过来的标签

   mov eax,[ebp]

我们查看自己的代码看是否正确

e1163c470b47db2c95be5dc55db04c36.png

发现是正确的

当然偷功能的时候

我们尽量偷完整的函数代码,而不是中间一段

并且要传递正确的参数,才能执行成功。

绝对地址跳转

257aacd220fa8e779c32c71721c6f513.png

同样是不可以直接 jmp   xxxx

29e02aa672052677fbe912847c5f1960.png

dc73773895b23e59e04c6b1abcaa5f16.png

这样就可以了

如果是JMP 这样写就可以了

因为我们知道 jmp 的操作数可以是寄存器

而JNZEAX  是不合法的

jnz 等条件跳转怎么绝对地址跳转呢

8ede74780166c6437da266f8458cb463.png

7c06f216f78be8f087ffa81f886a96e2.png

不可以

我们增加代码 继续尝试

627f705a47c31cd163f0a0f7ecbbe7cb.png

489a9d4508106f521592feddcaedfe49.png

发现无论你后面立即数写多少 都是没用的

他只是跳到下一条

因为   立即数  都被编译成了  00000000

目前已经知道解决方案有3个

一个是用标签

一个是自己判断数据,进行跳转

最后一个方法就是 自己计算跳转值  在代码里进行修改了,这种方式推荐裸函数,容易计算偏移

608091da5d7968832b5f599eb2ba5627.png

现在确实是不对的

那么我们  自己改写即可

00f2a9238e5050d874b8a9524c8fe7bb.png

0x371C5960 到 0x371C5984 是0x24字节  加上  0F84  的两字节

偏移是 0x26字节

也就是说 我们要修改的地址 是从函数头部+0x26的位置

我们修改成的值 是  跳转目标  -  本条代码地址  -5

既下面代码

DWORD temp =0x12345678 - ((DWORD)&aaa+0x24) -5 ;

然后把值写入  函数头部+0x26的位置

*(DWORD*)((DWORD)&aaa+0x26)= temp;

b9e1310ec6ada93f8cadbbdb8798f724.png

调用以后的效果

c9834d2790c752268a1eeedefdc85df9.png

相当于串改了代码,不过是自己的代码

值得注意的是,如果函数有Jmp跳转需要再加入计算。

先讲到这里,欢迎补充和探讨!

422e9073ada42afa5c4db115c94c257c.png

飞郁网络培训:www.feiyuol.com

微信公众号:任鸟飞逆向

腾讯课堂:feiyuol.ke.qq.com        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值