foolish 发表于 2015-11-30 14:02

易语言逆向分析

本帖最后由 foolish 于 2015-11-30 14:09 编辑

0x1 背景
之前看到论坛上易语言的恶意样本,就用这个样本对易语言进行分析学习,整理了几个易语言逆向分析的技巧,希望对你有帮助。
本文以下面的样本为例进行说明:
样本名称:1.exe
样本MD5:ab58a0167e3f318226e1937e983be3d0
样本SHA1:f28d8d6a4074ba296209e69e4a59e278cadb95e7
样本来源:http://www.52pojie.cn/thread-430260-1-1.html
0x2 支持库加载过程
易语言生成的程序,运行后,首先进行的操作是在Temp目录下创建新的文件夹,并将自身所需要的支持库释放到该目录下,文件后缀为.fnr、.fne等等,如下图
http://i.imgur.com/oASfph3.png接着会使用函数LoadLibraryA将支持库krnln.fnr加载到内存中,使用GetProcAddress获取其导出函数GetNewSock的地址,并通过call eax调用进行初始化的相关操作。
http://i.imgur.com/c4bFsit.png初始化前后,第二个data段的对比
http://i.imgur.com/uYkxso6.png可以看到,当程序执行到0x46DD99时,0x46DDD0-0x46DE12处的函数地址已经填充完毕,程序通过这些函数来执行操作。
而其它支持库(如:eAPI.fne、EThread.fne)的调用过程是通过krnln.fnr来完成的,同样也会使用LoadLibraryA来加载支持库,GetProcAddress获取函数GetNewInf地址,并进行调用,同样,该函数的功能也是进行相关初始化操作。
易语言实现各种功能,最终还是需要调用系统函数,但是如何进行调用的,我们如何扒开这些外壳,来找到系统函数的调用地址。
0x3 函数调用
程序所实现的功能,需要依赖于系统动态链接库中的各种函数。通过分析发现,易语言的程序有两种函数调用,一是直接调用系统动态链接库函数,二是调用其它支持库函数。但两种函数的调用,都是经过支持库krnln.fnr发起。

- krnln.fnr --> 系统动态链接库函数
- krnln.fnr --> 其它支持库(如:eAPI.fne、EThread.fne) --> 系统动态链接库函数

下面对两种调用进行分析,围绕如何调用和参数传递来介绍
0x3.1 直接调用系统函数
以Process32Next为例,说明一下调用过程:
0046D05D    B8 06000000   mov eax,0x6                              ; ID号,用来指明调用哪个函数
0046D062    E8 A50D0000   call 1.0046DE0C                        ; 获取并调用该函数
0046D067    3965 EC         cmp dword ptr ss:,esp
当执行到0x46D062时,此时栈中的数据,就是所调用函数的参数
0012FA48   00000108
0012FA4C   00242820
F7进入该函数
0046DE0C- FF25 01C44600   jmp dword ptr ds:            ; krnln.1002CC84
再次F7进入到函数内部,在分析时,可以把此部分当作特征,定位调用系统函数的位置
1002CC84    50            push eax                                 ; 传入ID号
1002CC85    E8 E7FFFFFF   call <krnln.GetProcAddress_ByID>         ; 获取函数地址
1002CC8A    83C4 04         add esp,0x4
1002CC8D    FFE0            jmp eax                                  ; 调用该函数
执行到0x1002CC8D,F7进入,看栈中数据
0012FA44   0046D067/CALL 到 Process32Next 来自 1.0046D062
0012FA48   00000108|hSnapshot = 00000108 (window)
0012FA4C   00242820\lppe
这样就完成了一次调用过程。
还有一个问题没有解决,如何通过ID号,来判断要获取的是哪个函数的地址呢?猜测在程序中应该有一个表,跟到GetProcAddress_ByID函数中,来看一下如何从ID号获取的函数名称。在该函数内部,我们找到下面的代码
这样就完成了一次调用过程。
还有一个问题没有解决,如何通过ID号,来判断要获取的是哪个函数的地址呢?猜测在程序中应该有一个表,跟到GetProcAddress_ByID函数中,来看一下如何从ID号获取的函数名称。在该函数内部,我们找到下面的代码
得到函数名称列表
0015E5380040947AASCII "UnhookWindowsHookEx"
0015E53C0040948EASCII "OpenProcess"
0015E5400040949AASCII "ZwResumeProcess"
0015E544004094AAASCII "CloseHandle"
0015E548004094B6ASCII "CreateToolhelp32Snapshot"
0015E54C004094CFASCII "Process32First"
0015E550004094DEASCII "Process32Next"
0015E554004094ECASCII "GetModuleHandleA"
0015E558004094FDASCII "SetWindowsHookExA"
0015E55C0040950FASCII "RtlAdjustPrivilege"
0015E56000409522ASCII "ZwSuspendProcess"
0015E56400409533ASCII "ClipCursor"
0015E5680040953EASCII "CallNextHookEx"
有了这个函数列表,也知道了参数是如何进行传递的,这就可以当函数执行到0x46D062时,我们就知道本次调用的函数是哪个,也知道传入的参数有哪些,无需再进行krnln模块中进行分析。同时,我们还可以使用指令搜索,来获取哪个函数会在什么地址处被调用。
例如:我们想知道函数CreateToolhelp32Snapshot在哪些被调用,查找函数名称表,可以知道该函数的ID号为4。我们就可以查找命令序列
mov eax,0x4
call 0046DE0C
来定位调用该函数的地址。
0x3.2 调用易语言模块的函数
以调用EThread支持库中某函数为例
0046D645    BB 00000000   mov ebx,0x0
0046D64A    B8 04000000   mov eax,0x4
0046D64F    E8 9A070000   call 1.0046DDEE
通过eax、ebx来计算需要调用的模块及函数地址,而函数调用的参数则通过栈来传递,F7进入该函数
0046DDEE- FF25 05C44600   jmp dword ptr ds:            ; krnln.1002CC8F
再次F7,进入到函数内部,该部分代码片段可以用来定位调用支持库函数的地址
1002CC8F    8B15 70861110   mov edx,dword ptr ds:
1002CC95    8B1482          mov edx,dword ptr ds:         ; 读取函数地址
1002CC98    85D2            test edx,edx
1002CC9A    75 0D         jnz Xkrnln.1002CCA9                      ; 若不为0,则跳过函数0x1005BA00
1002CC9C    51            push ecx
1002CC9D    53            push ebx
1002CC9E    50            push eax
1002CC9F    E8 5CED0200   call krnln.1005BA00                      ; 计算获取地址
1002CCA4    8BD0            mov edx,eax
1002CCA6    58            pop eax
1002CCA7    5B            pop ebx
1002CCA8    59            pop ecx
1002CCA9    03DA            add ebx,edx
1002CCAB    8D5424 08       lea edx,dword ptr ss:
1002CCAF    83EC 0C         sub esp,0xC
1002CCB2    52            push edx
1002CCB3    FF7424 14       push dword ptr ss:
1002CCB7    C74424 08 00000>mov dword ptr ss:,0x0
1002CCBF    C74424 0C 00000>mov dword ptr ss:,0x0
1002CCC7    C74424 10 00000>mov dword ptr ss:,0x0
1002CCCF    8D5424 08       lea edx,dword ptr ss:
1002CCD3    52            push edx
1002CCD4    FF13            call dword ptr ds:                  ; 调用函数
1002CCD6    8B4424 0C       mov eax,dword ptr ss:
1002CCDA    8B5424 10       mov edx,dword ptr ss:
1002CCDE    8B4C24 14       mov ecx,dword ptr ss:
1002CCE2    83C4 18         add esp,0x18
1002CCE5    C3            retn
当程序执行到0x1002CCD4时,栈中的数据如下:
0012FA54   0012FA60
0012FA58   00000003
0012FA5C   0012FA74a3
0012FA60   00000000
0012FA64   00000000
0012FA68   00000000
0012FA6C   0046D654返回到 1.0046D654 来自 1.0046DDEE
0012FA70   00000003
0012FA74   0046DA791.0046DA79
ebx的值为0x155C97C,而ds:=015510D0 (EThread.015510D0),由于EThread模块未进行加密,我们可以通过IDA来加载EThread模块进行分析,加载时,请注意选择Manual load,根据OD中该模块所在的基地址进行设置,这样方法定位函数位置。
在IDA中,定位到函数0x15510D0
void *__cdecl sub_15510D0(int a1, signed int ThreadId, int a3)
{
int v3; // esi@1
signed int v4; // edi@1
void *v5; // eax@3
void *result; // eax@5

v3 = a3;
v4 = ThreadId;
if ( ThreadId > 1 && *(_DWORD *)(a3 + 20) )
    v5 = *(void **)(a3 + 12);
else
    v5 = 0;
result = CreateThread(0, 0, *(LPTHREAD_START_ROUTINE *)a3, v5, 0, (LPDWORD)&ThreadId);
*(_DWORD *)a1 = result != 0;
if ( v4 >= 3 && *(_DWORD *)(v3 + 32) )
    **(_DWORD **)(v3 + 24) = result;
else
    result = (void *)CloseHandle(result);
return result;
}
可以很清楚的看出该函数的功能是创建线程执行函数,而关键的参数则是a3,通过栈中的数据,得到a3 = 0x0012FA74,而该地址所指向的函数为:0046DA79,当函数0x15510D0执行完成后,会返回到地址0x1002CCD6,再通过0x1002CCE5返回到程序空间,完成一次支持库的调用。如果模块被加密,无法静态分析,可以在模块加载的时候进行分析,找出加密算法,编写IDA脚本进行解密,或是直接进入动态跟踪进行分析。
0x4 分析方法
下面来介绍一下我的分析方法,以及如何快速定位核心代码,希望对你有帮助

[*]OD载入样本,CTRL+F查找指令call eax,CTRL+L查找下一条,找到下面的位置0040150E|.FFD0          call eax
00401510|.EB 11         jmp X1.00401523
00401512|>6A 10         push 0x10                              ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401514|.68 30704000   push 1.00407030                        ; |Title = "Error"
00401519|.FF75 FC       push                            ; |Text
0040151C|.53            push ebx                                 ; |hOwner
0040151D|.FF15 AC604000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
00401523|>5F            pop edi
00401524|.5E            pop esi
00401525|.33C0          xor eax,eax
00401527|.5B            pop ebx
00401528|.C9            leave
00401529\.C2 1000       retn 0x10
[*]F4运行到0x40150E,在此之前的代码可以忽略掉,它们的功能是释放所需要的各种支持库,并加载krnln.fnr,F7进入。1002D69A    55            push ebp
1002D69B    8BEC            mov ebp,esp
1002D69D    6A 00         push 0x0
1002D69F    6A 00         push 0x0
1002D6A1    8B45 08         mov eax,dword ptr ss:
1002D6A4    50            push eax
1002D6A5    B9 78861110   mov ecx,krnln.10118678
1002D6AA    E8 B0F4FFFF   call krnln.1002CB5F
1002D6AF    5D            pop ebp
1002D6B0    C2 0400         retn 0x4
[*]F4运行到0x1002D6AA,F7进入。1002CB5F    55            push ebp
1002CB60    8BEC            mov ebp,esp
1002CB62    83EC 08         sub esp,0x8
1002CB65    53            push ebx
1002CB66    56            push esi
1002CB67    57            push edi
1002CB68    894D F8         mov dword ptr ss:,ecx
1002CB6B    FF15 E4630E10   call dword ptr ds:[<&KERNEL32.GetProcess>; kernel32.GetProcessHeap
1002CB71    8B4D F8         mov ecx,dword ptr ss:
1002CB74    8981 A8040000   mov dword ptr ds:,eax
1002CB7A    8B55 F8         mov edx,dword ptr ss:
1002CB7D    8B82 C4000000   mov eax,dword ptr ds:
1002CB83    83C0 01         add eax,0x1
1002CB86    8B4D F8         mov ecx,dword ptr ss:
1002CB89    8981 C4000000   mov dword ptr ds:,eax
1002CB8F    8B55 10         mov edx,dword ptr ss:
1002CB92    52            push edx
1002CB93    8B45 0C         mov eax,dword ptr ss:
1002CB96    50            push eax
1002CB97    8B4D 08         mov ecx,dword ptr ss:
1002CB9A    51            push ecx
1002CB9B    8B4D F8         mov ecx,dword ptr ss:
1002CB9E    E8 FD1F0300   call krnln.1005EBA0
1002CBA3    FFD0            call eax
[*]F4运行到0x1002CBA3,F7进入,至此,已经回到了用户空间,从现在开始才到了程序实现功能的位置,从这里开始进行分析。进入每一个jmp的地址,找出直接调用系统的地址和调用自带支持库的地址,对两个地址下断点,就可以直接F9运行,直接后续的分析操作了。0046DD99    FC            cld
0046DD9A    DBE3            finit
0046DD9C    E8 F7FFFFFF   call 1.0046DD98
0046DDA1    68 84DD4600   push 1.0046DD84
0046DDA6    B8 03000000   mov eax,0x3
0046DDAB    E8 32000000   call 1.0046DDE2
0046DDB0    83C4 04         add esp,0x4
0046DDB3    68 01000152   push 0x52010001
0046DDB8    E8 1F000000   call 1.0046DDDC
0046DDBD    83C4 04         add esp,0x4
0046DDC0    E8 11000000   call 1.0046DDD6
0046DDC5    6A 00         push 0x0
0046DDC7    E8 04000000   call 1.0046DDD0
0046DDCC    83C4 04         add esp,0x4
0046DDCF    C3            retn
0046DDD0- FF25 21C44600   jmp dword ptr ds:            ; krnln.1002D56F
0046DDD6- FF25 25C44600   jmp dword ptr ds:            ; krnln.1002D4D2
0046DDDC- FF25 29C44600   jmp dword ptr ds:            ; krnln.1002D505
0046DDE2- FF25 2DC44600   jmp dword ptr ds:            ; krnln.1002CC6A
0046DDE8 >- FF25 1DC44600   jmp dword ptr ds:            ; krnln.1002D66A
0046DDEE- FF25 05C44600   jmp dword ptr ds:            ; krnln.1002CC8F
0046DDF4- FF25 11C44600   jmp dword ptr ds:            ; krnln.1002D4AB
0046DDFA- FF25 09C44600   jmp dword ptr ds:            ; krnln.1002CCE6
0046DE00 >- FF25 15C44600   jmp dword ptr ds:            ; krnln.1002D58C
0046DE06- FF25 FDC34600   jmp dword ptr ds:            ; krnln.1002D46E
0046DE0C- FF25 01C44600   jmp dword ptr ds:            ; krnln.1002CC84
0046DE12- FF25 0DC44600   jmp dword ptr ds:            ; krnln.1002D48C
0x5 最后
通过这一个样本的分析,我们知道了一些易语言分析的规律,支持库的加载过程、函数的调用过程。分辨出支持库的加载过程可以让我们跳过不必要的分析,直接进入功能代码分析;而函数的调用过程更像是把握住了程序的两个入口,这对于逆向分析,判断样本的功能具有极大的作用。


常黑屏 发表于 2015-11-30 14:07

感谢分享。

forumid 发表于 2015-11-30 14:25

有很强的指导意义!谢谢lz分享!

xjun 发表于 2015-11-30 14:26

静态编译有加载支持库吗?

foolish 发表于 2015-11-30 15:10

xjun 发表于 2015-11-30 14:26
静态编译有加载支持库吗?

有的,被打包到程序中了。运行之前先释放。

xjun 发表于 2015-11-30 17:18

foolish 发表于 2015-11-30 15:10
有的,被打包到程序中了。运行之前先释放。

静态编译的程序不会释放支持库文件

foolish 发表于 2015-11-30 19:59

xjun 发表于 2015-11-30 17:18
静态编译的程序不会释放支持库文件

谢谢提醒,可否发一个静态编译的程序,我来分析一下。

a422 发表于 2015-12-8 20:31

那么易语言的开发更像是搬砖 苦力活,就是转换一下。。。

夲跑的小蜗牛 发表于 2015-12-9 14:15

前排板凳学习,说不定什么时候就能遇上了

356987544 发表于 2016-5-16 10:13

好东西,在平时下载别人的软件的时候偶尔能遇到这些报毒的文件,楼主给了一个超赞的分析教程。学习了!
页: [1] 2
查看完整版本: 易语言逆向分析