既存のバイナリのとあるコードから、別途用意した自作のプロセスまたはDLLのコードを実行させる方法(呼び名があったんだけど、忘れてしまった><)にはいくつかあって、代表的な手法がDLLinjectionとDebugを使う手法だったと思う。
ということで、リハビリがてら、サンプルプログラムを作成しました。
今や時代遅れなWindowsXP 32bitでの動作確認です。アドバンスド大戦略自体、Win98が主流の時代のソフトですので許して><
こちら
DLLinjectionは結構おなじみ?
DLLを注入しておいて、IATに置かれたWin32の関数(MessageBoxAとか)のアドレスを、注入したDLLのアドレスに置き換えるってやつですね!
この関数でDLLをインジェクションして、
注入したDLLに用意しておいた関数を実行して置き換える。int LoadLibraryOnProcess(HANDLE hProcess, HANDLE hThread, char** LibData, int LibCount){/** Client is started in a suspended state, so I patch it in memory* in such a way that it loads LoaderDLL.dll when resumed.* On NT4+ I allocate needed memory with VirtualAllocEx, and* on 9x I use free space in PE header, because VirtualAllocEx is* not implemented there. I can't use PE header space under all OSes* because XP does not allow writing there even after calling VirtualProtectEx.*/unsigned char SaveStack[2] ={0x9c, /* 00 pushfd */0x60 /* 01 pushad */};unsigned char PopStack[7] ={0x61, /* 00 popad */0x9d, /* 01 popfd */0xe9, 0x00, 0x00, 0x00, 0x00 /* 02 jmp oldeip */};unsigned char LoadLib[10] ={0x68, 0x00, 0x00, 0x00, 0x00, /* 00 push "dll path" */0xe8, 0x00, 0x00, 0x00, 0x00 /* 05 call LoadLibraryA */};unsigned char CallFunc[13] ={0x68, 0x90, 0x90, 0x90, 0x90, /* 00 push "function" */0x50, /* 05 push eax */0xe8, 0x90, 0x90, 0x90, 0x90, /* 06 call GetProcAddress */0xff, 0xd0 /* 11 call eax */};CONTEXT ctx;HINSTANCE Lib=NULL;int AllOk=TRUE, i=0;unsigned int Tmp=0, PatchBase=0, AsmSize=0, TextSize=0, AsmPos=0, TextPos=0;unsigned char *Patch=NULL;/* get the thread's info */AllOk=TRUE;ctx.ContextFlags=CONTEXT_FULL;AllOk&=GetThreadContext(hThread, &ctx);/* determine the patch size based on the opcodes and library text (names and functions) */AsmSize+=sizeof(SaveStack);for(i=0; i/* the even indices are the library name *//* the uneven indices are the function name */AsmSize+=sizeof(LoadLib);TextSize+=(unsigned int)(strlen(LibData[i])&0xFFFFFFFF)+1;if(LibData[i+1]!=NULL){AsmSize+=sizeof(CallFunc);TextSize+=(unsigned int)(strlen(LibData[i+1])&0xFFFFFFFF)+1;}}AsmSize+=sizeof(PopStack);/* get a patchbase. 0x400400 should always be alright */PatchBase=(unsigned int)(UINT_PTR)VirtualAllocEx(hProcess, 0, AsmSize+TextSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if(!PatchBase){PatchBase = 0x400400;}Patch=malloc(AsmSize+TextSize);if(Patch==NULL){return FALSE;}/* build the patch data */memset(Patch, 0, AsmSize + TextSize);memcpy(Patch, SaveStack, sizeof(SaveStack));AsmPos+=sizeof(SaveStack);
for(i=0; imemcpy(Patch+AsmPos, LoadLib, sizeof(LoadLib));/* "push" lib name, and lib name */Tmp=PatchBase+AsmSize+TextPos;memcpy(Patch+AsmPos+1, &Tmp, sizeof(Tmp));memcpy(Patch+AsmSize+TextPos, LibData[i], strlen(LibData[i]));
/* the loadlibrary call */Tmp=(unsigned int)(UINT_PTR)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA")-(PatchBase+AsmPos+5)-5;memcpy(Patch+AsmPos+6, &Tmp, sizeof(Tmp));AsmPos+=sizeof(LoadLib);TextPos+=(unsigned int)(strlen(LibData[i])&0xFFFFFFFF)+1;/* build GetProcAddress-Call */if(LibData[i+1]!=NULL){memcpy(Patch+AsmPos, CallFunc, sizeof(CallFunc));/* "push" function name, and the function name */Tmp=PatchBase+AsmSize+TextPos;memcpy(Patch+AsmPos+1, &Tmp, sizeof(Tmp));memcpy(Patch+AsmSize+TextPos, LibData[i+1], strlen(LibData[i+1]));
/* the getprocaddress call */Tmp=(unsigned int)(UINT_PTR)GetProcAddress(GetModuleHandle("kernel32"), "GetProcAddress")-(PatchBase+AsmPos+6)-5;memcpy(Patch + AsmPos + 7, &Tmp, sizeof(Tmp));AsmPos+=sizeof(CallFunc);TextPos+=(unsigned int)(strlen(LibData[i+1])&0xFFFFFFFF)+1;}}
memcpy(Patch+AsmPos, PopStack, sizeof(PopStack));
Tmp=ctx.Eip-(PatchBase+AsmPos+2)-5;memcpy(Patch+AsmPos+3, &Tmp, sizeof(Tmp));/* write the patch */AllOk&=WriteProcessMemory(hProcess, (void *)(UINT_PTR)PatchBase, Patch, AsmSize+TextSize, 0);/* set the current eip to our patch */ctx.Eip=PatchBase;AllOk&=SetThreadContext(hThread, &ctx);free(Patch);return AllOk;}
void *HookImportedFunction(void *ImageBase, const char *Dll, const char *FuncName,int Ordinal, void *Function){DWORD oldProtect=0;void *PrevValue=NULL;
IMAGE_DOS_HEADER *idh=NULL;IMAGE_FILE_HEADER *ifh=NULL;IMAGE_OPTIONAL_HEADER *ioh=NULL;IMAGE_IMPORT_DESCRIPTOR *iid=NULL;IMAGE_THUNK_DATA *pThunk1=NULL, *pThunk2=NULL;IMAGE_IMPORT_BY_NAME *pname=NULL;unsigned short ordinal=0;char *name=NULL;
/* ImageBase = (DWORD)GetModuleHandle(NULL); */idh=(IMAGE_DOS_HEADER *)ImageBase;
/* idh->e_lfanew is the beginning of the PE header with the signature "PE" */ifh=(IMAGE_FILE_HEADER *)(UINT_PTR)((unsigned int)(UINT_PTR)ImageBase+idh->e_lfanew+sizeof(IMAGE_NT_SIGNATURE));ioh=(IMAGE_OPTIONAL_HEADER *)(UINT_PTR)((unsigned int)(UINT_PTR)ifh+sizeof(IMAGE_FILE_HEADER));iid=(IMAGE_IMPORT_DESCRIPTOR *)(UINT_PTR)((unsigned int)(UINT_PTR)ImageBase+ioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);/* set the entire IAT (IID + THUNKS) as READWRITE */VirtualProtect((void*)(UINT_PTR)((unsigned int)(UINT_PTR)ImageBase+ioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress),ioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size,PAGE_READWRITE, &oldProtect);/* loop through the IID until it's over */while(iid->Name){
/* if this is the IID of the dll we're after... */if(_stricmp(Dll, (const char*)(UINT_PTR)((unsigned int)(UINT_PTR)ImageBase+iid->Name))==0){
pThunk1=(IMAGE_THUNK_DATA *)(UINT_PTR)(iid->OriginalFirstThunk+(unsigned int)(UINT_PTR)ImageBase);pThunk2=(IMAGE_THUNK_DATA *)(UINT_PTR)(iid->FirstThunk+(unsigned int)(UINT_PTR)ImageBase);
/* loop through the IAT until it's over */while(pThunk1->u1.AddressOfData){
if(pThunk1->u1.Ordinal & 0x80000000){/* Imported by ordinal only: */ordinal=(unsigned short)(pThunk1->u1.Ordinal)&0xffff;}else{/* Imported by name, with ordinal hint */pname=(IMAGE_IMPORT_BY_NAME *)((DWORD)(UINT_PTR)(pThunk1->u1.AddressOfData)+(DWORD)(UINT_PTR)ImageBase);
ordinal=pname->Hint;name=(char*)pname->Name;}
if(name!=NULL && FuncName!=NULL && strcmp(name, FuncName)==0){// hook if found by namePrevValue=(void *)(UINT_PTR)pThunk2->u1.Function;pThunk2->u1.Function=(DWORD *)(UINT_PTR)Function;}else if(ordinal==Ordinal){// hook if found by ordinalPrevValue=(void *)(UINT_PTR)pThunk2->u1.Function;pThunk2->u1.Function=(DWORD *)(UINT_PTR)Function;}
pThunk1++;pThunk2++;}}/* skip to the next IID */iid++;}
return PrevValue;}
Debugの方は
DebugActiveProcess(pi->dwProcessId);でデバッグ状態にしておき、
case LOAD_DLL_DEBUG_EVENT:
{
SYSTEM_INFO SystemInfo;
BOOL b;
DWORD lpAddress;
if(huser32==de.u.LoadDll.lpBaseOfDll){
GetSystemInfo(&SystemInfo);
lpAddress=(HookAddress/SystemInfo.dwPageSize)*SystemInfo.dwPageSize;
b=VirtualProtectEx(pi->hProcess, (LPVOID)lpAddress, SystemInfo.dwPageSize,
PAGE_EXECUTE_WRITECOPY, &OrgProtect);
if(!b) goto DebugExit;
b=ReadProcessMemory(pi->hProcess, HookAddress, &OrgOpeCode, 1, NULL);
if(OrgOpeCode!=NewOpeCode){
b=b&WriteProcessMemory(pi->hProcess, HookAddress, &NewOpeCode, 1, NULL);
}
if(!b) goto DebugExit;
FlushInstructionCache(pi->hProcess, NULL, 0);
}
}break;
で、バイナリの所望の位置を0xccに置き換えておいて、ブレークポイント例外発生したときに特定処理を実行し、またもとのコードに置き換えて元のコードを実行し、
再び、0xccに書き戻す。case EXCEPTION_BREAKPOINT: // @0xcc{if((i=FindThreadInfo(de.dwThreadId))==0xffffffff)goto DebugExit;ct.ContextFlags=CONTEXT_CONTROL;if(!GetThreadContext(ThreadInfos[i].hThread, &ct)) goto DebugExit;if(ct.Eip-1==HookAddress){
counter++;SetSharedValue(counter);ct.Eip--;if(!WriteProcessMemory(pi->hProcess, (void *)ct.Eip, &OrgOpeCode, 1, NULL)){goto DebugExit;}FlushInstructionCache(pi->hProcess, NULL, 0);ct.EFlags|=0x00000100;if(!SetThreadContext(ThreadInfos[i].hThread, &ct)) goto DebugExit;}ContinueStatus=DBG_CONTINUE;}break;
case EXCEPTION_SINGLE_STEP:
{
if((i==FindThreadInfo(de.dwThreadId))==0xffffffff) goto DebugExit;
if(!WriteProcessMemory(pi->hProcess, HookAddress, &NewOpeCode, 1, NULL)){
goto DebugExit;
}
FlushInstructionCache(pi->hProcess, NULL, 0);
ct.ContextFlags=CONTEXT_CONTROL;
if(!GetThreadContext(ThreadInfos[i].hThread, &ct)) goto DebugExit;
ct.EFlags&=~0x00000100;
if(!SetThreadContext(ThreadInfos[i].hThread, &ct)) goto DebugExit;
ContinueStatus=DBG_CONTINUE;
}break;
うーん、トリッキーだなぁwww
0 件のコメント:
コメントを投稿