Skip to content

Instantly share code, notes, and snippets.

@a1ext
Last active December 5, 2022 03:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a1ext/ab604cce9147c5184e466aa6ba2f19e4 to your computer and use it in GitHub Desktop.
Save a1ext/ab604cce9147c5184e466aa6ba2f19e4 to your computer and use it in GitHub Desktop.
Log and scripts used in the following video [Resolving APIs dynamically with Labeless & OllyDbg 2] https://youtu.be/Dv8YlzXJ5x8

Resolving APIs dynamically with Labeless & OllyDbg 2

Hello guys, today I'm going to demonstrate how to use Labeless to obtain API names.

The malware often use not trivial methods to call some API. In our case there is some function which receives hashes of dll and procedure name and returns API address and then calls it...

So, the easiest way to get result is to trace all references in debugger

There are some tools which we can use:

  • OllyDbg 1.10
  • OllyDbg 2.01
  • x64dbg x32
  • x64dbg x64

Unforunatelly, OllyDbg 1.10 have no easy way to trace/manage debuggee as we want. The OllyDbg 2.01 and x64dbg (x32, x64) are useful for us. So, for this case I choose OllyDbg 2.01...

.text:00401437                 push    16A7B107h
.text:0040143C                 push    0D8DF5355h
.text:00401441                 call    fmfu_GetProcAddr

The call to our function looks like sequence of push + push + call There are 14 references to this function 0x00401190

Our task is to prepare groups of addresses, which should be traced IDA PRO has useful API for us:

  • CodeRefsTo(ea, flow) - get a list of refs
  • FuncItems(start) - get a list of function items (instructions)

Let's collect needed addresses first...

Before doing anything, we should check the connectivity with the debugger in the settings view (by pressing Test connection) Ok, connected successfully, then we should check the remote module base (base address, where the target module (exe, dll) is mapped) 0x00F00000 - the remote base, it's different with the IDA's base (0x400000). We should put this base in the settings view...

There are some functions, named by IDA using signatures, let's propagate names (Labels) to the debugger Cool :)

In the main() function there are few calls to LoadLibrary. let's trace to initialize... Ok

Ok, now we have trace points, but we need to convert local base to remote one... Ok, we should transfer the trace points to debugger, the __extern__ variable will help us do this.

__extern__ will be a list of dictionaries with one field eas

Ok, we may see the addresses in the output from debuggee

The next step is to trace over all trace points and for each try to resolve API name from EAX register after each call

The OllyDbg 2.01 API has few useful functions:

  • SetEip() in threads module
  • StepOver() in utils module
  • FindnameW(ulong addr, int type, wchar_t * name, int nname) -> int (in the api module)
  • Findmodule(ulong addr) -> t_module (in the api module)
  • GetEax() in threads module

Cool, we have traced over all trace points and got the APIs Now, we should transfer them back to the IDA using __result__ variable and then propagate the names of APIs as comments...

Let's try to add the module name of API before to be like 'kernel32.OpenProcess' Ok

set_cmt API will help us to set comment set_cmt(ea, comm, rptble) One more thing to do - convert remote EA to local

we have unicode string name, need to convert it to ansi Ok, that's better

IDA-side script

# let's prepare trace points...
from idautils import CodeRefsTo, FuncItems

fn_ea = 0x00401190
base = 0x400000
remote = 0x00F00000

refs = list(CodeRefsTo(fn_ea, 0))
print 'found %d refs' % len(refs)

__extern__ = list()

for ref in refs:
	items = list(FuncItems(ref))
	# there we have all EAs (addresses) of function where ref is

	# use only tree of them (ea of push, ea of push, ea of call)
	idx = items.index(ref)
	EAs = items[idx - 2: idx + 1]

	EAs = [remote + (ea - base) for ea in EAs]
	# for example ea = 0x401324
	# the remote ea = F00000 + (401324 - 400000)

	# print [hex(int(ea)) for ea in EAs]
	__extern__.append({'eas': EAs})

Olly2-side script

# --- olly2
import ctypes as C

buff = C.create_unicode_buffer(ll.api.TEXTLEN)

__result__ = list()

for item in __extern__:
	EAs = item['eas']

	for ea in EAs:
		ll.threads.SetEip(ea)
		ll.utils.StepOver()

	# at this point we have the context after 'call' insn
	eax = ll.threads.GetEax()

	# now we should try to resolve the name of API in EAX
	if ll.api.FindnameW(eax, ll.api.NM_EXPORT, buff, ll.api.TEXTLEN) > 0:
		name = buff.value.replace('\0', '')
		module = ll.api.Findmodule(eax)
		if module:
			name = '%s.%s' % (module.modname.replace('\0', '').lower(), name)
		print 'eax: %#08x %s' % (eax, name)
		__result__.append({'ea': EAs[-1], 'name': name})
	else:
		print '[-] unable to get name for %#08x' % eax
		__result__.append({'ea': EAs[-1], 'name': None})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment