Gui disassembling

Share your advanced PureBasic knowledge/code with the community.
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Gui disassembling

Post by firace »

This code is able to analyze existing compiled GUIs and "disassemble" them to functional PB code. It once helped me recover some GUI code for a lost project source.

Note that this is a quick and dirty demo and definitely flaky and incomplete. It was fun to play with, but I have no plans to further develop it at this stage - consider it a starting point. Enjoy!

edit: replaced with the example in the next post
Last edited by firace on Wed Jun 16, 2021 5:03 pm, edited 1 time in total.
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Re: Gui disassembling

Post by firace »

Demo below. Again, expect bugs, but should be fun to play with nonetheless.
Note: works best with PB programs.

Code: Select all

;; note: windows only.

Global winleft
Global wintop
Global reconstructedGUIcode$
Debug "--- ---"

Procedure EnumChildWindowsProcedure(hwnd, lparam)
  gadgetnum = GetDlgCtrlID_(hwnd)
  
  Protected Classname$ = Space(#MAX_PATH)
  Protected parentclassname$ = Space(#MAX_PATH)
  Protected Textname$ = Space(#MAX_PATH)
  
  parentHandle = GetParent_(hwnd)
  SendMessage_(hwnd,#WM_GETTEXT,128,@Textname$) 
  
  GetClassName_(hwnd, @Classname$, #MAX_PATH-1)
  GetClassName_(parentHandle , @parentclassname$, #MAX_PATH-1)
  
  rc.rect
  GetWindowRect_(hwnd,rc)
  XXX = rc\left - winleft 
  YYY = rc\top - wintop - GetSystemMetrics_(#SM_CYCAPTION)
  
  If Classname$ = "Button" 
    If gadgetnum > 13000 And gadgetnum < 13100     
      CheckBoxGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
    Else
      ButtonGadget(#PB_Any, XXX, YYY , rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
      
      reconstructedGUIcode$ + #CRLF$ + "ButtonGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + #DQUOTE$ + Left(Textname$,60) + #DQUOTE$ + ")"
      
    EndIf
  ElseIf Classname$ = "Static"
    TextGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
    reconstructedGUIcode$ + #CRLF$ + "TextGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + #DQUOTE$ + Left(Textname$,60) + #DQUOTE$ + ")"
    
  ElseIf Classname$ = "Scintilla"
    EG = EditorGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top ) : SetGadgetText(EG, Classname$)
    reconstructedGUIcode$ + #CRLF$ + "ScintillaGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ")"
    
  ElseIf Classname$ = "ListBox"
    ListViewGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top)
    reconstructedGUIcode$ + #CRLF$ + "ListIconGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + ")"
    
  ElseIf Classname$ = "SysListView32"
    ListViewGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top)
    reconstructedGUIcode$ + #CRLF$ + "ListViewGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + ")"
    
    
  ElseIf Classname$ = "Edit" And parentclassname$ <> "ComboBox"
    StringGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
  ElseIf Classname$ = "ComboBox"
    ComboBoxGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top, #PB_ComboBox_Editable )
    
    reconstructedGUIcode$ + #CRLF$ + "ComboBoxGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ", #PB_ComboBox_Editable) "
    
  ElseIf Classname$ = "RichEdit20W"  
    EG = EditorGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top ) : SetGadgetText(EG, Classname$)
    
    reconstructedGUIcode$ + #CRLF$ + "EditorGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ") "
    
  EndIf 
  
  ProcedureReturn #True
  
EndProcedure

Procedure Deconstruct(targetWindowHandle)
  
  Debug targetWindowHandle
  
  rc.rect
  
  Textname$ = Space(128)
  
  SendMessage_(targetWindowHandle,#WM_GETTEXT,128,@Textname$) 
  
  GetWindowRect_(targetWindowHandle,rc)
  
  winleft = rc\left
  wintop = rc\top
  
  WW =  rc\right - rc\left - GetSystemMetrics_(#SM_CYFIXEDFRAME) *2
  HH =  rc\bottom - rc\top - GetSystemMetrics_(#SM_CYCAPTION) - GetSystemMetrics_(#SM_CYFIXEDFRAME)* 2
  
  OpenWindow(123, 100, 100, WW, HH, "[ReconstructedGUI]", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered | #PB_Window_MaximizeGadget | #PB_Window_Invisible)   : StickyWindow(123, 1)
  
  reconstructedGUIcode$ = "; Automatically generated code! " + #CRLF$
  reconstructedGUIcode$ + ~"OpenWindow(#PB_Any, 100, 100, " + WW + "," + HH + ~", \"\", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)" + #CRLF$ + #CRLF$
  
  EnumChildWindows_(targetWindowHandle, @EnumChildWindowsProcedure(), 0)
  
  reconstructedGUIcode$ + #CRLF$ + #CRLF$ + "Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow "
  
  SetGadgetText(6, reconstructedGUIcode$)
EndProcedure   


Procedure.l enumWins(hwnd.l, lParam.l) 
  If hwnd 
    If IsWindowVisible_(hwnd)  And Not IsIconic_(hwnd)       ;... Only looking for visible windows  
      pText.s = Space(256)
      SendMessage_(hwnd, #WM_GETTEXT, 256, @pText) 
      If Len(pText) > 0              ;... Skip windows with empty title 
        
        AddGadgetItem(52, 0, Str(hwnd) + " - " + Left(pText,48)  )
      EndIf 
    EndIf 
    ProcedureReturn 1 
  Else 
    ProcedureReturn 0 
  EndIf 
EndProcedure 

OpenWindow(0,30,30,540,700,"GUI Reverser", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget)

TextGadget(50, 10, 15, 100, 22, "Analyze window -->") 
ComboBoxGadget(52, 120, 10, 345, 20) 

ButtonGadget(20,10,620,140,20,"Test code")      : DisableGadget(20, 1)

EditorGadget(6, 10, 100, 460, 500, #PB_Editor_ReadOnly) : LoadFont(10, "Consolas", 12) : SetGadgetFont(6, FontID(10))

Classname$ = Space(#MAX_PATH)
parentHandleClassname$ = Space(#MAX_PATH)
Textname$ = Space(#MAX_PATH)

EnumWindows_(@enumWins(), 0) 


Repeat
  Event = WaitWindowEvent()
  
  If event = #PB_Event_Gadget
    If EventGadget() = 52
      handle = Val(StringField(GetGadgetText(52), 1, " ")) 
      Deconstruct(handle) : DisableGadget(20, 0)
    EndIf
    
    If EventGadget() = 20
      HideWindow(123, 0)
    EndIf
    
  EndIf
Until Event = #PB_Event_CloseWindow
Last edited by firace on Thu Jun 17, 2021 7:51 am, edited 1 time in total.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Gui disassembling

Post by ChrisR »

I like the idea and this kind of tools :)
I tried it on itself, I only have the window!

Code: Select all

; Automatically generated code! 
OpenWindow(123, 100, 100, 1544,809, "", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow 
A refresh button would be good to refresh the "Analyze window" combobox, if an app is launched after.
Good continuation
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Gui disassembling

Post by Kwai chang caine »

Yes very good idea, practice when you have lost the source code of a complex GUI or recreate a fake of another 8)
I believe CodeCaddy have this function in the old time :wink:
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
Caronte3D
Addict
Addict
Posts: 1027
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Gui disassembling

Post by Caronte3D »

Interesthing :wink:
Post Reply