It is currently Sat Dec 05, 2020 4:00 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 32 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: intercept API
PostPosted: Thu Jun 24, 2010 5:44 am 
Offline
Enthusiast
Enthusiast

Joined: Sun Jun 28, 2009 7:07 pm
Posts: 176
Location: RUS
Hi all
Code:
Procedure.s PutDirektorii(PutPapka); #CSIDL_SYSTEM
Put$=Space(#MAX_PATH):SHGetSpecialFolderLocation_(0,PutPapka,@Raz)
SHGetPathFromIDList_(Raz, @Put$):ProcedureReturn Trim(Put$)
EndProcedure

Procedure HookedProcedure(a,b)
; newmessagebox:
; Debug a
; Debug b
; Debug c
; Debug d
MessageRequester("!!!!", "+++++++++++++++++++++++")
ProcedureReturn
EndProcedure

Procedure Hook(process,library$,function$,HookedProcAddr)
   dwAddr=GetProcAddress_(GetModuleHandle_(library$),function$)
   Debug dwaddr
   ReadProcessMemory_(process,dwAddr,@Backup(0),6,@readbytes)
   Dim a.b(6) : a(0)=$68 : a(5)=$C3 : dwCalc=HookedProcAddr-dwAddr-5
   CopyMemory(@dwCalc,@a(1),4)
;    VirtualProtect_(dwAddr,8,#PAGE_EXECUTE_READWRITE,@oldP)
   WriteProcessMemory_(process,dwAddr,@a(0),6,@written)
EndProcedure

Procedure OpenWindow_Window_0()
Protected res=0
  If OpenWindow(0, 100, 100, 145, 260, "HookMe", #PB_Window_SystemMenu)
    ButtonGadget(1, 40,  60,  60, 25, "Delete")
    ButtonGadget(2, 40,  20,  60, 25, "Create")
    ButtonGadget(3, 40,  100,  60, 25, "MSB")
    StringGadget(4, 40,  140, 60, 25, Str(GetCurrentProcessId_()), #PB_String_ReadOnly)
    ButtonGadget(5, 40,  180,  60, 25, "inject")
    ButtonGadget(6, 40,  220,  60, 25, "Hook")
    res=1
  EndIf
ProcedureReturn res
EndProcedure

  If OpenWindow_Window_0()
    Repeat
      Event = WaitWindowEvent()
      Select Event
        Case #PB_Event_Gadget
          EventGadget = EventGadget()
          Select EventGadget
            Case 1 : f$="c:\test.txt" : DeleteFile_(f$)
            Case 2 : f$="c:\test.txt" : CreateFile(0,f$) : WriteString(0,"hi") : CloseFile(0)
            Case 3 : MessageBox_(0," О  п  а .            ","В н и м а н и е !", #MB_ICONHAND) 
;             Case 5 : InjectDll("scan.dll") 
            Case 6
                    hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, 0, Val(GetGadgetText(4)))
                    Hook(hProcess,"kernel32.dll","DeleteFileA",@HookedProcedure())
          EndSelect
      EndSelect
    Until Event=#PB_Event_CloseWindow
  EndIf
End


program crashes... :(
What am I doing wrong?
Correctly find the address of the procedure, why it does not work??

Thanks for your help in advance!


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 12:11 am 
Offline
User
User
User avatar

Joined: Mon Jun 07, 2010 11:36 pm
Posts: 45
Location: Normandy, France
Interesting/fun piece of code.
What seems to be wrong for me:
- You were using $68 (PUSH). I think you intended to use $E9 (CALL). ;)
- Remove the all parameter from HookedProcedure() (I think you're not pushing any parameter on the stack with your patch).
If you want to intercept the parameters given to DeleteFileA(), it's probably more complicated.

The code below is working (replace those 2 procedures in your code).
Code:
Procedure HookedProcedure()                   ; <--- No parameter here
MessageRequester("!!!!", "+++++++++++++++++++++++")
ProcedureReturn
EndProcedure

Procedure Hook(process,library$,function$,HookedProcAddr)
   dwAddr=GetProcAddress_(GetModuleHandle_(library$),function$)
   Debug dwaddr
   Debug Hex(PeekL(dwaddr))
   ReadProcessMemory_(process,dwAddr,@Backup(0),6,@readbytes)
   Dim a.b(5) : a(0)=$E8 : a(5)=$c3 : dwCalc=HookedProcAddr-dwAddr-5    ; <--- fixed line
   CopyMemory(@dwCalc,@a(1),4)
   WriteProcessMemory_(process,dwAddr,@a(0),6,@written)
 EndProcedure


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 5:07 am 
Offline
Enthusiast
Enthusiast

Joined: Sun Jun 28, 2009 7:07 pm
Posts: 176
Location: RUS
Jihugen wrote:

Hi.
Thank you.

How then can intercept the parameters given to DeleteFileA(?) ?


Yours sincerely.


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 7:38 am 
Offline
Enthusiast
Enthusiast

Joined: Sun Jun 28, 2009 7:07 pm
Posts: 176
Location: RUS
alternatively can use this:
Code:
Global Dim Backup(0)

Global buf1.s
Global buf11,buf2,buf3.s,buf4.s,buf5

Procedure.s PutDirektorii(PutPapka); #CSIDL_SYSTEM
Put$=Space(#MAX_PATH):SHGetSpecialFolderLocation_(0,PutPapka,@Raz)
SHGetPathFromIDList_(Raz, @Put$):ProcedureReturn Trim(Put$)
EndProcedure

Procedure HookedProcedure1()

 ! mov eax,[esp+8]
 ! mov dword[v_buf1],eax

Debug "DeleteFile_("+Chr($22)+buf1+Chr($22)+")"

EndProcedure
Procedure HookedProcedure2()

 ! mov eax,[esp+8]
 ! mov dword[v_buf2],eax
 ! mov eax,[esp+12]
 ! mov dword[v_buf3],eax
 ! mov eax,[esp+16]
 ! mov dword[v_buf4],eax
 ! mov eax,[esp+20]
 ! mov dword[v_buf5],eax

Debug "MessageBox("+Str(buf2)+", "+Chr($22)+buf3+Chr($22)+", "+Chr($22)+buf4+Chr($22)+", "+Str(buf5)+")"

EndProcedure

Procedure Hook(process,library$,function$,HookedProcAddr)
   dwAddr=GetProcAddress_(GetModuleHandle_(library$),function$)
   ReadProcessMemory_(process,dwAddr,@Backup(0),6,@readbytes)
   Dim a.b(5) : a(0)=$E8 : a(5)=$c3 : dwCalc=HookedProcAddr-dwAddr-5    ; <--- fixed line
   CopyMemory(@dwCalc,@a(1),4)
   WriteProcessMemory_(process,dwAddr,@a(0),6,@written)
EndProcedure
Procedure OpenWindow_Window_0()
Protected res=0
  If OpenWindow(0, 100, 100, 145, 260, "HookMe", #PB_Window_SystemMenu)
    ButtonGadget(1, 40,  60,  60, 25, "Delete")
    ButtonGadget(2, 40,  20,  60, 25, "Create")
    ButtonGadget(3, 40,  100,  60, 25, "MSB")
    StringGadget(4, 40,  140, 60, 25, Str(GetCurrentProcessId_()), #PB_String_ReadOnly)
    ButtonGadget(5, 40,  180,  60, 25, "inject")
    ButtonGadget(6, 40,  220,  60, 25, "Hook")
    res=1
  EndIf
ProcedureReturn res
EndProcedure

  If OpenWindow_Window_0()
    Repeat
      Event = WaitWindowEvent()
      Select Event
        Case #PB_Event_Gadget
          EventGadget = EventGadget()
          Select EventGadget
            Case 1 : f$="c:\test1.txt" : DeleteFile_(f$)
            Case 2 : f$="c:\test.txt" : CreateFile(0,f$) : WriteString(0,"hi") : CloseFile(0)
            Case 3 : MessageBox_(0," О  п  а .            ","В н и м а н и е !", #MB_ICONHAND) 
;             Case 5 : InjectDll("scan.dll") 
            Case 6
                    hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, 0, Val(GetGadgetText(4)))
                    Hook(hProcess,"kernel32.dll","DeleteFileA",@HookedProcedure1())
                    Hook(hProcess,"user32.dll","MessageBoxA",@HookedProcedure2())
          EndSelect
      EndSelect
    Until Event=#PB_Event_CloseWindow
  EndIf
End

BUT, I do not want to use ASM... :(
than can replace these inserts from ASM?? :?:


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 12:50 pm 
Offline
User
User
User avatar

Joined: Mon Jun 07, 2010 11:36 pm
Posts: 45
Location: Normandy, France
registrymechanic22 wrote:
BUT, I do not want to use ASM... :(
than can replace these inserts from ASM?? :?:

I don't know if there is another feasible way (but well, I'm far from an expert in this field :? ).
Why not use ASM?
In this case it seems actually pertinent, no?


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 1:23 pm 
Offline
Enthusiast
Enthusiast

Joined: Sun Jun 28, 2009 7:07 pm
Posts: 176
Location: RUS
I do not know ASM? I can not work with him .... :oops:

in HookedProcedure () to add to intercept functions, its treatment, and return it to either suspend or continue.

.. Sorry for my english... :oops:

Code:
Procedure UnHook(library$,function$)
  dwAddr=GetProcAddress_(GetModuleHandle_(library$),function$)
  WriteProcessMemory_(GetCurrentProcess_(),dwAddr,@Backup(0),6,@written)
EndProcedure

Procedure HookedProcedure1()

! mov eax,[esp+8]
! mov dword[v_buf1],eax

UnHook("kernel32.dll","DeleteFileA")
DeleteFile_(buf2) ; or stop!
Hook("kernel32.dll","DeleteFileA",@NewDeleteFile())
EndProcedure


I think there's a lot extra, and how to make beautiful, myself can not understand


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 6:16 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Aug 15, 2009 6:59 pm
Posts: 1258
If you hook API functions you can just put the parameter in the hook procedure as normal parameters. No assembler needed, if it's StdCall.


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 8:32 pm 
Offline
Addict
Addict

Joined: Sat Dec 31, 2005 5:24 pm
Posts: 2970
Location: Where ya would never look.....
Whats the whole thing look like? I lost track between the asm conversions and the replacement procedures as to what exactly you were trying to accomplish. And whats 'backup' look like?

_________________
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 9:23 pm 
Offline
Enthusiast
Enthusiast

Joined: Sun Jun 28, 2009 7:07 pm
Posts: 176
Location: RUS
Prompt please example (code), to correct my mistake?
I do not know how to implement it.... :(

Fhanks in advance.


PS:
********* executable EXE *********
Code:
Procedure.s PutDirektorii(PutPapka)
; PutPapka=#CSIDL_SYSTEMX86
Put$=Space(#MAX_PATH):SHGetSpecialFolderLocation_(0,PutPapka,@Raz)
SHGetPathFromIDList_(Raz, @Put$):ProcedureReturn Trim(Put$)
EndProcedure

Procedure InjectDll(NameDll.s)
   hProcess=OpenProcess_(#PROCESS_CREATE_THREAD | #PROCESS_VM_OPERATION | #PROCESS_VM_READ | #PROCESS_VM_WRITE,0,Val(GetGadgetText(4)))
   LL=GetProcAddress_(GetModuleHandle_(PutDirektorii(#CSIDL_SYSTEM)+"\kernel32.dll"),"LoadLibraryA")
   VA=VirtualAllocEx_(hProcess,0,12,#MEM_RESERVE | #MEM_COMMIT,#PAGE_READWRITE)
   WriteProcessMemory_(hProcess,VA,NameDll.s,12,0)
   CreateRemoteThread_(hProcess,0,0,LL,VA,0,0)
   CloseHandle_(hProcess)
EndProcedure

Procedure OpenWindow_Window_0()
Protected res=0
  If OpenWindow(0, 100, 100, 145, 260, "HookMe", #PB_Window_SystemMenu)
    ButtonGadget(1, 40,  60,  60, 25, "Delete")
    ButtonGadget(2, 40,  20,  60, 25, "Create")
    ButtonGadget(3, 40,  100,  60, 25, "MSB")
    StringGadget(4, 40,  140, 60, 25, Str(GetCurrentProcessId_()), #PB_String_ReadOnly)
    ButtonGadget(5, 40,  180,  60, 25, "inject")
    ButtonGadget(6, 40,  220,  60, 25, "Hook")
    res=1
  EndIf
ProcedureReturn res
EndProcedure

  If OpenWindow_Window_0()
    Repeat
      Event = WaitWindowEvent()
      Select Event
        Case #PB_Event_Gadget
          EventGadget = EventGadget()
          Select EventGadget
            Case 1 : f$="c:\test.txt" : DeleteFile_(f$)
            Case 2 : f$="c:\test.txt" : CreateFile(0,f$) : WriteString(0,"hi") : CloseFile(0)
            Case 3 : MessageBox_(0," О  п  а .            ","В н и м а н и е !", #MB_ICONHAND) 
            Case 5 : InjectDll("scan.dll") 
            Case 6
;                     hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, 0, 1448)
;                     Hook(hProcess,"kernel32.dll","DeleteFileA",@HookedProcedure1())
;                     Hook(hProcess,"user32.dll","MessageBoxA",@HookedProcedure2())
          EndSelect
      EndSelect
    Until Event=#PB_Event_CloseWindow
  EndIf
End

********* DLL *********
Code:
Global Dim Backup(0)

Global oldP

Global buf1.s,buf2.s,buf3.s,buf4.s,buf5.s,buf6.s

Declare NewDeleteFile()

Procedure EnableDebugPrivilege()
If OpenProcessToken_(GetCurrentProcess_(),#TOKEN_ADJUST_PRIVILEGES|#TOKEN_QUERY,@hToken)
tp.TOKEN_PRIVILEGES : tp\PrivilegeCount=1
If LookupPrivilegeValue_(#Null,"SeDebugPrivilege",tp\Privileges[0]\Luid)
tp\Privileges[0]\Attributes = #SE_PRIVILEGE_ENABLED
If AdjustTokenPrivileges_(hToken,#False,@tp,SizeOf(tp),#Null,#Null)
EndIf : EndIf : EndIf
EndProcedure
EnableDebugPrivilege()


Procedure.s PutDirektorii(PutPapka)
Protected Put.s
PutPapka=#CSIDL_SYSTEMX86
Put.s=Space(#MAX_PATH):SHGetSpecialFolderLocation_(0,PutPapka,@Raz)
SHGetPathFromIDList_(Raz, @Put):ProcedureReturn Trim(Put)
EndProcedure

Procedure Hook(library$,function$,HookedProcAddr)
Protected dwAddr, old
Protected process = OpenProcess_(#PROCESS_ALL_ACCESS, 0, GetCurrentProcessId_())
old=GetModuleHandle_(library$)
dwAddr=GetProcAddress_(old,function$)
ReadProcessMemory_(process,dwAddr,@Backup(0),6,@readbytes)
Dim a.b(5) : a(0)=$E8 : a(5)=$c3 : dwCalc=HookedProcAddr-dwAddr-5    ; <--- fixed line
CopyMemory(@dwCalc,@a(1),4)
WriteProcessMemory_(process,dwAddr,@a(0),6,@written)
EndProcedure

Procedure UnHook(library$,function$)
  dwAddr=GetProcAddress_(GetModuleHandle_(library$),function$)
  WriteProcessMemory_(GetCurrentProcess_(),dwAddr,@Backup(0),6,@written)
EndProcedure


ProcedureDLL AttachProcess(Instance)

Hook("kernel32.dll","DeleteFileA",@NewDeleteFile())

EndProcedure

; *******************************************************
; user32.dll


Procedure NewMessageBox()

         ! mov eax,[esp+4]
         ! mov dword[v_buf1],eax         ;hwnd
         ! mov eax,[esp+8]
         ! mov dword[v_buf2],eax      ;text
         ! mov eax,[esp+12]
         ! mov dword[v_buf3],eax      ;caption
         ! mov eax,[esp+16]
         ! mov dword[v_buf4],eax         ;type
         
         OpenFile(1,"qqqqq.txt")
         FileSeek(1,Lof(1))
         WriteStringN(1, "MessageBox("+Str(v_buf1)+", "+Chr($22)+buf2+Chr($22)+", "+Chr($22)+buf3+Chr($22)+", "+Str(v_buf4)+")" )
         CloseFile(1)
         
; call_newmessagebox=delta_newmessagebox+NewUser32
; ! jmp dword[v_call_newmessagebox]
EndProcedure



; *******************************************************
; kernel32.dll

Procedure NewDeleteFile()

;          ! mov eax,[esp+4]
;          ! mov dword[v_buf1],eax         ;name
         ! mov eax,[esp+8]
         ! mov dword[v_buf2],eax         ;name


         OpenFile(1,"qqqqq.txt")
         FileSeek(1,Lof(1))
         WriteStringN(1, buf2)
         CloseFile(1)
; MessageRequester("",buf2)

; call_newdeletefile =delta_newdeletefile+NewKernel32
; ! jmp dword[v_call_newdeletefile]

UnHook("kernel32.dll","DeleteFileA")
; DeleteFile_(buf2)
OpenLibrary(1,"kernel32.dll")
CallFunction(1, "DeleteFileA", buf2)
CloseLibrary(1)
Hook("kernel32.dll","DeleteFileA",@NewDeleteFile())
EndProcedure

; *******************************************************
; !jmp @F
; !MP10 ;3 по счету
; !MP12 ;4
; !@@:
; *******************************************************
; *******************************************************


???


Last edited by registrymechanic22 on Fri Jun 25, 2010 9:29 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 9:28 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Aug 15, 2009 6:59 pm
Posts: 1258
SFSxOI wrote:
Whats the whole thing look like? I lost track between the asm conversions and the replacement procedures as to what exactly you were trying to accomplish. And whats 'backup' look like?

'backup' is the original code fragment he is overwriting. It's needed if he wants to call the original procedure and to uninstall the hook. However it's not good in his code. He just uses a length of 6 byte for the backup, which can lead to a crash if the procedure don't starts with the code he is expecting. If the instructions are longer or shorter and not match the 6 byte border he will not be able to call the original procedure without uninstalling his hook. The normal solution to that is using a simple disassembler engine to get the length of the instructions.


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Fri Jun 25, 2010 9:33 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Aug 15, 2009 6:59 pm
Posts: 1258
Code:
Procedure NewMessageBox(hwnd.i, text.s, caption.s, type.i)

         OpenFile(1,"qqqqq.txt")
         FileSeek(1,Lof(1))
         WriteStringN(1, "MessageBox("+Str(v_buf1)+", "+Chr($22)+buf2+Chr($22)+", "+Chr($22)+buf3+Chr($22)+", "+Str(v_buf4)+")" )
         CloseFile(1)
         
; call_newmessagebox=delta_newmessagebox+NewUser32
; ! jmp dword[v_call_newmessagebox]
EndProcedure

Not tested but thats what i mean with the parameters.


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Sat Jun 26, 2010 2:32 am 
Offline
Addict
Addict

Joined: Sat Dec 31, 2005 5:24 pm
Posts: 2970
Location: Where ya would never look.....
Thorium wrote:
SFSxOI wrote:
Whats the whole thing look like? I lost track between the asm conversions and the replacement procedures as to what exactly you were trying to accomplish. And whats 'backup' look like?

'backup' is the original code fragment he is overwriting. It's needed if he wants to call the original procedure and to uninstall the hook. However it's not good in his code. He just uses a length of 6 byte for the backup, which can lead to a crash if the procedure don't starts with the code he is expecting. If the instructions are longer or shorter and not match the 6 byte border he will not be able to call the original procedure without uninstalling his hook. The normal solution to that is using a simple disassembler engine to get the length of the instructions.



Ahhhh...ok, got it. Thanks

But why even use ASM to begin with, what he wants to do is just StdCall isn't it ?

_________________
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Sat Jun 26, 2010 3:12 am 
Offline
User
User
User avatar

Joined: Mon Jun 07, 2010 11:36 pm
Posts: 45
Location: Normandy, France
SFSxOI wrote:
But why even use ASM to begin with, what he wants to do is just StdCall isn't it ?
The goal was to write a Call to HookedProcedure() directly at the beginning of the DeleteFileA function, so that each call to DeleteFileA will be forwarded to HookedProcedure.
It's fast, dirty and fun... There is probably other (and better) ways.


Thorium wrote:
However it's not good in his code. He just uses a length of 6 byte for the backup, which can lead to a crash if the procedure don't starts with the code he is expecting. If the instructions are longer or shorter and not match the 6 byte border he will not be able to call the original procedure without uninstalling his hook. The normal solution to that is using a simple disassembler engine to get the length of the instructions.
Sorry, Thorium, but I don't understand what you mean here.
For me, 6 bytes is enough for the backup, and it seems clear that calling the original procedure without uninstalling the hook is impossible. What did I get wrong?


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Sat Jun 26, 2010 7:48 am 
Offline
Enthusiast
Enthusiast

Joined: Sun Jun 28, 2009 7:07 pm
Posts: 176
Location: RUS
Thorium wrote:
Code:
Procedure NewMessageBox(hwnd.i, text.s, caption.s, type.i)
......................
EndProcedure

Not tested but thats what i mean with the parameters.

... Tried, not working, the program crashes :?


Top
 Profile  
Reply with quote  
 Post subject: Re: intercept API
PostPosted: Sat Jun 26, 2010 8:27 am 
Offline
Addict
Addict
User avatar

Joined: Sat Aug 15, 2009 6:59 pm
Posts: 1258
Calling the original procedure without uninstalling the hook is not only possible, its a normal thing. It's not that hard. You just need to copy the correct number of bytes on hook installation. The code you will write to the procedure is likely 5 byte. What you need to do is copy the complete instructions that you overwrite. There can be some shorter instructions or one bigger. But you cant expect them to allways end at byte 6. You need a intact code fragment not a cutted instruction. Therefor you need to get the length of the intructions you overwrite to copy them intact. Thats easy with the Error Lib of PB.
Once you have that intact code fragment you may need to fix it. For example if there is a jump in it you have to recalculate the destination, because its saved relative to the instruction. Then you write a jmp to the end of the copied code that jumps right after your patched code in the original procedure. Now you just need to call the copied code to Call the original procedure.
Most people skip the part of fixing instructions because its very unlikely that there is a jump in the first 5 byte.

I hope thats better understandable, sorry english is not my nativ language.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 32 posts ]  Go to page 1, 2, 3  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 70 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye