程序脱壳分析

脱壳对比

脱壳是在逆向中比较重要的一门技术,和反调试一样不管是在PC端还是在移动端都是令人比较头疼的一步,所以下面介绍一下两个平台的脱壳技巧(相对比较简单的壳)。

Android脱壳

这里介绍dump脱掉dex的壳
首先载入Android Killer,发现是阿里的壳。
壳
看一下入口类:

android:name="com.ali.mobisecenhance.StubApplication"

明显是加壳了,而且文件结构中只有一个类,典型的加固,使用IDA进行脱壳。

使用调试模式打开APP,使用到的命令:
adb shell am start -D -n com.ali.tg.testapp/.MainActivity


等待我们的调试进程进行附加

我们使用IDA附加进程。来到:/system/lib/libdvm.so 这个库文件中,在导出函数:dvmDexFileOpenPartial的开始位置下断:

下好断点之后,连接调试进程:jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700

输入这条命令之后,JDB就会在等待IDA运行,运行之后JDB就输出:

C:\Users\a>jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700
设置未捕获的java.lang.Throwable
设置延迟的未捕获的java.lang.Throwable
正在初始化jdb...

这个可能失败,要注意看看DDMS怎么个情况。
然后我们程序就会断在:

我们是在dvmDexFileOpenPartial这个函数断下的,所以我们的第一个参数是dex文件的起始地址,第二个参数是dex文件的长度。第一个参数的值存在R0,第二个参数的值存在R1。我们转到R0看一下:


可以看到起始地址就是我们想要要的,然后使用脱壳脚本进行脱壳,R0:0x4E472008,R1:0x941FC

static main(void)
{
    auto fp, begin, end, dexbyte;
    fp = fopen("d:\\dump.dex", "wb"); //打开或创建一个文件
    begin =  0x4E472008;              //dex基址
    end = begin + 0x941fc;            //dex基址 + dex文件大小
    for ( dexbyte = begin; dexbyte < end;dexbyte ++ )
    {
        fputc(Byte(dexbyte), fp);     //按字节将其dump到本地文件中
    }
}

运行这个脚本,会在D盘下生成一个dex文件。


然后我们将脱掉的dex反编译。

最后得到smali文件:
生成文件
然后我们将这两个文件拖进Android Killer的。

新文件
然后我们将我们的入口APP类删除掉,最后就是剩下我们脱掉壳的程序了。

然后我们进行编译!最终就会生成脱壳之后的apk文件。
能够正常运行。

PC端

续之前的帖子:Wker_EXEDebug

首先我们打开Wker_EXEDebug,我相对之前的版本更新了一下,如果你看到了下面的一个界面,说明程序加载成功:
程序启动
我们点击确定就好了,这个时候我们我们使用打开程序的方式进行调试,也就是操作菜单下的打开程序来打开一个程序,目的是为了我们能够在壳的OEP出断下,我们载入我们要脱壳的程序:


载入程序之后发现这个是比较典型的一个压缩壳,这里我们就是用ESP的一个堆栈平衡的脱法,其实很简单,就是在我们ESP改变之后我们对ESP的地址下硬件写入断点就可以,然后让他运行起来,当程序执行出栈的指令的时候我们就可以单步走几步就可以走到程序真正的OEP了,所以我们这里要单步步入(比较关键,就是要这个):

到了这个地方也就是只有ESP和EIP改变的时候我们我们对这个ESP的地址下硬件的一个写入断点(双字就可以了):

可以看到在断点窗口会增加我们的断点,也就是我们ESP的地址。
然后我们点击运行按钮,这个时候程序会断下:

可以看到程序进行了一个跨页面的一个大跳转,看字节数就知道了,说明我们就要走到我们的OEP了,这个时候我们使用单步步入来走,因为我之前讲解我这个程序编写的过程的时候提到过,我这个单步步过是在下一处下断,然后运行,所以不支持这个跳转指令,所以跳转指令我们要用单步步入进行走:

可以看到我们就真的走到了我们程序的入口点,也就是一个push ebp,当然简单的看一下也就会发现这个是一个易语言的程序。

然后我们就可以用LordPE和import REC进行dump和修复了,步骤就比较简单了。

