; Find a string in a file and replace it
;
; REPAIR.ASM Help from Fetten,Dave,Frank K.,Qword,TightCoderEx,Gunther,bilbo,blabberer,
;
INCLUDE \masm32\include\masm32rt.inc
WinMain proto

WORD,

WORD,

WORD,

WORD
Convert proto

WORD,

WORD
.const
MAXSTR equ 100
MAXSIZE equ 260
ButtonOpenID equ 100
ButtonPatchID equ 101
StringID equ 200
NStringID equ 201
.data
ClassName db "WinClass",0
NotEqual db "Length of strings must be equal",0
Error db "Error!",0
NoMem db "Error allocating memory",0
FilterString db "All Files",0,"*.*",0
; in other source, looks incomplete ??
;db "Executable Files",0,"*.exe",0,0
FilePath db 260 dup (0)
Caption db "Choose the file to fix :",0
AppName db "REPARIEREN",0
Done db "File fixed succesfully !",0
NoFile db "Can't find the file !",0
WrFile db "Error writing to file !",0
ofn OPENFILENAME <>
FSize dd 0
NString db 50 dup (0)
OString db 50 dup (0)
StrLenA dd 0
StrLen2 dd 0
ButtonOpen db "Open item to be fixed.",0
ButtonPatch db "Repair Item",0
EditClassName db "edit",0
ButtonClassName db "button",0
WinName db " ",0
StringBuf db MAXSTR dup (0)
NStringBuf db MAXSTR dup (0)
NotFound db "The string can not be found !",0
.data?
hwndOpen HWND ?
hwndPatch HWND ?
hwndString HWND ?
hwndNString HWND ?
hInstance HINSTANCE ?
hwndname HWND ?
hFile HANDLE ?
Numb dd ?
FPointer dd ?
CommandLine LPSTR ?
.code
start:
invoke GetModuleHandle, NULL ; provides the instance handle
mov hInstance, eax
invoke GetCommandLine ; provides the command line address
mov CommandLine, eax
invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
invoke ExitProcess,eax ; cleanup & return to operating system
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow

WORD
LOCAL wc:WNDCLASSEX ; create local variables on stack
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX ; fill values in members of wc
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc ; register our window class
;invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
; width height
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW or WS_VISIBLE,3,3, 400, 280,NULL,NULL,hInstance,NULL ; Create the window
mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow ; show our window
invoke UpdateWindow, hwnd
.WHILE TRUE ; The MessageLoop use of register assumed to ERROR <error FIXED Friday, December 07, 2012>
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL Fpointer
.IF uMsg==WM_CREATE
; Create 2 buttons and 2 edit boxes
;invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,ADDR WinName,WS_CHILD,30,15,210,20,hWnd,StringID,hInstance,NULL
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,ADDR WinName,WS_CHILD or WS_VISIBLE,30,15,210,20,hWnd,StringID,hInstance,NULL
; CreateWindowEx(
;
; DWORD dwExStyle, // extended window style
; LPCTSTR lpClassName, // pointer to registered class name
; LPCTSTR lpWindowName, // pointer to window name
; DWORD dwStyle, // window style
; int x, // horizontal position of window
; int y, // vertical position of window
; int nWidth, // window width
; int nHeight, // window height
; HWND hWndParent, // handle to parent or owner window
; HMENU hMenu, // handle to menu, or child-window identifier
; HINSTANCE hInstance, // handle to application instance
; LPVOID lpParam // pointer to window-creation data
mov hwndString,eax
; Window where input is made 3rd number is width
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL,30,40,310,20,hWnd,NStringID,hInstance,NULL
mov hwndNString,eax
invoke SetFocus, hwndString
; open item
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonOpen,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,10,70,200,20,hWnd,ButtonOpenID,hInstance,NULL
mov hwndOpen,eax
; repair window ; horiz and vertical position
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonPatch,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,250,70,100,20,hWnd,ButtonPatchID,hInstance,NULL
mov hwndPatch,eax
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
mov edx,wParam
shr edx,16
.IF dx==BN_CLICKED
.IF ax==ButtonOpenID ; Open button clicked?
mov ofn.lStructSize,SIZEOF ofn
mov ofn.lpstrFile, OFFSET FilePath
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.nMaxFile,MAXSIZE
mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
mov ofn.lpstrTitle, OFFSET Caption
invoke GetOpenFileName, ADDR ofn
.IF eax == TRUE
; Open file to be repaired
invoke CreateFile, ofn.lpstrFile, GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
.IF eax!=INVALID_HANDLE_VALUE
jmp next
mark db "After CreateFile",0
next:
mov hFile, eax
Invoke GetFileSize, hFile, NULL ; program is opening file and getting size sucessfully
mov FSize, eax ; Save FileSize
Invoke GlobalAlloc, GMEM_FIXED, FSize
mov FPointer, eax
.IF eax == NULL ; If no pointer, error message
push MB_OK OR MB_ICONINFORMATION
push OFFSET NoMem
push OFFSET Error
JMP MESSAGE
.ENDIF
Invoke ReadFile, hFile,FPointer,FSize, ADDR Numb, NULL
.ELSE ; error message if no valid handle
push MB_OK OR MB_ICONINFORMATION
push OFFSET NoFile
push OFFSET Error
jmp MESSAGE
.ENDIF
.ENDIF
.ELSEIF ax == ButtonPatchID ; See if Fix Button has been hit
Invoke GetDlgItemText,hWnd,StringID,ADDR StringBuf,MAXSTR ; Get first string
mov StrLenA, eax ; Save string length
Invoke Convert,ADDR StringBuf, ADDR OString ; Convert to bytes
; NString = 201
Invoke GetDlgItemText,hWnd,NStringID,ADDR NStringBuf,MAXSTR ;Get 2nd string
.IF eax != StrLenA ; both strings equal?
push MB_OK OR MB_ICONINFORMATION ; If not, error message
push OFFSET Error
push OFFSET NotEqual
JMP MESSAGE
.ENDIF
Invoke Convert,ADDR NStringBuf, ADDR NString ; Convert to bytes
mov edi,FPointer ;move pointer to memory to edi
mov ecx,FSize ;move Filesize to ecx
mov esi,offset OString ;set ESI to the Opcode string we search
mov al, byte ptr [esi] ;move the first byte of the string to AL
SEARCH :
repnz scasb ;repeat until ECX=0 or AL equals the value of the byte [EDI], EDI is incremented by 1 every run
cmp ecx,0 ;If ECX=0, no matching string is found
jz NOT_FOUND
FOUND_A_MATCH : ;found matching byte
push ecx ;save registers
push edi
push esi
dec edi ;EDI-1 because REPZ added 1 byte to much
mov ecx,StrLen2 ;ECX = length of the string
repz cmpsb ;repeat until the values in the memory o ;[EDI] and [ESI] are not equal, or ecx=0
cmp ecx,0 ;If ecx = 0, we have found the correct string
jz FIX
pop esi ;Restore registers for continuing search
pop edi
pop ecx
jmp SEARCH ;go on with search
FIX :
pop esi
pop edi
pop ecx
dec edi ;EDI - 1
inc ecx ;ECX + 1
mov eax,FSize
sub eax,ecx ;compute the File Offset to repair FileSize - remaining bytes (ecx) = Offset to repair
Invoke SetFilePointer, hFile, eax, NULL, FILE_BEGIN
Invoke WriteFile, hFile,offset NString, StrLen2, ADDR Numb, NULL
mov eax, Numb
.IF eax == StrLen2 ; bytes written = Bytes to write ?
push MB_OK ; If so success-message
push OFFSET AppName
push OFFSET Done
JMP MESSAGE
.ELSE
push MB_OK OR MB_ICONINFORMATION ; If not, error message
push OFFSET Error
push OFFSET WrFile
.ENDIF
NOT_FOUND :
push MB_OK OR MB_ICONINFORMATION ; If no handle, error message
push OFFSET Error
push OFFSET NotFound
MESSAGE :
push NULL
Call MessageBox
.ENDIF
.ENDIF
.ELSEIF uMsg==WM_DESTROY ; Close program
invoke CloseHandle, hFile ; Release handle
invoke GlobalFree,Fpointer ; Release memory block
invoke ExitProcess,eax ; Exit
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
; This routine converts the ascii strings to their byte equivalent. eg
; string 7415FF -> 74h,15h,FFh
; Use only numbers, and uppercase letters (A,B,C,D,E,F)
; Change a string to its byte equivalent.
; Use only numbers, and uppercase letters
; For every loop (between MORE: and JMP MORE) two ascii characters are taken and converted into a single byte: the first character will go in the high
; nibble, the second character into the second nibble (e.g "41" represented as 34 31 must be converted as one byte 0x41 into AL.
;
; First character:
; (a) if it is a digit (.if (al>=30h) && (al<=39h)) we shift it by 4 bits (IMUL eax, 10h) to bring it in the high nibble, e.g. 34 -> 340,
; the 3 is lost -> resulting AL will hold 4x (where x will be the result of the conversion of the second character)
; (b) if it is a letter (.IF al>64) A-F = 41-46 it will be subtracted by 37 before the shift, becoming 0A-0F -> Ax-Fx after the shift (x as above)
;
; Second character:
; In order to replace the 'x' we saw above we do not shift anymore but we ADD.
; (a) if it is a digit (.if (al>=30h) && (al<=39h)) we first remove the '3' in the high nibble position (SUB AL,30h) - this is the correction you
; didn't apply - then ADD what remains to replace the 'x' (ADD byte ptr [edx+ecx], al)
; E.g. 31 -> 01 after the sub, 4x(resulted from first character conversion) -> 41 after the ADD
; (b) if it is a letter 41-46 (.IF al>64) we first remove the '4' in the high nibble position (SUB AL,40h) - this is the correction you didn't apply
; - then ADD what remains to replace the 'x' (ADD byte ptr [edx+ecx], al)
;
; Obviously this algorithm can be highly optimized but I think it is better to fully understand it before modifying it.
Convert proc LpBuffer

WORD,LpString

WORD
push eax
push esi
push ecx
push edx
mov esi, LpBuffer ; IN
mov edx, LpString ; OUT
xor ecx, ecx
MORE:
; code to obtain high nibble
MOV al, [esi]
.IF al > 29h
.if (al>=30h) && (al<=39h)
IMUL eax, 10h ; is a digit
.ELSE
.IF al>64 ; 41,...
.IF al
SUB al, 37h ; is a letter 41 -> 0A,...
IMUL eax, 10h
.ENDIF
.ENDIF
.ENDIF
.ENDIF
MOV byte ptr [edx+ecx], al
INC esi
; code to obtain low nibble
mov al, [esi]
.IF al >29h
.if (al>=30h) && (al<=39h)
sub al, 30h ; is a digit: 31->01
ADD byte ptr [edx+ecx], al
.ELSE
.IF al > 64
sub al, 37h ; is a letter 41 -> 0A,...
.if !ZERO?
ADD byte ptr [edx+ecx], al
.ENDIF
.ENDIF
.ENDIF
.ENDIF
; continue with next loop
.IF byte ptr [edx+ecx] != NULL
INC esi
INC ecx
JMP MORE
.ENDIF
mov StrLen2, ecx
pop edx
pop ecx
pop esi
pop eax
ret
Convert endp
end start