之前只是知道怎样脱去fsg壳,对壳的压缩算法没有太多的注意,今天就对算法进行一些分析
- 使用的版本是fsg1.33,首先用peid查壳:
2.将程序载入OD,看到如下代码
可以看到这段代码主要是从以esi为起始的地方取出dword类型的数据存入寄存器中,那么这一段数据是什么呢?在数据窗口中跟随一下:
看到一些长得像代码地址的一些数据,在后面的分析中,也会发现这些地址或者加上偏移就是程序要执行的代码的地址或者是要解压缩的数据的地址
我们单步走到第一个call的位置,可以看到各寄存器的值如下:
而且4001B0被压入了栈中:
这里可以看到上面的那些地址分别被存入了ebx,edi,esi.这里esi的值等于数据窗口中的第三个地址加1,是因为在4103F1处执行了movs指令。这里关于dl和dh的赋值我推测是用来影响标志位从而控制程序执行的流程的,在后面的分析中也发现除了影响标志位以外,EDX没太多参与其他运算
这里的call指令将跳转到ebx指向的代码,跟进去看看:
这段代码后面会经常被执行,可以看到代码主要是对dl进行运算,而运算的目的就是为了修改标志位。还可以看到当zf标志位等于1的时候esi的值将自加1.因此这里除了影响了标志位外,很可能也是在调整将要被解压缩的源地址
继续往下走:
后面又是在重复的执行ebx指向的代码,先往下走:
可以看到费了这么多功夫程序就是调整了esi的值,修改了一些标志位,注意这里eax和ecx被置零了。并不是eax存放的地址没用了而是前面已经压入栈了
这里有一个跳转,跳过去看看:
继续往下走,410426有一个未实现的跳转,走到jmp位置,看各寄存器的值:
jmp指令跳到了410446
单步往下走:
这里有一个movs指令,在数据窗口中跟随一下esi指向的地址:
什么情况,程序在这里用0去覆盖0?其实这里程序是为了抬高edi的值。为什么没说esi,因为esi的值前面在41044D处被压入了栈,在movs执行完后又被弹出了栈。
----------------------------------------------------------------------------------------------------------------------------------------------------
先分析到这里,先吃饭,2015年10月16日11:40:39,by:http://www.cnblogs.com/wd1001/
继续往下走是一个jmp指令,看一下他跳到了那里:
程序又跳回了4103F4,不过到这里标志位和寄存器发生了一些变化,程序的流程可能就跟之前不同了:
看到这个之前未实现的跳转实现了,可以看到下一条将要执行的是movs指令,在数据窗口中跟随一下esi指向的位置:
可以推测出壳程序将要从这里解压出原程序真正的数据
下面是edi指向的位置:
继续单步走直到4103F6处的跳转不再往回跳,此时edi指向的位置的数据变成了:
此时esi和edi的值分别为:40C91D和401009
继续单步走,直到下一个跳转
这之前不过是做了一些调整,修改了eax,ecx,edx和一些标志位
往下看跳到了哪里:
又来到了这个位置,不过这次跟刚才不一样了这时edi减去eax的地方不再是0了,走到410452的时候,esi和edi的值分别是:401004和401009
而此时401004处是刚才movs指令传送过来的数据:
因此这里不仅仅只是为了抬高edi的值了,rep movs后的结果:
后面又是jmp跳回4103F4,因此这里是循环进行解压缩了,解压缩结束后:
edi的值变成了404E9B,而在数据窗口中可以看到401004到404E9B已经有了数据:
继续往下走,看到刚才压入栈中的4001B0,被弹出了栈:
esi变成了4001B0,数据窗口中跟随esi指向位置:
又看到一些类似地址的数据,可以推测接下来将要对另一块数据进行解压缩
继续往下走:
可以看到程序又要往前跳了,而此时edi的值变成了40500,而且esi又被压入了栈中,上面的mov edx esi,将esi的值临时存入了edx,在这里又将edx给了esi.因此接下来的代码解压缩的源地址还是紧接着前面的,即:40EB53
接下来看跳回去的代码:
程序又跳回了这段解压缩程序,只是esi,和edi发生了变化,此时在数据窗口中跟随edi指向的数据:
后面的解压缩算法跟刚才一样,直接跳过解压程序
405000处的数据已经完成了解压缩,跟401004处一样,第一趟走下来只是为了抬高edi,因此实际解压缩的目的地址是405004.后面以406004,407004,40658A为目的地址进行了解压缩
解压缩完成后再看后面的代码:
这里修改了ebx的值,继续往下走:
从这里可以看到被修改的ebx指向了一个系统函数LoadLibrary的地址,而函数的参数是SHELL32.dll
继续往下走是一个循环:
函数的目的是让esi指向SHELL32.dll这个字符串的后一个字符串,在数据窗口中看一下(这一段数据是前面解压缩出来的):
循环结束后是dec语句,把406598处的U改成了T,接下来又是一个跳转到一个dec语句:
将406598处的T改成了S:
接下来是一个大跳转,按enter过去可以看到就是程序的入口了。但这里没有跳,继续往下走
调用了GetProcAddress函数,从栈中可以看出这里载入的函数就是刚才修改了名字后的函数
下面将函数返回的地址存入了edi指向的地址中
往下走是一条jmp指令,程序又跳回了刚才那个循环让esi指向了下一个字符串
因此最后在跳转到程序入口前程序对解压出来的数据进行了调整,即最后解压出来的这一段数据就是程序将要调用的系统函数,不过前面解压出来的函数名的第一个字母需要进行调整,调整完后用GetProcAddress函数载入并将函数地址存放在指定位置最后跳转到正常的入口:
3.至此壳的代码就分析完了,总结一下,其实就是两个大的过程,第一个过程就是从源地址处解压缩数据到目的地址。其中有一段数据是程序将要调用的函数和所在的dll文件名,不过直接解压出来的函数名的第一个字母需要调整,第二个过程就是调整函数名并用GetProcAddress函数载入并将返回值即函数地址存放起来,最后跳转到程序入口。