I am having trouble understanding working with structs in assembly from disassembly. can you please explain the code below. Disassembly below:
Source code:
Reverse Engineering Stack Exchange is a question and answer site for researchers and developers who explore the principles of a system through analysis of its structure, function, and operation. It only takes a minute to sign up.
Sign up to join this communityI am having trouble understanding working with structs in assembly from disassembly. can you please explain the code below. Disassembly below:
Source code:
This is called inlining. strcpy was substituted by mov instructions, because compiler decided that in such way code will run faster.
This is manual string assignment. Notice how all DWORDs are composed of printable characters? hit r in IDA to convert the hex values to string characters.
This code seems to be assigning strings into different stack addresses, 4 characters at a time. This is a common optimization over calling a function that iterates over strings byte by byte.
As others pointed out, this optimization is usually called "loop unrolling" as it unrolls several iterations of a loop into sequential assembly, however I often find that type of direct string characters assignment to be more commonly the output of a code similar to the following:
char str[] = "this is a string";
memmove
(or strcpy
).
char a[] = "this is a string";
, no?
the optimizations mantras of the answers got me intrigued so i thought i'll check what actual optimizations are being done on the code (OP appears to be beginner and i doubted OP would be capable of optimizing at all )
it appears this is default compilation without optimization enabled in gcc
gcc version 8.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)
visual studio 2017 15.7.3 doesn't do this kind of code at all whether optimized or not
with optimizations disabled vs resorts to calling strcpy ()
:\>cl /nologo /Zi booky.cpp
booky.cpp
:\>cdb -c ".lines;uf booky!main;q" booky.exe | grep -E " 15| 16| 17"
15 001c6b35 6850be2100 push offset booky!__xt_z+0x108 (0021be50)
15 001c6b3a 8d8530ffffff lea eax,[ebp-0D0h]
15 001c6b40 50 push eax
15 001c6b41 e86bb2ffff call booky!ILT+3500(_strcpy) (001c1db1) <<<<
15 001c6b46 83c408 add esp,8
16 001c6b49 685cbe2100 push offset booky!__xt_z+0x114 (0021be5c)
16 001c6b4e 8d8d62ffffff lea ecx,[ebp-9Eh]
16 001c6b54 51 push ecx
16 001c6b55 e857b2ffff call booky!ILT+3500(_strcpy) (001c1db1) <<<<
16 001c6b5a 83c408 add esp,8
17 001c6b5d 6868be2100 push offset booky!__xt_z+0x120 (0021be68)
17 001c6b62 8d5594 lea edx,[ebp-6Ch]
17 001c6b65 52 push edx
17 001c6b66 e846b2ffff call booky!ILT+3500(_strcpy) (001c1db1) <<<<
17 001c6b6b 83c408 add esp,8
optimising for space (O2) or full optimisations (Ox) vs2017 resorts to using sse2 moves
:\>cl /Zi /O2 booky.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.14.26430 for x86
:\>cdb -c ".lines;uf booky!main;q" booky.exe | grep -E " 15| 16| 17"
15 00f7aae4 a158befc00 mov eax,dword ptr [booky!`string'+0x8 (00fcbe58)]
:\>cdb -c ".lines;uf booky!main;q" booky.exe | grep -A 5 -E " 15| 16| 17"
15 00f7aae4 a158befc00 mov eax,dword ptr [booky!`string'+0x8 (00fcbe58)]
19 00f7aae9 b933000000 mov ecx,33h
19 00f7aaee f30f7e0550befc00 movq xmm0,mmword ptr [booky!`string' (00fcbe50)] <<<<<<<<
19 00f7aaf6 89442408 mov dword ptr [esp+8],eax
19 00f7aafa 0fb60568befc00 movzx eax,byte ptr [booky!`string'+0x8 (00fcbe68)] <<<<<<<
19 00f7ab01 8844243a mov byte ptr [esp+3Ah],al
:\>cl /Zi /Ox booky.cpp
:\>cdb -c ".lines;uf booky!main;q" booky.exe | grep -A 5 -E " 15| 16| 17"
15 0138ac74 a198e83d01 mov eax,dword ptr [booky!__newctype+0x308 (013de898)]
19 0138ac79 b933000000 mov ecx,33h
19 0138ac7e f30f7e0590e83d01 movq xmm0,mmword ptr [booky!__newctype+0x300 (013de890)]
19 0138ac86 89442408 mov dword ptr [esp+8],eax
19 0138ac8a 0fb605a4e83d01 movzx eax,byte ptr [booky!__newctype+0x314 (013de8a4)]
19 0138ac91 8844243a mov byte ptr [esp+3Ah],al
mingw gcc 8.10 seems to break the strings into dwords even when explicitly disabling optimization with O0
:>e:\mingw\mingw-w64.bat
:>ls
booky.cpp
:>gcc -g -O0 booky.cpp
g++ -g -O0 also disassembles to same construct as below
:\>objdump -d --start-address 0x4015c0 --stop-address 0x401600 -M intel a.exe
a.exe: file format pei-i386
Disassembly of section .text:
004015c0 <_main>:
4015c0: 55 push ebp
4015c1: 89 e5 mov ebp,esp
4015c3: 57 push edi
4015c4: 56 push esi
4015c5: 53 push ebx
4015c6: 83 e4 f0 and esp,0xfffffff0
4015c9: 81 ec 70 02 00 00 sub esp,0x270
4015cf: e8 ec 01 00 00 call 4017c0 <___main>
4015d4: 8d 84 24 a4 01 00 00 lea eax,[esp+0x1a4]
4015db: c7 00 50 72 6f 67 mov DWORD PTR [eax],0x676f7250
4015e1: c7 40 04 72 61 6d 6d mov DWORD PTR [eax+0x4],0x6d6d6172
4015e8: c7 40 08 69 6e 67 00 mov DWORD PTR [eax+0x8],0x676e69
4015ef: 8d 84 24 a4 01 00 00 lea eax,[esp+0x1a4]
4015f6: 83 c0 32 add eax,0x32
4015f9: c7 00 4e 75 68 61 mov DWORD PTR [eax],0x6168754e
4015ff: c7 .byte 0xc7
to disable this -fno-builtin-strcpy needs to be passed to gcc in commandline if -fno-builtin-strcpy is passed gcc wont chunk the strings even when compiled with -O3 optimization
compilation
:\>ls
booky.cpp
:\>g++ -O3 -g -fno-builtin-strcpy -o booky.exe booky.cpp
:\>ls
booky.cpp booky.exe
execution
:\>booky.exe
ProgrammingTelecom Billing
disassembly
:\>objdump -d --start-address=0x402670 --stop-address=0x4026b0 -M intel booky.exe
booky.exe: file format pei-i386
Disassembly of section .text:
00402670 <_main>:
402670: 55 push ebp
402671: 89 e5 mov ebp,esp
402673: 57 push edi
402674: 56 push esi
402675: 53 push ebx
402676: 83 e4 f0 and esp,0xfffffff0
402679: 81 ec 80 02 00 00 sub esp,0x280
40267f: e8 ec ef ff ff call 401670 <___main>
402684: 8d 74 24 1c lea esi,[esp+0x1c]
402688: c7 44 24 04 44 40 40 mov DWORD PTR [esp+0x4],0x404044
40268f: 00
402690: 8d bc 24 b4 01 00 00 lea edi,[esp+0x1b4]
402697: 89 34 24 mov DWORD PTR [esp],esi
40269a: 8d 9c 24 b4 01 00 00 lea ebx,[esp+0x1b4]
4026a1: e8 ee fe ff ff call 402594 <_strcpy>
4026a6: 8d 44 24 4e lea eax,[esp+0x4e]
4026aa: c7 .byte 0xc7
4026ab: 44 inc esp
4026ac: 24 04 and al,0x4
4026ae: 50 push eax
4026af: 40 inc eax
an analysed output from radare2
| ; JMP XREF from 0x0040262f (sym.___getmainargs)
| 0x00402671 89e5 mov ebp, esp
| 0x00402673 57 push edi
| 0x00402674 56 push esi
| 0x00402675 53 push ebx
| 0x00402676 83e4f0 and esp, 0xfffffff0
| 0x00402679 81ec80020000 sub esp, 0x280
| 0x0040267f e8ecefffff call sym.___main
| 0x00402684 8d74241c lea esi, [local_1ch] ; 0x1c ; 28
| 0x00402688 c74424044440. mov dword [local_4h], str.Programming ;
[0x404044:4]=0x676f7250 ; "Programming" ; const char * src
| 0x00402690 8dbc24b40100. lea edi, [local_1b4h] ; 0x1b4 ; 436
| 0x00402697 893424 mov dword [esp], esi ; char * dest
| 0x0040269a 8d9c24b40100. lea ebx, [local_1b4h] ; 0x1b4 ; 436
| 0x004026a1 e8eefeffff call sym._strcpy ;
char *strcpy(char *dest, const char *src)
| 0x004026a6 8d44244e lea eax, [local_4eh] ; 0x4e ; 'N' ; 78
| 0x004026aa c74424045040. mov dword [local_4h], str.Nuha_Ali ;
[0x404050:4]=0x6168754e ; "Nuha Ali" ; const char * src
| 0x004026b2 890424 mov dword [esp], eax ; char * dest
| 0x004026b5 e8dafeffff call sym._strcpy ;
char *strcpy(char *dest, const char *src)