Wker_EXEDebug插件

我这次增加了插件的功能,并且编写了插件的一个例子,也就是可以读取导入模块。
可以发现,在我们程序载入之后,原先的操作菜单换成了插件菜单,我们点击插件发现我给你们带了一个查看程序DLL的一个插件,然后点开之后就会载入一个窗口,查看当前程序载入的模块:

其实保存的插件和我在之前写的那个XSS漏洞检测工具(Wker_XSSTool)里面的插件加载原理有点类似,但是那个是在启动的时候就加载了,而这个是在点击选中插件之后才会去加载DLL,插件调用代码:

if (wParam >= MN_STARTID)
{
	HMODULE h = LoadLibrary("plugins\\"+m_pluginID.GetAt(wParam-MN_STARTID));
	if (h!=NULL)
	{
		typedef void (*hpDllFun)(_DestPT* d);
		hpDllFun pShowDlg = (hpDllFun)GetProcAddress(h,"LoadPlugin");
		if (NULL == pShowDlg)
		{
			MessageBox("DLL中函数寻找失败");
			return TRUE;
		}
		pShowDlg(&g_DestProcess);
	}else
	{
		MessageBox("这个DLL打开出错了小老弟!","错误",MB_OK | MB_ICONERROR);
	}
}

PS:这个地方我没有释放这个DLL句柄,主要因素是我不确定你是用什么方式去执行,就比如说你用的是非模态对话框的话呢就会出错(我这个示例DLL就是),之后我们使用链表统一管理。

所以说你只要编写好一个插件之后放在plugins目录下就好,导出一个名称为LoadPlugin这个函数,这个函数的定义:

extern "C" __declspec(dllexport) void LoadPlugin(_DestPT* dest)

也就是我会传递给你一个_DestPT的指针,那这个结构体的定义是:

typedef struct _DestPT
{
	DWORD PID;
	DWORD TID;
	HANDLE hProcess;
	HANDLE hThread;
}DestPT,*pDestPT;//程序启动的一些信息

也就是程序的一个载入信息,信息相对有点少,但是基本的一些功能也就够了,之后我会提供给你断点接口和文件PE信息。

模块载入的示例代码:
导出函数:

extern "C" __declspec(dllexport) void LoadPlugin(_DestPT* dest)
{
	// 切记,此处需要加下面这行代码,不然对话框显示不出来
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	MainDialog *ph = new MainDialog;
	ph->m_dest = dest;
	ph->Create(MainDialog::IDD);
	ph->ShowWindow(SW_SHOW);
	
}

关键代码:

BOOL MainDialog::OnInitDialog()
{
	CDialog::OnInitDialog();
	int dwStyle =0;
	dwStyle |= LVS_EX_FULLROWSELECT; //选中某行使整行高亮(report风格时)  
	dwStyle |= LVS_EX_GRIDLINES; //网格线(report风格时)  
	m_showList.SetExtendedStyle(dwStyle); //设置扩展风格 
	HANDLE hShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,m_dest->PID);
	m_showList.InsertColumn(0,"模块地址",LVCFMT_LEFT,130);
	m_showList.InsertColumn(1,"模块名称",LVCFMT_LEFT,150);
	m_showList.InsertColumn(2,"模块路径",LVCFMT_LEFT,350);
	if (hShot == INVALID_HANDLE_VALUE)
	{
		return 0;
	}
	MODULEENTRY32 te = { sizeof(te) };
	BOOL bRet = Module32First(hShot, &te);
	while (bRet) { 
		CString str;
		str.Format("0X%08X",te.hModule);
		m_showList.InsertItem(0,str);
		str.Format("%s",te.szModule);
		m_showList.SetItemText(0,1,str);
		str.Format("%s",te.szExePath);
		m_showList.SetItemText(0,2,str);
		bRet = Module32Next(hShot, &te);
	}
	CloseHandle(hShot);
	return TRUE;
}

这样就可以实现一个简单的DLL,这里推荐使用MFC框架的DLL,因为比较简单一些。

百度云下载地址
提取码:jyss

6 Likes

希望楼主可以多分享一些安卓脱壳的教程 :+1:。以及安卓脱壳的入门教程