首页
社区
课程
招聘
一个themida加壳的程序LOADER 破解
发表于: 2006-11-24 15:06 13875

一个themida加壳的程序LOADER 破解

2006-11-24 15:06
13875

【文章标题】: 一个themida加壳的程序LOADER 破解
【文章作者】: rockhard
【作者邮箱】: wnh1@sohu.com
【软件名称】: test.exe
【加壳方式】: themida
【保护方式】: themida
【使用工具】: ollydbg,VC
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  遇到一个程序,在Ollydbg修改某些关键点可以运行了,但由于用THEMIDA加壳了,水平菜,脱不掉壳,每次只能在
  OLLYDBG中运行,想给他写个LOADER加载,然后修改内存。发现用CreateProcess 、ShellExecute和WinExec运行起来就报错,
  看来是加了LOADER的ANTI了。不能脱壳,只能在LOADER上做文章了。
  
  
  最直接的想法就是模拟explorer启动它,然后再修改。整个思路是这样:
  
  1、创建一个Event,用于进程间同步。
  2、将DLL注入到explorer中,
  3、Loader 调用WaitForObject进入等待状态。
  4、DLL 的DLL_PROCESS_ATTACH中创建一线程,用CreateProcess启动目标程序,这地方用CREATE_SUSPENDED标志,免得跑飞了
  5、DLL中用OpenEvent找到上面Event ,通知一次,激活LOADER线程。
  6、LOADER 查找目录程序,然后不断的SUSPEND目标进程,查看要修改的地方有没有已解码,如果已解码,PATCH,没有,将目标进程恢复运行一小段时间。
  
  源代码中没有什么注释,这儿帖出部分写上注释:
  
  PATCH代码段如下:
  //////////////////////////////////////////////////////////////////////////
  /**
    *  dwProcessID : Process ID which wanted to patch
    *
    *  return      : return TRUE if patch successfully ,otherwise return FALSE
    */
  
  #define PATCH_ADDRESS   0x401019      //目标进程中要PATCH 的地址
  #define PATCH_SIZE      16            //从目标进程中读取的字节数
  #define KEY_CODE        ("\x72\x12\x68\x3C\x60\x40\x00\x6A\x00\xFF\x15\xA0\x50\x40\x00\x33") //这个是壳解码后没有改动的程序代码
  #define NEW_CODE        ("\xEB\x12\x68\x3C\x60\x40\x00\x6A\x00\xFF\x15\xA0\x50\x40\x00\x33") //要修改后的代码
  #define TRY_TIMES       400  //只试搜索N次,如果还没出现,就不再查找了。
  
  
  BOOL CrackIt(DWORD dwProcessID)
  {
    BOOL bContinueRun=TRUE;
    BOOL bPatchSucess =FALSE;
    DWORD dwTryTimes=0;
    DWORD dwOldProtection,dwDummy;
  
    HANDLE hProcess =OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);
    VirtualProtectEx (hProcess, (LPVOID)PATCH_ADDRESS, PATCH_SIZE,PAGE_EXECUTE_READWRITE, &dwOldProtection);
  
    //如果PATCH成功,设置bContinue为假,或尝试了N次后还不能找到要修改的代码,放弃了。
    while(bContinueRun && ++dwTryTimes<TRY_TIMES)
    {
      BYTE OldKeyCode[17];
  
      //让目标程序运行3ms 然后停下来,此时读目标程序的进程,查看是否已解码了
      SuspendResumeProcess(dwProcessID,FALSE);
      Sleep(3);
      SuspendResumeProcess(dwProcessID,TRUE);
  
      //读目标进程要PATCH地址处的数据
      ReadProcessMemory(hProcess, (LPVOID)PATCH_ADDRESS, OldKeyCode, PATCH_SIZE,&dwDummy);
  
      //判断是不是完全解码出来了
      if( !memcmp(OldKeyCode,(BYTE *)KEY_CODE, 16) ){
  
        //相同,说明已解码了,写入我们的新值
        WriteProcessMemory(hProcess, (LPVOID)PATCH_ADDRESS, NEW_CODE, PATCH_SIZE,&dwDummy);
        //we have patched ,stop check :-)
        bPatchSucess=TRUE;
        bContinueRun=false;
      }
    }
    VirtualProtectEx(hProcess, (LPVOID)PATCH_ADDRESS, PATCH_SIZE,dwOldProtection, &dwDummy);
  
    //for debug
    if(dwTryTimes >= TRY_TIMES)
      OutputDebugString("Cannot Match Code In Program !\n");
  
    //恢复目标程序执行
    SuspendResumeProcess(dwProcessID,FALSE);
  
    return bPatchSucess;
  
  }
  
  
  另外,在创建目标进程时不能写入绝对路径,不知道什么原因,后来通过SetCurrentDirectory改变工作目录解决了这个问题:
  #define WORK_DIRECTORY          "C:\\test"
  #define CRACK_PROGRAM_NAME      "test.exe"  //这地方如果写成c:\\test\\test.exe传给CreateProcess尽管目标程序运行,但explorer会出错.不明白
  
  DWORD WINAPI StartProcess(LPVOID lpParam)
  {
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
  
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = TRUE;
  
    SetCurrentDirectory(WORK_DIRECTORY); //IMPORTANT!!
  
    BOOL bRet = CreateProcess(NULL, CRACK_PROGRAM_NAME,
      NULL, NULL, FALSE,  CREATE_SUSPENDED,
      NULL, NULL, &si,  &pi);
  
    if(bRet){
      CloseHandle (pi.hThread);
      CloseHandle (pi.hProcess);
    }
  
    HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,EVENT_OBJECT_NAME);
    SetEvent(hEvent);
  
    return 0;
  }
  
  注入到explorer的代码(感谢CSDN上的kesummer帮助):
  BOOL CInjector::InjectModuleInto(DWORD dwProcessId)
  {
    //不要给自己注入
    if(GetCurrentProcessId() == dwProcessId)
      return FALSE;
  
    //下面一段代码用于在explorer中查找dll是不是已经注入过了。
    BOOL bFound = FALSE;
    MODULEENTRY32 me32 = { 0 };
    me32.dwSize = sizeof(MODULEENTRY32);
  
    HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
    if(Module32First(hModuleSnap, &me32)) {
      do{
        if(lstrcmpiA(me32.szExePath, m_szDllName) == 0) {
          bFound = TRUE;
          break;
        }
      }
      while(Module32Next(hModuleSnap, &me32));
    }
    CloseHandle(hModuleSnap);
  
    if(bFound)  return FALSE;
  
  
    HANDLE hProcess = OpenProcess(
      PROCESS_VM_WRITE|PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION,
      FALSE, dwProcessId);
  
    if(hProcess == NULL) return FALSE;
  
   
  
    int cbSize = (strlen(m_szDllName) + 1);
    LPVOID lpRemoteDllName = VirtualAllocEx(hProcess, NULL, cbSize, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, lpRemoteDllName, m_szDllName, cbSize, NULL);
  
    HMODULE hModule=GetModuleHandle("kernel32.dll");
    LPTHREAD_START_ROUTINE pfnStartRoutine =
        (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
  
    //用远程进程的LoadLibraryA函数的入口点做为远程线程的入口点,线程运行就相当于运行了LoadLibraryA
    HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, lpRemoteDllName, 0, NULL);
    if(hRemoteThread == NULL)
    {
      CloseHandle(hProcess);
      return FALSE;
    }
  
    WaitForSingleObject(hRemoteThread, INFINITE);
  
    CloseHandle(hRemoteThread);
    CloseHandle(hProcess);
    m_dwProcessId=dwProcessId;
  
    return TRUE;
  }
  
  
  附件为全部源代码
  
--------------------------------------------------------------------------------
【经验总结】
  如果没有Loader检测,不需要注入到explorer了,直接用代码CreateProcess然后用上面的CrackIt函数就可以解决问题了。
  有检测只好找办法绕过去了。不知道有没有更简单的方法。
  
  本文是写给跟我一样菜鸟的,搞不定壳的,高手就别笑了。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年11月24日 14:47:43


[招生]系统0day安全班,企业级设备固件漏洞挖掘,Linux平台漏洞挖掘!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (20)
雪    币: 340
活跃值: (932)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
2
themida写loader应该可以直接用CreateProcess的,你是不是加了DEBUG_ONLY_THIS_PROCESS?
2006-11-24 15:23
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
最初由 noword_forever 发布
themida写loader应该可以直接用CreateProcess的,你是不是加了DEBUG_ONLY_THIS_PROCESS?


不是themida的检测,这原程序中有检测。

用了几个办法都没办法加载起来,只好注入到explorer,让explorer启示录动它了。

注入explorer真是麻烦,不知道有没有更好的办法。
2006-11-24 15:34
0
雪    币: 215
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
野猪力量注入!!!
2006-11-24 19:16
0
雪    币: 331
活跃值: (56)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
5
野猪力量注入!!!
2006-11-24 19:25
0
雪    币: 277
活跃值: (312)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
6
野猪力量再注入!!!
2006-11-24 21:09
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
7
呵呵,完全不用这么麻烦D
不用0环,不用注入
只要稍微做些手脚,父进程就是Explorer了
2006-11-24 21:25
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
8
最初由 shoooo 发布
呵呵,完全不用这么麻烦D
不用0环,不用注入
只要稍微做些手脚,父进程就是Explorer了


我这就去找火柴棍把耳朵掏干净点,认真听指点,shoooo的每一行代码都闪耀着火花
2006-11-24 21:41
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
9
好像heXer曾经说过的
貌似对DEBUG方式的无效

#include <windows.h>
#include <tlhelp32.h>
#include <commdlg.h>
#pragma comment (lib, "comdlg32.lib")
#pragma comment (linker, "/filealign:0x200")
#pragma comment (linker, "/subsystem:windows")
#pragma comment (linker, "/entry:entry")

void AdjustPrivilege(int pid, BOOL bEnable)
{
    HANDLE    hProcess;
	HANDLE    hToken=0;
    TOKEN_PRIVILEGES tkp;
	tkp.PrivilegeCount = 1;  
	tkp.Privileges[0].Attributes = 0;
	if (bEnable)
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	if (LookupPrivilegeValue(NULL, "SeDebugPrivilege", &tkp.Privileges[0].Luid))
	{
		if (hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
		{
			if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken))
			{
				if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL))
				{
					CloseHandle(hToken);
				}
			}
			CloseHandle(hProcess);
		}
	}
}

DWORD FindExplorer()
{
	HANDLE	hC;
	DWORD	i;
	BOOL	Next;
	char	szName[MAX_PATH];

	PROCESSENTRY32 p32 = {sizeof(p32)};
	hC = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL); 
	Next = Process32First(hC, &p32); 
	i = 0; 
	while (Next) 
	{ 
		wsprintf(szName, "%s", p32.szExeFile);
		if (lstrcmpi(szName, "EXPLORER.EXE") == 0)
			return p32.th32ProcessID ;
		Next = Process32Next(hC, &p32); 
		i++; 
	} 
	CloseHandle(hC); 
	return 0;
}

HANDLE	hProcess;
DWORD	ZwCP;
DWORD	ZwCPEx;
DWORD	NoCP;
DWORD	NoCPEx;


void __declspec(naked) FuckZwCP()
{
	__asm
	{
		mov eax, hProcess
		mov [esp+0x10], eax
		mov eax, NoCP;
		push ZwCP;
		add dword ptr [esp], 5
		retn
	}
}

void __declspec(naked) FuckZwCPEx()
{
	__asm
	{
		mov eax, hProcess
		mov [esp+0x10], eax
		mov eax, NoCPEx;
		push ZwCPEx;
		add dword ptr [esp], 5
		retn
	}
}

void Patch()
{
	DWORD	odpt;

	ZwCP   = (DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwCreateProcess");
	ZwCPEx = (DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwCreateProcessEx");
	if (ZwCP != 0)
	{
		NoCP = *(LPDWORD)(ZwCP+1);
		VirtualProtect((LPVOID)ZwCP, 5, PAGE_EXECUTE_READWRITE, &odpt);
		*(LPBYTE)(ZwCP+0x00) = 0xE9;
		*(LPDWORD)(ZwCP+0x01) = (DWORD)FuckZwCP - ZwCP - 5;
	}
	if (ZwCPEx != 0)
	{
		NoCPEx = *(LPDWORD)(ZwCPEx+1);
		VirtualProtect((LPVOID)ZwCPEx, 5, PAGE_EXECUTE_READWRITE, &odpt);
		*(LPBYTE)(ZwCPEx+0x00) = 0xE9;
		*(LPDWORD)(ZwCPEx+0x01) = (DWORD)FuckZwCPEx - ZwCPEx - 5;
	}
}

void entry()
{
	AdjustPrivilege(GetCurrentProcessId(), TRUE);
	DWORD	Pid;
	Pid = FindExplorer();
	if (Pid == 0)
	{
		return ;
	}
	hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_CREATE_PROCESS, FALSE, Pid);
	if (hProcess == NULL)
	{
		return ;
	}
	Patch();

	OPENFILENAME ofn = { sizeof(ofn) };
	char	szFilter[] = "EXE Files\0*.EXE\0\0";
	char	szFileName[MAX_PATH];
	char	szFilePath[MAX_PATH];
	
	ofn.hwndOwner = NULL;
	ofn.lpstrFile = szFileName;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile[0] = 0;
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrTitle = "Loader";
	ofn.lpstrFilter = szFilter;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
	if (!GetOpenFileName(&ofn))
	{
		return ;
	}
	strcpy(szFilePath, szFileName);
	*(strrchr(szFilePath, '\\')+1) = 0;  //神奇,贴出来后两个反的变成一个了

	STARTUPINFO si = {sizeof(si)};
	PROCESS_INFORMATION pi = {0};
	CreateProcess(NULL, szFileName, NULL, NULL, FALSE, 0, NULL, szFilePath, &si, &pi);
	WaitForSingleObject(pi.hProcess, INFINITE);
	ExitProcess(0);
}
上传的附件:
2006-11-24 22:08
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
10
THX verrry much  shoooo,学习。。。
2006-11-24 23:09
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
11
试了一下,果然行。爽!
2006-11-24 23:11
0
雪    币: 898
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
12
可以 SMC
2006-11-24 23:20
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
13
最初由 fly 发布
可以 SMC


这么说fly大侠还有其它办法.给点思路.
2006-11-24 23:24
0
雪    币: 898
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
14
层层解码层层Patch就行了
原理都是一样的啊
2006-11-24 23:27
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
15
在网上找关于这方面资料时,
发现这个问题也是360给360安全卫士程序员志愿者出的问题:

http://blog.csdn.net/dedodong/archive/2006/10/07/1323925.aspx
2006-11-24 23:31
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
16
最初由 fly 发布
层层解码层层Patch就行了
原理都是一样的啊


对,这样也行,就是要多检查几处,多PATCH几个地方。不过要是被加壳程序有多种方法检测,改起来就有点麻烦了,一把他们得全找出来,然后再得修改。
2006-11-24 23:35
0
雪    币: 89
活跃值: (181)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
猛人呀,
2006-11-25 10:02
0
雪    币: 214
活跃值: (70)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
18
强贴留名``
2006-11-26 14:50
0
雪    币: 255
活跃值: (207)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
19
期待脱壳机.
否则到处都是themida.
2006-11-26 15:16
0
雪    币: 255
活跃值: (207)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
20
期待脱壳机.
否则到处都是themida,连俺也顺手用上它了。
2006-11-26 15:16
0
雪    币: 224
活跃值: (147)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
21
嘿嘿 好好研究下:)
2006-11-28 01:13
0
游客
登录 | 注册 方可回帖
返回
// // 统计代码