2012年1月20日金曜日

DLLinjection/Debug

アドバンスド大戦略のバイナリを改造したいなぁと思いつつも、すでにプログラミングから離れた僕にとって忘れてしまった技術も多いわけで。。。

既存のバイナリのとあるコードから、別途用意した自作のプロセスまたはDLLのコードを実行させる方法(呼び名があったんだけど、忘れてしまった><)にはいくつかあって、代表的な手法がDLLinjectionとDebugを使う手法だったと思う。

ということで、リハビリがてら、サンプルプログラムを作成しました。
今や時代遅れなWindowsXP 32bitでの動作確認です。アドバンスド大戦略自体、Win98が主流の時代のソフトですので許して><

こちら

DLLinjectionは結構おなじみ?
DLLを注入しておいて、IATに置かれたWin32の関数(MessageBoxAとか)のアドレスを、注入した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; i
    memcpy(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;
}
注入したDLLに用意しておいた関数を実行して置き換える。
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 name
 PrevValue=(void *)(UINT_PTR)pThunk2->u1.Function;
 pThunk2->u1.Function=(DWORD *)(UINT_PTR)Function;
 
}else if(ordinal==Ordinal){
 // hook if found by ordinal
 PrevValue=(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に置き換えておいて、ブレークポイント例外発生したときに特定処理を実行し、またもとのコードに置き換えて元のコードを実行し、
   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;
再び、0xccに書き戻す。


   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 件のコメント:

コメントを投稿