淺談IAT加密原理及過程

__阿阿阿怪發表於2020-12-30

上一次做完程式碼段加密後,又接觸到了新的加密方式:IAT加密

IAT加密是通過隱藏程式的匯入表資訊,以達到增加分析程式的難度。因為沒有匯入表,就無法單純的從靜態狀態下分析呼叫了什麼函式,動態除錯時,也無法直接從呼叫處判斷出呼叫的函式。從而增加了程式分析的難度。

IAT加密思路如下:

1.把IAT資訊全部刪除掉,只留下DLL名和第一個呼叫函式的資訊(目的是讓程式在最開始執行時能正常呼叫那些初始化類的函式)

2.根據匯入表的資訊,自己實現函式載入,通過LoadLibraryA和GetProcAddress函式把DLL中的所有函式載入進來,並在程式中儲存載入後的函式地址。(但由於是用程式A給程式B加密,程式B未執行時,如何獲得這2個函式的地址呢?而且IAT表也已經被刪掉了。此時只能藉助PEB和TEB來獲取這2個函式的地址,並記錄在程式B中,當程式B執行時,就可以通過對應的位置取得函式地址)

3.找到所有 “指令中包含了IAT載入地址” 的指令,並記錄這些指令的位置(指令所在位置而不是指令中的地址位置)

4.構建一個shellcode表,裡面寫著每個3.裡所找到的指令的處理程式碼,簡單來說就是把3.所找到的地址,修改成跳到4的這個shellcode表對應的處理指令中,這段處理指令實現和3.的指令一樣的效果。

5.把3.中的指令修改為一個jmp,jmp到4.中對應的處理程式碼。實現一樣的效果。

6.把重定位表中,有關於IAT表的項刪掉,因為IAT加密後,原呼叫位置的指令是跳轉到自己寫的shellcode中,裡面的指令是在函式執行後修改的。無需重定位了。

 

實現步驟:

1.先常規的檢查該程式是否為PE檔案(檢查"MZ"和“PE”是否存在)

2.通過匯入表,找到對應的IAT值,並構建一個向量vector用於儲存。(注:這裡為IAT的值,而不是IAT的值指向的地址中的值)

3.根據2.中所找到的IAT,找到在原始檔中,又那些是使用了這個值的,分別是mov exx(每個暫存器對應的二進位制數不同),push,call,jmp。記得這裡記錄的是呼叫指令的位置,而不是呼叫指令中IAT地址所在的位置

4.用向量vector+結構體的方式先將IAT表中的資訊儲存下來(DLL個數,每個DLL的名及名字的長度。這個DLL是通過什麼方式匯出函式(名稱/序號);DLL有幾個匯出函式,他們分別的值是多少,如果他是以名稱匯出的話,該名稱的長度和值分別又是多少)。

5.接著隱藏被加密檔案的匯入表,每個DLL下面的INT和IAT表只保留第一個值,其他的全部賦值為0,去除掉匯入資訊

6.更新重定位表資料,如果沒有重定位表則跳過這項。看7.

  6.1先把重定位表的值儲存下來,以便後續移動到程式末尾(這麼做的目的是為了後續新增新的重定位項時,重定位塊的範圍能覆蓋上) (例:當前程式最大記憶體偏移為0x15000,增加後的偏移為0x18000,如果不把重定位移到最後的話,新增加的這0x3000中就無法被重定位到)

  6.2把目標程式的重定位表資料清空

7.將每個節的保護屬性修改為可寫(本來只修改.text節的屬性即可,但由於該節的位置不在data陣列裡面記錄,單憑節名判斷未必準確,所以將所以節都改為可寫)

8.建立一個新的節,把4中儲存的匯入表資訊填充進去。由於已知這些資訊的長度,寫shellcode時把位置一一對應即可。

  8.1再在這個節當中,申請匯入函式個數*4的空間,以便存放後續匯入的函式地址

  8.2記錄當前的記憶體地址,設為新的OEP;

  8.3最為重要的一點:編寫shellcode

    8.3.1:由於LoadLibraryA和GetProcAddress都為Kernel32中的匯出函式;所以先通過程式的fs:[0x30]獲取到PEB結構,再在該結構的0x0C的位置獲取到該程式的DLL列表指標,再通過這個列表的0x1C的位置獲取初始化模組列表;通過這個初始化模組列表取得結構,對比結構成員中的長度和值,取到目標DLL(Kernel32.dll)在該程式的裝載基址

    8.3.2獲得Kernel32的裝載基址後,把該地址當成一個PE檔案處理,通過偏移取值,最後找到匯出表位置,根據匯出表中的資料一一比對,得到LoadLibraryA和GetProcAddress在該程式的位置。儲存起來

    8.3.3根據8中儲存的匯入表資料,一一呼叫LoadLibraryA獲得目標DLL的裝載地址,再根據該裝載地址,呼叫GetProcAddress把其中的函式地址全部獲取出來,並按匯入表的順序,儲存到8.1所預留出來的空間中

    8.3.4構造代替原匯入表功能的shellcode塊,每個shellcode塊對應一個使用到IAT地址的位置(2.所儲存的值)。

    8.3.5把原呼叫到IAT指令的位置,全部修改為jmp到8.3.4對應的shellcode塊,注意指令長度,替換後的指令為jmp [目標shellcode塊地址],佔5位元組,如原指令佔6位元組的話,填充一個nop對齊     流程為:找到呼叫IAT地址的指令位置(3.中儲存了),將其替換成jmp [對應shellcode塊地址],在對應shellcode塊中,把自己在8.3.3所獲得的函式地址,用混淆的方式,實現成原來的功能。(本來為mov eax [iat地址],修改成JMP 對應shellcode塊,對應shellcode塊裡實現mov eax [自己所獲得的函式地址])

9.如果沒有重定位表跳過這一步,看10.  把重定位把重定位表中有關呼叫函式的指令位置刪掉(注:這裡的位置為指令當中的地址的位置,例:E9 0X0123 4567在0x8的位置,那麼地址位置就為0x9)。這麼做的目的是因為IAT表加密後,函式都是自己實現匯入的,原匯入表的這些項無需再進行重定位了,填充時地址就已經是當前程式的位置了。

10.由於8.3.4的shellcode塊中需要用到基址,這裡利用了重定位表的方式獲得基址,所以要把每個shellcode塊中,對應獲得基址的指令地址也新增到重定位塊中

11.將新增的節按檔案對齊和記憶體對齊後,將重定位表恢復,恢復起始位置為新增節的起始位置+對齊後的大小   

11.IAT加密完成

 

下面附上程式碼:

程式碼長度1594 未精簡,有備註     其中有涉及jit編譯shellcode指令的程式碼,所以無法直接複製執行,這裡僅提供編寫思路。

淺談IAT加密原理及過程
   1 #include<iostream>
   2 #include<Windows.h>
   3 #include<map>
   4 #include<vector>
   5 #include "Asmjit\\asmjit.h"
   6 
   7 using namespace std;
   8 using namespace asmjit;
   9 using namespace asmjit::x86;
  10 
  11 int g_iFileSize = 0;        //檔案大小
  12 UCHAR *g_pFileSrc = NULL;  //檔案內容  (此處必須為無符號字元指標,因為char會將十六進位制的最高位預設為符號位)
  13 int g_iSectionAlignment = 0;//記憶體對齊大小
  14 int g_iFileAlignment = 0;//檔案對齊大小
  15 int g_iImageBase = 0;//程式基址
  16 int g_iNewOEP = 0;//新入口點
  17 int g_iOldOEP = 0;//舊入口點
  18 
  19 struct FuncTag
  20 {
  21     BYTE iFuncNameLenth;//函式名稱長度
  22     char *szFuncName;
  23 };
  24 struct DLLFuncTag
  25 {
  26     byte bDllNameLenth;//DLL名長度
  27     char *szDllName;
  28     WORD wFuncNum;//當前DLL的函式數量
  29     byte bTab;//匯入方式:0為序號匯入 1為名稱
  30     vector<FuncTag>vFuncNAMEorNo;//注意:此時的序號已經去掉最高位
  31 };
  32 vector<DLLFuncTag>g_tagDllFunc;//存放IAT資訊的結構體,成員為DLL名長度,DLL名,函式數量,標誌(最高位為1則是序號,否則為一個字串的地址),函式名列表
  33 
  34 
  35 //先通過遍歷獲得下面結構體中各個成員的值  注:成員的位置皆為RVA
  36 /*
  37 1.將dwRelocAddr這4位元組的地址放入重定位表中,使其在執行後該位置的值變為程式基址
  38 2.先去dwCallAddr的位置,將那行程式碼修改為jmp dwJmpAddr(如果是mov eax,則直接修改即可,其他的則要填充多一個nop(0x90),以便對齊指令長度)
  39 3.執行shellcode時,會將2的呼叫地址+5/+6作為返回地址壓棧 (jmp指令除外)
  40 4.將NewLatAddr的值,加上基址後,在得到的地址上取值,該地址上的值與原IAT所指向的值相同
  41 5.執行完後,根據3得到的值,返回到呼叫指令的下一條指令上
  42 */
  43 struct IatAddrTab
  44 {
  45     DWORD dwCallAddr;//以便區分當前是什麼型別下使用了這個IAT地址  注意,這裡為整個呼叫程式碼的位置,而非呼叫的程式碼的函式地址的位置    
  46     BYTE bType;//該呼叫型別 0-7為mov eax、edx、esp、esi、ecx、ebx、ebp、edi  8為call 9為jmp (其中8和9可以歸類成一個處理方式) 10為push  
  47     DWORD dwRelocAddr;//用於新增到重定位塊中,該位置存放的為基址
  48     WORD wIndex;//當前IAT位於載入表的下標 每個地址佔4位元組
  49     DWORD dwOldIatAddr;//原IAT地址
  50     DWORD dwNewIatAddr;//將要替換成的新IAT地址
  51     DWORD dwJmpAddr;//shellcode所在的起始位置RVA     
  52 };  
  53 vector<IatAddrTab>g_tagIAT;//存放IAT替換地址的結構體,成員為IAT地址,以及一個呼叫地址位置,還有一個該IAT型別
  54 
  55 
  56 vector<DWORD>g_IATaddrList;
  57 
  58 struct tagRelocation
  59 {
  60     DWORD wR_Head;
  61     vector<WORD>wR_Offset;
  62 };
  63 vector<tagRelocation>vOldReloction;
  64 
  65 
  66 VOID GetKernel32Base(X86Assembler &a)
  67 {
  68     Label GetInfoBegin = a.newLabel();
  69     Label cmpDllName = a.newLabel();
  70     Label cmpChar = a.newLabel();
  71     Label GetNextModule = a.newLabel();
  72     Label GetBaseOver = a.newLabel();
  73 
  74     a.push(ebx);
  75     a.push(ecx);
  76     a.push(edx);
  77     a.push(esi);
  78     a.push(edi);
  79     a.mov(edx, esp);
  80     a.sub(esp, 0x18);
  81     
  82     a.mov(dword_ptr(esp, 0x00), 0x0065006B);
  83     a.mov(dword_ptr(esp, 0x04), 0x006E0072);
  84     a.mov(dword_ptr(esp, 0x08), 0x006C0065);
  85     a.mov(dword_ptr(esp, 0x0C), 0x00320033);
  86     a.mov(dword_ptr(esp, 0x10), 0x0064002E);
  87     a.mov(dword_ptr(esp, 0x14), 0x006C006C);
  88     a.xor_(eax, eax);
  89     a.mov(esi, dword_ptr_abs(0x30).setSegment(fs));//獲得PEB 同理mov esi,fs:[30]
  90     a.mov(esi, dword_ptr(esi, 0x0C));            //取到dll的列表指標
  91     a.mov(esi, dword_ptr(esi, 0x1C));             //取到初始化序模組化
  92 
  93     a.bind(GetInfoBegin);//遍歷模組資訊
  94     a.mov(edi, dword_ptr(esi, 8));//取到當前模組基址
  95     a.lea(ebx, dword_ptr(esi, 0x1c));//取到UNICODE STRING結構
  96     a.movzx(ecx, word_ptr(ebx));//取出長度
  97     a.cmp (ecx, 24);//對比長度
  98     a.jne(GetNextModule);//不等則獲取下一個模組
  99     a.mov(ebx, dword_ptr(ebx , 4));//取到字串地址  此時ebx不再為結構地址
 100     a.mov(ecx, 0);//用於遍歷
 101 
 102     a.bind(cmpDllName);//對比模組名字
 103     a.cmp(ecx, 24);
 104     //遍歷到字元末尾 找到目標模組(先對的長度再對的字元,排除了前面同名後面A/W的情況)
 105     a.jnl(GetBaseOver);//獲取基址結束
 106     a.xor_ (eax, eax);//清空容器
 107     a.movzx(eax, word_ptr(ebx, ecx));
 108     a.cmp(eax, 0x41);//如果是大寫字元(41~5A)範圍內,轉成小寫
 109     a.jna(cmpChar);
 110     a.cmp(eax, 0x5A);
 111     a.ja(cmpChar);
 112     a.add(eax, 0x20);//大寫字元的值+0x20即轉成小寫字元
 113     
 114     a.bind(cmpChar);//對比每個字元
 115     a.cmp(ax, word_ptr(esp, ecx));//對比字串內容
 116     a.jne(GetNextModule);//不等則獲取下一個模組
 117     a.add(ecx, 2);
 118     a.jmp(cmpDllName);
 119 
 120     a.bind(GetNextModule);//獲得下一個模組資訊
 121     a.mov(esi, dword_ptr(esi));//取得下一個初始化模組
 122     a.test(esi, esi);
 123     a.jnz(GetInfoBegin);//不為空則繼續遍歷
 124 
 125     a.bind(GetBaseOver);
 126     a.mov(eax, edi);
 127     a.add(esp, 0x18);
 128     a.pop(edi);
 129     a.pop(esi);
 130     a.pop(edx);
 131     a.pop(ecx);
 132     a.pop(ebx);
 133 }
 134 
 135 VOID GetLoadLibraryAAddr(X86Assembler &a)
 136 {
 137     //跳轉標記
 138     Label FindFuncAddr = a.newLabel();
 139     Label FindFuncAddrOver = a.newLabel();
 140 
 141     a.push(ebx);
 142     a.push(ecx);
 143     a.push(esi);
 144     a.push(ebp);
 145     //記得開始獲取出來的都是RVA  要加上DLL的基址edi來取值
 146     a.mov(eax, dword_ptr(edi, 0x3c));
 147     a.mov(ebx, dword_ptr(edi, eax, 0, 0x78));//NT頭+0x78 = 匯入表RVA
 148     //上面等價於[edi + eax + 0x78],如果沒有0的話,他會把第三個引數當成2的N次方變成了edi + eax * 2的0x78次方
 149     a.add(ebx, edi);//ebx=匯入表
 150     a.mov(ecx, dword_ptr(ebx, 0x18));//ecx=按名稱匯出個數
 151     a.mov(esi, dword_ptr(ebx, 0x20));//esi=匯出函式名稱表rva
 152     a.add(esi, edi);
 153 
 154     a.bind(FindFuncAddr);
 155     a.sub(ecx, 1);//從後面往前遍歷 能少佔用一個暫存器,但要記得先-1再遍歷
 156     a.jz(FindFuncAddrOver);//減到0即遍歷完
 157     a.mov(ebp, dword_ptr(esi, ecx, 2));//取到函式名稱比拗最後一個函式名的RVA 等價於[esi,ecx*(2的n次方)]
 158     a.add(ebp, edi);
 159     a.cmp(dword_ptr(ebp), 0x64616F4C);
 160     a.jne(FindFuncAddr);
 161     a.cmp(dword_ptr(ebp, 4), 0x7262694C);
 162     a.jne(FindFuncAddr);
 163     a.cmp(dword_ptr(ebp, 8), 0x41797261);
 164     a.jne(FindFuncAddr);
 165     a.cmp(byte_ptr(ebp, 12), 0);//結束符  防止類似LoadLibrary和LoadLibraryA擴充套件函式名偏差出現
 166     a.jne(FindFuncAddr);
 167     //此時ecx為遍歷的下標 
 168     a.mov(esi, dword_ptr(ebx, 0x24));//esi=匯出函式序號表RVA(上面名稱對比完 回收esi)
 169     a.add(esi, edi);
 170     a.movzx(ebp, word_ptr(esi, ecx, 1));//ebp=函式序號 ebp=[esi+ecx*2一次方]
 171     //EBP:Get=2b0 Load=3c2
 172     a.mov(esi, dword_ptr(ebx, 0x1c));//esi=函式地址表RVA
 173     a.add(esi, edi);
 174     a.mov(eax, dword_ptr(esi, ebp, 2));//地址=地址表+序號*2的2次方
 175     a.add(eax, edi);
 176     //此時eax為loadlibraryA地址
 177     a.bind(FindFuncAddrOver);
 178     a.pop(ebp);
 179     a.pop(esi);
 180     a.pop(ecx);
 181     a.pop(ebx);
 182 }
 183 
 184 VOID GetGetProcAddressAddr(X86Assembler &a)
 185 {
 186     //跳轉標記
 187     Label FindFuncAddr = a.newLabel();
 188     Label FindFuncAddrOver = a.newLabel();
 189 
 190     a.push(ebx);
 191     a.push(ecx);
 192     a.push(esi);
 193     a.push(ebp);
 194     //記得開始獲取出來的都是RVA  要加上DLL的基址edi來取值
 195     a.mov(eax, dword_ptr(edi, 0x3c));
 196     a.mov(ebx, dword_ptr(edi, eax, 0, 0x78));//NT頭+0x78 = 匯入表RVA
 197     //上面等價於[edi + eax + 0x78],如果沒有0的話,他會把第三個引數當成2的N次方變成了edi + eax * 2的0x78次方
 198     a.add(ebx, edi);//ebx=匯入表
 199     a.mov(ecx, dword_ptr(ebx, 0x18));//ecx=按名稱匯出個數
 200     a.mov(esi, dword_ptr(ebx, 0x20));//esi=匯出函式名稱表rva
 201     a.add(esi, edi);
 202 
 203     a.bind(FindFuncAddr);
 204     a.sub(ecx, 1);//從後面往前遍歷 能少佔用一個暫存器,但要記得先-1再遍歷
 205     a.jz(FindFuncAddrOver);//減到0即遍歷完
 206     a.mov(ebp, dword_ptr(esi, ecx, 2));//取到函式名稱比拗最後一個函式名的RVA 等價於[esi,ecx*(2的n次方)]
 207     a.add(ebp, edi);
 208     a.cmp(dword_ptr(ebp), 0x50746547);
 209     a.jne(FindFuncAddr);
 210     a.cmp(dword_ptr(ebp, 4), 0x41636f72);
 211     a.jne(FindFuncAddr);
 212     a.cmp(dword_ptr(ebp, 8), 0x65726464);
 213     a.jne(FindFuncAddr);
 214     a.cmp(word_ptr(ebp, 12), 0x7373);
 215     a.jne(FindFuncAddr);
 216     a.cmp(byte_ptr(ebp, 14), 0);//結束符  防止類似LoadLibrary和LoadLibraryA擴充套件函式名偏差出現
 217     a.jne(FindFuncAddr);
 218 
 219     //此時ecx為遍歷的下標 
 220     a.mov(esi, dword_ptr(ebx, 0x24));//esi=匯出函式序號表RVA(上面名稱對比完 回收esi)
 221     a.add(esi, edi);
 222     a.movzx(ebp, word_ptr(esi, ecx, 1));//ebp=函式序號 ebp=[esi+ecx*2一次方]
 223     //EBP:Get=2b0 Load=3c2
 224     a.mov(esi, dword_ptr(ebx, 0x1c));//esi=函式地址表RVA
 225     a.add(esi, edi);
 226     a.mov(eax, dword_ptr(esi, ebp, 2));//地址=地址表+序號*2的2次方
 227     a.add(eax, edi);
 228     //此時eax為loadlibraryA地址
 229     a.bind(FindFuncAddrOver);
 230     a.pop(ebp);
 231     a.pop(esi);
 232     a.pop(ecx);
 233     a.pop(ebx);
 234 }
 235 DWORD RVAtoFA(DWORD dwRVA)        //RVA轉檔案地址
 236 {
 237     PIMAGE_DOS_HEADER pDosHead = PIMAGE_DOS_HEADER(g_pFileSrc);
 238     PIMAGE_NT_HEADERS pNtHead = PIMAGE_NT_HEADERS((DWORD)pDosHead + pDosHead->e_lfanew);
 239     PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)(pNtHead->FileHeader.SizeOfOptionalHeader + (DWORD)&pNtHead->OptionalHeader);
 240     int iSectionNum = pNtHead->FileHeader.NumberOfSections;
 241 
 242     for (int i = 0; i < iSectionNum; i++)
 243     {
 244         if (dwRVA >= pSection->VirtualAddress && dwRVA < (pSection->VirtualAddress + pSection->Misc.VirtualSize))
 245         {
 246             return (DWORD)g_pFileSrc + dwRVA - pSection->VirtualAddress + pSection->PointerToRawData;
 247         }
 248         pSection++;
 249     }
 250     return 0; //轉換失敗
 251 }
 252 
 253 DWORD FAtoRVA(DWORD dwFA)        //檔案地址轉RVA
 254 {
 255     PIMAGE_DOS_HEADER pDosHead = PIMAGE_DOS_HEADER(g_pFileSrc);
 256     PIMAGE_NT_HEADERS pNtHead = PIMAGE_NT_HEADERS((DWORD)pDosHead + pDosHead->e_lfanew);
 257     PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)(pNtHead->FileHeader.SizeOfOptionalHeader + (DWORD)&pNtHead->OptionalHeader);
 258     int iSectionNum = pNtHead->FileHeader.NumberOfSections;
 259 
 260     for (int i = 0; i < iSectionNum; i++)
 261     {
 262         if (dwFA >= pSection->PointerToRawData && dwFA < (pSection->PointerToRawData + pSection->SizeOfRawData))
 263         {
 264             return (DWORD)dwFA - pSection->PointerToRawData + pSection->VirtualAddress;
 265         }
 266         pSection++;
 267     }
 268     return 0; //轉換失敗
 269 }
 270 
 271 
 272 BOOL LoadFile(char *pFileName)    //獲取檔案內容
 273 {
 274     HANDLE hFile = CreateFileA(pFileName,
 275         GENERIC_READ,
 276         FILE_SHARE_READ,
 277         NULL,
 278         OPEN_EXISTING,
 279         FILE_ATTRIBUTE_NORMAL,
 280         0);
 281     if (!hFile)
 282     {
 283         return false;
 284     }
 285     g_iFileSize = GetFileSize(hFile, NULL);
 286 
 287     g_pFileSrc = (UCHAR*)malloc(g_iFileSize);
 288     if (!g_pFileSrc)
 289     {
 290         cout << "申請記憶體失敗" << endl;
 291         return false;
 292     }
 293 
 294     if (!ReadFile(hFile, g_pFileSrc, g_iFileSize, NULL, NULL))
 295     {
 296         cout << "讀取檔案失敗" << endl;
 297         return false;
 298     }
 299 
 300     return true;
 301 }
 302 
 303 
 304 BOOL CheckPE()        //檢查PE檔案格式
 305 {
 306     PIMAGE_DOS_HEADER pDosHead = PIMAGE_DOS_HEADER(g_pFileSrc);
 307     PIMAGE_NT_HEADERS pNtHead = PIMAGE_NT_HEADERS((DWORD)pDosHead + pDosHead->e_lfanew);
 308     if (pDosHead->e_magic != 0x5a4d || pNtHead->Signature != 0x4550)
 309     {
 310         return false;
 311     }
 312     g_iImageBase = pNtHead->OptionalHeader.ImageBase;
 313     g_iFileAlignment = pNtHead->OptionalHeader.FileAlignment;
 314     g_iSectionAlignment = pNtHead->OptionalHeader.SectionAlignment;
 315     g_iOldOEP = pNtHead->OptionalHeader.AddressOfEntryPoint;
 316     return true;
 317 }
 318 
 319 
 320 
 321 BOOL FindCodeAddr()         //找到呼叫了IAT中的地址的程式碼位置
 322 {
 323     PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 324     PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 325     if (!pNtHead->OptionalHeader.DataDirectory[1].VirtualAddress)
 326     {
 327         return false;
 328     }
 329 
 330     PIMAGE_IMPORT_DESCRIPTOR pImpotrList = (PIMAGE_IMPORT_DESCRIPTOR)RVAtoFA(pNtHead->OptionalHeader.DataDirectory[1].VirtualAddress);  //IAT
 331     while (pImpotrList->FirstThunk)//獲得該程式的IAT地址
 332     {
 333         int i = 0;
 334         while (1)
 335         {
 336             DWORD iIatAddr = *(DWORD*)RVAtoFA(pImpotrList->FirstThunk + i * sizeof(DWORD));
 337             if (!iIatAddr)
 338             {
 339                 break;
 340             }
 341             DWORD dwIatAddr = pImpotrList->FirstThunk + i * sizeof(DWORD);
 342             g_IATaddrList.push_back(dwIatAddr);
 343             i++;
 344         }
 345         pImpotrList++;
 346     }
 347 
 348 
 349     //獲取呼叫這些IAT地址的程式碼地址
 350     for (int i = 0; i < g_iFileSize - 5; i++)
 351     {
 352         //mov eax
 353         if (g_pFileSrc[i] == 0xA1)
 354         {
 355             DWORD IatAddr = *(DWORD*)(g_pFileSrc + i + 1);
 356             for (int iNum = 0; iNum < g_IATaddrList.size(); iNum++)
 357             {
 358                 if (g_IATaddrList[iNum] == (IatAddr^g_iImageBase))
 359                 {
 360                     IatAddrTab IatInfo;
 361                     IatInfo.dwCallAddr = FAtoRVA(i);
 362                     IatInfo.bType = 0;
 363                     IatInfo.wIndex = iNum;
 364                     IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 365                     g_tagIAT.push_back(IatInfo);
 366                     i += 4;   //for迴圈會加1,操作符+地址共5位元組
 367                     continue;
 368                 }
 369             }
 370         }
 371 
 372         //mov edx、esp、esi、ecx、ebx、ebp、edi
 373         if (g_pFileSrc[i] == 0x8B && (g_pFileSrc[i + 1] == 0x15 || g_pFileSrc[i + 1] == 0x25 || g_pFileSrc[i + 1] == 0x35 ||
 374             g_pFileSrc[i + 1] == 0x0D || g_pFileSrc[i + 1] == 0x1D || g_pFileSrc[i + 1] == 0x2D || g_pFileSrc[i + 1] == 0x3D))
 375         {
 376             DWORD IatAddr = *(DWORD*)(g_pFileSrc + i + 2);
 377             for (int iNum = 0; iNum < g_IATaddrList.size(); iNum++)
 378             {
 379                 if (g_IATaddrList[iNum] == (IatAddr^g_iImageBase))
 380                 {
 381                     DWORD ddddd = FAtoRVA(i);
 382                     if (g_pFileSrc[i + 1] == 0x15)    //edx
 383                     {
 384                         IatAddrTab IatInfo;
 385                         IatInfo.dwCallAddr = FAtoRVA(i);
 386                         IatInfo.bType = 1;
 387                         IatInfo.wIndex = iNum;
 388                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 389                         g_tagIAT.push_back(IatInfo);
 390                     }
 391                     else if (g_pFileSrc[i + 1] == 0x25)  //esp
 392                     {
 393                         IatAddrTab IatInfo;
 394                         IatInfo.dwCallAddr = FAtoRVA(i);
 395                         IatInfo.bType = 2;
 396                         IatInfo.wIndex = iNum;
 397                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 398                         g_tagIAT.push_back(IatInfo);
 399                     }
 400                     else if (g_pFileSrc[i + 1] == 0x35)        //esi
 401                     {
 402                         IatAddrTab IatInfo;
 403                         IatInfo.dwCallAddr = FAtoRVA(i);
 404                         IatInfo.bType = 3;
 405                         IatInfo.wIndex = iNum;
 406                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 407                         g_tagIAT.push_back(IatInfo);
 408                     }
 409                     else if (g_pFileSrc[i + 1] == 0x0D)        //ecx
 410                     {
 411                         IatAddrTab IatInfo;
 412                         IatInfo.dwCallAddr = FAtoRVA(i);
 413                         IatInfo.bType = 4;
 414                         IatInfo.wIndex = iNum;
 415                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 416                         g_tagIAT.push_back(IatInfo);
 417                     }
 418                     else if (g_pFileSrc[i + 1] == 0x1D)        //ebx
 419                     {
 420                         IatAddrTab IatInfo;
 421                         IatInfo.dwCallAddr = FAtoRVA(i);
 422                         IatInfo.bType = 5;
 423                         IatInfo.wIndex = iNum;
 424                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 425                         g_tagIAT.push_back(IatInfo);
 426                     }
 427                     else if (g_pFileSrc[i + 1] == 0x2D)        //ebp
 428                     {
 429                         IatAddrTab IatInfo;
 430                         IatInfo.dwCallAddr = FAtoRVA(i);
 431                         IatInfo.bType = 6;
 432                         IatInfo.wIndex = iNum;
 433                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 434                         g_tagIAT.push_back(IatInfo);
 435                     }
 436                     else if (g_pFileSrc[i + 1] == 0x3D)        //edi
 437                     {
 438                         IatAddrTab IatInfo;
 439                         IatInfo.dwCallAddr = FAtoRVA(i);
 440                         IatInfo.bType = 7;
 441                         IatInfo.wIndex = iNum;
 442                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 443                         g_tagIAT.push_back(IatInfo);
 444                     }
 445                     i += 5;   //for迴圈會加1,操作符+地址共6位元組
 446                     continue;
 447                 }
 448             }
 449         }
 450 
 451         //call jmp push
 452         if (g_pFileSrc[i] == 0xFF && (g_pFileSrc[i + 1] == 0x15 || g_pFileSrc[i + 1] == 0x25 || g_pFileSrc[i + 1] == 0x35))
 453         {
 454             DWORD IatAddr = *(DWORD*)(g_pFileSrc + i + 2);
 455             for (int iNum = 0; iNum < g_IATaddrList.size(); iNum++)
 456             {
 457                 if (g_IATaddrList[iNum] == (IatAddr^g_iImageBase))
 458                 {
 459                     DWORD ddddd = FAtoRVA(i);
 460                     if (g_pFileSrc[i + 1] == 0x15) //call
 461                     {
 462                         IatAddrTab IatInfo;
 463                         IatInfo.dwCallAddr = FAtoRVA(i);
 464                         IatInfo.bType = 8;
 465                         IatInfo.wIndex = iNum;
 466                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 467                         g_tagIAT.push_back(IatInfo);
 468                     }
 469                     else if (g_pFileSrc[i + 1] == 0x25)  //jmp
 470                     {
 471                         IatAddrTab IatInfo;
 472                         IatInfo.dwCallAddr = FAtoRVA(i);
 473                         IatInfo.bType = 9;
 474                         IatInfo.wIndex = iNum;
 475                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 476                         g_tagIAT.push_back(IatInfo);
 477                     }
 478                     else if (g_pFileSrc[i + 1] == 0x35)        //push
 479                     {
 480                         IatAddrTab IatInfo;
 481                         IatInfo.dwCallAddr = FAtoRVA(i);
 482                         IatInfo.bType = 10;
 483                         IatInfo.wIndex = iNum;
 484                         IatInfo.dwOldIatAddr = g_IATaddrList[iNum];
 485                         g_tagIAT.push_back(IatInfo);
 486                     }
 487                     i += 5;   //for迴圈會加1,操作符+地址共6位元組
 488                     continue;
 489                 }
 490             }
 491         }
 492     }
 493 
 494     return true;
 495 }
 496 
 497 
 498 bool SaveDll() //儲存DLL相關資料
 499 {
 500     PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 501     PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 502     DWORD dwImportRVA = pNtHead->OptionalHeader.DataDirectory[1].VirtualAddress;
 503     PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)RVAtoFA(dwImportRVA);
 504     while (pImport->FirstThunk)
 505     {
 506         DLLFuncTag tagDllList;//Dll資訊結構
 507 
 508         char *szDllName = (char*)RVAtoFA(pImport->Name);
 509         int iDllNameLen = strlen(szDllName) + 1;
 510         char *szNewDllName = (char*)malloc(iDllNameLen);
 511         memcpy(szNewDllName, szDllName, iDllNameLen);
 512 
 513         tagDllList.szDllName = szNewDllName;
 514         tagDllList.bDllNameLenth = iDllNameLen;
 515 
 516         PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)RVAtoFA(pImport->FirstThunk);
 517         while (pIAT->u1.Function)
 518         {
 519             FuncTag tagFuncInfo;//函式資訊結構
 520             DWORD dwFuncAddr = pIAT->u1.Function;
 521             if (dwFuncAddr & 0x80000000)
 522             {
 523                 tagDllList.bTab = 0;//按序號匯入
 524                 tagFuncInfo.iFuncNameLenth = 4;
 525                 tagFuncInfo.szFuncName = (char*)(dwFuncAddr ^ 0x80000000);
 526                 tagDllList.vFuncNAMEorNo.push_back(tagFuncInfo);
 527             }
 528             else
 529             {
 530                 tagDllList.bTab = 1;//按名稱匯入
 531                 PIMAGE_IMPORT_BY_NAME pFunc = (PIMAGE_IMPORT_BY_NAME)RVAtoFA(pIAT->u1.Function);
 532                 int iFuncNameLen = strlen(pFunc->Name) + 1;
 533                 char *szFuncName = (char*)malloc(iFuncNameLen);
 534                 memcpy(szFuncName, pFunc->Name, iFuncNameLen);
 535                 tagFuncInfo.iFuncNameLenth = iFuncNameLen;
 536                 tagFuncInfo.szFuncName = szFuncName;
 537                 tagDllList.vFuncNAMEorNo.push_back(tagFuncInfo);
 538             }
 539             pIAT++;
 540         }
 541         tagDllList.wFuncNum = tagDllList.vFuncNAMEorNo.size();
 542         g_tagDllFunc.push_back(tagDllList);
 543         pImport++;
 544     }
 545 
 546     //將IAT表資訊隱藏(每個DLL只有一個匯入函式)
 547     PIMAGE_IMPORT_DESCRIPTOR pImpotrList = (PIMAGE_IMPORT_DESCRIPTOR)RVAtoFA(pNtHead->OptionalHeader.DataDirectory[1].VirtualAddress);
 548     while (pImpotrList->FirstThunk)
 549     {
 550         PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)RVAtoFA(pImpotrList->FirstThunk);
 551         PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)RVAtoFA(pImpotrList->OriginalFirstThunk);
 552         pIAT++;//保留第一個
 553         pINT++;
 554         while (pIAT->u1.Function)//將IAT清空
 555         {
 556             pIAT->u1.Function = 0;
 557             pIAT++;
 558         }
 559         while (pINT->u1.Function)//將INT清空
 560         {
 561             pINT->u1.Function = 0;
 562             pINT++;
 563         }
 564         pImpotrList++;
 565     }
 566     return true;
 567 }
 568 
 569 
 570 
 571 
 572 bool NewIat()
 573 {
 574     PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 575     PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 576     PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
 577     for (int i = 0; i < pNtHead->FileHeader.NumberOfSections; i++)
 578     {
 579         //將每個節都改為可寫屬性,以便在後續修改。
 580         //本來只修改.text節的屬性即可,但由於該節的位置不在data陣列裡面記錄,單憑節名判斷未必準確,所以將所以節都改為可寫-
 581         pSection->Characteristics += 0x80000000;
 582     }
 583     if (!(pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress))//噹噹前程式沒有重定位表
 584     {
 585         PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
 586         pSection += pNtHead->FileHeader.NumberOfSections - 1;
 587         int iNewSectionFileAddr = pSection->PointerToRawData + pSection->SizeOfRawData;
 588         int iNewSectionFileMapAddr = 0;
 589         if (!(pSection->Misc.VirtualSize % g_iSectionAlignment))
 590         {
 591             iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize;
 592         }
 593         else
 594         {
 595             iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize
 596                 + g_iSectionAlignment - (pSection->Misc.VirtualSize % g_iSectionAlignment);
 597         }
 598 
 599         pSection++;
 600         memcpy(pSection->Name, ".KD", 3);
 601         pSection->VirtualAddress = iNewSectionFileMapAddr;
 602         pSection->PointerToRawData = iNewSectionFileAddr;
 603         pSection->Characteristics = (0x20000000 | 0x40000000 | 0x80000000 | 0x40); //節屬性 可讀可寫可執行已被初始化
 604         pNtHead->FileHeader.NumberOfSections += 1;//節表數量+1
 605 
 606 
 607         //將IAT的資料壓入新節中資料排列分別為DLL數量(2位元組)、DLL名字長度、該
 608         int iAddIatLenth = 0;//已經增加的iat資料長度
 609         //先新增2位元組資料說明該程式的DLL數量
 610         g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 1);
 611         byte bDllNum = g_tagDllFunc.size();
 612         memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, &bDllNum, 1);
 613         iAddIatLenth += 2;
 614         for (int i = 0; i < bDllNum; i++)
 615         {
 616             //新增DLL名長度
 617             int bDllNameLenth = g_tagDllFunc[i].bDllNameLenth;
 618             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 2);
 619             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, &bDllNameLenth, 2);
 620             iAddIatLenth += 2;
 621 
 622             //新增DLL名
 623             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + bDllNameLenth);
 624             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, g_tagDllFunc[i].szDllName, bDllNameLenth);
 625             iAddIatLenth += bDllNameLenth;
 626 
 627             //新增該DLL中的匯入函式數量  方便後續遍歷
 628             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 4);
 629             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (DWORD*)&g_tagDllFunc[i].wFuncNum, 4);
 630             iAddIatLenth += 4;
 631 
 632             //新增該DLL的匯入方式 0為序號1為名稱
 633             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 1);
 634             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (byte*)&g_tagDllFunc[i].bTab, 1);
 635             iAddIatLenth += 1;
 636             for (int j = 0; j < g_tagDllFunc[i].wFuncNum; j++)
 637             {
 638                 //新增函式名或序號
 639                 if (!(g_tagDllFunc[i].bTab)) //序號
 640                 {
 641                     g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 6);//2長度4序號
 642                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (WORD*)&g_tagDllFunc[i].vFuncNAMEorNo[j].iFuncNameLenth, 2);
 643                     iAddIatLenth += 2;
 644                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (DWORD*)&g_tagDllFunc[i].vFuncNAMEorNo[j].szFuncName, 4);
 645                     iAddIatLenth += 4;
 646                 }
 647                 else  //名字
 648                 {
 649                     int iFuncNameLenth = g_tagDllFunc[i].vFuncNAMEorNo[j].iFuncNameLenth;//長度
 650                     g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 2 + iFuncNameLenth);//前2位元組存放長度 後面為函式名稱
 651                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (WORD*)&iFuncNameLenth, 2);
 652                     iAddIatLenth += 2;
 653                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, g_tagDllFunc[i].vFuncNAMEorNo[j].szFuncName, iFuncNameLenth);
 654                     iAddIatLenth += iFuncNameLenth;
 655                 }
 656             }
 657         }
 658 
 659         //獲取新的OEP並修改舊的OEP
 660         g_iNewOEP = iNewSectionFileMapAddr + iAddIatLenth;//新節的
 661         pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 662         pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 663         pNtHead->OptionalHeader.AddressOfEntryPoint = g_iNewOEP;
 664 
 665         JitRuntime        _x86RunTimeObject;
 666         X86Assembler    a(&_x86RunTimeObject);
 667 
 668         //當程式沒有重定位表時,根據call 0000 0000來獲得程式基址,有重定位表時則根據重定位表
 669         char szGetOEP[] = { 0xE8, 0x00, 0x00, 0x00, 0x00 }; //同理jit.call(0x00000000);
 670         a.pop(eax);
 671         a.sub(eax, 5);//此時得到oep所在的地址 oep距離基址隔著
 672         a.sub(eax, g_iNewOEP);//OEP所在的基址-偏移  得到基址
 673         a.mov(ebp, eax);    //儲存程式基址到ebp
 674         a.add(eax, g_iOldOEP);
 675         a.jmp(eax);
 676 
 677         PVOID szJit = a.make();
 678         int iJitLenth = a.getCodeSize() + 5; //包括e8 0000 0000;
 679 
 680         g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + iJitLenth);
 681         memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, szGetOEP, 5);
 682         memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth + 5, szJit, iJitLenth);
 683 
 684         int iAddSrcLen = iAddIatLenth + iJitLenth;//一共增加的程式碼
 685         //完善節長度等資料  保守起見重新獲取節表資訊
 686         pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 687         pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 688         pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
 689         pSection += pNtHead->FileHeader.NumberOfSections - 1;
 690         if (!(iAddSrcLen%g_iFileAlignment))
 691         {
 692             pSection->SizeOfRawData = iAddSrcLen;
 693         }
 694         else
 695         {
 696             pSection->SizeOfRawData = iAddSrcLen + g_iFileAlignment - (iAddSrcLen%g_iFileAlignment);
 697         }
 698         if (!(iAddSrcLen%g_iSectionAlignment))
 699         {
 700             pSection->Misc.VirtualSize = iAddSrcLen;
 701         }
 702         else
 703         {
 704             pSection->Misc.VirtualSize = iAddSrcLen + g_iSectionAlignment - (iAddSrcLen%g_iSectionAlignment);
 705         }
 706         pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize;
 707 
 708         if (iAddSrcLen != pSection->SizeOfRawData)//當新增長度與對齊後的節長度不相等時,將其補全
 709         {
 710             int iLenth = pSection->SizeOfRawData;//先儲存,以免擴容後原地址被回收
 711             //g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iLenth);
 712             UCHAR *uuu = (UCHAR*)malloc(g_iFileSize + iAddSrcLen);
 713             memcpy(uuu, g_pFileSrc, g_iFileSize + iAddSrcLen);
 714             g_pFileSrc = (UCHAR*)malloc(g_iFileSize + iLenth);
 715             memcpy(g_pFileSrc, uuu, g_iFileSize + iAddSrcLen);
 716             memset(g_pFileSrc + g_iFileSize + iAddSrcLen, 0, iLenth - iAddSrcLen);
 717         }
 718         g_iFileSize += pSection->SizeOfRawData;
 719     }
 720 
 721 
 722 
 723     /////////////////////////////////////////////////////////////////////////////
 724     //當有重定位表時,先儲存相關資料,刪除重定位表,新增節,再在後面還原重定位表
 725     else
 726     {
 727         //把重定位表資料移動到新節當中
 728         //獲取位置和長度
 729         int iRelocationAddr = pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress;
 730         int iRelocationLenth = pNtHead->OptionalHeader.DataDirectory[5].Size;//資料長度
 731         char *szR_Src = (char*)malloc(iRelocationLenth);
 732         memcpy(szR_Src, (char*)RVAtoFA(iRelocationAddr), iRelocationLenth);
 733         
 734         PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
 735         char szR_Name[8] = { 0 };
 736         int iR_MapLenth = 0;//節中對映長度
 737         int iR_FileLenth = 0;//節中檔案長度
 738         while (1)
 739         {
 740             if (pSection->VirtualAddress == iRelocationAddr) //定位到重定位表節表
 741             {
 742                 strcpy(szR_Name, (char*)pSection->Name);
 743                 iR_MapLenth = pSection->Misc.VirtualSize;
 744                 iR_FileLenth = pSection->SizeOfRawData;
 745                 break;
 746             }
 747             pSection++;
 748         }
 749 
 750         //刪除重定位表相關資訊
 751         memset(g_pFileSrc + g_iFileSize - iR_FileLenth, 0, iR_FileLenth);//把重定位表資料用0覆蓋
 752         memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));
 753         pNtHead->FileHeader.NumberOfSections--;
 754         //調整檔案記憶體大小
 755         int iAlignmentSize = iR_MapLenth % g_iSectionAlignment;
 756         if (!iAlignmentSize)
 757         {
 758             pNtHead->OptionalHeader.SizeOfImage -= iR_MapLenth;        //調整檔案記憶體大小
 759         }
 760         else
 761         {
 762             pNtHead->OptionalHeader.SizeOfImage =
 763                 pNtHead->OptionalHeader.SizeOfImage
 764                 - (iR_MapLenth - iAlignmentSize + g_iSectionAlignment);
 765         }
 766         g_iFileSize -= iR_FileLenth;
 767 
 768 
 769         //新增節
 770         pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
 771         pSection += pNtHead->FileHeader.NumberOfSections - 1;
 772         int iNewSectionFileAddr = pSection->PointerToRawData + pSection->SizeOfRawData;
 773         int iNewSectionFileMapAddr = 0;
 774         if (!(pSection->Misc.VirtualSize % g_iSectionAlignment))
 775         {
 776             iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize;
 777         }
 778         else
 779         {
 780             iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize
 781                 + g_iSectionAlignment - (pSection->Misc.VirtualSize % g_iSectionAlignment);
 782         }
 783 
 784         pSection++;
 785         memcpy(pSection->Name, ".KD", 3);
 786         pSection->VirtualAddress = iNewSectionFileMapAddr;
 787         pSection->PointerToRawData = iNewSectionFileAddr;
 788         pSection->Characteristics = (0x20000000 | 0x40000000 | 0x80000000 | 0x40); //節屬性 可讀可寫可執行已被初始化
 789         pNtHead->FileHeader.NumberOfSections += 1;//節表數量+1
 790 
 791 
 792         //將IAT的資料壓入新節中資料排列分別為DLL數量(2位元組)、DLL名字長度、該
 793         int iAddIatLenth = 0;//已經增加的iat資料長度
 794         //先新增1位元組資料說明該程式的DLL數量
 795         g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 1);
 796         byte bDllNum = g_tagDllFunc.size();
 797         memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, &bDllNum, 1);
 798         iAddIatLenth += 1;
 799         for (int i = 0; i < bDllNum; i++)
 800         {
 801             //新增DLL名長度
 802             byte bDllNameLenth = g_tagDllFunc[i].bDllNameLenth;
 803             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 1);
 804             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, &bDllNameLenth, 1);
 805             iAddIatLenth += 1;
 806 
 807             //新增DLL名
 808             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + bDllNameLenth);
 809             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, g_tagDllFunc[i].szDllName, bDllNameLenth);
 810             iAddIatLenth += bDllNameLenth;
 811 
 812             //新增該DLL中的匯入函式數量  方便後續遍歷
 813             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 2);
 814             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (DWORD*)&g_tagDllFunc[i].wFuncNum, 2);
 815             iAddIatLenth += 2;
 816 
 817             //新增該DLL的匯入方式 0為序號1為名稱
 818             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 1);
 819             memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (byte*)&g_tagDllFunc[i].bTab, 1);
 820             iAddIatLenth += 1;
 821             for (int j = 0; j < g_tagDllFunc[i].wFuncNum; j++)
 822             {
 823                 //新增函式名或序號
 824                 if (!(g_tagDllFunc[i].bTab)) //序號
 825                 {
 826                     g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 5);//1長度4序號
 827                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (BYTE*)&g_tagDllFunc[i].vFuncNAMEorNo[j].iFuncNameLenth, 1);
 828                     iAddIatLenth += 1;
 829                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (DWORD*)&g_tagDllFunc[i].vFuncNAMEorNo[j].szFuncName, 4);
 830                     iAddIatLenth += 4;
 831                 }
 832                 else  //名字
 833                 {
 834                     int iFuncNameLenth = g_tagDllFunc[i].vFuncNAMEorNo[j].iFuncNameLenth;//長度
 835                     g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + 1 + iFuncNameLenth);//前2位元組存放長度 後面為函式名稱
 836                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, (BYTE*)&iFuncNameLenth, 1);
 837                     iAddIatLenth += 1;
 838                     memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth, g_tagDllFunc[i].vFuncNAMEorNo[j].szFuncName, iFuncNameLenth);
 839                     iAddIatLenth += iFuncNameLenth;
 840                 }
 841             }
 842         }
 843 
 844         //存放載入的函式地址表的RVA
 845         int iLoadAddressTabRVA = iNewSectionFileMapAddr + iAddIatLenth;
 846         int iLoadIatLenth = g_IATaddrList.size() * 4;//用於存放載入的函式地址表長度  此處多4位元組來存放基址
 847 
 848 
 849         //獲取新的OEP並修改舊的OEP
 850         g_iNewOEP = iNewSectionFileMapAddr + iAddIatLenth + iLoadIatLenth;
 851         pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 852         pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 853          pNtHead->OptionalHeader.AddressOfEntryPoint = g_iNewOEP;
 854 
 855         JitRuntime        _x86RunTimeObject;
 856         X86Assembler    a(&_x86RunTimeObject);
 857 
 858         //跳轉標記
 859         Label LoadDllBegin = a.newLabel();
 860         Label LoadDllOver = a.newLabel();
 861         
 862         Label NameExport = a.newLabel();
 863         Label NameExportBegin = a.newLabel();
 864         Label NameExportOver = a.newLabel();
 865         Label NoExportBegin = a.newLabel();
 866         Label NoExportOver = a.newLabel();
 867 
 868         //test
 869         Label lTest = a.newLabel();
 870         
 871         //當有重定位表時,將NewOEP+1新增到重定位項中,重定位後的地址-OldOEP即可得到新的基址
 872         //這裡push g_iOldOEP + g_iImageBase而不是g_iImageBase(直接得到新基址 不用再-舊OEP),是因為push完還要ret到舊OEP
 873         //執行完這段後,棧上是舊OEP的地址,ebx是程式基址
 874         a.push(g_iOldOEP + g_iImageBase);
 875         a.mov(ebx, dword_ptr(esp));
 876         a.sub(ebx, g_iOldOEP);
 877         
 878         /*a.call(lTest);
 879         a.bind(lTest);
 880         a.pop(eax);*/
 881 
 882         a.pusha();
 883         a.push(ebx);//將程式基址儲存到棧上
 884         a.sub(esp, 0x8);
 885         a.mov(ebp, esp); //ebp從上往下分別是LoadLibraryA函式地址、GetProcAddress函式地址和程式基址  ebp-4為Dll基址 -8為填充函式載入地址起始地址
 886         a.sub(esp, 0x8);
 887         
 888         GetKernel32Base(a);
 889         a.mov(edi, eax);//edi為所遍歷到的DLL基址
 890 
 891         //計算函式地址
 892         GetLoadLibraryAAddr(a);
 893         a.mov(dword_ptr(ebp, 0), eax);//儲存LoadLibraryA的值到棧上
 894         GetGetProcAddressAddr(a);
 895         a.mov(dword_ptr(ebp, 4), eax);//儲存GetProcAddress的值到棧上
 896         a.mov(eax, iLoadAddressTabRVA);
 897         a.add(eax, dword_ptr(ebp, 8));//填充函式地址RVA+程式基址
 898         a.mov(dword_ptr(ebp, -8), eax);//放到ebp-8的地址上
 899 
 900 
 901         //開始遍歷IAT資料,填充函式載入地址表
 902         a.mov(esi, iNewSectionFileMapAddr);
 903         a.add(esi, dword_ptr(ebp, 8));//esi為IAT資料起點
 904         a.mov(ebx, 1);//以ebx開始遍歷  達到長度 退出
 905 
 906 
 907         //載入DLL
 908         a.bind(LoadDllBegin);
 909         a.cmp(ebx, iAddIatLenth);//如果遍歷長度大於等於IAT資料長度
 910         a.jnl(LoadDllOver);
 911         a.movzx(ecx, byte_ptr(esi, ebx));//取得DLL名長度
 912         a.add(ebx, 1);//+Dll名長度的變數1
 913         a.lea(eax, dword_ptr(esi,ebx));//獲得函式名地址
 914         a.push(eax);
 915         a.add(ebx, ecx);//+DLL名長度
 916         a.mov(eax, dword_ptr(ebp));//獲得loadlibrary地址
 917         a.call(eax);
 918         a.mov(dword_ptr(ebp,-4),eax);//ebp為load.. +4為get.. +8為程式基址  -4為當前Dll基址 -8為填充函式載入地址位置
 919         a.movzx(edi, word_ptr(esi, ebx));//取出該DLL匯出函式數量
 920         a.add(ebx, 2);
 921         a.movzx(edx, byte_ptr(esi, ebx));//取出匯出方式
 922         a.add(ebx, 1);
 923         a.cmp(edx, 1);//為1時,名稱匯出
 924         a.je(NameExportBegin);
 925 
 926 
 927         //序號匯出
 928         a.bind(NoExportBegin);//序號匯出迴圈起點
 929         a.cmp(edi, 0);
 930         a.je(NoExportOver);
 931         a.add(ebx, 1);
 932         a.mov(eax, dword_ptr(esi, ebx));//取得序號
 933         a.add(ebx, 4);
 934         a.push(eax);
 935         a.push(dword_ptr(ebp, -4));
 936         a.mov(eax, dword_ptr(ebp, 4));//獲得GetProcAddress
 937         a.call(eax);
 938         a.mov(edx, dword_ptr(ebp, -8));//取得裝載函式表
 939         a.mov(dword_ptr(edx), eax);
 940         a.add(dword_ptr(ebp, -8), 4);//裝載函式表RVA+4
 941         a.sub(edi, 1);
 942         a.jmp(NoExportBegin);
 943         a.bind(NoExportOver);//序號匯出迴圈終點
 944         a.jmp(LoadDllBegin); //重新載入新DLL
 945 
 946         //名稱匯出
 947         a.bind(NameExportBegin);//名稱匯出迴圈起點
 948         a.cmp(edi, 0);
 949         a.je(NameExportOver);
 950         a.movzx(ecx, byte_ptr(esi, ebx));//取得名稱長度
 951         a.add(ebx, 1);
 952         a.lea(eax, dword_ptr(esi, ebx));//名稱地址
 953         a.push(eax);
 954         a.add(ebx, ecx);
 955         a.push(dword_ptr(ebp, -4));
 956         a.mov(eax, dword_ptr(ebp, 4));
 957         a.call(eax);
 958         a.mov(edx, dword_ptr(ebp, -8));//取得裝載函式表
 959         a.mov(dword_ptr(edx), eax);
 960         a.add(dword_ptr(ebp, -8), 4);//裝載函式表RVA+4
 961         a.sub(edi, 1);
 962         a.jmp(NameExportBegin);
 963         a.bind(NameExportOver);//名稱匯出迴圈終點
 964         a.jmp(LoadDllBegin); //重新載入新DLL
 965         
 966 
 967         
 968         ///////////開始構造替換呼叫程式碼的shellcode//////////////
 969         //注意esp為當前操作的棧頂  ebp+8為程式所在基址
 970         int iNewJitBeginAddrRva = iNewSectionFileMapAddr + iAddIatLenth + iLoadIatLenth; //替換的shellcode開始的地址
 971         int iNewJitLenth = a.getCodeSize();
 972         for (int i = 0; i < g_tagIAT.size(); i++)
 973         {
 974             if (g_tagIAT[i].bType == 0)    //EAX
 975             {
 976                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
 977                 a.push(ebx);
 978                 a.push(ebx);
 979                 a.push(ebx);
 980                 a.push(esi);
 981                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 5);
 982                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
 983                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
 984                 a.lea(ebx, dword_ptr(ebx, esi));
 985                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+5+程式基址 =返回地址
 986                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
 987                 a.lea(ebx, dword_ptr(ebx, esi));
 988                 a.mov(ebx, dword_ptr(ebx));
 989                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
 990                 a.pop(esi);
 991                 a.pop(ebx);
 992                 a.pop(eax);
 993                 a.ret();
 994                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
 995                 iNewJitLenth = a.getCodeSize();
 996             }
 997             else if (g_tagIAT[i].bType == 1) //EDX
 998             {
 999                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1000                 a.push(ebx);
1001                 a.push(ebx);
1002                 a.push(ebx);
1003                 a.push(esi);
1004                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1005                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1006                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1007                 a.lea(ebx, dword_ptr(ebx, esi));
1008                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+5+程式基址 =返回地址
1009                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1010                 a.lea(ebx, dword_ptr(ebx, esi));
1011                 a.mov(ebx, dword_ptr(ebx));
1012                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1013                 a.pop(esi);
1014                 a.pop(ebx);
1015                 a.pop(edx);
1016                 a.ret();
1017                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1018                 iNewJitLenth = a.getCodeSize();
1019             }
1020             else if (g_tagIAT[i].bType == 2)    //ESP
1021             {
1022                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1023                 a.push(ebx);
1024                 a.push(ebx);
1025                 a.push(ebx);
1026                 a.push(esi);
1027                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1028                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1029                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1030                 a.lea(ebx, dword_ptr(ebx, esi));
1031                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+6+程式基址 =返回地址
1032                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1033                 a.lea(ebx, dword_ptr(ebx, esi));
1034                 a.mov(ebx, dword_ptr(ebx));
1035                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1036                 a.pop(esi);
1037                 a.pop(ebx);
1038                 a.pop(esp);
1039                 a.ret();
1040                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1041                 iNewJitLenth = a.getCodeSize();
1042             }
1043             else if (g_tagIAT[i].bType == 3)    //ESI
1044             {
1045                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1046                 a.push(ebx);
1047                 a.push(ebx);
1048                 a.push(ebx);
1049                 a.push(esi);
1050                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1051                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1052                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1053                 a.lea(ebx, dword_ptr(ebx, esi));
1054                 a.mov(ebx, dword_ptr(ebx));
1055                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1056                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1057                 a.lea(ebx, dword_ptr(ebx, esi));
1058                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+6+程式基址 =返回地址
1059                 a.pop(esi);
1060                 a.pop(ebx);
1061                 a.pop(esi);
1062                 a.ret();
1063                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1064                 iNewJitLenth = a.getCodeSize();
1065             }
1066             else if (g_tagIAT[i].bType == 4)    //ECX
1067             {
1068                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1069                 a.push(ebx);
1070                 a.push(ebx);
1071                 a.push(ebx);
1072                 a.push(esi);
1073                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1074                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1075                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1076                 a.lea(ebx, dword_ptr(ebx, esi));
1077                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+6+程式基址 =返回地址
1078                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1079                 a.lea(ebx, dword_ptr(ebx, esi));
1080                 a.mov(ebx, dword_ptr(ebx));
1081                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1082                 a.pop(esi);
1083                 a.pop(ebx);
1084                 a.pop(ecx);
1085                 a.ret();
1086                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1087                 iNewJitLenth = a.getCodeSize();
1088             }
1089             else if (g_tagIAT[i].bType == 5)    //EBX
1090             {
1091                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1092                 a.push(ebx);
1093                 a.push(ebx);
1094                 a.push(ebx);
1095                 a.push(esi);
1096                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1097                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1098                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1099                 a.lea(ebx, dword_ptr(ebx, esi));
1100                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+6+程式基址 =返回地址
1101                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1102                 a.lea(ebx, dword_ptr(ebx, esi));
1103                 a.mov(ebx, dword_ptr(ebx));
1104                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1105                 a.pop(esi);
1106                 a.pop(ebx);
1107                 a.pop(ebx);
1108                 a.ret();
1109                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1110                 iNewJitLenth = a.getCodeSize();
1111             }
1112             else if (g_tagIAT[i].bType == 6)    //EBP
1113             {
1114                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1115                 a.push(ebx);
1116                 a.push(ebx);
1117                 a.push(ebx);
1118                 a.push(esi);
1119                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1120                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1121                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1122                 a.lea(ebx, dword_ptr(ebx, esi));
1123                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+6+程式基址 =返回地址
1124                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1125                 a.lea(ebx, dword_ptr(ebx, esi));
1126                 a.mov(ebx, dword_ptr(ebx));
1127                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1128                 a.pop(esi);
1129                 a.pop(ebx);
1130                 a.pop(ebp);
1131                 a.ret();
1132                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1133                 iNewJitLenth = a.getCodeSize();
1134             }
1135             else if (g_tagIAT[i].bType == 7)    //EDI
1136             {
1137                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1138                 a.push(ebx);
1139                 a.push(ebx);
1140                 a.push(ebx);
1141                 a.push(esi);
1142                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);
1143                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1144                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1145                 a.lea(ebx, dword_ptr(ebx, esi));
1146                 a.mov(dword_ptr(esp, 0xC), ebx);//當前指令所在RVA+6+程式基址 =返回地址
1147                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1148                 a.lea(ebx, dword_ptr(ebx, esi));
1149                 a.mov(ebx, dword_ptr(ebx));
1150                 a.mov(dword_ptr(esp, 0x8), ebx);//代替原函式地址的地址
1151                 a.pop(esi);
1152                 a.pop(ebx);
1153                 a.pop(edi);
1154                 a.ret();
1155                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1156                 iNewJitLenth = a.getCodeSize();
1157             }
1158             else if (g_tagIAT[i].bType == 8)    //Call
1159             {
1160                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1161                 a.push(ebx);
1162                 a.push(ebx);
1163                 a.push(ebx);
1164                 a.push(esi);
1165                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1166                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1167                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1168                 a.lea(ebx, dword_ptr(ebx, esi));
1169                 a.mov(ebx, dword_ptr(ebx));
1170                 a.mov(dword_ptr(esp, 8), ebx);//存放目標函式地址的地址的RVA
1171                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6);//返回地址RVA
1172                 a.lea(ebx, dword_ptr(ebx, esi));
1173                 a.mov(dword_ptr(esp, 0xC), ebx); 
1174                 a.pop(esi);
1175                 a.pop(ebx);
1176                 a.ret();
1177                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1178                 iNewJitLenth = a.getCodeSize();
1179             }
1180             else if (g_tagIAT[i].bType == 9) //JMP
1181             {
1182                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1183                 a.push(ebx);
1184                 a.push(ebx);
1185                 a.push(ebx);
1186                 a.push(esi);
1187                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1188                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1189                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1190                 a.lea(ebx, dword_ptr(ebx, esi));
1191                 a.mov(dword_ptr(esp,4), 0x3535);//填充垃圾資料
1192                 a.mov(ebx, dword_ptr(ebx));
1193                 a.mov(dword_ptr(esp, 0xC), ebx);//存放目標函式地址的地址的RVA
1194                 a.pop(esi);
1195                 a.pop(ebx);
1196                 a.pop(ebx);
1197                 a.ret();
1198                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1199                 iNewJitLenth = a.getCodeSize();
1200             }
1201             else if (g_tagIAT[i].bType == 10) //PUSH
1202             {
1203                 g_tagIAT[i].dwNewIatAddr = iNewSectionFileMapAddr + iAddIatLenth + g_tagIAT[i].wIndex * 4;
1204                 a.push(ebx);
1205                 a.push(ebx);
1206                 a.push(ebx);
1207                 a.push(esi);
1208                 a.mov(ebx, (int)g_tagIAT[i].dwNewIatAddr);
1209                 a.mov(esi, g_iImageBase);//程式基址 後續會將這個位置新增到重定位塊處,執行時即變為程式基址了
1210                 g_tagIAT[i].dwRelocAddr = iNewJitBeginAddrRva + a.getCodeSize() - 4;//shellcode開始的位置+當前jit長度 再往回4位元組  即為存放基址的位置
1211                 a.lea(ebx, dword_ptr(ebx, esi));
1212                 a.mov(ebx, dword_ptr(ebx));
1213                 a.mov(dword_ptr(esp, 0xC), ebx);//存放目標函式地址的地址的RVA
1214                 a.mov(ebx, (int)g_tagIAT[i].dwCallAddr + 6); //返回地址RVA
1215                 a.lea(ebx, dword_ptr(ebx, esi));
1216                 a.mov(dword_ptr(esp, 8), ebx);
1217                 a.pop(esi);
1218                 a.pop(ebx);
1219                 a.ret();
1220                 g_tagIAT[i].dwJmpAddr = iNewJitBeginAddrRva + iNewJitLenth;
1221                 iNewJitLenth = a.getCodeSize();
1222             }
1223         }
1224         a.bind(LoadDllOver);//載入函式地址表以及構建替代IAT的ShellCode完成
1225         a.mov(edi, dword_ptr(ebp, 8));//獲得基址
1226         for (int i = 0; i < g_tagIAT.size(); i++)
1227         {    
1228             a.lea(ecx, dword_ptr(edi, g_tagIAT[i].dwCallAddr));
1229             a.mov(byte_ptr(ecx), 0xE9);
1230             //由於這裡的E9後面的值,是偏移,所以不用加上基址。偏移量=目標地址-當前指令地址-指令長度
1231             a.mov(dword_ptr(ecx, 1), (int)(g_tagIAT[i].dwJmpAddr - g_tagIAT[i].dwCallAddr - 5));
1232             if (g_tagIAT[i].bType == 0)
1233             {
1234                 continue;
1235             }
1236             a.mov(byte_ptr(ecx, 5), 0x90);
1237         }
1238         a.add(esp, 0x10);//8+8
1239         a.pop(ebx);
1240         a.popa();
1241         a.ret();
1242 
1243         PVOID szJit = a.make();
1244         int iJitLenth = a.getCodeSize(); 
1245          g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iAddIatLenth + iLoadIatLenth + iJitLenth);
1246         memcpy(g_pFileSrc + g_iFileSize + iAddIatLenth + iLoadIatLenth , szJit, iJitLenth);
1247 
1248 
1249         //一共增加的程式碼=IAT相關資料+載入後的函式地址表大小+JIT程式碼長度
1250          int iAddSrcLen = iAddIatLenth + iLoadIatLenth + iJitLenth;
1251         //完善節長度等資料  保守起見重新獲取節表資訊
1252         pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
1253         pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
1254         pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
1255         pSection += pNtHead->FileHeader.NumberOfSections - 1;
1256         if (!(iAddSrcLen%g_iFileAlignment))
1257         {
1258             pSection->SizeOfRawData = iAddSrcLen;
1259         }
1260         else
1261         {
1262             pSection->SizeOfRawData = iAddSrcLen + g_iFileAlignment - (iAddSrcLen%g_iFileAlignment);
1263         }
1264         if (!(iAddSrcLen%g_iSectionAlignment))
1265         {
1266             pSection->Misc.VirtualSize = iAddSrcLen;
1267         }
1268         else
1269         {
1270             pSection->Misc.VirtualSize = iAddSrcLen + g_iSectionAlignment - (iAddSrcLen%g_iSectionAlignment);
1271         }
1272         pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize;
1273 
1274         if (iAddSrcLen != pSection->SizeOfRawData)//當新增長度與對齊後的節長度不相等時,將其補全
1275         {
1276             int iLenth = pSection->SizeOfRawData;//先儲存,以免擴容後原地址被回收
1277             g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iLenth);
1278             memset(g_pFileSrc + g_iFileSize + iAddSrcLen, 0, iLenth - iAddSrcLen);
1279         }
1280           g_iFileSize += pSection->SizeOfRawData;
1281 
1282         
1283 
1284         //將原重定位中  涉及IAT的重定位項刪掉
1285         //1.先統計IAT中的地址位置程式碼(除了mov eax的地址位置在指令位置+1處,其他皆為+2)
1286         vector<WORD>vDeleteIatAddr;//需要刪除的重定位資訊
1287         for (int i = 0; i < g_tagIAT.size(); i++)
1288         {
1289             if (g_tagIAT[i].bType==0) //mov eax  重定位地址位於呼叫地址+1處
1290             {
1291                 vDeleteIatAddr.push_back(g_tagIAT[i].dwCallAddr + 1);
1292             }
1293             else
1294             {
1295                 vDeleteIatAddr.push_back(g_tagIAT[i].dwCallAddr + 2);
1296             }
1297             
1298         }
1299 
1300         //2.統計出重定位表有幾個重定位塊,每個塊的基址是多少,裡面有多少個偏移
1301         for (int i = 0; i < iRelocationLenth; i++)
1302         {
1303             tagRelocation addR;
1304             addR.wR_Head = *(int*)(szR_Src + i);
1305             int iR_Lenth = *(int*)(szR_Src + i + 4);
1306             for (int j = 8; j < iR_Lenth; j+=2)
1307             {
1308                 WORD ccc = *(WORD*)(szR_Src + i + j);
1309                 addR.wR_Offset.push_back(ccc);
1310             }
1311             vOldReloction.push_back(addR);
1312             i += iR_Lenth - 1;
1313         }
1314 
1315         //3.根據IAT裡的地址,在重定位地址表查詢,找到之後將其刪除 
1316         for (int i = 0; i < vDeleteIatAddr.size(); i++)
1317         {
1318             for (int j = 0; j < vOldReloction.size(); j++)
1319             {
1320                 if ((vDeleteIatAddr[i] & 0xFFF000) > vOldReloction[j].wR_Head) //當呼叫地址的重定位塊大於當前重定位表塊時
1321                 {
1322                     continue;
1323                 }
1324                 else if ((vDeleteIatAddr[i] & 0xFFF000) < vOldReloction[j].wR_Head)
1325                 {
1326                     break;
1327                 }
1328                 else        //找到對應重定位塊
1329                 {
1330                     for (int k = 0; k < vOldReloction[j].wR_Offset.size(); k++)
1331                     {
1332                         if ((vDeleteIatAddr[i] & 0xFFF) == (vOldReloction[j].wR_Offset[k] & 0xFFF))
1333                         {
1334                             vOldReloction[j].wR_Offset.erase(vOldReloction[j].wR_Offset.begin() + k);
1335                             goto NextIatAddr;
1336                         }
1337                     }
1338                 }
1339 
1340             }
1341         NextIatAddr:;    
1342         }
1343 
1344         //4.對齊重定位表資料,
1345         /*如果重定位塊中專案為偶數個,則跳過
1346         如果是奇數個,看最後一個是否為0。如果為0,刪掉;如果不為0,補0*/
1347         for (int i = 0; i < vOldReloction.size(); i++)
1348         {
1349             int iReloctionNum = vOldReloction[i].wR_Offset.size();
1350             if (iReloctionNum % 2) //取餘  如果能取到 說明為奇數
1351             {
1352                 if (vOldReloction[i].wR_Offset[iReloctionNum-1] == 0)
1353                 {
1354                     vOldReloction[i].wR_Offset.pop_back();
1355                 }
1356                 else
1357                 {
1358                     vOldReloction[i].wR_Offset.push_back(0);
1359                 }
1360             }
1361         }
1362 
1363         //恢復重定位表
1364         szR_Src = NULL;
1365         iRelocationLenth = 0;
1366         for (int i = 0; i < vOldReloction.size(); i++)
1367         {
1368             szR_Src = (char*)realloc(szR_Src, iRelocationLenth + 8);
1369             memcpy(szR_Src + iRelocationLenth, &vOldReloction[i].wR_Head, 4);
1370             int iR_Lenth = vOldReloction[i].wR_Offset.size() * 2 + 8;
1371             memcpy(szR_Src + iRelocationLenth + 4, &iR_Lenth, 4);
1372             iRelocationLenth += 8;
1373             for (int j = 0; j < vOldReloction[i].wR_Offset.size(); j++)
1374             {
1375                 szR_Src = (char*)realloc(szR_Src, iRelocationLenth + 8 + j*2);
1376                 memcpy(szR_Src + iRelocationLenth + j*2, &vOldReloction[i].wR_Offset[j], 2);            
1377             }
1378             iRelocationLenth += vOldReloction[i].wR_Offset.size() * 2 ;
1379         }
1380         
1381 
1382         //增加新的重定位項
1383         char *szAddRelocSrc = NULL;    //總新增重定位資料
1384         char *szNowAddrelocSrc = NULL;//子新增重定位資料
1385         int iAddRelocLen = 0;        //總重定位資料長度
1386         int iNowAddRelocLen = 0;    //子重定位資料長度
1387         
1388         int iPage = 0;        //頁大小
1389         int iPageOffset = 0;    //頁內偏移
1390 
1391 
1392         //先增加新OEP用於求得基址的重定位項
1393         iNowAddRelocLen = 10;  //子長度
1394         szNowAddrelocSrc = (char*)malloc(iNowAddRelocLen);//子資料
1395         iPage = (g_iNewOEP + 1) & 0xfff000;    //PUSH 0000 0000  重定位資料為地址 偏移+1
1396         iPageOffset = ((g_iNewOEP + 1) & 0xfff) | 0x3000;
1397         memcpy(szNowAddrelocSrc, &iPage, 4);//頁偏移
1398         memcpy(szNowAddrelocSrc + 4, &iNowAddRelocLen, 4);//頁大小
1399         memcpy(szNowAddrelocSrc + 8, &iPageOffset, 2);
1400 
1401         //開始新增shellcode中的重定位項
1402         for (int i = 0; i < g_tagIAT.size(); i++)
1403         {            
1404             if (iPage == ((g_tagIAT[i].dwRelocAddr) & 0xfff000))  //判斷是否與當前頁大小一致
1405             {
1406                 szNowAddrelocSrc = (char*)realloc(szNowAddrelocSrc, iNowAddRelocLen + 2);
1407                 iPageOffset = (g_tagIAT[i].dwRelocAddr & 0xfff) | 0x3000;
1408                 memcpy(szNowAddrelocSrc + iNowAddRelocLen, &iPageOffset, 2);
1409                 iNowAddRelocLen += 2;
1410                 memcpy(szNowAddrelocSrc + 4, &iNowAddRelocLen, 4);//頁大小
1411             }
1412             else   //開啟新頁
1413             {
1414                 if ((iNowAddRelocLen % 4))  //是否4位元組對齊
1415                 {
1416                     szNowAddrelocSrc = (char*)realloc(szNowAddrelocSrc, iNowAddRelocLen + 2);
1417                     memset(szNowAddrelocSrc + iNowAddRelocLen, 0, 2);
1418                     iNowAddRelocLen += 2;
1419                     memcpy(szNowAddrelocSrc + 4, &iNowAddRelocLen, 4);//頁大小
1420                 }
1421                 
1422                 szAddRelocSrc = (char*)realloc(szAddRelocSrc, iAddRelocLen + iNowAddRelocLen);//將總重定位資料擴容
1423                 memcpy(szAddRelocSrc + iAddRelocLen, szNowAddrelocSrc, iNowAddRelocLen);    //複製子重定位塊到總重定位塊中
1424                 iAddRelocLen += iNowAddRelocLen;    //更新總重定位塊大小
1425 
1426                 iNowAddRelocLen = 10;
1427                 szNowAddrelocSrc = (char*)malloc(iNowAddRelocLen);//子資料
1428                 iPage = g_tagIAT[i].dwRelocAddr & 0xfff000;
1429                 iPageOffset = (g_tagIAT[i].dwRelocAddr & 0xfff) | 0x3000;
1430                 memcpy(szNowAddrelocSrc, &iPage, 4);//頁偏移
1431                 memcpy(szNowAddrelocSrc + 4, &iNowAddRelocLen, 4);//頁大小
1432                 memcpy(szNowAddrelocSrc + 8, &iPageOffset, 2);
1433             }
1434         }
1435         //此時 由於已經遍歷到tagIAT結構最後 但當前重定位塊併為新增到總重定位塊中,故需要再進行填充
1436         if ((iNowAddRelocLen % 4))  //是否4位元組對齊
1437         {
1438             szNowAddrelocSrc = (char*)realloc(szNowAddrelocSrc, iNowAddRelocLen + 2);
1439             memset(szNowAddrelocSrc + iNowAddRelocLen, 0, 2);
1440             iNowAddRelocLen += 2;
1441             memcpy(szNowAddrelocSrc + 4, &iNowAddRelocLen, 4);//頁大小
1442         }
1443 
1444         szAddRelocSrc = (char*)realloc(szAddRelocSrc, iAddRelocLen + iNowAddRelocLen);//將總重定位資料擴容
1445         memcpy(szAddRelocSrc + iAddRelocLen, szNowAddrelocSrc, iNowAddRelocLen);    //複製子重定位塊到總重定位塊中
1446         iAddRelocLen += iNowAddRelocLen;    //更新總重定位塊大小
1447 
1448 
1449         if ((iR_FileLenth - iRelocationLenth) < iAddRelocLen) //節表長度和實際資料長度是否足夠填充新的重定位塊
1450         {
1451             //擴充重定位資料
1452             //整數取餘 = (新增資料總大小- 之前剩餘空間間隙)%對齊大小
1453             int iZeroLenth = (iAddRelocLen - (iR_FileLenth - iRelocationLenth)) % g_iFileAlignment;
1454             if (iZeroLenth)
1455             { 
1456                 iR_FileLenth = iRelocationLenth + iAddRelocLen + g_iFileAlignment - iZeroLenth;
1457             }
1458             else
1459             {
1460                 iR_FileLenth = iRelocationLenth + iAddRelocLen;
1461             }
1462             szR_Src = (char*)realloc(szR_Src, iR_FileLenth);
1463             memcpy(szR_Src + iRelocationLenth, szAddRelocSrc, iAddRelocLen);
1464             memset(szR_Src + iRelocationLenth + iAddRelocLen, 0, iR_FileLenth - iRelocationLenth - iAddRelocLen);
1465         }
1466         else
1467         {
1468             szR_Src = (char*)realloc(szR_Src, iR_FileLenth);
1469             memcpy(szR_Src + iRelocationLenth, szAddRelocSrc, iAddRelocLen);
1470             iRelocationLenth += iAddRelocLen;
1471             memset(szR_Src + iRelocationLenth, 0, iR_FileLenth - iRelocationLenth);
1472         }
1473 
1474         //還原重定位
1475         g_pFileSrc = (UCHAR*)realloc(g_pFileSrc, g_iFileSize + iR_FileLenth);
1476         memcpy(g_pFileSrc + g_iFileSize, szR_Src, iR_FileLenth);
1477         g_iFileSize += iR_FileLenth;
1478         
1479         ////直接用realloc又抽風 用其他轉移吧
1480         //char *szCopyBuff = (char*)malloc(g_iFileSize + iR_FileLenth);
1481         //memcpy(szCopyBuff, g_pFileSrc, g_iFileSize);
1482         //memcpy(szCopyBuff + g_iFileSize, szR_Src, iR_FileLenth);
1483         //g_iFileSize += iR_FileLenth;
1484         //memcpy(g_pFileSrc, szCopyBuff, g_iFileSize);
1485         
1486 
1487         pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
1488         pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
1489         pSection = (PIMAGE_SECTION_HEADER)((DWORD)&pNtHead->OptionalHeader + pNtHead->FileHeader.SizeOfOptionalHeader);
1490         pSection += pNtHead->FileHeader.NumberOfSections - 1;
1491         int iR_FileAddr = pSection->PointerToRawData + pSection->SizeOfRawData;
1492         int iR_MapAddr = 0;
1493         if (!(pSection->Misc.VirtualSize % g_iSectionAlignment))
1494         {
1495             iR_MapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize;
1496         }
1497         else
1498         {
1499             iR_MapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize
1500                 + g_iSectionAlignment - (pSection->Misc.VirtualSize % g_iSectionAlignment);
1501         }
1502 
1503         pSection++;
1504         strcpy((char*)pSection->Name, szR_Name);
1505         pSection->Characteristics = (0x20000000 | 0x40000000 | 0x80000000 | 0x40);
1506         pSection->VirtualAddress = iR_MapAddr;
1507         pSection->Misc.VirtualSize = iR_FileLenth;
1508         pSection->PointerToRawData = iR_FileAddr;
1509         pSection->SizeOfRawData = iR_FileLenth;
1510         pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = iR_MapAddr;
1511         pNtHead->OptionalHeader.DataDirectory[5].Size = iRelocationLenth;
1512         pNtHead->FileHeader.NumberOfSections += 1;
1513         if (!(iR_FileLenth%g_iSectionAlignment))
1514         {
1515             pNtHead->OptionalHeader.SizeOfImage += iR_FileLenth;
1516         }
1517         else
1518         {
1519             pNtHead->OptionalHeader.SizeOfImage += iR_FileLenth + g_iSectionAlignment - (iR_FileLenth%g_iSectionAlignment);
1520         }
1521 
1522     }
1523     return true;
1524 }
1525 
1526 
1527 
1528 int main()
1529 {
1530     //"C:\\Users\\Admin\\Desktop\\彈窗.exe
1531     char FileName[] = { "C:\\Users\\Admin\\Desktop\\彈窗.exe" };
1532     if (!LoadFile(FileName))
1533     {
1534         cout << "檔案開啟失敗" << endl;
1535         system("pause");
1536         return -1;
1537     }
1538 
1539     if (!CheckPE())
1540     {
1541         cout << "該程式不是PE檔案" << endl;
1542         system("pause");
1543         return -1;
1544     }
1545 
1546     if (!FindCodeAddr())    //找到對應呼叫IAT的地址
1547     {
1548         cout << "該程式沒有匯入表,請檢查" << endl;
1549         system("pause");
1550         return -1;
1551     }
1552 
1553     if (!SaveDll())
1554     {
1555         cout << "儲存DLL資訊失敗" << endl;
1556         system("pause");
1557         return -1;
1558     }
1559 
1560     if (!NewIat())
1561     {
1562         cout << "新建IAT表失敗" << endl;
1563         system("pause");
1564         return -1;
1565     }
1566 
1567     //C:\\Users\\Admin\\Desktop\\彈窗IAT加密測試.exe
1568     char NewFilePath[] = "C:\\Users\\Admin\\Desktop\\彈窗IAT加密測試.exe";
1569     HANDLE hNewFile = CreateFileA(NewFilePath,
1570         GENERIC_READ | GENERIC_WRITE,
1571         FILE_SHARE_READ | FILE_SHARE_WRITE,
1572         NULL,
1573         CREATE_ALWAYS,   //建立並覆蓋上一個檔案
1574         FILE_ATTRIBUTE_ARCHIVE,
1575         NULL);
1576 
1577     if (hNewFile == INVALID_HANDLE_VALUE)
1578     {
1579         printf("檔案儲存失敗\n");
1580         int iError = GetLastError();
1581         return 1;
1582     }
1583 
1584     LPDWORD iNum = NULL;
1585     WriteFile(hNewFile, g_pFileSrc, g_iFileSize, iNum, NULL); //寫入檔案
1586     CloseHandle(hNewFile);
1587     char *NewFileName1 = strrchr(NewFilePath, '\\') + 1;
1588     char *NewFileName = strtok(NewFileName1, ".");
1589     MessageBoxA(0, NewFileName, "加密成功", 0);
1590     MessageBoxA(0, "by:阿怪\n          2020.12.26", "IAT加密", 0);
1591 
1592     system("pause");
1593 }
View Code

 

IAT加密相對於程式碼段加密較為複雜,特別是在自己實現獲得函式地址,以及建立對應的shellcode,並把原呼叫指令全部一一修改的流程較為繁瑣,需要用到各種變數記錄位置。博主也是剛接觸到這塊的新手小白,如果有疑惑或者說的不對的地方歡迎在評論指出,互相學習。感謝!!

相關文章