调call和偷功能时,VC中内联汇编容易产生的错误

社区服务
高级搜索
猴岛论坛电脑百科调call和偷功能时,VC中内联汇编容易产生的错误
发帖 回复
倒序阅读 最近浏览的帖子最近浏览的版块
3个回复

[网站源码]调call和偷功能时,VC中内联汇编容易产生的错误

楼层直达
o任鸟飞o

ZxID:86155661

等级: 上等兵
举报 图酷模式  只看楼主 使用道具 楼主   发表于: 2019-06-20 0
— 本帖被 步行南街 从 和平精英 移动到本区(2019-06-24) —
  最近,有不少刚学习内联汇编的网友和同学跟我聊,他写的内联汇编代码总是出现一些莫名其妙的错误,看上去明明是对的,却怎么也执行不成功。
    
        甚至有的是,代码注入器都可以测试成功的汇编代码,复制到VC里不行了!感到很困惑。
那么,今天我们就一起来探讨一下这个问题,帮助刚学习内联汇编的同学和刚逆向入门的同学少走些弯路,少遇一些坑。
        

[attachment=10285957]

       我们先来看下什么是内联汇编?
内联汇编,指在C语言中插入汇编语言。

       内联汇编可以帮我们做什么?
1.可以帮我们在C语言中插入汇编代码。
2.可以帮我们调用目标程序的子程序(CALL)。
3.可以协助我们偷功能。
2和3  是可以做一些羞羞的事的,功能很强大

[attachment=10285959]

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

[attachment=10285960]

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

push的第一个参数是 push 0
push的第二个参数是 push edx  
edx 是表示的药品位置(当然正常情况  我们不会选择这样参数的函数调用,位置参数一定是没有ID 参数调用方便的,这里只是随便举一个简单例子)

call 004FACD0  只是为了取一个返回值eax
然后把返回值赋值给  参数ECX
即mov  ecx,eax
最后调用函数  call 00491C50




函数我们分析清楚了,就可以写代码了
首先先在代码注入器中编写代码


代码注入器中测试成功的代码(药品我们选择第一个位置的 ,edx 为0 )
我们看到除了变化的参数我们需要给其赋值以外,其他的代码就照抄的



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



这里需要注意的只有两点
第一点,从反汇编窗口复制出来的数值都是16进制的,到VC里要加上个0x
例如  004F1CD0  写成  0x004F1CD0

第二点,call  立即数  语法是不合法的   需要用寄存器转一下
call 0x00491C50   写成  mov  eax,0x00491C50   call eax  


上面的例子可以看出内联汇编写法很容易
__asm{},把自己想插入的汇编代码直接按照汇编的语法编写即可。




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

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

第一种错误,编译错误

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



不能直接call 立即数



标点符号要英文的




立即数不能作为左值

根据提示修改即可。



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

我们拿个例子看下


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




我们按照规则修改成内联汇编代码如下
也编译通过了



但是这段内联汇编代码测试了好多次
调用一定是会崩溃程序的(代码注入器的完全没有问题)
为什么?



那我们可以选择逆向我们自己的代码来看看情况,由于我写的是MFC dll
需要注入到游戏中运行的,所以我们附加上这个小游戏 ,然后去看我们的代码
首先我们在自己的内联汇编中加一些特征  ,方便我们直接定位跳转过去。



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


点开E

[attachment=10286109]

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



入口地址  37542D16


CTRL+G 跳过去





回车来到模块领空




CTRL + S  搜索我们的特征码



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



我们来对比一下代码






是不是发现 我们代码  mov  ecx,[0x00D0DF1C]  的括号没有了
有的人会说,  这是你没有加  dword ptr
那么我们加一下试试




发现代码依然是这样的




那么怎么办?
我们怎么写才能不出错误
代码写成这样就可以了



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

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


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




看上去没有什么错误,但是实际上这样运行是百分百会崩溃的。
原因很简单,汇编代码不合法!


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


我们看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
这样就合法了
如下图




正确的方法二
把指针转换成  用局部变量存放 然后再PUSH



总之,使用内联汇编,一定要对其机制比较了解
否则稍不小心,会在上面浪费很多调试时间






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

1.




上面的例子,就是我们第二种错误的情况
代码需要写成

push 0
mov eax,0x1E22DD8
mov eax,[eax]
push eax
push 0

这样就可以了



2.

上面的例子,就是我们第三种错误的情况
编译器认为代码不合法,强行编译的错误


并不是内联汇编不把字符串名当指针看待,而是你的汇编代码不合法,
编译器为了能够编译,编译结果自然和你想的不是一样的了。

a 是字符串名,看上去 mov  ebx,a   就可以了。
但是  真要是编程出来了,你还认识这样的汇编代码吗? mov  ebx, ebp-xx ????




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



偷功能


偷功能其实和写call  没有什么本质的区别了,只是需要抄下来的代码更多一些
更确切的说是复制粘贴出来修改的代码更多一些。



复制出来的汇编代码需要进行初步处理才能写到内联汇编中
例如常数全部要加上0x
例如,call xxxxx  都要用寄存器转一下
例如 mov  eax,[0x12345678] 这种代码也要转一下
等等


还有一个最重要的,就是跳转要修改
例如 jnz  12345678  他是要跳转到目的代码地址执行的,我们偷出来的代码已经不是原来的地址了
所以要通过标签修改跳转的地址。


跳转的写法


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



内联汇编中的写法
__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]
}


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


发现是正确的


当然偷功能的时候
我们尽量偷完整的函数代码,而不是中间一段
并且要传递正确的参数,才能执行成功。


绝对地址跳转











这样就可以了

如果是JMP 这样写就可以了
因为我们知道 jmp 的操作数可以是寄存器
而JNZ EAX  是不合法的



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






不可以


我们增加代码 继续尝试






发现无论你后面立即数写多少 都是没用的
他只是跳到下一条
因为   立即数  都被编译成了  00000000


目前已经知道解决方案有3个
一个是用标签

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


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

现在确实是不对的
那么我们  自己改写即可



0x371C5960  到 0x371C5984  是0x24字节  加上  0F84  的两字节
偏移是 0x26字节
也就是说 我们要修改的地址 是从函数头部+0x26的位置
我们修改成的值 是  跳转目标  -  本条代码地址  -5
既下面代码
DWORD temp = 0x12345678 - ((DWORD)&aaa+0x24) -5 ;
然后把值写入  函数头部+0x26的位置
*(DWORD*)((DWORD)&aaa+0x26) = temp;


调用以后的效果



相当于串改了代码,不过是自己的代码
值得注意的是,如果函数有Jmp 跳转需要再加入计算

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





o任鸟飞o

ZxID:86155661

等级: 上等兵
举报 只看该作者 沙发   发表于: 2019-06-20 0
步行南街

ZxID:85871973

等级: 禁止发言
举报 只看该作者 板凳   发表于: 2019-06-20 0
感谢分享
« 返回列表
发帖 回复