ASM: ASM (assembly language) to machine code via FASM (flat assembler)

Talk about things ASM, Machine code, some related to AutoHotkey
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

ASM: ASM (assembly language) to machine code via FASM (flat assembler)

Post by jeeswg » 26 May 2018, 16:56

tl;dr 1 when the ASM is converted to an exe, how do I tell where the relevant machine code starts and ends
tl;dr 2 how to convert the example ASM below from x32 to x64
tl;dr 3 people are welcome to comment generally about AutoHotkey, machine code, and FASM

- I have seen one example on the forums so far of ASM to machine code.
InBuf function currently 32-bit only (machine code binary buffer searching) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=28393
- Do share any others.
- ASM code is converted to machine code, and the machine code is used with DllCall to perform an action. For this particular example, it searches binary data for a binary needle, in a similar way to the InStr function.
- This example works on AHK x32 only:

Code: Select all

q:: ;test InBuf function
VarSetCapacity(vData, 100, 0)
VarSetCapacity(vDataN, 4, 0)
NumPut(0x12345678, &vData, 90, "UInt")
NumPut(0x12345678, &vDataN, 0, "UInt")
MsgBox, % InBuf(&vData, &vDataN, 100, 4) ;90
return

InBuf(haystackAddr, needleAddr, haystackSize, needleSize, StartOffset=0)
{   Static fun
   IfEqual,fun,
   {
      h=
      ( LTrim join
         5589E583EC0C53515256579C8B5D1483FB000F8EC20000008B4D108B451829C129D9410F8E
         B10000008B7D0801C78B750C31C0FCAC4B742A4B742D4B74364B74144B753F93AD93F2AE0F
         858B000000391F75F4EB754EADF2AE757F3947FF75F7EB68F2AE7574EB628A26F2AE756C38
         2775F8EB569366AD93F2AE755E66391F75F7EB474E43AD8975FC89DAC1EB02895DF483E203
         8955F887DF87D187FB87CAF2AE75373947FF75F789FB89CA83C7038B75FC8B4DF485C97404
         F3A775DE8B4DF885C97404F3A675D389DF4F89F82B45089D5F5E5A595BC9C2140031C0F7D0EBF0
      )
      VarSetCapacity(fun,StrLen(h)//2)
      Loop % StrLen(h)//2
         NumPut("0x" . SubStr(h,2*A_Index-1,2), fun, A_Index-1, "Char")
   }
   Return DllCall(&fun
      , "uint",haystackAddr, "uint",needleAddr
      , "uint",haystackSize, "uint",needleSize
      , "uint",StartOffset)
}
- How to generate the machine code used in the function:
- First we copy the ASM code to a file, let's call it InBuf.asm.

Code: Select all

format PE GUI 4.0
entry start

include 'win32a.inc'

section '.data' data readable writeable

  hayStack db '1111111122222111111'
  Needle db '22222'

section '.code' code readable executable

  start:

	push	0 5 19 Needle hayStack
	call	InBuf
	push	-1 5 19 Needle hayStack
	call	InBufRev
	invoke	ExitProcess,0

proc InBuf stdcall uses ebx ecx edx esi edi, hayStack,Needle,hayStackSize,NeedleSize,StartOffset
	local	lNeedleRemDwords:DWORD	;(NeedleSize-4)>>2
	local	lNeedleRemTail:DWORD	;Needle remainder byte count (NeedleSize-4) mod 4 -> (0..3)
	local	lNeedleRemPtr4:DWORD	;&Needle[4]

	pushfd

	mov	ebx,[NeedleSize]
	cmp	ebx,0
	jle	.NotFound
	mov	ecx,[hayStackSize]
	mov	eax,[StartOffset]
	sub	ecx,eax
	sub	ecx,ebx
	inc	ecx	;repetitions=hayStackSize-StartOffset-NeedleSize+1
	jle	.NotFound

	mov	edi,[hayStack]
	add	edi,eax ;edi=&(hayStack[StartOffset])

	;load Needle FirstByte
	mov	esi,[Needle]
	xor	eax,eax
	cld
	lodsb	; AL=Needle[0], keep EAX now!

	;decide on needle length
	dec	ebx
	jz	.NeedleLenIs1
	dec	ebx
	jz	.NeedleLenIs2
	dec	ebx
	jz	.NeedleLenIs3
	dec	ebx
	jz	.NeedleLenIs4
	dec	ebx
	jnz	.NeedleLenIsLong

;.NeedleLenIs5:
	xchg	eax,ebx
	lodsd		;AL=Needle[0]
	xchg	eax,ebx ;EBX=bytes 1..5 of Needle

   .ScanNeedleLenIs5:
	repne	scasb
	jne	.NotFound
	cmp	[edi],ebx
	jne	.ScanNeedleLenIs5
	jmp	.Found

.NeedleLenIs4:
	dec	esi
	lodsd	;EAX=first 4 bytes of Needle
   .ScanNeedleLenIs4:
	repne	scasb
	jne	.NotFound
	cmp	[edi-1],eax
	jne	.ScanNeedleLenIs4
	jmp	.Found

.NeedleLenIs1:
	repne	scasb
	jne	.NotFound
	jmp	.Found

.NeedleLenIs2:
	mov	ah,[esi]
   .ScanNeedleLenIs2:
	repne	scasb
	jne	.NotFound
	cmp	[edi],ah
	jne	.ScanNeedleLenIs2
	jmp	.Found

.NeedleLenIs3:
	xchg	ebx,eax
	lodsw
	xchg	ebx,eax
   .ScanNeedleLenIs3:
	repne	scasb
	jne	.NotFound
	cmp	[edi],bx
	jne	.ScanNeedleLenIs3
	jmp	.Found

.NeedleLenIsLong:
	; get (needleSize-1)//4, (needleSize-1) mod 4
	dec	esi	;ESI=&(Needle[0])
	inc	ebx	;EBX=NeedleSize-4
	lodsd	;EAX=first 4 bytes of Needle
	mov	[lNeedleRemPtr4],esi
	mov	edx,ebx
	shr	ebx,2
	mov	[lNeedleRemDwords],ebx
	and	edx,3
	mov	[lNeedleRemTail],edx

	xchg	ebx,edi ;EBX=save EDI buf ptr for scasb
	xchg	edx,ecx ;EDX=save ECX counter for scasb

   .ScanNeedleLenIsLong:
	xchg	edi,ebx ;load saved buf ptr
	xchg	ecx,edx ;load saved counter
   .ScanNeedleLenIsLongJustScan:
	repne	scasb
	jne	.NotFound

	;check all 4 bytes
	cmp	[edi-1],eax
	jne	.ScanNeedleLenIsLongJustScan

	;check up to Needle's tail
	mov	ebx,edi
	mov	edx,ecx
	add	edi,3
	mov	esi,[lNeedleRemPtr4]
	mov	ecx,[lNeedleRemDwords]
	test	ecx,ecx
	jz	.ScanNeedleLenIsLongTail
	repe	cmpsd
	jne	.ScanNeedleLenIsLong
   .ScanNeedleLenIsLongTail:
	mov	ecx,[lNeedleRemTail]
	test	ecx,ecx
	jz	.ScanNeedleLenIsLongFound
	repe	cmpsb
	jne	.ScanNeedleLenIsLong
   .ScanNeedleLenIsLongFound:
	mov	edi,ebx ;FOUND!

.Found:
	dec	edi
	mov	eax,edi
	sub	eax,[hayStack]
.popOut:
	popfd
	ret
.NotFound:
	xor	eax,eax
	not	eax
	jmp	.popOut
endp

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

proc InBufRev stdcall uses ebx ecx edx esi edi, hayStack,Needle,hayStackSize,NeedleSize,StartOffsetOfLastByte
	local	lNeedleRemDwords:DWORD	;(NeedleSize-4)>>2
	local	lNeedleRemTail:DWORD	;Needle remainder byte count (NeedleSize-4) mod 4 -> (0..3)
	local	lNeedleRemPtr4:DWORD	;&Needle[4]

	pushfd

	mov	ebx,[NeedleSize]
	cmp	ebx,0
	jle	.NotFound
	mov	eax,[hayStackSize]
	dec	eax
	mov	ecx,[StartOffsetOfLastByte]
	cmp	ecx,-1
	cmovE	ecx,eax
	cmp	eax,ecx
	cmovL	ecx,eax
	sub	ecx,ebx
	mov	edi,ecx
	inc	ecx	;repetitions=min(hayStackSize-1,StartOffsetOfLastByte)-NeedleSize+2
	jle	.NotFound

	add	edi,[hayStack]	;edi=&(hayStack[min(hayStackSize-1,StartOffsetOfLastByte)-NeedleSize+1])

	;load Needle FirstByte
	mov	esi,[Needle]
	and	eax,0
	cld
	lodsb	; AL=Needle[0], keep EAX now!

	;decide on needle length
	dec	ebx
	jz	.NeedleLenIs1
	dec	ebx
	jz	.NeedleLenIs2
	dec	ebx
	jz	.NeedleLenIs3
	dec	ebx
	jz	.NeedleLenIs4
	dec	ebx
	jnz	.NeedleLenIsLong

;.NeedleLenIs5:
	xchg	eax,ebx
	lodsd		;AL=Needle[0]
	xchg	eax,ebx ;EBX=bytes 1..4 of Needle (0-based)
	std

   .ScanNeedleLenIs5:
	repne	scasb
	jne	.NotFound
	cmp	[edi+2],ebx
	jne	.ScanNeedleLenIs5
	jmp	.Found

.NeedleLenIs1:
	std
	repne	scasb
	jne	.NotFound
	jmp	.Found

.NeedleLenIs2:
	std
	mov	ah,[esi]	;AH=Needle[1]
   .ScanNeedleLenIs2:
	repne	scasb
	jne	.NotFound
	cmp	[edi+2],ah
	jne	.ScanNeedleLenIs2
	jmp	.Found

.NeedleLenIs3:
	xchg	ebx,eax
	lodsw
	xchg	ebx,eax
	std
   .ScanNeedleLenIs3:
	repne	scasb
	jne	.NotFound
	cmp	[edi+2],bx
	jne	.ScanNeedleLenIs3
	jmp	.Found

.NeedleLenIs4:
	dec	esi
	lodsd	;EAX=first 4 bytes of Needle
	std
   .ScanNeedleLenIs4:
	repne	scasb
	jne	.NotFound
	cmp	[edi+1],eax
	jne	.ScanNeedleLenIs4
	jmp	.Found

.NeedleLenIsLong:
	; get (needleSize-1)//4, (needleSize-1) mod 4
	dec	esi	;ESI=&(Needle[0])
	inc	ebx	;EBX=NeedleSize-4
	lodsd	;EAX=first 4 bytes of Needle
	mov	[lNeedleRemPtr4],esi
	mov	edx,ebx
	shr	ebx,2
	mov	[lNeedleRemDwords],ebx
	and	edx,3
	mov	[lNeedleRemTail],edx

	xchg	ebx,edi ;EBX=save EDI buf ptr for scasb
	xchg	edx,ecx ;EDX=save ECX counter for scasb

   .ScanNeedleLenIsLong:
	std
	xchg	edi,ebx ;load saved buf ptr
	xchg	ecx,edx ;load saved counter
   .ScanNeedleLenIsLongJustScan:
	repne	scasb
	jne	.NotFound

	;check all 4 bytes
	cmp	[edi+1],eax
	jne	.ScanNeedleLenIsLongJustScan

	;check up to Needle's tail
	cld
	mov	ebx,edi
	mov	edx,ecx
	add	edi,5
	mov	esi,[lNeedleRemPtr4]
	mov	ecx,[lNeedleRemDwords]
	test	ecx,ecx
	jz	.ScanNeedleLenIsLongTail
	repe	cmpsd
	jne	.ScanNeedleLenIsLong
   .ScanNeedleLenIsLongTail:
	mov	ecx,[lNeedleRemTail]
	test	ecx,ecx
	jz	.ScanNeedleLenIsLongFound
	repe	cmpsb
	jne	.ScanNeedleLenIsLong
   .ScanNeedleLenIsLongFound:
	mov	edi,ebx ;FOUND!

.Found:
	inc	edi
	mov	eax,edi
	sub	eax,[hayStack]
.popOut:
	popfd
	ret
.NotFound:
	xor	eax,eax
	not	eax
	jmp	.popOut
endp

data import

library kernel32,'KERNEL32.DLL'
import kernel32,ExitProcess,'ExitProcess'

end data
- We then use FASM to create an exe file based on the asm file.
flat assembler
http://flatassembler.net/
- (When I first tried to compile the asm to exe, it said that is was missing include files, I moved FASM.EXE to the INCLUDE folder and it worked.)
- I compiled simply by doing: Run, "%vPathFASM%" "%vPathAsm%"
- I read the contents of the exe file as a hex string. The required hex string used in the function was in the exe file, however I would not have known which bytes to copy and paste into the function.
- The resulting exe was 2048 bytes in size: 1074+224+750, the required code of 224 bytes began at 1074 bytes.

Code: Select all

[start of file]
4D5A80000100000004001000FFFF00004001000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0A240000000000000000504500004C010200EB2E005B0000000000000000E0000F010B010147000400000002000000000000002000000020000000100000000040000010000000020000010000000000000004000000000000000030000000020000396E00000200000000100000001000000000010000000000000000001000000000000000000000000E22000054000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E6461746100000018000000001000000002000000020000000000000000000000000000400000C02E636F64650000006202000000200000000400000004000000000000000000000000000020000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031313131313131313232323232313131313131323232323200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006A006A056A1368131040006800104000E81D0000006AFF6A056A1368131040006800104000E8E80000006A00FF154C224000

[hex for InBuf]
5589E583EC0C53515256579C8B5D1483FB000F8EC20000008B4D108B451829C129D9410F8EB10000008B7D0801C78B750C31C0FCAC4B742A4B742D4B74364B74144B753F93AD93F2AE0F858B000000391F75F4EB754EADF2AE757F3947FF75F7EB68F2AE7574EB628A26F2AE756C382775F8EB569366AD93F2AE755E66391F75F7EB474E43AD8975FC89DAC1EB02895DF483E2038955F887DF87D187FB87CAF2AE75373947FF75F789FB89CA83C7038B75FC8B4DF485C97404F3A775DE8B4DF885C97404F3A675D389DF4F89F82B45089D5F5E5A595BC9C2140031C0F7D0EBF0

[hex for InBufRev]
5589E583EC0C53515256579C8B5D1483FB000F8EDE0000008B4510488B4D1883F9FF0F44C839C80F4CC829D989CF410F8EC1000000037D088B750C83E000FCAC4B74224B742A4B74354B74434B754E93AD93FDF2AE0F859B000000395F0275F3E981000000FDF2AE0F8588000000EB76FD8A26F2AE757F38670275F7EB689366AD93FDF2AE756F66395F0275F6EB574EADFDF2AE756039470175F7EB494E43AD8975FC89DAC1EB02895DF483E2038955F887DF87D1FD87FB87CAF2AE753839470175F7FC89FB89CA83C7058B75FC8B4DF485C97404F3A775DC8B4DF885C97404F3A675D189DF4789F82B45089D5F5E5A595BC9C2140031C0F7D0EBF0

[end of file]
442200000000000000000000362200004C22000000000000000000000000000000000000000000004B45524E454C33322E444C4C00005422000000000000542200000000000000004578697450726F6365737300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- Two things I don't know:
- How to identify where the machine code starts/ends.
- How to convert the asm above to 64-bit.

Links:
- There are various links here. There is also a note re. converting exe to asm via OllyDbg.
InBuf function currently 32-bit only (machine code binary buffer searching) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 17#p219717
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: ASM: ASM (assembly language) to machine code via FASM (flat assembler)

Post by nnnik » 26 May 2018, 22:46

Assembly code is different for 32 and 64 bit PCs.
I tried with Assembler before - in one of my encryptions - but it didn't work out.
Also its very difficult to load the function from an .exe if you use an Assembly compiler try to get some debugging format to do the work for you.
Recommends AHK Studio

YMP2
Posts: 48
Joined: 20 Apr 2014, 06:55

Re: ASM: ASM (assembly language) to machine code via FASM (flat assembler)

Post by YMP2 » 28 May 2018, 06:35

jeeswg wrote:1 when the ASM is converted to an exe, how do I tell where the relevant machine code starts and ends
You don't have to know that. Let the exe do all the work for you. That is, you compile it, then run it and it converts the machine code of your function into hex and puts in on the clipboard or saves it to a file. That's how I've done it in GoAsm. Unfortunately I am not familiar with FASM and can't write the conversion and output code in it.

The start of the machine code will be your function's label, and you can put another label right after its end. So the exe will read all that's between them.

Post Reply

Return to “ASM”