Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

telling data from code when disassembling

52 views
Skip to first unread message

cg_chas

unread,
May 2, 2012, 4:29:47 PM5/2/12
to
I am writing a disassembler and am considering what ways there might be to
identify data bytes and data segments.

For example, consider this basic program:

..MODEL small
.STACK 100h
.DATA
HelloMessage DB 'Hello, world',13,10,'$'
.CODE
mov ax,@data
mov ds,ax ;set DS to point to the data segment
mov ah,9 ;DOS print string function
mov dx,OFFSET HelloMessage ;point to "Hello, world"
int 21h ;display "Hello, world"
mov ah,4ch ;DOS terminate program function
int 21h ;terminate the program
END
---------------------------------------------
Here is the MZ header info.

DOS File Size 21Fh ( 543. )
Load Image Size 001Fh ( 31. )
Relocation Table entry count 0001h ( 1. )
Relocation Table address 003Eh ( 62. )
Size of header record (in paragraphs) 0020h ( 32. )
Minimum Memory Requirement (in paragraphs) 0011h ( 17. )
Maximum Memory Requirement (in paragraphs) FFFFh ( 65535. )
File load checksum 0000h ( 0. )
Overlay Number 0000h ( 0. )

Initial Stack Segment (SS:SP) 0002:0100
Program Entry Point (CS:IP) 0000:0000

Relocation Locations (1 Entry)

0000:0001

When looking at a the EXE file in a hex viewer I can see that the code starts at
the absolute file position of 200h and relative position of 0000:0000.

Similarly, I can see that the data string is at the absolute file position of
210h.

This seems right as the data is 1 segment offset from the code just as the
relocation table's entry is 0000:0001.

When I decode the code section I can see that the very first instruction moves
the relative location value (I realized this gets fixed up at runtime) of the
first relocation entry into the AX register and then afterwards into the DS
register. I haven't looked at another other implementations of a dissassemblers
yet because I wanted try this on my own.

Right now I am considering building a map using a multi-pass analysis that first
attempts to identify data by examining the values being passed into registers
including relative addresses.

Can anybody suggest some logic for identifying data bytes and segments? Any
hints would be appreciated.

Here's the code section decoded.

00000200 B80100 mov ax,0001h ; MOV immediate AX
00000203 8ED8 mov ds,ax ; MOV Sw,Ew
00000205 B409 mov ah,09h ; MOV immediate AH
00000207 BA0000 mov dx,0000h ; MOV immediate DX
0000020A CD21 int 21h ; INT Ib
0000020C B44C mov ah,4ch ; MOV immediate AH
0000020E CD21 int 21h ; INT Ib
00000210 48 unhandled byte
00000211 656C unhandled byte
00000213 6C unhandled byte
00000214 6F unhandled byte
00000215 2C unhandled byte
00000216 20 unhandled byte
00000217 77 unhandled byte
00000218 6F unhandled byte
00000219 72 unhandled byte
0000021A 6C unhandled byte
0000021B 640D unhandled byte
0000021D 0A unhandled byte
0000021E 24 unhandled byte

Chas

Robert Wessel

unread,
May 2, 2012, 11:43:35 PM5/2/12
to
Basically, it's impossible.

More practically, most disassembler use a combination of heuristics
and interaction with the user to disassemble code. For example, if
you look through code, following branches, you can often identify much
executable code, but it's not 100% reliable (for example, some code
might be reachable only via a table of pointers). Likewise, if you
see what looks like text, it's probably data. Again, not anywhere
near 100% reliable (is that an "ABCD" or a series of increment
instructions). Most disassemblers also look for common idioms
(strings ending with a NUL, setups to system/library calls, common
function prologs and epilogs, etc.) to identify parts of the program.

Worse, it's entirely possible to execute out of a data segment, or
read data out of a code segment. And data and code may easily
alternate in a single segment. Even worse, it's possible to treat a
series of bytes as *both* code and data.

Some of the more advanced executable (or object) formats can provide
considerably more information than an MZ does, and can provide
considerably better hints (again, not 100% reliable) about what is
program text and what's data.

The best disassemblers use an iterative process, and allow the user to
identify data and code (or correct incorrect identifications). This
usually happens as the user adds labels, structure definitions,
comments and whatnot in the same iterative process. With each
iteration of input from the user, the disassembly repeats, and becomes
(hopefully) closer and closer to correct and clear.

Robert Wessel

unread,
May 2, 2012, 11:49:45 PM5/2/12
to
On Wed, 02 May 2012 16:29:47 -0400, cg_chas
<cg_...@nospicedham.hotmail.com> wrote:

>I am writing a disassembler and am considering what ways there might be to
>identify data bytes and data segments.
>(...)


I should add that some executable formats *do* provide enough
information to accurately identify code and data. For example the
usual executable formats used to store compiled Java byte code clearly
define all functions and their inputs/outputs.

wolfgang kern

unread,
May 3, 2012, 6:43:23 AM5/3/12
to

"cg_chas" wrote:

>I am writing a disassembler and am considering what ways there might be to
> identify data bytes and data segments.

...
an MZ-exe-loader will add the segment (where it is actually loaded to)
to all relocation items.

> ... yet because I wanted try this on my own.
> Right now I am considering building a map using a multi-pass analysis
> that first attempts to identify data by examining the values being
> passed into registers including relative addresses.

It's not easy to determine if a passed value mean data- or code-label.
Me too work since long on an automated code-analyser ...
but I mainly use the faster manually way to figure on foreign code.

> Can anybody suggest some logic for identifying data bytes and segments?
> Any hints would be appreciated.

Full analysis need a complete static tracking of all code-parts and
all values of registers, stack- and accessed memory contents beside
full emulation/calculation for all CPU-instructions.
Many tables and passes are required, and it needs loop detection,
(worst case) detection of SMC with shared code/data ...
And not to forget that it need to know in deep detail how OS-ABI-
and BIOS-calls will modify registers (some guessing required here),
or even more worse are external referenced code-parts (.dll).

this method usually needs several thousand times more memory than
the code under test will occupy.


A restricted solution could just check on code flow and so isolate
data blocks, but this may let too many questions left open then.

The other possibility would be a dynamical check with a debugger,
but except for OllyDBG, I dont know one which can load relocated
and allow stepping through from the very start (but it may exist).

> Here's the code section decoded.
>
> 00000200 B80100 mov ax,0001h ; MOV immediate AX

this line above would show another immediate value when executed
0200 B8
0201 01 ;this is pointed to by the relative 0000:0001
0202 00

not a problem as long everything is relative to a known point ...
but what could we do if such a relocated ("guess where it actual is")
value become modified ? ie: by add/shift/mul ... So I learned to use
a dummy load segment for static analysis ie: 0x0800.

__
wolfgang


cg_chas

unread,
May 3, 2012, 10:43:41 AM5/3/12
to
I could not have hoped for a more helpful response. Thank you.

I can see how an iterative process involving the user for data and code
identification is helpful.

While I continue to grasp the scope of the analysis process, I've limited my
project to 16-bit programs and decoding x86 instructions with that size
assumption where applicable.

For my purposes I will probably go ahead with following branches and decoding
all the instructions I find along the way and in addition, supply an input
file with data locations, relative and absolute, that will allow the
disassembler to treat bytes as data.

King Harold

unread,
May 3, 2012, 2:04:22 PM5/3/12
to
While there are some easy cases of "obviously code", there are a lot
more
cases where it's unclear (1) whether some bytes are code or data, or
they
are both (2), or they might even be multiple different kinds of code
(3), or
even multiple kinds of code /and/ data at the same time.
1 a well-known (but not very effective) anti-reverse-engineering trick
is
inserting a conditional branch to some data, with a condition will
never be
true but such that that fact is hard to statically determine. The data
is
just data, but it looks like (gibberish) code. Or the code might use
the
"data follows callsite"-pattern, where the callee pops the "return
address"
to read the data (that immediately follows the call instruction), to
then
return /after/ the data. Where it actually returns to is very hard to
figure
out statically, and no disassembler I know of even tries.
2 self-modifying code for example, but that already causes worse
problems
(the disassembled code will not match the executed code anyway). Or
data
might be "hidden" in the operands of nops (0F 01 where the data is in
the
ModR\M and\or offset) and conditional jumps (the offset bytes) that
are
never taken.
3 it's possible to carefully design your code such that it still makes
sense
when entered from a second or even third entry point, in such a way
that the
bytes are decoded into a different (not just shorter) sequence of
instructions. This is also a generalization of 2, with code bytes
encoded in
unused operands instead of data bytes.

Some of these things actually happen, even in code that isn't
specifically
meant to be hard to disassemble.

PS: somehow I can't post the regular way and I forgot where to mail
posts manually, could someone please remind me of the address?

s_dub...@nospicedham.yahoo.com

unread,
May 3, 2012, 10:07:02 PM5/3/12
to
> disassembler to treat bytes as data.- Hide quoted text -
>
> - Show quoted text -

I'd start by looking for RET's to block off code from other
subroutines, or the start of data. Then table the next address to
matchup with decoded CALL's. Sure there maybe false RET's in data,
but after a time this can be spotted, you've indicated x86 16 bit code
so this will be easier. After enough iterations, what is left is the
data. Then profile for INT's, this tells alot about code intentions.

There are two approaches, decoding statically like the above hints at,
and dynamically, by incorporating a loader in your dissassembler (in
the case for .exe) to expand and do the fixups like a runtime image.

If the target is compacted code, such as that done by PKLite
obfuscation, I give up. But others have uncompressed such before
dissassembly, but it seems to me that the second approach could be
helpful.

Data references on cpu's < 386 are simpler than later cpu's as I
recall. You about have to have the code blocked off, and data areas
identified, before you can make sense of complex data references. It
helps to identify the stack area(s) early in the process.

There are different reasons for dissassembly, not all reasons involve
a complete source code reconstruction. Sometimes info about the ABI
used is needed, or if a HLL -block structured language was used, or if
code compaction is involved, or undocumented INT's used, etc.

hth.

Steve

hopcode

unread,
May 3, 2012, 10:33:21 PM5/3/12
to
Il 04.05.2012 04:07, s_dub...@nospicedham.yahoo.com ha scritto:
> I'd start by looking for RET's to block off code from other
> subroutines, or the start of data. Then table the next address to
> matchup with decoded CALL's. Sure there maybe false RET's in data,
> but after a time this can be spotted, you've indicated x86 16 bit code
> so this will be easier. After enough iterations, what is left is the
> data. Then profile for INT's, this tells alot about code intentions.

yep, and the LDE should work like a cursor essentially top->bottom
bottom->top and locally. but keep in mind that there are a lot of
other methods to achieve a good disassembly. i suggest to start
thinking the disassembler from now, i.e. by design, as an interactive
tool, in wich user (supposedly a human being) may suggest
the disassembler to interpret local data as raw or as code or other.
and warning: this is to avoid to reinvent the wheel. ;-)

Cheers,


--
.:mrk[hopcode]
.:x64lab:.
group http://groups.google.com/group/x64lab
site http://sites.google.com/site/x64lab

Terje Mathisen

unread,
May 4, 2012, 3:19:41 AM5/4/12
to
cg_chas wrote:
> While I continue to grasp the scope of the analysis process, I've limited my
> project to 16-bit programs and decoding x86 instructions with that size
> assumption where applicable.
>
> For my purposes I will probably go ahead with following branches and decoding
> all the instructions I find along the way and in addition, supply an input
> file with data locations, relative and absolute, that will allow the
> disassembler to treat bytes as data.

Are you planning to use this tool to disassemble virus/worm code?

In that case the job is pretty much impossible, the best you can
probably do is to have a hybrid process where you start by disassembling
everything as code, then add a second layer for any parts identified as
data.

The next step is interactive: Allow the operator to point at any given
byte boundary/range and toggle the status: This is Data/Code.

You save this work alongside the project so that you can come back and
refine the results!

For really obfuscated/compressed/encrypted code you need the capability
to let the code run for a short while, in order to let the decoding
stage do its work, this is made significantly harder because virus/worm
code tends to work in such a way that simply single-stepping through it
doesn't work.

Terje

--
- <Terje.Mathisen at tmsw.no>
"almost all programming can be viewed as an exercise in caching"

cg_chas

unread,
May 4, 2012, 6:00:48 AM5/4/12
to
On Fri, 04 May 2012 09:19:41 +0200, Terje Mathisen <"terje.mathisen at
tmsw.no"@giganews.com> wrote:

>cg_chas wrote:
>> While I continue to grasp the scope of the analysis process, I've limited my
>> project to 16-bit programs and decoding x86 instructions with that size
>> assumption where applicable.
>>
>> For my purposes I will probably go ahead with following branches and decoding
>> all the instructions I find along the way and in addition, supply an input
>> file with data locations, relative and absolute, that will allow the
>> disassembler to treat bytes as data.
>
>Are you planning to use this tool to disassemble virus/worm code?

Not at all. The main incentive for the tool is to gain better understanding of
static code analysis by developing the tool itself.

>
>In that case the job is pretty much impossible, the best you can
>probably do is to have a hybrid process where you start by disassembling
>everything as code, then add a second layer for any parts identified as
>data.
>
>The next step is interactive: Allow the operator to point at any given
>byte boundary/range and toggle the status: This is Data/Code.

This much I understand.

>
>You save this work alongside the project so that you can come back and
>refine the results!

Makes perfect sense.

>
>For really obfuscated/compressed/encrypted code you need the capability
>to let the code run for a short while, in order to let the decoding
>stage do its work, this is made significantly harder because virus/worm
>code tends to work in such a way that simply single-stepping through it
>doesn't work.
>

I really had not planned on doing any runtime analysis. I do not know if this
makes disassembly impossible.

>Terje

Thank you.

hopcode

unread,
May 4, 2012, 6:46:01 AM5/4/12
to
worm ? virus ?, that's the interesting side of the task.
also, no/weak runtime analysis means no creativity in disassembly,
example: PWDASM,BORG too!. good runtime analysis means good tools,like
in order of power, SOFTICE,IDA,OLLYDBG.
as suggested, *layers* are a must by disassembly. analysis on the
opcoded Mod/RM SIB byte gives hints on the data/addresses/immediates to
be analysed in one or more ways, or even statistically.
0 new messages