VB6反編譯詳解
標 題: VB6反編譯詳解(一)
作 者: kenmark
時 間: 2006-07-09 16:59
鏈 接: http://bbs.pediy.com/showthread.php?threadid=28715
詳細資訊:
VB6反編譯詳解 by Kenmark-Fenix
**************************************************
最新於2006-7-13更新!
**************************************************
寫本文已經惦記了好幾年了,由於一直沒有完整的資料和充裕的時間,所以一直沒有動手。
在這裡一方面是寫給大家看看,另一方面是招募更多有志於反編譯VB6的同志們一起來研究學習!
我的E-MAIL:ken.mingyuan@hotmail.com ken.mingyuan@gmail.com
我的BLOG: blog.csdn.net/kenmark
我的QQ:188916915
十分期待著與大家一起學習!
——Kenmark
VB6是一個半編譯半解釋的語言,編譯後程式主要在執行庫MSVBVM60.DLL下轉悠,通過與MSVBVM60的互動來完成程式執行的過程。
1.引入(參考:《VB程式大揭密》我的部落格上有轉載http://blog.csdn.net/Kenmark/archive/2005/08/11/450985.aspx)
我們用W32DASM開啟一箇中型的VB程式來反彙編,我們發現程式中用到的MSGBOX÷FileCopy等理應對應API函式居然一個都沒有出現在編譯後程式的IMPORT TABLE裡,一般VC和DEPHI都是直接出現在編譯後程式的IMPORT TABLE裡的,而我們的VB程式用到了如此之多的API函式居然只使用了一個DLL——MSVBVM60.DLL。
然後用工具開啟MSVBVM60.DLL,一看,輸出的函式還真不少,其中有用__vba和rtc開頭的也有直接就是函式名的,仔細一看,哇賽,可以說完全是一個windows API的代理,應有盡有:
rtcRandomize :Randomize 函式的對應API;
rtcMidCharVar :Mid 函式的對應API;
rtcLeftCharVar、rtcRightCharVar :看出來了吧,這些是Left、Right函式的對應API;
rtcUpperCaseVar :UCase 函式的對應API;
rtcKillFiles :Kill 語句的對應API;
rtcFileCopy :FileCopy 語句的對應API;
rtcFileLength :EOF、FileLen函式的對應API;
rtcGetTimer :Randomize Timer中獲取Timer的對應API;
rtcShell :Shell函式的的對應API;
rtcMakeDir :MkDir 語句的對應API;
rtcRemoveDir :RmDir 語句的對應API;
rtcDir :Dir 函式的對應API;
rtcSpaceVar :Space 函式的對應API;
原來,所有VB的操作函式都是在呼叫MSVBVM60.DLL裡面實現
字首是rtc的是一般的語句和函式
涉及字串處理的都叫var,例如:
__vbaUbound : UBound 的對應API;
__vbaFileOpen :Open 語句的對應API;
__vbaStrCmp :比較兩個字串:If String1 = String2 Then ......
__vbaVarOr :Or 運算子的對應API;
__vbaRedim :Redim 語句的對應API;
__vbaRedimPreserve :Redim 語句加上 Preserve 引數的對應API;
__vbaGet、vbaPut :Get、Put語句的對應API……
我們還看到一個DLL:DllFunctionCall,這個就是我們呼叫其他DLL時需要向MSVBVM60申請的,…………。
可以說VB的程式是一個包裹在MSVBVM60陰影控制下的孩子,所有的操作都要直接向它請求,而MSVBVM60完全可以稱得上是一個代理的機器。從程式開始,到執行中的所有操作,函式呼叫,錯誤報告等等,都是由它一手包辦的。
來吧,我們這裡不是來介紹它是怎麼構成的我們要搞掉它虛偽的外表,把我們的程式碼從MSVBVM60的封建保護下救出來。
我的資料大量是參考一個開源的VB程式解析程式(居然也是用VB編寫的),這個程式可以完全分析出VB程式(沒有加殼)的PROJECT資訊以及完全將FORM變回來,對於程式碼呢,可以獲得SUB MAIN的彙編程式碼地址,但是不能返回到VB程式碼,裡面還內建了一個假的反彙編器,以後我會說的!
我提供了它的下載,大家去看看,不知道它的資料是哪裡來的十分全面,就是用VB寫的比較繁!還有由於介面控制太多,程式碼有點亂!
2.程式初始化
我們用W32DASM開啟任何VB程式,跳到ENTRY POINT之後看到的總是一個PUSH *****一個地址,然構呼叫MSVBVM60.DLL的ThunRTMain
而在VC程式裡,我們至少要看到程式的建立什麼的,其實這個VB函式是一切的開端,由它開始來解析整個編譯後的程式,完成系統環境的初始化,然後找到真正的程式入口,跳轉到程式的領空。
所以這個PUSH指令壓入棧的是VBEXE(姑且這麼稱呼)初始化結構的開始。我們存下這個地址(這個是一個VA要正確地轉換成檔案地OFFSET先要-ImageBase然後要對照塊表,可以用工具完成,熟悉了一下就能看出來),然後我們跳到那裡。
看看是什麼,哇賽,是VB程式的招牌也!“VB5!”是所有VB程式初始化結構入口地址指向的MAGIC字元,找到這裡,開始能夠讀取VB初始化結構了(我們現在做的就是ThunRTMain要完成的)
我們開始了!
部分名詞解讀:
RVA 相對虛擬地址,一般需要使用RVA2Offset來將其轉換成絕對的檔案偏移 變數字首為pr或是a
VA 虛擬地址,減去IMAGEBASE就是RVA 變數字首為p或是a
offset 相對結構的偏移 變數字首為o
1)VBHEADER
從PE檔案的ENTRY POINT進入後第一個指令是壓入一個指標來表示VBHEADER的位置,這個指令為
push xxxxxxxx地址是經過基址IMAGE_BASE偏移後的地址,所以減去IMAGE_BASE後獲得的就是VBHEADER開始的VA然後用PEFile類中的函式加以分析可以得到的是檔案中VBHEADER的偏移量。
這個指令在機器碼中是這樣表示的:68 C0 11 40 00
68是PUSH的機器碼,而後是記憶體儲存方式的地址化成組合語言就是:
push 004011c0 所以減去基址後就是11C0然後進行VA2OFFSET得到的是11c0的偏移,轉向那段資料就能得到VBHEADER。
這是VBHEADER結構的C語言描述:
typedef struct
{
char Signature[4]; // 四個位元組的簽名符號,和PEHEADER裡的那個signature是類似性質的東西,VB檔案都是"VB5!"
WORD RtBuild; // 執行時創立的變數(類似編譯的時間)
BYTE LangDLL[14]; // 語言DLL檔案的名字(如果是0x2A的話就代表是空或者是預設的)
BYTE BakLangDLL[14]; // 備份DLL語言檔案的名字(如果是0x7F的話就代表是空或者是預設的,改變這個值堆EXE檔案的執行沒有作用)
WORD RtDLLVer; // 執行是DLL檔案的版本
DWORD LangID; // 語言的ID
DWORD BakLangID; // 備份語言的ID(只有當語言ID存在時它才存在)
DWORD pSubMain; // RVA(實際研究下來是VA) sub main過程的地址指標(3.)(如果時00000000則代表這個EXE時從FORM窗體檔案開始執行的)
DWORD pProjInfo; // VA 工程資訊的地址指標,指向一個ProjectInfo_t結構(2.)
DWORD fMDLIntObjs; // ?詳細見"MDL 內部組建的標誌表"
DWORD fMDLIntObjs2; // ?詳細見"MDL 內部組建的標誌表"
DWORD ThreadFlags; // 執行緒的標誌
//* 標記的定義(ThreadFlags數值的含義)
//+-------+----------------+--------------------------------------------------------+
//| 值 | 名字 | 描述 |
//+-------+----------------+--------------------------------------------------------+
//| 0x01 | ApartmentModel | 特別化的多執行緒使用一個分開的模型 |
//| 0x02 | RequireLicense | 特別化需要進行認證(只對OCX) |
//| 0x04 | Unattended | 特別化的沒有GUI圖形介面的元素需要初始化 |
//| 0x08 | SingleThreaded | 特別化的靜態區時單執行緒的 |
//| 0x10 | Retained | 特別化的將檔案儲存在記憶體中(只對Unattended) |
//+-------+----------------+--------------------------------------------------------+
//ex: 如果是0x15就表示是一個既有多執行緒,記憶體常駐,並且沒有GUI元素要初始化
DWORD ThreadCount; // 執行緒個數
WORD FrmCount; // 窗體個數
WORD pExternalComponentCount; // VA 外部引用個數例如WINSOCK元件的引用
DWORD ThunkCount; // ?大概是記憶體對齊相關的東西
DWORD pGUITable; // VA GUI元素表的地址指標(指向一個GUITable_t結構(4.四))
DWORD pExternalComponentTable; // VA 外部引用表的地址指標
// DWORD pProjDep; // VA 工程的描述的地址指標(這個其實沒有)
DWORD pComRegData; // VA COM註冊資料的地址指標
DWORD oProjExename; // Offset 指向工程EXE名字的字串
DWORD oProjTitle; // Offset 指向工程標題的字串
DWORD oHelpFile; // Offset 指向幫助檔案的字串
DWORD oProjName; // Offset 指向工程名的字串
}VBHeader_t;
//* MDL 內部組建的標誌表
//+---------+------------+---------------+
//| ID | 值 | 組建名稱 |
//+---------+------------+---------------+
//| 第一個標誌 |
//+---------+------------+---------------+
//| 0x00 | 0x00000001 | PictureBox |
//| 0x01 | 0x00000002 | Label |
//| 0x02 | 0x00000004 | TextBox |
//| 0x03 | 0x00000008 | Frame |
//| 0x04 | 0x00000010 | CommandButton |
//| 0x05 | 0x00000020 | CheckBox |
//| 0x06 | 0x00000040 | OptionButton |
//| 0x07 | 0x00000080 | ComboBox |
//| 0x08 | 0x00000100 | ListBox |
//| 0x09 | 0x00000200 | HScrollBar |
//| 0x0A | 0x00000400 | VScrollBar |
//| 0x0B | 0x00000800 | Timer |
//| 0x0C | 0x00001000 | Print |
//| 0x0D | 0x00002000 | Form |
//| 0x0E | 0x00004000 | Screen |
//| 0x0F | 0x00008000 | Clipboard |
//| 0x10 | 0x00010000 | Drive |
//| 0x11 | 0x00020000 | Dir |
//| 0x12 | 0x00040000 | FileListBox |
//| 0x13 | 0x00080000 | Menu |
//| 0x14 | 0x00100000 | MDIForm |
//| 0x15 | 0x00200000 | App |
//| 0x16 | 0x00400000 | Shape |
//| 0x17 | 0x00800000 | Line |
//| 0x18 | 0x01000000 | Image |
//| 0x19 | 0x02000000 | Unsupported |
//| 0x1A | 0x04000000 | Unsupported |
//| 0x1B | 0x08000000 | Unsupported |
//| 0x1C | 0x10000000 | Unsupported |
//| 0x1D | 0x20000000 | Unsupported |
//| 0x1E | 0x40000000 | Unsupported |
//| 0x1F | 0x80000000 | Unsupported |
//+---------+------------+---------------+
//| 第二個標誌 |
//+---------+------------+---------------+
//| 0x20 | 0x00000001 | Unsupported |
//| 0x21 | 0x00000002 | Unsupported |
//| 0x22 | 0x00000004 | Unsupported |
//| 0x23 | 0x00000008 | Unsupported |
//| 0x24 | 0x00000010 | Unsupported |
//| 0x25 | 0x00000020 | DataQuery |
//| 0x26 | 0x00000040 | OLE |
//| 0x27 | 0x00000080 | Unsupported |
//| 0x28 | 0x00000100 | UserControl |
//| 0x29 | 0x00000200 | PropertyPage |
//| 0x2A | 0x00000400 | Document |
//| 0x2B | 0x00000800 | Unsupported |
//+---------+------------+---------------+
//ex: 如果值是0x30F000 (那個被叫做 "靜態二進位制常量定義在大多數的地方")就是意味著來初始化印表機,窗體,螢幕,剪貼簿,組建(0xF000)也有Drive/Dir 組建(0x30000).
//這是VB工程的一個預設的設定因為這些組建都能從一個模組中獲得module (例如,他們是沒有影象的除了經常被創造窗體)
2)工程資訊
從VBHEADER->ProjectInfo(是一個VA)-IMAGEBASE後在通過RAV2OFFSET得到的是 工程資訊 的偏移地址.
這是PROJECT_INFO結構的C語言描述:
const long MAX_PATH = 260; //最長的PATH長度
typedef struct
{
DWORD Signature; // 結構的簽名特性,和魔術字元類似
DWORD pObjectTable; // VA 結構指向的元件列表的地址指標(很重要的!(7.))
DWORD Null1; // ?沒有用的東西
WORD pStartOfCode; // VA 程式碼開始點,類似PEHEAD->EntryPoint這裡告訴了VB程式碼實際的開始點
DWORD Flag1; // 標誌1
DWORD ThreadSpace; // 多執行緒的空間?????????????????
DWORD pVBAExcrptionhandler; // VA VBA意外處理機器地址指標
DWORD pNativeCode; // VA 本地機器碼開始位置的地址指標
WORD oProjectLocation; // Offset 工程位置?????????????????
WORD Flag2; // 標誌2
WORD Flag3; // 標誌3
BYTE OriginalPathName[MAX_PATH*2]; // 原檔案地址,一個字串,長度最長為MAX_PATH
BYTE NullSpacer; // 無用的東西,用來佔位置????????????????????
DWORD pExternalTable; // VA 引用表的指標地址
DWORD ExternalCount; // 引用表大小(個數)
// sizeof() = 0x23c
}ProjectInfo_t;
這個東西基本包含了這個PROJECT的資訊,但是主要的意義還是提供了之後的結構的索引,最重要的是它提供了傳說中的OBJECT TABLE的入口地址,對於EXE裡面有多少FORM,MODULE十分重要。
3)SubMain
從VBHEADER->SubMain可以獲得SubMain程式碼的入口處,但是有些VB程式是從FORM開始執行的所以當這個值為0時就代表此程式不是從SubMain開始執行的而是從FORM開始執行的.
4)GUITable
從VBHEADER->GUITable可以獲得GUI元素表的地址指標,也就是指向WINDOWS圖形元素的表.
這是GUITable結構的C語言描述:
typedef struct
{
DWORD lStructSize; // 這個結構的總大小
BYTE uuidObjectGUI[15]; // Object GUI的UUID
DWORD Unknown1; // ???????????????????????????????????
DWORD Unknown2; // ???????????????????????????????????
DWORD Unknown3; // ???????????????????????????????????
DWORD Unknown4; // ???????????????????????????????????
DWORD lObjectID; // 當前工程的元件ID
DWORD Unknown5; // ???????????????????????????????????
DWORD fOLEMisc; // OLEMisc標誌
BYTE uuidObject[15]; // 元件的UUID
DWORD Unknown6; // ???????????????????????????????????
DWORD Unknown7; // ???????????????????????????????????
DWORD pFormPointer; // VA 指向GUI Object Info結構的地址指標
DWORD Unknown8; // ???????????????????????????????????
// sizeof() = 0x50
}GUITable_t;
這個表沒有什麼重要,主要就是那個pFormPointer比較有用。
5)ExternalComponentTable
從VBHEADER->ExternalComponentTable可以獲得外部引用表的地址指標.
這是ExternalTable以及ExternalLibrary的VB結構以及C語言描述:
typedef struct
{
DWORD Flag; // 標誌
DWORD pExternalLibrary; // VA 指向ExternalLibrary結構的地址指標
}ExternalTable_t;
typedef struct
{
DWORD pLibraryName; // VA 指向 NTS
DWORD pLibraryFunction; // VA 指向 NTS
}ExternalLibrary_t;
6)ComRegData and COMRegInfo
從VBHEADER->ComRegData可以獲得COM註冊資料的地址指標.
這是COMRegData以及COMRegInfo結構的C語言描述:
typedef struct
{
DWORD oRegInfo; //Offset 指向COM Interfaces Info結構(COM介面資訊)
DWORD oNTSProjectName; //Offset 指向Project/Typelib Name(工程名)
DWORD oNTSHelpDirectory; //Offset 指向Help Directory(幫助檔案目錄)
DWORD oNTSProjectDescription; //Offset 指向Project Description(工程描述)
BYTE uuidProjectClsId(15); //Project/Typelib的CLSID
DWORD lTlbLcid; //Type Library的LCID
WORD iPadding1; //沒有用的記憶體對齊空間1
WORD iTlbVerMajor; //Typelib 主版本
WORD iTlbVerMinor; //Typelib 次版本
WORD iPadding2; //沒有用的記憶體對齊空間2
DWORD lPadding3; //沒有用的記憶體對齊空間3
// sizeof() = 0x30
}COMRegData_t;
typedef struct
{
DWORD oNextObject; //Offset to COM Interfaces Info
DWORD oObjectName; //Offset to Object Name
DWORD oObjectDescription; //Offset to Object Description
DWORD lInstancing; //Instancing Mode
DWORD lObjectID; //Current Object ID in the Project
BYTE uuidObjectClsID[15]; //CLSID of Object
DWORD fIsInterface; // Specifies if the next CLSID is valid
DWORD oObjectClsID; // Offset to CLSID of Object Interface
DWORD oControlClsID; // Offset to CLSID of Control Interface
DWORD fIsControl; // Specifies if the CLSID above is valid
DWORD lMiscStatus; // OLEMISC Flags (see MSDN docs)
BYTE fClassType; // Class Type
BYTE fObjectType; // Flag identifying the Object Type
WORD iToolboxBitmap32; // Control Bitmap ID in Toolbox
WORD iDefaultIcon; // Minimized Icon of Control Window
WORD fIsDesigner; // Specifies whether this is a Designer
DWORD oDesignerData; // Offset to Designer Data
// sizeof() = 0x44
}COMRegInfo_t;
'Object Type part of tCOMRegInfo//沒有翻譯的意義,就沒有翻譯,只是看看而已
'+-------+---------------+-------------------------------------------+
'| Value | Name | Description |
'+-------+---------------+-------------------------------------------+
'| 0x02 | Designer | A Visual Basic Designer for an Add.in |
'| 0x10 | Class Module | A Visual Basic Class |
'| 0x20 | User Control | A Visual Basic ActiveX User Control (OCX) |
'| 0x80 | User Document | A Visual Basic User Document |
'+-------+---------------+-------------------------------------------+
*7)ObjectInfo
這些東西十分重要,是提取VB工程元素的重要標誌,從這裡我們可以得到這個工程裡有多少FORM,多少MODULE,以及他們的重要索引資料,可以說重要性和SECTION HEAD在EXE程式中重要性相同。
入口的地址是由ProjectInfo_t結構提供的(semi提供關於這裡提供的資料少之又少,所以需要自己摸索)
從ProjectInfo_t開始指向的是一個ObjectTable_t,由ObjectTable_t提供第一個OBJECT_t結構的地址
typedef struct //這個是OBJECT 的總表,可以索引以後的每個OBJECT
{
DWORD lNull1 As Long; //沒有用的填充東西
DWORD aExecProj; //VA指向一塊記憶體結構(研究下來既不沒見著這個東西由什麼用處
DWORD aProjectInfo2; //VA指向Project Info 2
DWORD Const1; //沒有用的填充東西
DWORD Null2; //沒有用的填充東西
DWORD lpProjectObject As Long ' 0x14
DWORD Flag1; //標誌1
DWORD Flag2; //標誌2
DWORD Flag3; //標誌3
DWORD Flag4; //標誌4
WORD fCompileType; //Internal flag used during compilation
WORD ObjectCount1; //OBEJCT數量1????
WORD iCompiledObjects; //編譯後OBJECT數量
WORD iObjectsInUse As Integer; //Updated in the IDE to correspond the total number ' but will go up or down when initializing/unloading modules.
DWORD aObject; //VA指向第一個OBJECT_t結構,很重要
DWORD Null3; //沒有用的填充東西
DWORD Null4; //沒有用的填充東西
DWORD Null5; //沒有用的填充東西
DWORD aProjectName; //執行工程名字的字串
DWORD LangID1; //language ID1
DWORD LangID2; //language ID2
DWORD Null6; //沒有用的填充東西
DWORD Const3; //沒有用的填充東西
' 0x54
}ObjectTable_t;
type struct//這個就是每個OBJECT的結構,
{
DWORD aObjectInfo; //VA 指向一個ObjectInfo_t型別,來顯示這個OBJECT的資料
DWORD Const1; //沒有用的填充東西
DWORD aPublicBytes; //VA 指向公用變數表大小
DWORD aStaticBytes; //VA 指向靜態變數表地址
DWORD aModulePublic; //VA 指向公用變數表
DWORD aModuleStatic; //VA 指向靜態變數表
DWORD aObjectName; //VA 字串,這個OBJECT的名字
DWORD ProcCount; // events, funcs, subs(時間/函式/過程)數目
DWORD aProcNamesArray; //VA 一般都是0
DWORD oStaticVar; //OFFSET 從aModuleStatic指向的靜態變數表偏移
DWORD ObjectType; //比較重要顯示了這個OBJECT的實行,具體見下表
DWORD Null3; //沒有用的填充東西
//sizeof() = 0x30
}Object_t;
'Object_t.ObjectTyper 屬性...//重要的屬性表部分
'#########################################################
'form: 0000 0001 1000 0000 1000 0011 --> 18083
' 0000 0001 1000 0000 1010 0011 --> 180A3
' 0000 0001 1000 0000 1100 0011 --> 180C3
'module: 0000 0001 1000 0000 0000 0001 --> 18001
' 0000 0001 1000 0000 0010 0001 --> 18021
'class: 0001 0001 1000 0000 0000 0011 --> 118003
' 0001 0011 1000 0000 0000 0011 --> 138003
' 0000 0001 1000 0000 0010 0011 --> 18023
' 0000 0001 1000 1000 0000 0011 --> 18803
' 0001 0001 1000 1000 0000 0011 --> 118803
'usercontrol: 0001 1101 1010 0000 0000 0011 --> 1DA003
' 0001 1101 1010 0000 0010 0011 --> 1DA023
' 0001 1101 1010 1000 0000 0011 --> 1DA803
'propertypage: 0001 0101 1000 0000 0000 0011 --> 158003
' | || | | | | |
'[moog] | || | | | | |
'HasPublicInterface ---+ || | | | | | (有公用的介面)
'HasPublicEvents --------+| | | | | | (有公用的事件)
'IsCreatable/Visible? ----+ | | | | | (是否可以建立,可見)
'Same as "HasPublicEvents" -----+ | | | |
'[aLfa] | | | | |
'usercontrol (1) ---------------+ | | | | (使用者控制)
'ocx/dll (1) ----------------------+ | | | (OCX/DLL)
'form (1) ------------------------------+ | | (是不是FORM是就是1)
'vb5 (1) ---------------------------------+ | (是不是VB5是就是1)
'HasOptInfo (1) -------------------------------+ (有沒有額外的資訊資訊由就是1,決定是不是指向OptionalObjectInfo_t類似與PEHEAD裡的Optional資訊一樣)
' |
'module(0) ------------------------------------+ (如果是Module模組就這裡是0)
typedef struct//這個是顯示這個OBJECT資訊的結構,每一個OBJECT都有一個
{
WORD Flag1;
WORD ObjectIndex;
DWORD aObjectTable;
DWORD Null1;
DWORD aSmallRecord; // when it is a module this value is -1 [better name?]
DWORD Const1;
DWORD Null2;
DWORD aObject;
DWORD RunTimeLoaded; //[can someone verify this?]
DWORD NumberOfProcs;
DWORD aProcTable;
WORD iConstantsCount; // Number of Constants
WORD iMaxConstants; // Maximum Constants to allocate.
DWORD Flag5;
WORD Flag6;
WORD Flag7;
WORD aConstantPool;
// sizeof() = 0x38
'the rest is optional items[OptionalObjectInfo]
}ObjectInfo_t;
Private Type tOptionalObjectInfo ' if ((tObject.ObjectType AND &H80)=&H80)
fDesigner As Long ' 0x00 (0d) If this value is 2 then this object is a designer
aObjectCLSID As Long ' 0x04
Null1 As Long ' 0x08
aGuidObjectGUI As Long ' 0x0C
lObjectDefaultIIDCount As Long ' 0x10 01 00 00 00
aObjectEventsIIDTable As Long ' 0x14
lObjectEventsIIDCount As Long ' 0x18
aObjectDefaultIIDTable As Long ' 0x1C
ControlCount As Long ' 0x20
aControlArray As Long ' 0x24
iEventCount As Integer ' 0x28 (40d) Number of Events
iPCodeCount As Integer ' 0x2C
oInitializeEvent As Integer ' 0x2C (44d) Offset to Initialize Event from aMethodLinkTable
oTerminateEvent As Integer ' 0x2E (46d) Offset to Terminate Event from aMethodLinkTable
aEventLinkArray As Long ' 0x30 Pointer to pointers of MethodLink
aBasicClassObject As Long ' 0x34 Pointer to an in-memory
Null3 As Long ' 0x38
Flag2 As Long ' 0x3C usually null
' 0x40 <-- Structure size
End Type
Type tProjectInfo2
lNull1 As Long ' 0x00 (00d)
aObjectTable As Long ' 0x04 (04d) Pointer to Object Table
lConst1 As Long ' 0x08 (08d)
lNull2 As Long ' 0x0C (12d)
aObjectDescriptorTable As Long ' 0x10 (16d) Pointer to a table of ObjectDescriptors
lNull3 As Long ' 0x14 (20d)
aNTSPrjDescription As Long ' 0x18 (24d) Pointer to Project Description
aNTSPrjHelpFile As Long ' 0x1C (28d) Pointer to Project Help File
lConst2 As Long ' 0x20 (32d)
lHelpContextID As Long ' 0x24 (36d) Project Help Context ID
' 0x28 (40d) <- Structure size
End Type
Type ObjectDescriptor
lNull1 As Long '0x00 (00d)
aObjectInfo As Long '0x04 (04d) Pointer to Object Info
lConst1 As Long '0x08 (08d)
lNull2 As Long '0x0C (12d)
lFlag1 As Long '0x10 (16d)
lNull3 As Long '0x14 (20d)
aUnknown1 As Long '0x18 (24d)
lNull4 As Long '0x1C (28d)
aUnknown2 As Long '0x20 (32d)
aUnknown3 As Long '0x24 (36d)
aUnknown4 As Long '0x28 (40d)
lNull5 As Long '0x2C (44d)
lNull6 As Long '0x30 (48d)
lNull7 As Long '0x34 (52d)
lFlag2 As Long '0x38 (56d)
fObjectType As Long '0x3C (60d) Flags for this Object
'0x40 (64d) <- Structure Size
End Type
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
未完待續!!!!!(還有很大大大大一部分類!!!)
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
標 題: VB6反編譯詳解(二)
作 者: kenmark
時 間: 2006-07-20 10:57
鏈 接: http://bbs.pediy.com/showthread.php?threadid=29307
詳細資訊:
****************************************************************************************************
上次說到最重要的部分OBJECT TABLE就斷了實在是對不起大家,所以最近加緊研究馬上出了第二篇(連載會很長哦,要考驗大家的耐心的哦)
****************************************************************************************************
*7)ObjectInfo
這些東西十分重要,是提取VB工程元素的重要標誌,從這裡我們可以得到這個工程裡有多少FORM,多少MODULE,以及他們的重要索引資料,可以說重要性和SECTION HEAD在EXE程式中重要性相同。
我們反編譯只是初期水平,現在由於筆者能力有限,我們只討論到如何完全反會MOD,VBP,FRX和FRM檔案,關於其他檔案例如.CLS類檔案什麼的不再本文討論範圍之內。
入口的地址是由ProjectInfo_t結構提供的(semi提供關於這裡提供的資料少之又少,所以需要自己摸索)
從ProjectInfo_t開始指向的是一個ObjectTable_t,由ObjectTable_t提供第一個OBJECT_t結構的地址,索引其他的OBJECT和Import Table裡索引IID一樣。
typedef struct //這個是OBJECT 的總表,可以索引以後的每個OBJECT
{
DWORD lNull1 As Long; //沒有用的填充東西
DWORD aExecProj; //VA指向一塊記憶體結構(研究下來既不沒見著這個東西由什麼用處
DWORD aProjectInfo2; //VA指向Project Info 2
DWORD Const1; //沒有用的填充東西
DWORD Null2; //沒有用的填充東西
DWORD lpProjectObject As Long ' 0x14
DWORD Flag1; //標誌1
DWORD Flag2; //標誌2
DWORD Flag3; //標誌3
DWORD Flag4; //標誌4
WORD fCompileType; //Internal flag used during compilation
WORD ObjectCount1; //OBEJCT數量1????
WORD iCompiledObjects; //編譯後OBJECT數量
WORD iObjectsInUse As Integer; //Updated in the IDE to correspond the total number ' but will go up or down when initializing/unloading modules.
DWORD aObject; //VA指向第一個OBJECT_t結構,很重要
DWORD Null3; //沒有用的填充東西
DWORD Null4; //沒有用的填充東西
DWORD Null5; //沒有用的填充東西
DWORD aProjectName; //執行工程名字的字串
DWORD LangID1; //language ID1
DWORD LangID2; //language ID2
DWORD Null6; //沒有用的填充東西
DWORD Const3; //沒有用的填充東西
' 0x54
}ObjectTable_t;
type struct//這個就是每個OBJECT的結構,
{
DWORD aObjectInfo; //VA 指向一個ObjectInfo_t型別,來顯示這個OBJECT的資料
DWORD Const1; //沒有用的填充東西
DWORD aPublicBytes; //VA 指向公用變數表大小
DWORD aStaticBytes; //VA 指向靜態變數表地址
DWORD aModulePublic; //VA 指向公用變數表
DWORD aModuleStatic; //VA 指向靜態變數表
DWORD aObjectName; //VA 字串,這個OBJECT的名字
DWORD ProcCount; // events, funcs, subs(事件/函式/過程)數目
DWORD aProcNamesArray; //VA 一般都是0
DWORD oStaticVar; //OFFSET 從aModuleStatic指向的靜態變數表偏移
DWORD ObjectType; //比較重要顯示了這個OBJECT的實行,具體見下表
DWORD Null3; //沒有用的填充東西
//sizeof() = 0x30
}Object_t;
'Object_t.ObjectTyper 屬性...//重要的屬性表部分
'#########################################################
'form: 0000 0001 1000 0000 1000 0011 --> 18083
' 0000 0001 1000 0000 1010 0011 --> 180A3
' 0000 0001 1000 0000 1100 0011 --> 180C3
'module: 0000 0001 1000 0000 0000 0001 --> 18001
' 0000 0001 1000 0000 0010 0001 --> 18021
'class: 0001 0001 1000 0000 0000 0011 --> 118003
' 0001 0011 1000 0000 0000 0011 --> 138003
' 0000 0001 1000 0000 0010 0011 --> 18023
' 0000 0001 1000 1000 0000 0011 --> 18803
' 0001 0001 1000 1000 0000 0011 --> 118803
'usercontrol: 0001 1101 1010 0000 0000 0011 --> 1DA003
' 0001 1101 1010 0000 0010 0011 --> 1DA023
' 0001 1101 1010 1000 0000 0011 --> 1DA803
'propertypage: 0001 0101 1000 0000 0000 0011 --> 158003
' | || | | | | |
'[moog] | || | | | | |
'HasPublicInterface ---+ || | | | | | (有公用的介面)
'HasPublicEvents --------+| | | | | | (有公用的事件)
'IsCreatable/Visible? ----+ | | | | | (是否可以建立,可見)
'Same as "HasPublicEvents" -----+ | | | |
'[aLfa] | | | | |
'usercontrol (1) ---------------+ | | | | (使用者控制)
'ocx/dll (1) ----------------------+ | | | (OCX/DLL)
'form (1) ------------------------------+ | | (是不是FORM是就是1)
'vb5 (1) ---------------------------------+ | (是不是VB5是就是1)
'HasOptInfo (1) -------------------------------+ (有沒有額外的資訊資訊由就是1,決定是不是指向OptionalObjectInfo_t類似與PEHEAD裡的Optional資訊一樣)
' |
'module(0) ------------------------------------+ (如果是Module模組就這裡是0)
typedef struct//這個是顯示這個OBJECT資訊的結構,每一個OBJECT都有一個
{
WORD Flag1;
WORD ObjectIndex; //OBJECT的索引????????????????????????
DWORD aObjectTable; //指向OBJECT TABLE??????????????????
DWORD Null1; //沒有用的填充東西
DWORD aSmallRecord; // 如果這個物件是一個模組(module)那麼這個數值是-1
DWORD Const1; //沒有用的填充東西
DWORD Null2; //沒有用的填充東西
DWORD aObject; //指向OBJECT??????????????????????????????
DWORD RunTimeLoaded; //[can someone verify this?]
DWORD NumberOfProcs; //proc個數
DWORD aProcTable; //指向proc表
WORD iConstantsCount; // 常量個數
WORD iMaxConstants; // 最大的要求分配的常量
DWORD Flag5;
WORD Flag6;
WORD Flag7;
WORD aConstantPool; //指向常量池
// sizeof() = 0x38
'the rest is optional items[OptionalObjectInfo]
}ObjectInfo_t;
typedef struct // 這個是可選的OBJECT_INFO和PEHEADER裡的OPTIONAL_HEADER類似,是否有要看每個Object_t裡面的ObjectTyper表裡的倒數第二個位(詳細看上表)
{
DWORD fDesigner; // 如果這個數值是2則表示是一個designer
DWORD aObjectCLSID; //指向CLSID物件
DWORD Null1; //沒有用的填充東西
DWORD aGuidObjectGUI; //?????????????????????????????
DWORD lObjectDefaultIIDCount; // 01 00 00 00 ???????????????????????????
DWORD aObjectEventsIIDTable; //指向物件行為IID表
DWORD lObjectEventsIIDCount; //物件行為IID個數
DWORD aObjectDefaultIIDTable; //指向預設物件IID表
DWORD ControlCount; //控制元件個數
DWORD aControlArray; //指向控制元件表
WORD iEventCount; // 行為的個數,比較重要,知道有幾個行為
WORD iPCodeCount; // PCode個數
WORD oInitializeEvent; // offset從aMethodLinkTable指向初始化行為
WORD oTerminateEvent; // offset從aMethodLinkTable指向終止行為
DWORD aEventLinkArray; //Pointer to pointers of MethodLink
DWORD aBasicClassObject; // Pointer to an in-memory
DWORD Null3; //沒有用的填充東西
DWORD Flag2; //一般都是空的
//sizeof() = 0x40
}OptionalObjectInfo_t;
typedef struct
{
WORD Flag1;//Integer ' 0x00
WORD EventCount;//Integer ' 0x02
DWORD Flag2;//Long ' 0x04
DWORD aGUID;//Long ' 0x08
WORD index;//Integer ' 0x0C
WORD Const1;//Integer ' 0x0E
DWORD Null1;//Long ' 0x10
DWORD Null2;//Long ' 0x14
DWORD aEventTable;//Long ' 0x18
BYTE Flag3;//Byte ' 0x1C
BYTE Const2;//Byte ' 0x1D
WORD Const3;//Integer ' 0x1E
DWORD aName;//Long ' 0x20
WORD Index2;//Integer ' 0x24
WORD Const1Copy;//Integer ' 0x26
// 0x28 <-- Structure Size
}Control_t;
看到這裡我們已經可以通過這個表粗略地勾畫出那個工程是怎樣的,通過PROJECT_INFO和其他的一些東西,我們已經能夠重建VBP檔案,而且我們已經知道這個工程到底有多少原始檔(FRM,MOD不包括FRX)
接下來,我們來讀取EXE檔案中儲存的資料來重建每個MOD,FRM,CLS,VBP
重建VBP
我們本文不是主要介紹VB工程組檔案的內部結構,所以只是一筆帶過,VBP檔案內部格式和普通的配置檔案類似都是(關鍵詞,對應值)對的形式
主要有這麼幾個欄位我們要注意(輸出的東西和SEMI一樣,關於VERSION會比SEMI詳細,將在之後介紹)
Type=Exe(這個就是這個工程是什麼我們當然是EXE)
Startup="frmMain"(這個就是工程的啟動專案,關於它我們後面要詳細介紹)
Description="Visual Basic Decompiler"(這個是描述,(char*)COMRegData+COMRegData->oNTSProjectDescription)
HelpFile=""(幫助檔案(char*)VBHEADER+VBHEADER->oHelpFile就是其在檔案中的偏移)
Name="VBDecompiler"(工程名字,獲得方式和HELPFILE類似,只要看VBHEADER裡面就有儲存其偏移地址)
Title="VBDecompiler"(工程標題,獲得方式和HELPFILE類似,只要看VBHEADER裡面就有儲存其偏移地址)
ExeName32="SemiVBDecompiler"(工程EXE32的名字,獲得方式和HELPFILE類似,只要看VBHEADER裡面就有儲存其偏移地址)
注意:每找到一個OBJECT對應的檔案,我們要將其新增到VBP工程裡面,具體的方式是:
如果這個OBJECT是FRM檔案,只要在VBP檔案裡面新增一條Form=窗體檔案的名字(檔名)(沒有空格要求=以後緊跟)
如果這個OBJECT是MOD檔案,只要在VBP檔案裡面新增一條Module=內部標識名;檔名(之間需要一個分號來格開)
如果這個OBJECT是CLASS檔案,只要在VBP檔案裡面新增一條Class=內部標識名;檔名(之間需要一個分號來格開)
這樣的話,在VBP檔案中維護各個工程檔案的工作就大致完成了。
其他重要的項還有例如OBJECT項(新增使用的控制元件)以及版本資訊相關的(在後面的資源反編譯一節會著重介紹)
剩下的就是關於編譯時的優化選項了,這些不是很重要,所以就沒有加入。
其實VBP檔案裡包含了很多工程的細節,但是我們主要只要抓住重要的欄位。
重建MOD
MOD檔案比較簡單,由於檔名可能和模組名不同,編譯的時候捨棄了實際檔名,而用模組名來作為標識,所以我們生成的MOD檔案的名字選用模組名,可能與原始的原始檔組不同。
獲得一個OBJECT之後,我們看Object_t.ObjectType通過查表我們能夠確定它的性質
我們確定這個是MOD檔案之後,我們通過Object_t.aObjectName(指向一個字串)這個就是這個模組的名字,也是這個模組在編譯後的檔案中的標識。
我們用這個標識名來作為檔名,建立一個檔案,然後我們通過Object_t.ProcCount知道這個MOD裡儲存著多少個FUNCTION和SUB,並且由於MOD是全編譯,我們得不到具體的SUB和FUNCTION的名字,這些名字在編譯的時候被丟棄的所以我們只能知道到底有多少個SUB和FUNCTION。
所以MOD的重建並不能得到什麼東西,只能空建立一個檔案然後最多寫入:'There is totally 100 methods in this module.But we can't show you them.:)
還要記得一點就是我們要為這個BAS檔案寫上它的VB_ATTRIB,具體的格式就是Attribute VB_Name = "模組的名字(內部標識名)"
這部分的具體程式碼重建(一般來說不能完全重建),我們只能有待更加強大的程式碼反編譯來完成。
重建FRM
FRM和MOD不同的是我們可以得到FRM裡面的所有控制元件的基本靜態狀態(屬性),並且我們可以得到裡面儲存的SUB 和FUNCTION的名字(不同於MOD)
同樣如何知道這個OBJECT是一個FRM檔案還是要查表
知道它是一個FRM檔案之後我們首先要了解一下FRM檔案的結構,
FRM檔案是類似配置檔案的格式儲存的,主要有外部的VB_ATTRIB定義以及FORM成員的定義
VERSION 5.00//檔案的編譯版本
Begin VB.Form 內部標識名(表示這個FORM的開始)
Begin VB.VB內部控制元件物件 內部標識名
End
End
Attribute VB_Name = "FORM的內部標識名"
多重的巢狀就完成了這個FORM裡面控制元件的從屬關係(例如一個PICTUREBOX從屬與FORM,而一個TEXT從屬於一個FRAME)
之後我們要得到這個FRM檔案裡面的控制元件資訊和SUB以及FUNCTION的資訊,來完成我們的FRM寫入
每個OBJECT裡有一個TYPE表示這個OBJECT的屬性,我們已經知道凡是是一個FRM都是有OptionalObjectInfo
這個結構告訴我們很多元素的索引,獲取的方式十分簡單,只要先確定有OptionalObjectInfo然後它的地址就是這個Object的(char*)ObjectInfo+sizeof(ObjectInfo)其實就是緊緊跟在ObjectInfo之後的。
結構的描述已經在上面列出了,這個結構中的資訊十分複雜,我們要注意這麼幾個專案
ControlCount 列出控制元件個數
aControlArray 指向控制元件表
控制元件表是一個緊緊挨著的一個指標陣列,我們可以逐個讀取獲得資訊
虛擬碼:
for (i = 0 ; i<=this->ObjectTable->ObjectCount1 -1 ; ++i,++t)
{//迴圈獲得所有的OBJECT的指標
s = this->Get_OptionalObjectInfo(t); //獲取一個OBJECT的OptionalObjectInfo
if (s==NULL) // it is a module and haven't any optional object info
continue;
cout<<"One frm found!"<<endl;//列印出:獲得一個FRM
ct = this->Get_Control_t(s);//獲取第一個Control的指標
for (d = 0 ; d<=s->ControlCount-1 ; d++)//迴圈獲得每一個CONTROL的索引
cout<<"one control:"<<this->Get_Control_Name(ct++)<<endl;//獲得了一個Control列印出它的名字
}
inline BYTE * VBEXE::Get_Control_Name(VBST::Control_t *x)
{
return (this->buffer + this->sf.VA2Offset(x->aName));
}
這裡其實是我的DECOMPILER的一些片段,僅僅是用作除錯的還未成型所以就不敢貼出來丟臉了:)。
每個Control_t的aName是一個VA指向一個字串就是表示這個控制元件的名字,至於控制元件的屬性我們以後再說:)
我們看看我的VBDECOMPILER輸出的讀取那個SEMI的DECOMPILER的東西
Load file sucessfully!
Get contols in object
One frm found!
one control:mnuFile
one control:mnuHelpAbout
one control:txtFinal
one control:mnuFileOpen
one control:mnuTools
one control:Label1
one control:Form
one control:Label2
one control:mnuFileRecent1
one control:mnuFileSep1
one control:mnuFileRecent2
one control:mnuFileRecent3
one control:mnuFileRecent4
one control:mnuFileSep2
one control:lblObjectName
one control:mnuOptions
one control:mnuFileDebugProcess
one control:mnuToolsPCodeProcedure
one control:txtCode
one control:tvProject
one control:StatusBar1
one control:sstViewFile
one control:fxgEXEInfo
one control:picPreview
one control:mnuFileAntiDecompiler
one control:mnuFileExportMemoryMap
one control:mnuFileGenerate
one control:lstMembers
one control:lstTypeInfos
one control:mnuFileSaveExe
one control:txtBuffer
one control:txtFunctions
one control:txtEditArray
one control:lblArrayEdit
one control:buffCodeAv
one control:buffCodeAp
one control:mnuFileExit
one control:txtResult
one control:cmdCancel
one control:txtStatus
one control:mnuHelp
one control:FrameStatus
One frm found!
one control:imgFlame
one control:lblTitle
one control:TmrLight
one control:tmrIcon
one control:Form
One frm found!
one control:chkShowOffsets
one control:chkSkipCOM
one control:chkDumpControls
one control:cmdClose
one control:chkPCODE
one control:chkShowColors
one control:Form
One frm found!
one control:Class
One frm found!
one control:Class
One frm found!
one control:lblTitle
one control:cmdClose
one control:Form
one control:Label1
one control:lstProcedures
one control:txtView
到這裡,我們以及能夠基本將原來的工程組還原出來了,但是仍然有缺點:
1)我們得不到(應該說實我水平不夠)VBP檔案裡的Reference
2)我們即將討論關於VERSION資訊的東西(要從一個EXE的RESOURCE中看出端倪)
3)我們的CODE仍然還是一大問題
4)我們的STARTUP開始段,(馬上會研究出來的)
5)我們還不知道哪個FRM有FRX檔案哪個沒有而且不能還原,而且FRM裡面的控制元件還是不能獲得屬性
6)PCODE一點都沒有討論
7)還有一些細節的資訊沒能弄出來
相關文章
- 安卓反編譯詳解安卓編譯
- Java 反彙編、反編譯、volitale解讀Java編譯
- VB6 dll 自動編譯工具編譯
- Java編譯與反編譯Java編譯
- Android反編譯:反編譯工具和方法Android編譯
- lamp編譯詳解LAMP編譯
- 反編譯apk編譯APK
- Android APK反編譯就這麼簡單 詳解(附圖)AndroidAPK編譯
- Unity3D放破解反編譯。DLL加密,mono解密。全程詳解。Unity3D編譯加密Mono解密
- Android Apk反編譯系列教程(一)如何反編譯APKAndroidAPK編譯
- java反編譯工具Java編譯
- 反編譯 iOS APP編譯iOSAPP
- android 反編譯Android編譯
- Android APK反編譯技巧全講解AndroidAPK編譯
- 反編譯系列教程(上)編譯
- 反編譯系列教程(中)編譯
- Android 反編譯指南Android編譯
- Eclipse配置反編譯Eclipse編譯
- 小程式反編譯教程編譯
- .net反編譯工具ILSpy編譯
- Java反編譯器剖析Java編譯
- jive論壇反編譯編譯
- 反編譯技術探究編譯
- c#程式反編譯C#編譯
- Hive SQL 編譯過程詳解HiveSQL編譯
- js預編譯 --預編譯詳解四部曲JS編譯
- Mac平臺反編譯Unity編譯的安卓apkMac編譯Unity安卓APK
- 如何反編譯微信小程式?編譯微信小程式
- 安卓apk檔案反編譯安卓APK編譯
- [java]javap命令列反編譯Java命令列編譯
- Java程式碼的編譯與反編譯那些事兒Java編譯
- [轉]:xmake編譯配置過程詳解編譯
- 詳解Linux 程式編譯過程Linux編譯
- nginx原始碼編譯安裝(詳解)Nginx原始碼編譯
- 詳解LAMP原始碼編譯安裝LAMP原始碼編譯
- C/C++編譯過程詳解C++編譯
- Linux下nginx編譯安裝教程和編譯引數詳解LinuxNginx編譯
- Eclipse反編譯外掛jadclipse安裝詳細教程Eclipse編譯